diff options
author | David Malcolm <dmalcolm@redhat.com> | 2021-03-24 20:47:57 -0400 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2021-03-24 20:47:57 -0400 |
commit | 71fc4655ab86ab66b40165b2cb49c1395ca82a9a (patch) | |
tree | cd0439f92df03d57f9d79983cdd9b76ead8f09fc /gcc/analyzer | |
parent | 8bf52ffa92f7d1539cbb82fbc0e95389e084ec31 (diff) | |
download | gcc-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.cc | 25 | ||||
-rw-r--r-- | gcc/analyzer/program-state.cc | 30 | ||||
-rw-r--r-- | gcc/analyzer/program-state.h | 3 | ||||
-rw-r--r-- | gcc/analyzer/region-model.cc | 32 | ||||
-rw-r--r-- | gcc/analyzer/svalue.cc | 34 | ||||
-rw-r--r-- | gcc/analyzer/svalue.h | 2 |
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) |