aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer
diff options
context:
space:
mode:
authorMartin Liska <mliska@suse.cz>2022-08-16 10:06:14 +0200
committerMartin Liska <mliska@suse.cz>2022-08-16 10:06:14 +0200
commit091222fb0aaa09dcf90f2bc747f1d8a6a8ef1575 (patch)
tree07de02401c3374395a453724c4163d769c02e644 /gcc/analyzer
parentb629a7958faf817ef658e3ce59183bfb9ccefe96 (diff)
parent1c596391e150a6b0c55960c1c1cf1da76ea78230 (diff)
downloadgcc-091222fb0aaa09dcf90f2bc747f1d8a6a8ef1575.zip
gcc-091222fb0aaa09dcf90f2bc747f1d8a6a8ef1575.tar.gz
gcc-091222fb0aaa09dcf90f2bc747f1d8a6a8ef1575.tar.bz2
Merge branch 'master' into devel/sphinx
Diffstat (limited to 'gcc/analyzer')
-rw-r--r--gcc/analyzer/ChangeLog65
-rw-r--r--gcc/analyzer/analyzer.opt4
-rw-r--r--gcc/analyzer/region-model-impl-calls.cc48
-rw-r--r--gcc/analyzer/region-model.cc426
-rw-r--r--gcc/analyzer/region-model.h3
-rw-r--r--gcc/analyzer/region.cc32
-rw-r--r--gcc/analyzer/region.h4
-rw-r--r--gcc/analyzer/sm-fd.cc13
-rw-r--r--gcc/analyzer/store.cc67
-rw-r--r--gcc/analyzer/store.h9
10 files changed, 656 insertions, 15 deletions
diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog
index b16971b..d190956 100644
--- a/gcc/analyzer/ChangeLog
+++ b/gcc/analyzer/ChangeLog
@@ -1,3 +1,68 @@
+2022-08-15 David Malcolm <dmalcolm@redhat.com>
+
+ PR analyzer/106626
+ * region-model.cc (buffer_overread::emit): Fix copy&paste error in
+ direction of the access in the note.
+
+2022-08-15 David Malcolm <dmalcolm@redhat.com>
+
+ PR analyzer/106573
+ * region-model.cc (region_model::on_call_pre): Use check_call_args
+ when ensuring that we call get_arg_svalue on all args. Remove
+ redundant call from handling for stdio builtins.
+
+2022-08-15 Immad Mir <mirimmad@outlook.com>
+
+ PR analyzer/106551
+ * sm-fd.cc (check_for_dup): exit early if first
+ argument is invalid for all dup functions.
+
+2022-08-12 Tim Lange <mail@tim-lange.me>
+
+ PR analyzer/106000
+ * analyzer.opt: Add Wanalyzer-out-of-bounds.
+ * region-model.cc (class out_of_bounds): Diagnostics base class
+ for all out-of-bounds diagnostics.
+ (class past_the_end): Base class derived from out_of_bounds for
+ the buffer_overflow and buffer_overread diagnostics.
+ (class buffer_overflow): Buffer overflow diagnostics.
+ (class buffer_overread): Buffer overread diagnostics.
+ (class buffer_underflow): Buffer underflow diagnostics.
+ (class buffer_underread): Buffer overread diagnostics.
+ (region_model::check_region_bounds): New function to check region
+ bounds for out-of-bounds accesses.
+ (region_model::check_region_access):
+ Add call to check_region_bounds.
+ (region_model::get_representative_tree): New function that accepts
+ a region instead of an svalue.
+ * region-model.h (class region_model):
+ Add region_model::check_region_bounds.
+ * region.cc (region::symbolic_p): New predicate.
+ (offset_region::get_byte_size_sval): Only return the remaining
+ byte size on offset_regions.
+ * region.h: Add region::symbolic_p.
+ * store.cc (byte_range::intersects_p):
+ Add new function equivalent to bit_range::intersects_p.
+ (byte_range::exceeds_p): New function.
+ (byte_range::falls_short_of_p): New function.
+ * store.h (struct byte_range): Add byte_range::intersects_p,
+ byte_range::exceeds_p and byte_range::falls_short_of_p.
+
+2022-08-12 Tim Lange <mail@tim-lange.me>
+
+ PR analyzer/106539
+ * region-model-impl-calls.cc (region_model::impl_call_realloc):
+ Use the result of get_copied_size as the size for the
+ sized_regions in realloc.
+ (success_with_move::get_copied_size): New function.
+
+2022-08-11 Immad Mir <mirimmad@outlook.com>
+
+ PR analyzer/106551
+ * sm-fd.cc (check_for_dup): handle the m_start
+ state when transitioning the state of LHS
+ of dup, dup2 and dup3 call.
+
2022-08-09 David Malcolm <dmalcolm@redhat.com>
PR analyzer/106573
diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index c6d9c53..61b58c5 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -110,6 +110,10 @@ Wanalyzer-mismatching-deallocation
Common Var(warn_analyzer_mismatching_deallocation) Init(1) Warning
Warn about code paths in which the wrong deallocation function is called.
+Wanalyzer-out-of-bounds
+Common Var(warn_analyzer_out_of_bounds) Init(1) Warning
+Warn about code paths in which a write or read to a buffer is out-of-bounds.
+
Wanalyzer-possible-null-argument
Common Var(warn_analyzer_possible_null_argument) Init(1) Warning
Warn about code paths in which a possibly-NULL value is passed to a must-not-be-NULL function argument.
diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc
index 3f821ff..8eebd12 100644
--- a/gcc/analyzer/region-model-impl-calls.cc
+++ b/gcc/analyzer/region-model-impl-calls.cc
@@ -849,15 +849,17 @@ region_model::impl_call_realloc (const call_details &cd)
const svalue *old_size_sval = model->get_dynamic_extents (freed_reg);
if (old_size_sval)
{
- const region *sized_old_reg
+ const svalue *copied_size_sval
+ = get_copied_size (old_size_sval, new_size_sval);
+ const region *copied_old_reg
= model->m_mgr->get_sized_region (freed_reg, NULL,
- old_size_sval);
+ copied_size_sval);
const svalue *buffer_content_sval
- = model->get_store_value (sized_old_reg, cd.get_ctxt ());
- const region *sized_new_reg
+ = model->get_store_value (copied_old_reg, cd.get_ctxt ());
+ const region *copied_new_reg
= model->m_mgr->get_sized_region (new_reg, NULL,
- old_size_sval);
- model->set_value (sized_new_reg, buffer_content_sval,
+ copied_size_sval);
+ model->set_value (copied_new_reg, buffer_content_sval,
cd.get_ctxt ());
}
else
@@ -891,6 +893,40 @@ region_model::impl_call_realloc (const call_details &cd)
else
return true;
}
+
+ private:
+ /* Return the lesser of OLD_SIZE_SVAL and NEW_SIZE_SVAL.
+ If either one is symbolic, the symbolic svalue is returned. */
+ const svalue *get_copied_size (const svalue *old_size_sval,
+ const svalue *new_size_sval) const
+ {
+ tree old_size_cst = old_size_sval->maybe_get_constant ();
+ tree new_size_cst = new_size_sval->maybe_get_constant ();
+
+ if (old_size_cst && new_size_cst)
+ {
+ /* Both are constants and comparable. */
+ tree cmp = fold_binary (LT_EXPR, boolean_type_node,
+ old_size_cst, new_size_cst);
+
+ if (cmp == boolean_true_node)
+ return old_size_sval;
+ else
+ return new_size_sval;
+ }
+ else if (new_size_cst)
+ {
+ /* OLD_SIZE_SVAL is symbolic, so return that. */
+ return old_size_sval;
+ }
+ else
+ {
+ /* NEW_SIZE_SVAL is symbolic or both are symbolic.
+ Return NEW_SIZE_SVAL, because implementations of realloc
+ probably only moves the buffer if the new size is larger. */
+ return new_size_sval;
+ }
+ }
};
/* Body of region_model::impl_call_realloc. */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 8393c7d..b05b709 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1268,6 +1268,414 @@ region_model::on_stmt_pre (const gimple *stmt,
}
}
+/* Abstract base class for all out-of-bounds warnings. */
+
+class out_of_bounds : public pending_diagnostic_subclass<out_of_bounds>
+{
+public:
+ out_of_bounds (const region *reg, tree diag_arg,
+ byte_range out_of_bounds_range)
+ : m_reg (reg), m_diag_arg (diag_arg),
+ m_out_of_bounds_range (out_of_bounds_range)
+ {}
+
+ const char *get_kind () const final override
+ {
+ return "out_of_bounds_diagnostic";
+ }
+
+ bool operator== (const out_of_bounds &other) const
+ {
+ return m_reg == other.m_reg
+ && m_out_of_bounds_range == other.m_out_of_bounds_range
+ && pending_diagnostic::same_tree_p (m_diag_arg, other.m_diag_arg);
+ }
+
+ int get_controlling_option () const final override
+ {
+ return OPT_Wanalyzer_out_of_bounds;
+ }
+
+ void mark_interesting_stuff (interesting_t *interest) final override
+ {
+ interest->add_region_creation (m_reg);
+ }
+
+protected:
+ const region *m_reg;
+ tree m_diag_arg;
+ byte_range m_out_of_bounds_range;
+};
+
+/* Abstract subclass to complaing about out-of-bounds
+ past the end of the buffer. */
+
+class past_the_end : public out_of_bounds
+{
+public:
+ past_the_end (const region *reg, tree diag_arg, byte_range range,
+ tree byte_bound)
+ : out_of_bounds (reg, diag_arg, range), m_byte_bound (byte_bound)
+ {}
+
+ bool operator== (const past_the_end &other) const
+ {
+ return out_of_bounds::operator== (other)
+ && pending_diagnostic::same_tree_p (m_byte_bound,
+ other.m_byte_bound);
+ }
+
+ label_text
+ describe_region_creation_event (const evdesc::region_creation &ev) final
+ override
+ {
+ if (m_byte_bound && TREE_CODE (m_byte_bound) == INTEGER_CST)
+ return ev.formatted_print ("capacity is %E bytes", m_byte_bound);
+
+ return label_text ();
+ }
+
+protected:
+ tree m_byte_bound;
+};
+
+/* Concrete subclass to complain about buffer overflows. */
+
+class buffer_overflow : public past_the_end
+{
+public:
+ buffer_overflow (const region *reg, tree diag_arg,
+ byte_range range, tree byte_bound)
+ : past_the_end (reg, diag_arg, range, byte_bound)
+ {}
+
+ bool emit (rich_location *rich_loc) final override
+ {
+ diagnostic_metadata m;
+ bool warned;
+ switch (m_reg->get_memory_space ())
+ {
+ default:
+ m.add_cwe (787);
+ warned = warning_meta (rich_loc, m, get_controlling_option (),
+ "buffer overflow");
+ break;
+ case MEMSPACE_STACK:
+ m.add_cwe (121);
+ warned = warning_meta (rich_loc, m, get_controlling_option (),
+ "stack-based buffer overflow");
+ break;
+ case MEMSPACE_HEAP:
+ m.add_cwe (122);
+ warned = warning_meta (rich_loc, m, get_controlling_option (),
+ "heap-based buffer overflow");
+ break;
+ }
+
+ if (warned)
+ {
+ char num_bytes_past_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+ print_dec (m_out_of_bounds_range.m_size_in_bytes,
+ num_bytes_past_buf, UNSIGNED);
+ if (m_diag_arg)
+ inform (rich_loc->get_loc (), "write is %s bytes past the end"
+ " of %qE", num_bytes_past_buf,
+ m_diag_arg);
+ else
+ inform (rich_loc->get_loc (), "write is %s bytes past the end"
+ "of the region",
+ num_bytes_past_buf);
+ }
+
+ return warned;
+ }
+
+ label_text describe_final_event (const evdesc::final_event &ev)
+ final override
+ {
+ byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
+ byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
+ char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+ print_dec (start, start_buf, SIGNED);
+ char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+ print_dec (end, end_buf, SIGNED);
+
+ if (start == end)
+ {
+ if (m_diag_arg)
+ return ev.formatted_print ("out-of-bounds write at byte %s but %qE"
+ " ends at byte %E", start_buf, m_diag_arg,
+ m_byte_bound);
+ return ev.formatted_print ("out-of-bounds write at byte %s but region"
+ " ends at byte %E", start_buf,
+ m_byte_bound);
+ }
+ else
+ {
+ if (m_diag_arg)
+ return ev.formatted_print ("out-of-bounds write from byte %s till"
+ " byte %s but %qE ends at byte %E",
+ start_buf, end_buf, m_diag_arg,
+ m_byte_bound);
+ return ev.formatted_print ("out-of-bounds write from byte %s till"
+ " byte %s but region ends at byte %E",
+ start_buf, end_buf, m_byte_bound);
+ }
+ }
+};
+
+/* Concrete subclass to complain about buffer overreads. */
+
+class buffer_overread : public past_the_end
+{
+public:
+ buffer_overread (const region *reg, tree diag_arg,
+ byte_range range, tree byte_bound)
+ : past_the_end (reg, diag_arg, range, byte_bound)
+ {}
+
+ bool emit (rich_location *rich_loc) final override
+ {
+ diagnostic_metadata m;
+ m.add_cwe (126);
+ bool warned = warning_meta (rich_loc, m, get_controlling_option (),
+ "buffer overread");
+
+ if (warned)
+ {
+ char num_bytes_past_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+ print_dec (m_out_of_bounds_range.m_size_in_bytes,
+ num_bytes_past_buf, UNSIGNED);
+ if (m_diag_arg)
+ inform (rich_loc->get_loc (), "read is %s bytes past the end"
+ " of %qE", num_bytes_past_buf,
+ m_diag_arg);
+ else
+ inform (rich_loc->get_loc (), "read is %s bytes past the end"
+ "of the region",
+ num_bytes_past_buf);
+ }
+
+ return warned;
+ }
+
+ label_text describe_final_event (const evdesc::final_event &ev)
+ final override
+ {
+ byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
+ byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
+ char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+ print_dec (start, start_buf, SIGNED);
+ char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+ print_dec (end, end_buf, SIGNED);
+
+ if (start == end)
+ {
+ if (m_diag_arg)
+ return ev.formatted_print ("out-of-bounds read at byte %s but %qE"
+ " ends at byte %E", start_buf, m_diag_arg,
+ m_byte_bound);
+ return ev.formatted_print ("out-of-bounds read at byte %s but region"
+ " ends at byte %E", start_buf,
+ m_byte_bound);
+ }
+ else
+ {
+ if (m_diag_arg)
+ return ev.formatted_print ("out-of-bounds read from byte %s till"
+ " byte %s but %qE ends at byte %E",
+ start_buf, end_buf, m_diag_arg,
+ m_byte_bound);
+ return ev.formatted_print ("out-of-bounds read from byte %s till"
+ " byte %s but region ends at byte %E",
+ start_buf, end_buf, m_byte_bound);
+ }
+ }
+};
+
+/* Concrete subclass to complain about buffer underflows. */
+
+class buffer_underflow : public out_of_bounds
+{
+public:
+ buffer_underflow (const region *reg, tree diag_arg, byte_range range)
+ : out_of_bounds (reg, diag_arg, range)
+ {}
+
+ bool emit (rich_location *rich_loc) final override
+ {
+ diagnostic_metadata m;
+ m.add_cwe (124);
+ return warning_meta (rich_loc, m, get_controlling_option (),
+ "buffer underflow");
+ }
+
+ label_text describe_final_event (const evdesc::final_event &ev)
+ final override
+ {
+ byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
+ byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
+ char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+ print_dec (start, start_buf, SIGNED);
+ char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+ print_dec (end, end_buf, SIGNED);
+
+ if (start == end)
+ {
+ if (m_diag_arg)
+ return ev.formatted_print ("out-of-bounds write at byte %s but %qE"
+ " starts at byte 0", start_buf,
+ m_diag_arg);
+ return ev.formatted_print ("out-of-bounds write at byte %s but region"
+ " starts at byte 0", start_buf);
+ }
+ else
+ {
+ if (m_diag_arg)
+ return ev.formatted_print ("out-of-bounds write from byte %s till"
+ " byte %s but %qE starts at byte 0",
+ start_buf, end_buf, m_diag_arg);
+ return ev.formatted_print ("out-of-bounds write from byte %s till"
+ " byte %s but region starts at byte 0",
+ start_buf, end_buf);;
+ }
+ }
+};
+
+/* Concrete subclass to complain about buffer underreads. */
+
+class buffer_underread : public out_of_bounds
+{
+public:
+ buffer_underread (const region *reg, tree diag_arg, byte_range range)
+ : out_of_bounds (reg, diag_arg, range)
+ {}
+
+ bool emit (rich_location *rich_loc) final override
+ {
+ diagnostic_metadata m;
+ m.add_cwe (127);
+ return warning_meta (rich_loc, m, get_controlling_option (),
+ "buffer underread");
+ }
+
+ label_text describe_final_event (const evdesc::final_event &ev)
+ final override
+ {
+ byte_size_t start = m_out_of_bounds_range.get_start_byte_offset ();
+ byte_size_t end = m_out_of_bounds_range.get_last_byte_offset ();
+ char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+ print_dec (start, start_buf, SIGNED);
+ char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
+ print_dec (end, end_buf, SIGNED);
+
+ if (start == end)
+ {
+ if (m_diag_arg)
+ return ev.formatted_print ("out-of-bounds read at byte %s but %qE"
+ " starts at byte 0", start_buf,
+ m_diag_arg);
+ return ev.formatted_print ("out-of-bounds read at byte %s but region"
+ " starts at byte 0", start_buf);
+ }
+ else
+ {
+ if (m_diag_arg)
+ return ev.formatted_print ("out-of-bounds read from byte %s till"
+ " byte %s but %qE starts at byte 0",
+ start_buf, end_buf, m_diag_arg);
+ return ev.formatted_print ("out-of-bounds read from byte %s till"
+ " byte %s but region starts at byte 0",
+ start_buf, end_buf);;
+ }
+ }
+};
+
+/* May complain when the access on REG is out-of-bounds. */
+
+void region_model::check_region_bounds (const region *reg,
+ enum access_direction dir,
+ region_model_context *ctxt) const
+{
+ gcc_assert (ctxt);
+
+ region_offset reg_offset = reg->get_offset ();
+ const region *base_reg = reg_offset.get_base_region ();
+
+ /* Bail out on symbolic offsets or symbolic regions.
+ (e.g. because the analyzer did not see previous offsets on the latter,
+ it might think that a negative access is before the buffer). */
+ if (reg_offset.symbolic_p () || base_reg->symbolic_p ())
+ return;
+ byte_offset_t offset_unsigned
+ = reg_offset.get_bit_offset () >> LOG2_BITS_PER_UNIT;
+ /* The constant offset from a pointer is represented internally as a sizetype
+ but should be interpreted as a signed value here. The statement below
+ converts the offset to a signed integer with the same precision the
+ sizetype has on the target system.
+
+ For example, this is needed for out-of-bounds-3.c test1 to pass when
+ compiled with a 64-bit gcc build targeting 32-bit systems. */
+ byte_offset_t offset
+ = offset_unsigned.to_shwi (TYPE_PRECISION (size_type_node));
+
+ /* Find out how many bytes were accessed. */
+ const svalue *num_bytes_sval = reg->get_byte_size_sval (m_mgr);
+ tree num_bytes_tree = num_bytes_sval->maybe_get_constant ();
+ if (!num_bytes_tree || TREE_CODE (num_bytes_tree) != INTEGER_CST)
+ /* If we do not know how many bytes were read/written,
+ assume that at least one byte was read/written. */
+ num_bytes_tree = integer_one_node;
+
+ byte_range out (0, 0);
+ /* NUM_BYTES_TREE should always be interpreted as unsigned. */
+ byte_range read_bytes (offset, wi::to_offset (num_bytes_tree).to_uhwi ());
+ /* If read_bytes has a subset < 0, we do have an underflow. */
+ if (read_bytes.falls_short_of_p (0, &out))
+ {
+ tree diag_arg = get_representative_tree (reg->get_base_region ());
+ switch (dir)
+ {
+ default:
+ gcc_unreachable ();
+ break;
+ case DIR_READ:
+ ctxt->warn (new buffer_underread (reg, diag_arg, out));
+ break;
+ case DIR_WRITE:
+ ctxt->warn (new buffer_underflow (reg, diag_arg, out));
+ break;
+ }
+ }
+
+ const svalue *capacity = get_capacity (base_reg);
+ tree cst_capacity_tree = capacity->maybe_get_constant ();
+ if (!cst_capacity_tree || TREE_CODE (cst_capacity_tree) != INTEGER_CST)
+ return;
+
+ byte_range buffer (0, wi::to_offset (cst_capacity_tree));
+ /* If READ_BYTES exceeds BUFFER, we do have an overflow. */
+ if (read_bytes.exceeds_p (buffer, &out))
+ {
+ tree byte_bound = wide_int_to_tree (size_type_node,
+ buffer.get_next_byte_offset ());
+ tree diag_arg = get_representative_tree (reg->get_base_region ());
+
+ switch (dir)
+ {
+ default:
+ gcc_unreachable ();
+ break;
+ case DIR_READ:
+ ctxt->warn (new buffer_overread (reg, diag_arg, out, byte_bound));
+ break;
+ case DIR_WRITE:
+ ctxt->warn (new buffer_overflow (reg, diag_arg, out, byte_bound));
+ break;
+ }
+ }
+}
+
/* Ensure that all arguments at the call described by CD are checked
for poisoned values, by calling get_rvalue on each argument. */
@@ -1360,8 +1768,7 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt,
duplicates if any of the handling below also looks up the svalues,
but the deduplication code should deal with that. */
if (ctxt)
- for (unsigned arg_idx = 0; arg_idx < cd.num_args (); arg_idx++)
- cd.get_arg_svalue (arg_idx);
+ check_call_args (cd);
/* Some of the cases below update the lhs of the call based on the
return value, but not all. Provide a default value, which may
@@ -1481,7 +1888,6 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt,
/* These stdio builtins have external effects that are out
of scope for the analyzer: we only want to model the effects
on the return value. */
- check_call_args (cd);
break;
case BUILT_IN_VA_START:
@@ -2825,6 +3231,7 @@ region_model::check_region_access (const region *reg,
return;
check_region_for_taint (reg, dir, ctxt);
+ check_region_bounds (reg, dir, ctxt);
switch (dir)
{
@@ -3820,6 +4227,19 @@ region_model::get_representative_tree (const svalue *sval) const
return fixup_tree_for_diagnostic (expr);
}
+tree
+region_model::get_representative_tree (const region *reg) const
+{
+ svalue_set visited;
+ tree expr = get_representative_path_var (reg, &visited).m_tree;
+
+ /* Strip off any top-level cast. */
+ if (expr && TREE_CODE (expr) == NOP_EXPR)
+ expr = TREE_OPERAND (expr, 0);
+
+ return fixup_tree_for_diagnostic (expr);
+}
+
/* Implementation of region_model::get_representative_path_var.
Attempt to return a path_var that represents REG, or return
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index a9657e0..cdf1087 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -733,6 +733,7 @@ class region_model
region_model_context *ctxt);
tree get_representative_tree (const svalue *sval) const;
+ tree get_representative_tree (const region *reg) const;
path_var
get_representative_path_var (const svalue *sval,
svalue_set *visited) const;
@@ -868,6 +869,8 @@ class region_model
region_model_context *ctxt) const;
void check_region_size (const region *lhs_reg, const svalue *rhs_sval,
region_model_context *ctxt) const;
+ void check_region_bounds (const region *reg, enum access_direction dir,
+ region_model_context *ctxt) const;
void check_call_args (const call_details &cd) const;
void check_external_function_for_access_attr (const gcall *call,
diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc
index b78bf4e..f4aba6b 100644
--- a/gcc/analyzer/region.cc
+++ b/gcc/analyzer/region.cc
@@ -629,6 +629,14 @@ region::symbolic_for_unknown_ptr_p () const
return false;
}
+/* Return true if this is a symbolic region. */
+
+bool
+region::symbolic_p () const
+{
+ return get_kind () == RK_SYMBOLIC;
+}
+
/* Return true if this is a region for a decl with name DECL_NAME.
Intended for use when debugging (for assertions and conditional
breakpoints). */
@@ -1430,6 +1438,30 @@ offset_region::get_relative_concrete_offset (bit_offset_t *out) const
return false;
}
+/* Implementation of region::get_byte_size_sval vfunc for offset_region. */
+
+const svalue *
+offset_region::get_byte_size_sval (region_model_manager *mgr) const
+{
+ tree offset_cst = get_byte_offset ()->maybe_get_constant ();
+ byte_size_t byte_size;
+ /* If the offset points in the middle of the region,
+ return the remaining bytes. */
+ if (get_byte_size (&byte_size) && offset_cst)
+ {
+ byte_size_t offset = wi::to_offset (offset_cst);
+ byte_range r (0, byte_size);
+ if (r.contains_p (offset))
+ {
+ tree remaining_byte_size = wide_int_to_tree (size_type_node,
+ byte_size - offset);
+ return mgr->get_or_create_constant_svalue (remaining_byte_size);
+ }
+ }
+
+ return region::get_byte_size_sval (mgr);
+}
+
/* class sized_region : public region. */
/* Implementation of region::accept vfunc for sized_region. */
diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h
index a5b3029..20dffc7 100644
--- a/gcc/analyzer/region.h
+++ b/gcc/analyzer/region.h
@@ -211,6 +211,8 @@ public:
bool symbolic_for_unknown_ptr_p () const;
+ bool symbolic_p () const;
+
/* For most base regions it makes sense to track the bindings of the region
within the store. As an optimization, some are not tracked (to avoid
bloating the store object with redundant binding clusters). */
@@ -917,6 +919,8 @@ public:
const svalue *get_byte_offset () const { return m_byte_offset; }
bool get_relative_concrete_offset (bit_offset_t *out) const final override;
+ const svalue * get_byte_size_sval (region_model_manager *mgr) const;
+
private:
const svalue *m_byte_offset;
diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc
index 8bb76d7..505d598 100644
--- a/gcc/analyzer/sm-fd.cc
+++ b/gcc/analyzer/sm-fd.cc
@@ -971,19 +971,19 @@ fd_state_machine::check_for_dup (sm_context *sm_ctxt, const supernode *node,
state_t state_arg_1 = sm_ctxt->get_state (stmt, arg_1);
if (state_arg_1 == m_stop)
return;
- if (!(is_constant_fd_p (state_arg_1) || is_valid_fd_p (state_arg_1)))
+ if (!(is_constant_fd_p (state_arg_1) || is_valid_fd_p (state_arg_1)
+ || state_arg_1 == m_start))
{
check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl,
DIRS_READ_WRITE);
- if (kind == DUP_1)
- return;
+ return;
}
switch (kind)
{
case DUP_1:
if (lhs)
{
- if (is_constant_fd_p (state_arg_1))
+ if (is_constant_fd_p (state_arg_1) || state_arg_1 == m_start)
sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write);
else
sm_ctxt->set_next_state (stmt, lhs,
@@ -999,7 +999,8 @@ fd_state_machine::check_for_dup (sm_context *sm_ctxt, const supernode *node,
if (state_arg_2 == m_stop)
return;
/* Check if -1 was passed as second argument to dup2. */
- if (!(is_constant_fd_p (state_arg_2) || is_valid_fd_p (state_arg_2)))
+ if (!(is_constant_fd_p (state_arg_2) || is_valid_fd_p (state_arg_2)
+ || state_arg_2 == m_start))
{
sm_ctxt->warn (
node, stmt, arg_2,
@@ -1011,7 +1012,7 @@ fd_state_machine::check_for_dup (sm_context *sm_ctxt, const supernode *node,
file descriptor i.e the first argument. */
if (lhs)
{
- if (is_constant_fd_p (state_arg_1))
+ if (is_constant_fd_p (state_arg_1) || state_arg_1 == m_start)
sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write);
else
sm_ctxt->set_next_state (stmt, lhs,
diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc
index 46475f6..848c5e1 100644
--- a/gcc/analyzer/store.cc
+++ b/gcc/analyzer/store.cc
@@ -424,6 +424,73 @@ byte_range::contains_p (const byte_range &other, byte_range *out) const
return false;
}
+/* Return true if THIS and OTHER intersect and write the number
+ of bytes both buffers overlap to *OUT_NUM_OVERLAP_BYTES.
+
+ Otherwise return false. */
+
+bool
+byte_range::intersects_p (const byte_range &other,
+ byte_size_t *out_num_overlap_bytes) const
+{
+ if (get_start_byte_offset () < other.get_next_byte_offset ()
+ && other.get_start_byte_offset () < get_next_byte_offset ())
+ {
+ byte_offset_t overlap_start = MAX (get_start_byte_offset (),
+ other.get_start_byte_offset ());
+ byte_offset_t overlap_next = MIN (get_next_byte_offset (),
+ other.get_next_byte_offset ());
+ gcc_assert (overlap_next > overlap_start);
+ *out_num_overlap_bytes = overlap_next - overlap_start;
+ return true;
+ }
+ else
+ return false;
+}
+
+/* Return true if THIS exceeds OTHER and write the overhanging
+ byte range to OUT_OVERHANGING_BYTE_RANGE. */
+
+bool
+byte_range::exceeds_p (const byte_range &other,
+ byte_range *out_overhanging_byte_range) const
+{
+ if (other.get_last_byte_offset () < get_last_byte_offset ())
+ {
+ /* THIS definitely exceeds OTHER. */
+ byte_offset_t start = MAX (get_start_byte_offset (),
+ other.get_next_byte_offset ());
+ byte_offset_t size = get_next_byte_offset () - start;
+ gcc_assert (size > 0);
+ out_overhanging_byte_range->m_start_byte_offset = start;
+ out_overhanging_byte_range->m_size_in_bytes = size;
+ return true;
+ }
+ else
+ return false;
+}
+
+/* Return true if THIS falls short of OFFSET and write the
+ byte range fallen short to OUT_FALL_SHORT_BYTES. */
+
+bool
+byte_range::falls_short_of_p (byte_offset_t offset,
+ byte_range *out_fall_short_bytes) const
+{
+ if (get_start_byte_offset () < offset)
+ {
+ /* THIS falls short of OFFSET. */
+ byte_offset_t start = get_start_byte_offset ();
+ byte_offset_t size = MIN (offset, get_next_byte_offset ()) - start;
+ gcc_assert (size > 0);
+ out_fall_short_bytes->m_start_byte_offset = start;
+ out_fall_short_bytes->m_size_in_bytes = size;
+ return true;
+ }
+ else
+ return false;
+}
+
/* qsort comparator for byte ranges. */
int
diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h
index 9b54c7b..ac8b685 100644
--- a/gcc/analyzer/store.h
+++ b/gcc/analyzer/store.h
@@ -310,6 +310,15 @@ struct byte_range
&& m_size_in_bytes == other.m_size_in_bytes);
}
+ bool intersects_p (const byte_range &other,
+ byte_size_t *out_num_overlap_bytes) const;
+
+ bool exceeds_p (const byte_range &other,
+ byte_range *out_overhanging_byte_range) const;
+
+ bool falls_short_of_p (byte_offset_t offset,
+ byte_range *out_fall_short_bytes) const;
+
byte_offset_t get_start_byte_offset () const
{
return m_start_byte_offset;