diff options
-rw-r--r-- | gcc/analyzer/engine.cc | 10 | ||||
-rw-r--r-- | gcc/analyzer/program-state.cc | 9 | ||||
-rw-r--r-- | gcc/analyzer/program-state.h | 3 | ||||
-rw-r--r-- | gcc/analyzer/region-model.cc | 33 | ||||
-rw-r--r-- | gcc/analyzer/region-model.h | 20 | ||||
-rw-r--r-- | gcc/analyzer/svalue.cc | 8 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/explode-1.c | 4 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/pr103217.c | 42 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/pr94858-1.c | 2 |
9 files changed, 117 insertions, 14 deletions
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 096e219..e8a7cca 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -2417,7 +2417,7 @@ exploded_graph::get_or_create_node (const program_point &point, /* This merges successfully within the loop. */ program_state merged_state (m_ext_state); - if (pruned_state.can_merge_with_p (existing_state, point, + if (pruned_state.can_merge_with_p (existing_state, m_ext_state, point, &merged_state)) { merged_state.validate (m_ext_state); @@ -2717,7 +2717,8 @@ exploded_graph::process_worklist () gcc_assert (state != state_2); program_state merged_state (m_ext_state); - if (state.can_merge_with_p (state_2, point, &merged_state)) + if (state.can_merge_with_p (state_2, m_ext_state, + point, &merged_state)) { if (logger) logger->log ("merging EN: %i and EN: %i", @@ -2973,7 +2974,8 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) { merged_state->validate (m_ext_state); program_state merge (m_ext_state); - if (it_state.can_merge_with_p (*merged_state, next_point, &merge)) + if (it_state.can_merge_with_p (*merged_state, m_ext_state, + next_point, &merge)) { *merged_state = merge; merged_state->validate (m_ext_state); @@ -3305,6 +3307,8 @@ exploded_graph::process_node (exploded_node *node) (node->get_supernode (), last_cfg_superedge, &ctxt); + program_state::detect_leaks (state, next_state, NULL, + get_ext_state (), &ctxt); } program_point next_point (point.get_next ()); diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index 1c87af0..47e4eca 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -1197,6 +1197,7 @@ program_state::get_representative_tree (const svalue *sval) const bool program_state::can_merge_with_p (const program_state &other, + const extrinsic_state &ext_state, const program_point &point, program_state *out) const { @@ -1213,7 +1214,9 @@ program_state::can_merge_with_p (const program_state &other, /* Attempt to merge the region_models. */ if (!m_region_model->can_merge_with_p (*other.m_region_model, point, - out->m_region_model)) + out->m_region_model, + &ext_state, + this, &other)) return false; /* Copy m_checker_states to OUT. */ @@ -1645,7 +1648,7 @@ test_program_state_merging () with the given sm-state. They ought to be mergeable, preserving the sm-state. */ program_state merged (ext_state); - ASSERT_TRUE (s0.can_merge_with_p (s1, point, &merged)); + ASSERT_TRUE (s0.can_merge_with_p (s1, ext_state, point, &merged)); merged.validate (ext_state); /* Verify that the merged state has the sm-state for "p". */ @@ -1703,7 +1706,7 @@ test_program_state_merging_2 () /* They ought to not be mergeable. */ program_state merged (ext_state); - ASSERT_FALSE (s0.can_merge_with_p (s1, point, &merged)); + ASSERT_FALSE (s0.can_merge_with_p (s1, ext_state, point, &merged)); } /* Run all of the selftests within this file. */ diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h index eb49006..4579e2a 100644 --- a/gcc/analyzer/program-state.h +++ b/gcc/analyzer/program-state.h @@ -242,7 +242,7 @@ public: tree get_representative_tree (const svalue *sval) const; bool can_purge_p (const extrinsic_state &ext_state, - const svalue *sval) + const svalue *sval) const { /* Don't purge vars that have non-purgeable sm state, to avoid generating false "leak" complaints. */ @@ -258,6 +258,7 @@ public: } bool can_merge_with_p (const program_state &other, + const extrinsic_state &ext_state, const program_point &point, program_state *out) const; diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index bbb15ab..dccf902 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -64,6 +64,7 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/pending-diagnostic.h" #include "analyzer/region-model-reachability.h" #include "analyzer/analyzer-selftests.h" +#include "analyzer/program-state.h" #include "stor-layout.h" #include "attribs.h" #include "tree-object-size.h" @@ -3683,7 +3684,10 @@ region_model::poison_any_pointers_to_descendents (const region *reg, bool region_model::can_merge_with_p (const region_model &other_model, const program_point &point, - region_model *out_model) const + region_model *out_model, + const extrinsic_state *ext_state, + const program_state *state_a, + const program_state *state_b) const { gcc_assert (out_model); gcc_assert (m_mgr == other_model.m_mgr); @@ -3693,7 +3697,8 @@ region_model::can_merge_with_p (const region_model &other_model, return false; out_model->m_current_frame = m_current_frame; - model_merger m (this, &other_model, point, out_model); + model_merger m (this, &other_model, point, out_model, + ext_state, state_a, state_b); if (!store::can_merge_p (&m_store, &other_model.m_store, &out_model->m_store, m_mgr->get_store_manager (), @@ -3897,6 +3902,30 @@ model_merger::dump (bool simple) const dump (stderr, simple); } +/* Return true if it's OK to merge SVAL with other svalues. */ + +bool +model_merger::mergeable_svalue_p (const svalue *sval) const +{ + if (m_ext_state) + { + /* Reject merging svalues that have non-purgable sm-state, + to avoid falsely reporting memory leaks by merging them + with something else. For example, given a local var "p", + reject the merger of a: + store_a mapping "p" to a malloc-ed ptr + with: + store_b mapping "p" to a NULL ptr. */ + if (m_state_a) + if (!m_state_a->can_purge_p (*m_ext_state, sval)) + return false; + if (m_state_b) + if (!m_state_b->can_purge_p (*m_ext_state, sval)) + return false; + } + return true; +} + } // namespace ana /* Dump RMODEL fully to stderr (i.e. without summarization). */ diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index 5434011..bffbdf2 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -721,7 +721,10 @@ class region_model bool can_merge_with_p (const region_model &other_model, const program_point &point, - region_model *out_model) const; + region_model *out_model, + const extrinsic_state *ext_state = NULL, + const program_state *state_a = NULL, + const program_state *state_b = NULL) const; tree get_fndecl_for_call (const gcall *call, region_model_context *ctxt); @@ -987,10 +990,15 @@ struct model_merger model_merger (const region_model *model_a, const region_model *model_b, const program_point &point, - region_model *merged_model) + region_model *merged_model, + const extrinsic_state *ext_state, + const program_state *state_a, + const program_state *state_b) : m_model_a (model_a), m_model_b (model_b), m_point (point), - m_merged_model (merged_model) + m_merged_model (merged_model), + m_ext_state (ext_state), + m_state_a (state_a), m_state_b (state_b) { } @@ -1003,10 +1011,16 @@ struct model_merger return m_model_a->get_manager (); } + bool mergeable_svalue_p (const svalue *) const; + const region_model *m_model_a; const region_model *m_model_b; const program_point &m_point; region_model *m_merged_model; + + const extrinsic_state *m_ext_state; + const program_state *m_state_a; + const program_state *m_state_b; }; /* A record that can (optionally) be written out when diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc index 5f2fe4c..7cbcf0c 100644 --- a/gcc/analyzer/svalue.cc +++ b/gcc/analyzer/svalue.cc @@ -193,6 +193,14 @@ svalue::can_merge_p (const svalue *other, return NULL; } + /* Reject merging svalues that have non-purgable sm-state, + to avoid falsely reporting memory leaks by merging them + with something else. */ + if (!merger->mergeable_svalue_p (this)) + return NULL; + if (!merger->mergeable_svalue_p (other)) + return NULL; + /* Widening. */ /* Merge: (new_cst, existing_cst) -> widen (existing, new). */ if (maybe_get_constant () && other->maybe_get_constant ()) diff --git a/gcc/testsuite/gcc.dg/analyzer/explode-1.c b/gcc/testsuite/gcc.dg/analyzer/explode-1.c index f48408e..9b95afd 100644 --- a/gcc/testsuite/gcc.dg/analyzer/explode-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/explode-1.c @@ -12,7 +12,7 @@ void test (void) { void *p0, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8; void **pp; - while (get ()) /* { dg-warning "leak" } */ + while (get ()) { switch (get ()) { @@ -47,7 +47,7 @@ void test (void) { default: case 0: - *pp = malloc (16); + *pp = malloc (16); /* { dg-warning "leak" } */ break; case 1: free (*pp); diff --git a/gcc/testsuite/gcc.dg/analyzer/pr103217.c b/gcc/testsuite/gcc.dg/analyzer/pr103217.c new file mode 100644 index 0000000..a0ef8bf --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pr103217.c @@ -0,0 +1,42 @@ +extern char *strdup (const char *__s) + __attribute__ ((__nothrow__ , __leaf__, __malloc__, __nonnull__ (1))); + +extern void abort (void) + __attribute__ ((__nothrow__ , __leaf__, __noreturn__)); + +extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) + __attribute__ ((__nothrow__ , __leaf__, __nonnull__ (2, 3))); +extern char *optarg; + +extern void free (void *__ptr) + __attribute__ ((__nothrow__ , __leaf__)); + +#define NULL ((void *)0) + +char *xstrdup(const char *src) { + char *val = strdup(src); + if (!val) + abort(); + return val; +} + +int main(int argc, char *argv[]) { + char *one = NULL, *two = NULL; + int rc; + + while ((rc = getopt(argc, argv, "a:b:")) != -1) { + switch (rc) { + case 'a': + free(one); + one = xstrdup(optarg); + break; + case 'b': + free(two); + two = xstrdup(optarg); + break; + } + } + free(one); + free(two); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/pr94858-1.c b/gcc/testsuite/gcc.dg/analyzer/pr94858-1.c index f7be1c6..d33c174 100644 --- a/gcc/testsuite/gcc.dg/analyzer/pr94858-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/pr94858-1.c @@ -1,3 +1,5 @@ +/* { dg-additional-options "-Wno-analyzer-too-complex" } */ + #include <stdlib.h> typedef short hashNx; |