aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer/bounds-checking.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/analyzer/bounds-checking.cc')
-rw-r--r--gcc/analyzer/bounds-checking.cc273
1 files changed, 211 insertions, 62 deletions
diff --git a/gcc/analyzer/bounds-checking.cc b/gcc/analyzer/bounds-checking.cc
index a5692cf..5e8de9a 100644
--- a/gcc/analyzer/bounds-checking.cc
+++ b/gcc/analyzer/bounds-checking.cc
@@ -19,21 +19,25 @@ along with GCC; see the file COPYING3. If not see
#include "config.h"
#define INCLUDE_MEMORY
+#define INCLUDE_VECTOR
#include "system.h"
#include "coretypes.h"
#include "make-unique.h"
#include "tree.h"
#include "function.h"
#include "basic-block.h"
+#include "intl.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "diagnostic-core.h"
#include "diagnostic-metadata.h"
+#include "diagnostic-diagram.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "analyzer/region-model.h"
#include "analyzer/checker-event.h"
#include "analyzer/checker-path.h"
+#include "analyzer/access-diagram.h"
#if ENABLE_ANALYZER
@@ -44,8 +48,35 @@ namespace ana {
class out_of_bounds : public pending_diagnostic
{
public:
- out_of_bounds (const region *reg, tree diag_arg)
- : m_reg (reg), m_diag_arg (diag_arg)
+ class oob_region_creation_event_capacity : public region_creation_event_capacity
+ {
+ public:
+ oob_region_creation_event_capacity (tree capacity,
+ const event_loc_info &loc_info,
+ out_of_bounds &oob)
+ : region_creation_event_capacity (capacity,
+ loc_info),
+ m_oob (oob)
+ {
+ }
+ void prepare_for_emission (checker_path *path,
+ pending_diagnostic *pd,
+ diagnostic_event_id_t emission_id) override
+ {
+ region_creation_event_capacity::prepare_for_emission (path,
+ pd,
+ emission_id);
+ m_oob.m_region_creation_event_id = emission_id;
+ }
+ private:
+ out_of_bounds &m_oob;
+ };
+
+ out_of_bounds (const region_model &model,
+ const region *reg,
+ tree diag_arg,
+ const svalue *sval_hint)
+ : m_model (model), m_reg (reg), m_diag_arg (diag_arg), m_sval_hint (sval_hint)
{}
bool subclass_equal_p (const pending_diagnostic &base_other) const override
@@ -63,7 +94,7 @@ public:
void mark_interesting_stuff (interesting_t *interest) final override
{
- interest->add_region_creation (m_reg);
+ interest->add_region_creation (m_reg->get_base_region ());
}
void add_region_creation_events (const region *,
@@ -75,15 +106,25 @@ public:
so we don't need an event for that. */
if (capacity)
emission_path.add_event
- (make_unique<region_creation_event_capacity> (capacity, loc_info));
+ (make_unique<oob_region_creation_event_capacity> (capacity, loc_info,
+ *this));
}
+ virtual enum access_direction get_dir () const = 0;
+
protected:
enum memory_space get_memory_space () const
{
return m_reg->get_memory_space ();
}
+ void
+ maybe_show_notes (location_t loc, logger *logger) const
+ {
+ maybe_describe_array_bounds (loc);
+ maybe_show_diagram (logger);
+ }
+
/* Potentially add a note about valid ways to index this array, such
as (given "int arr[10];"):
note: valid subscripts for 'arr' are '[0]' to '[9]'
@@ -112,8 +153,49 @@ protected:
m_diag_arg, min_idx, max_idx);
}
+ void
+ maybe_show_diagram (logger *logger) const
+ {
+ access_operation op (m_model, get_dir (), *m_reg, m_sval_hint);
+
+ /* Don't attempt to make a diagram if there's no valid way of
+ accessing the base region (e.g. a 0-element array). */
+ if (op.get_valid_bits ().empty_p ())
+ return;
+
+ if (const text_art::theme *theme = global_dc->m_diagrams.m_theme)
+ {
+ text_art::style_manager sm;
+ text_art::canvas canvas (make_access_diagram (op, sm, *theme, logger));
+ if (canvas.get_size ().w == 0 && canvas.get_size ().h == 0)
+ {
+ /* In lieu of exceptions, return a zero-sized diagram if there's
+ a problem. Give up if that's happened. */
+ return;
+ }
+ diagnostic_diagram diagram
+ (canvas,
+ /* Alt text. */
+ _("Diagram visualizing the predicted out-of-bounds access"));
+ diagnostic_emit_diagram (global_dc, diagram);
+ }
+ }
+
+ text_art::canvas
+ make_access_diagram (const access_operation &op,
+ text_art::style_manager &sm,
+ const text_art::theme &theme,
+ logger *logger) const
+ {
+ access_diagram d (op, m_region_creation_event_id, sm, theme, logger);
+ return d.to_canvas (sm);
+ }
+
+ region_model m_model;
const region *m_reg;
tree m_diag_arg;
+ const svalue *m_sval_hint;
+ diagnostic_event_id_t m_region_creation_event_id;
};
/* Abstract base class for all out-of-bounds warnings where the
@@ -122,9 +204,11 @@ protected:
class concrete_out_of_bounds : public out_of_bounds
{
public:
- concrete_out_of_bounds (const region *reg, tree diag_arg,
- byte_range out_of_bounds_range)
- : out_of_bounds (reg, diag_arg),
+ concrete_out_of_bounds (const region_model &model,
+ const region *reg, tree diag_arg,
+ byte_range out_of_bounds_range,
+ const svalue *sval_hint)
+ : out_of_bounds (model, reg, diag_arg, sval_hint),
m_out_of_bounds_range (out_of_bounds_range)
{}
@@ -146,9 +230,12 @@ protected:
class concrete_past_the_end : public concrete_out_of_bounds
{
public:
- concrete_past_the_end (const region *reg, tree diag_arg, byte_range range,
- tree byte_bound)
- : concrete_out_of_bounds (reg, diag_arg, range), m_byte_bound (byte_bound)
+ concrete_past_the_end (const region_model &model,
+ const region *reg, tree diag_arg, byte_range range,
+ tree byte_bound,
+ const svalue *sval_hint)
+ : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint),
+ m_byte_bound (byte_bound)
{}
bool
@@ -168,7 +255,9 @@ public:
{
if (m_byte_bound && TREE_CODE (m_byte_bound) == INTEGER_CST)
emission_path.add_event
- (make_unique<region_creation_event_capacity> (m_byte_bound, loc_info));
+ (make_unique<oob_region_creation_event_capacity> (m_byte_bound,
+ loc_info,
+ *this));
}
protected:
@@ -180,9 +269,11 @@ protected:
class concrete_buffer_overflow : public concrete_past_the_end
{
public:
- concrete_buffer_overflow (const region *reg, tree diag_arg,
- byte_range range, tree byte_bound)
- : concrete_past_the_end (reg, diag_arg, range, byte_bound)
+ concrete_buffer_overflow (const region_model &model,
+ const region *reg, tree diag_arg,
+ byte_range range, tree byte_bound,
+ const svalue *sval_hint)
+ : concrete_past_the_end (model, reg, diag_arg, range, byte_bound, sval_hint)
{}
const char *get_kind () const final override
@@ -190,7 +281,8 @@ public:
return "concrete_buffer_overflow";
}
- bool emit (rich_location *rich_loc) final override
+ bool emit (rich_location *rich_loc,
+ logger *logger) final override
{
diagnostic_metadata m;
bool warned;
@@ -238,7 +330,7 @@ public:
"write to beyond the end of %qE",
m_diag_arg);
- maybe_describe_array_bounds (rich_loc->get_loc ());
+ maybe_show_notes (rich_loc->get_loc (), logger);
}
return warned;
@@ -276,6 +368,8 @@ public:
start_buf, end_buf, m_byte_bound);
}
}
+
+ enum access_direction get_dir () const final override { return DIR_WRITE; }
};
/* Concrete subclass to complain about buffer over-reads. */
@@ -283,9 +377,10 @@ public:
class concrete_buffer_over_read : public concrete_past_the_end
{
public:
- concrete_buffer_over_read (const region *reg, tree diag_arg,
+ concrete_buffer_over_read (const region_model &model,
+ const region *reg, tree diag_arg,
byte_range range, tree byte_bound)
- : concrete_past_the_end (reg, diag_arg, range, byte_bound)
+ : concrete_past_the_end (model, reg, diag_arg, range, byte_bound, NULL)
{}
const char *get_kind () const final override
@@ -293,7 +388,7 @@ public:
return "concrete_buffer_over_read";
}
- bool emit (rich_location *rich_loc) final override
+ bool emit (rich_location *rich_loc, logger *logger) final override
{
diagnostic_metadata m;
bool warned;
@@ -339,7 +434,7 @@ public:
"read from after the end of %qE",
m_diag_arg);
- maybe_describe_array_bounds (rich_loc->get_loc ());
+ maybe_show_notes (rich_loc->get_loc (), logger);
}
return warned;
@@ -377,6 +472,8 @@ public:
start_buf, end_buf, m_byte_bound);
}
}
+
+ enum access_direction get_dir () const final override { return DIR_READ; }
};
/* Concrete subclass to complain about buffer underwrites. */
@@ -384,9 +481,11 @@ public:
class concrete_buffer_underwrite : public concrete_out_of_bounds
{
public:
- concrete_buffer_underwrite (const region *reg, tree diag_arg,
- byte_range range)
- : concrete_out_of_bounds (reg, diag_arg, range)
+ concrete_buffer_underwrite (const region_model &model,
+ const region *reg, tree diag_arg,
+ byte_range range,
+ const svalue *sval_hint)
+ : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint)
{}
const char *get_kind () const final override
@@ -394,7 +493,7 @@ public:
return "concrete_buffer_underwrite";
}
- bool emit (rich_location *rich_loc) final override
+ bool emit (rich_location *rich_loc, logger *logger) final override
{
diagnostic_metadata m;
bool warned;
@@ -415,7 +514,7 @@ public:
break;
}
if (warned)
- maybe_describe_array_bounds (rich_loc->get_loc ());
+ maybe_show_notes (rich_loc->get_loc (), logger);
return warned;
}
@@ -449,6 +548,8 @@ public:
start_buf, end_buf);;
}
}
+
+ enum access_direction get_dir () const final override { return DIR_WRITE; }
};
/* Concrete subclass to complain about buffer under-reads. */
@@ -456,9 +557,10 @@ public:
class concrete_buffer_under_read : public concrete_out_of_bounds
{
public:
- concrete_buffer_under_read (const region *reg, tree diag_arg,
+ concrete_buffer_under_read (const region_model &model,
+ const region *reg, tree diag_arg,
byte_range range)
- : concrete_out_of_bounds (reg, diag_arg, range)
+ : concrete_out_of_bounds (model, reg, diag_arg, range, NULL)
{}
const char *get_kind () const final override
@@ -466,7 +568,7 @@ public:
return "concrete_buffer_under_read";
}
- bool emit (rich_location *rich_loc) final override
+ bool emit (rich_location *rich_loc, logger *logger) final override
{
diagnostic_metadata m;
bool warned;
@@ -487,7 +589,7 @@ public:
break;
}
if (warned)
- maybe_describe_array_bounds (rich_loc->get_loc ());
+ maybe_show_notes (rich_loc->get_loc (), logger);
return warned;
}
@@ -521,6 +623,8 @@ public:
start_buf, end_buf);;
}
}
+
+ enum access_direction get_dir () const final override { return DIR_READ; }
};
/* Abstract class to complain about out-of-bounds read/writes where
@@ -529,9 +633,11 @@ public:
class symbolic_past_the_end : public out_of_bounds
{
public:
- symbolic_past_the_end (const region *reg, tree diag_arg, tree offset,
- tree num_bytes, tree capacity)
- : out_of_bounds (reg, diag_arg),
+ symbolic_past_the_end (const region_model &model,
+ const region *reg, tree diag_arg, tree offset,
+ tree num_bytes, tree capacity,
+ const svalue *sval_hint)
+ : out_of_bounds (model, reg, diag_arg, sval_hint),
m_offset (offset),
m_num_bytes (num_bytes),
m_capacity (capacity)
@@ -559,9 +665,12 @@ protected:
class symbolic_buffer_overflow : public symbolic_past_the_end
{
public:
- symbolic_buffer_overflow (const region *reg, tree diag_arg, tree offset,
- tree num_bytes, tree capacity)
- : symbolic_past_the_end (reg, diag_arg, offset, num_bytes, capacity)
+ symbolic_buffer_overflow (const region_model &model,
+ const region *reg, tree diag_arg, tree offset,
+ tree num_bytes, tree capacity,
+ const svalue *sval_hint)
+ : symbolic_past_the_end (model, reg, diag_arg, offset, num_bytes, capacity,
+ sval_hint)
{
}
@@ -570,24 +679,31 @@ public:
return "symbolic_buffer_overflow";
}
- bool emit (rich_location *rich_loc) final override
+ bool emit (rich_location *rich_loc, logger *logger) final override
{
diagnostic_metadata m;
+ bool warned;
switch (get_memory_space ())
{
default:
m.add_cwe (787);
- return warning_meta (rich_loc, m, get_controlling_option (),
- "buffer overflow");
+ warned = warning_meta (rich_loc, m, get_controlling_option (),
+ "buffer overflow");
+ break;
case MEMSPACE_STACK:
m.add_cwe (121);
- return warning_meta (rich_loc, m, get_controlling_option (),
- "stack-based buffer overflow");
+ warned = warning_meta (rich_loc, m, get_controlling_option (),
+ "stack-based buffer overflow");
+ break;
case MEMSPACE_HEAP:
m.add_cwe (122);
- return warning_meta (rich_loc, m, get_controlling_option (),
- "heap-based buffer overflow");
+ warned = warning_meta (rich_loc, m, get_controlling_option (),
+ "heap-based buffer overflow");
+ break;
}
+ if (warned)
+ maybe_show_notes (rich_loc->get_loc (), logger);
+ return warned;
}
label_text
@@ -658,6 +774,8 @@ public:
m_diag_arg);
return ev.formatted_print ("out-of-bounds write");
}
+
+ enum access_direction get_dir () const final override { return DIR_WRITE; }
};
/* Concrete subclass to complain about over-reads with symbolic values. */
@@ -665,9 +783,11 @@ public:
class symbolic_buffer_over_read : public symbolic_past_the_end
{
public:
- symbolic_buffer_over_read (const region *reg, tree diag_arg, tree offset,
+ symbolic_buffer_over_read (const region_model &model,
+ const region *reg, tree diag_arg, tree offset,
tree num_bytes, tree capacity)
- : symbolic_past_the_end (reg, diag_arg, offset, num_bytes, capacity)
+ : symbolic_past_the_end (model, reg, diag_arg, offset, num_bytes, capacity,
+ NULL)
{
}
@@ -676,25 +796,32 @@ public:
return "symbolic_buffer_over_read";
}
- bool emit (rich_location *rich_loc) final override
+ bool emit (rich_location *rich_loc, logger *logger) final override
{
diagnostic_metadata m;
m.add_cwe (126);
+ bool warned;
switch (get_memory_space ())
{
default:
m.add_cwe (787);
- return warning_meta (rich_loc, m, get_controlling_option (),
- "buffer over-read");
+ warned = warning_meta (rich_loc, m, get_controlling_option (),
+ "buffer over-read");
+ break;
case MEMSPACE_STACK:
m.add_cwe (121);
- return warning_meta (rich_loc, m, get_controlling_option (),
- "stack-based buffer over-read");
+ warned = warning_meta (rich_loc, m, get_controlling_option (),
+ "stack-based buffer over-read");
+ break;
case MEMSPACE_HEAP:
m.add_cwe (122);
- return warning_meta (rich_loc, m, get_controlling_option (),
- "heap-based buffer over-read");
+ warned = warning_meta (rich_loc, m, get_controlling_option (),
+ "heap-based buffer over-read");
+ break;
}
+ if (warned)
+ maybe_show_notes (rich_loc->get_loc (), logger);
+ return warned;
}
label_text
@@ -765,6 +892,8 @@ public:
m_diag_arg);
return ev.formatted_print ("out-of-bounds read");
}
+
+ enum access_direction get_dir () const final override { return DIR_READ; }
};
/* Check whether an access is past the end of the BASE_REG.
@@ -776,6 +905,7 @@ region_model::check_symbolic_bounds (const region *base_reg,
const svalue *num_bytes_sval,
const svalue *capacity,
enum access_direction dir,
+ const svalue *sval_hint,
region_model_context *ctxt) const
{
gcc_assert (ctxt);
@@ -790,13 +920,21 @@ region_model::check_symbolic_bounds (const region *base_reg,
tree offset_tree = get_representative_tree (sym_byte_offset);
tree num_bytes_tree = get_representative_tree (num_bytes_sval);
tree capacity_tree = get_representative_tree (capacity);
+ const region *offset_reg = m_mgr->get_offset_region (base_reg,
+ NULL_TREE,
+ sym_byte_offset);
+ const region *sized_offset_reg = m_mgr->get_sized_region (offset_reg,
+ NULL_TREE,
+ num_bytes_sval);
switch (dir)
{
default:
gcc_unreachable ();
break;
case DIR_READ:
- ctxt->warn (make_unique<symbolic_buffer_over_read> (base_reg,
+ gcc_assert (sval_hint == nullptr);
+ ctxt->warn (make_unique<symbolic_buffer_over_read> (*this,
+ sized_offset_reg,
diag_arg,
offset_tree,
num_bytes_tree,
@@ -804,11 +942,13 @@ region_model::check_symbolic_bounds (const region *base_reg,
return false;
break;
case DIR_WRITE:
- ctxt->warn (make_unique<symbolic_buffer_overflow> (base_reg,
+ ctxt->warn (make_unique<symbolic_buffer_overflow> (*this,
+ sized_offset_reg,
diag_arg,
offset_tree,
num_bytes_tree,
- capacity_tree));
+ capacity_tree,
+ sval_hint));
return false;
break;
}
@@ -832,6 +972,7 @@ maybe_get_integer_cst_tree (const svalue *sval)
bool
region_model::check_region_bounds (const region *reg,
enum access_direction dir,
+ const svalue *sval_hint,
region_model_context *ctxt) const
{
gcc_assert (ctxt);
@@ -882,8 +1023,8 @@ region_model::check_region_bounds (const region *reg,
}
else
byte_offset_sval = reg_offset.get_symbolic_byte_offset ();
- return check_symbolic_bounds (base_reg, byte_offset_sval, num_bytes_sval,
- capacity, dir, ctxt);
+ return check_symbolic_bounds (base_reg, byte_offset_sval, num_bytes_sval,
+ capacity, dir, sval_hint, ctxt);
}
/* Otherwise continue to check with concrete values. */
@@ -902,13 +1043,17 @@ region_model::check_region_bounds (const region *reg,
gcc_unreachable ();
break;
case DIR_READ:
- ctxt->warn (make_unique<concrete_buffer_under_read> (reg, diag_arg,
+ gcc_assert (sval_hint == nullptr);
+ ctxt->warn (make_unique<concrete_buffer_under_read> (*this, reg,
+ diag_arg,
out));
oob_safe = false;
break;
case DIR_WRITE:
- ctxt->warn (make_unique<concrete_buffer_underwrite> (reg, diag_arg,
- out));
+ ctxt->warn (make_unique<concrete_buffer_underwrite> (*this,
+ reg, diag_arg,
+ out,
+ sval_hint));
oob_safe = false;
break;
}
@@ -934,13 +1079,17 @@ region_model::check_region_bounds (const region *reg,
gcc_unreachable ();
break;
case DIR_READ:
- ctxt->warn (make_unique<concrete_buffer_over_read> (reg, diag_arg,
+ gcc_assert (sval_hint == nullptr);
+ ctxt->warn (make_unique<concrete_buffer_over_read> (*this,
+ reg, diag_arg,
out, byte_bound));
oob_safe = false;
break;
case DIR_WRITE:
- ctxt->warn (make_unique<concrete_buffer_overflow> (reg, diag_arg,
- out, byte_bound));
+ ctxt->warn (make_unique<concrete_buffer_overflow> (*this,
+ reg, diag_arg,
+ out, byte_bound,
+ sval_hint));
oob_safe = false;
break;
}