aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2021-03-24 20:47:57 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2021-03-24 20:47:57 -0400
commit71fc4655ab86ab66b40165b2cb49c1395ca82a9a (patch)
treecd0439f92df03d57f9d79983cdd9b76ead8f09fc /gcc/analyzer
parent8bf52ffa92f7d1539cbb82fbc0e95389e084ec31 (diff)
downloadgcc-71fc4655ab86ab66b40165b2cb49c1395ca82a9a.zip
gcc-71fc4655ab86ab66b40165b2cb49c1395ca82a9a.tar.gz
gcc-71fc4655ab86ab66b40165b2cb49c1395ca82a9a.tar.bz2
analyzer; reset sm-state for SSA names at def-stmts [PR93695,PR99044,PR99716]
Various false positives from -fanalyzer involve SSA names in loops, where sm-state associated with an SSA name from one iteration is erroneously reused in a subsequent iteration. For example, PR analyzer/99716 describes a false "double 'fclose' of FILE 'fp'" on: for (i = 0; i < 2; ++i) { FILE *fp = fopen ("/tmp/test", "w"); fprintf (fp, "hello"); fclose (fp); } where the gimple of the loop body is: fp_7 = fopen ("/tmp/test", "w"); __builtin_fwrite ("hello", 1, 5, fp_7); fclose (fp_7); i_10 = i_1 + 1; where fp_7 transitions to "closed" at the fclose, but is not reset at the subsequent fopen, leading to the false positive when the fclose is re-reached. The fix is to reset sm-state for svalues that involve an SSA name at the SSA name's def-stmt, since the def-stmt effectively changes the meaning of those related svalues. gcc/analyzer/ChangeLog: PR analyzer/93695 PR analyzer/99044 PR analyzer/99716 * engine.cc (exploded_node::on_stmt): Clear sm-state involving an SSA name at the def-stmt of that SSA name. * program-state.cc (sm_state_map::purge_state_involving): New. * program-state.h (sm_state_map::purge_state_involving): New decl. * region-model.cc (selftest::test_involves_p): New. (selftest::analyzer_region_model_cc_tests): Call it. * svalue.cc (class involvement_visitor): New class (svalue::involves_p): New. * svalue.h (svalue::involves_p): New decl. gcc/testsuite/ChangeLog: PR analyzer/93695 PR analyzer/99044 PR analyzer/99716 * gcc.dg/analyzer/attr-malloc-CVE-2019-19078-usb-leak.c: Remove xfail. * gcc.dg/analyzer/pr93695-1.c: New test. * gcc.dg/analyzer/pr99044-1.c: New test. * gcc.dg/analyzer/pr99044-2.c: New test. * gcc.dg/analyzer/pr99716-1.c: New test. * gcc.dg/analyzer/pr99716-2.c: New test. * gcc.dg/analyzer/pr99716-3.c: New test.
Diffstat (limited to 'gcc/analyzer')
-rw-r--r--gcc/analyzer/engine.cc25
-rw-r--r--gcc/analyzer/program-state.cc30
-rw-r--r--gcc/analyzer/program-state.h3
-rw-r--r--gcc/analyzer/region-model.cc32
-rw-r--r--gcc/analyzer/svalue.cc34
-rw-r--r--gcc/analyzer/svalue.h2
6 files changed, 126 insertions, 0 deletions
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 5792c14..d7866b5 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -1244,6 +1244,31 @@ exploded_node::on_stmt (exploded_graph &eg,
sm_state_map *new_smap = state->m_checker_states[sm_idx];
impl_sm_context sm_ctxt (eg, sm_idx, sm, this, &old_state, state,
old_smap, new_smap);
+
+ /* If we're at the def-stmt of an SSA name, then potentially purge
+ any sm-state for svalues that involve that SSA name. This avoids
+ false positives in loops, since a symbolic value referring to the
+ SSA name will be referring to the previous value of that SSA name.
+ For example, in:
+ while ((e = hashmap_iter_next(&iter))) {
+ struct oid2strbuf *e_strbuf = (struct oid2strbuf *)e;
+ free (e_strbuf->value);
+ }
+ at the def-stmt of e_8:
+ e_8 = hashmap_iter_next (&iter);
+ we should purge the "freed" state of:
+ INIT_VAL(CAST_REG(‘struct oid2strbuf’, (*INIT_VAL(e_8))).value)
+ which is the "e_strbuf->value" value from the previous iteration,
+ or we will erroneously report a double-free - the "e_8" within it
+ refers to the previous value. */
+ if (tree lhs = gimple_get_lhs (stmt))
+ if (TREE_CODE (lhs) == SSA_NAME)
+ {
+ const svalue *sval
+ = old_state.m_region_model->get_rvalue (lhs, &ctxt);
+ new_smap->purge_state_involving (sval, eg.get_ext_state ());
+ }
+
/* Allow the state_machine to handle the stmt. */
if (sm.on_stmt (&sm_ctxt, snode, stmt))
unknown_side_effects = false;
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index e427fff..fcea5ce 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -586,6 +586,36 @@ sm_state_map::on_unknown_change (const svalue *sval,
impl_set_state (*iter, (state_machine::state_t)0, NULL, ext_state);
}
+/* Purge state for things involving SVAL.
+ For use when SVAL changes meaning, at the def_stmt on an SSA_NAME. */
+
+void
+sm_state_map::purge_state_involving (const svalue *sval,
+ const extrinsic_state &ext_state)
+{
+ /* Currently svalue::involves_p requires this. */
+ if (sval->get_kind () != SK_INITIAL)
+ return;
+
+ svalue_set svals_to_unset;
+
+ for (map_t::iterator iter = m_map.begin ();
+ iter != m_map.end ();
+ ++iter)
+ {
+ const svalue *key = (*iter).first;
+ entry_t e = (*iter).second;
+ if (!m_sm.can_purge_p (e.m_state))
+ continue;
+ if (key->involves_p (sval))
+ svals_to_unset.add (key);
+ }
+
+ for (svalue_set::iterator iter = svals_to_unset.begin ();
+ iter != svals_to_unset.end (); ++iter)
+ impl_set_state (*iter, (state_machine::state_t)0, NULL, ext_state);
+}
+
/* Comparator for imposing an order on sm_state_map instances. */
int
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index d72945d..54fdb5b 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -157,6 +157,9 @@ public:
bool is_mutable,
const extrinsic_state &ext_state);
+ void purge_state_involving (const svalue *sval,
+ const extrinsic_state &ext_state);
+
iterator_t begin () const { return m_map.begin (); }
iterator_t end () const { return m_map.end (); }
size_t elements () const { return m_map.elements (); }
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 96ed549..fb5dc39 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -5114,6 +5114,37 @@ test_alloca ()
ASSERT_EQ (model.get_rvalue (p, &ctxt)->get_kind (), SK_POISONED);
}
+/* Verify that svalue::involves_p works. */
+
+static void
+test_involves_p ()
+{
+ region_model_manager mgr;
+ tree int_star = build_pointer_type (integer_type_node);
+ tree p = build_global_decl ("p", int_star);
+ tree q = build_global_decl ("q", int_star);
+
+ test_region_model_context ctxt;
+ region_model model (&mgr);
+ const svalue *p_init = model.get_rvalue (p, &ctxt);
+ const svalue *q_init = model.get_rvalue (q, &ctxt);
+
+ ASSERT_TRUE (p_init->involves_p (p_init));
+ ASSERT_FALSE (p_init->involves_p (q_init));
+
+ const region *star_p_reg = mgr.get_symbolic_region (p_init);
+ const region *star_q_reg = mgr.get_symbolic_region (q_init);
+
+ const svalue *init_star_p = mgr.get_or_create_initial_value (star_p_reg);
+ const svalue *init_star_q = mgr.get_or_create_initial_value (star_q_reg);
+
+ ASSERT_TRUE (init_star_p->involves_p (p_init));
+ ASSERT_FALSE (p_init->involves_p (init_star_p));
+ ASSERT_FALSE (init_star_p->involves_p (q_init));
+ ASSERT_TRUE (init_star_q->involves_p (q_init));
+ ASSERT_FALSE (init_star_q->involves_p (p_init));
+}
+
/* Run all of the selftests within this file. */
void
@@ -5150,6 +5181,7 @@ analyzer_region_model_cc_tests ()
test_POINTER_PLUS_EXPR_then_MEM_REF ();
test_malloc ();
test_alloca ();
+ test_involves_p ();
}
} // namespace selftest
diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc
index d6305a3..897e84e 100644
--- a/gcc/analyzer/svalue.cc
+++ b/gcc/analyzer/svalue.cc
@@ -481,6 +481,40 @@ svalue::cmp_ptr_ptr (const void *p1, const void *p2)
return cmp_ptr (sval1, sval2);
}
+/* Subclass of visitor for use in implementing svalue::involves_p. */
+
+class involvement_visitor : public visitor
+{
+public:
+ involvement_visitor (const svalue *needle)
+ : m_needle (needle), m_found (false) {}
+
+ void visit_initial_svalue (const initial_svalue *candidate)
+ {
+ if (candidate == m_needle)
+ m_found = true;
+ }
+
+ bool found_p () const { return m_found; }
+
+private:
+ const svalue *m_needle;
+ bool m_found;
+};
+
+/* Return true iff this svalue is defined in terms of OTHER. */
+
+bool
+svalue::involves_p (const svalue *other) const
+{
+ /* Currently only implemented for initial_svalue. */
+ gcc_assert (other->get_kind () == SK_INITIAL);
+
+ involvement_visitor v (other);
+ accept (&v);
+ return v.found_p ();
+}
+
/* class region_svalue : public svalue. */
/* Implementation of svalue::dump_to_pp vfunc for region_svalue. */
diff --git a/gcc/analyzer/svalue.h b/gcc/analyzer/svalue.h
index 672a89c..7fe0ba3 100644
--- a/gcc/analyzer/svalue.h
+++ b/gcc/analyzer/svalue.h
@@ -136,6 +136,8 @@ public:
static int cmp_ptr (const svalue *, const svalue *);
static int cmp_ptr_ptr (const void *, const void *);
+ bool involves_p (const svalue *other) const;
+
protected:
svalue (complexity c, tree type)
: m_complexity (c), m_type (type)