diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 50652b2cfa6b8376366fd8db369698a488bdab9e..df04b0ba5d6343025d056b4cf24204b1df03d107 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -1185,6 +1185,8 @@ exploded_node::on_stmt (exploded_graph &eg, to stderr. */ state->dump (eg.get_ext_state (), true); } + else if (is_special_named_call_p (call, "__analyzer_dump_capacity", 1)) + state->m_region_model->impl_call_analyzer_dump_capacity (call, &ctxt); else if (is_special_named_call_p (call, "__analyzer_dump_path", 0)) { /* Handle the builtin "__analyzer_dump_path" by queuing a @@ -1237,7 +1239,6 @@ exploded_node::on_stmt (exploded_graph &eg, if (terminate_path) return on_stmt_flags::terminate_path (); - bool any_sm_changes = false; int sm_idx; sm_state_map *smap; FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap) @@ -1276,14 +1277,12 @@ exploded_node::on_stmt (exploded_graph &eg, /* Allow the state_machine to handle the stmt. */ if (sm.on_stmt (&sm_ctxt, snode, stmt)) unknown_side_effects = false; - if (*old_smap != *new_smap) - any_sm_changes = true; } if (const gcall *call = dyn_cast <const gcall *> (stmt)) state->m_region_model->on_call_post (call, unknown_side_effects, &ctxt); - return on_stmt_flags (any_sm_changes); + return on_stmt_flags (); } /* Consider the effect of following superedge SUCC from this node. @@ -2925,6 +2924,36 @@ stmt_requires_new_enode_p (const gimple *stmt, return false; } +/* Return true if OLD_STATE and NEW_STATE are sufficiently different that + we should split enodes and create an exploded_edge separating them + (which makes it easier to identify state changes of intereset when + constructing checker_paths). */ + +static bool +state_change_requires_new_enode_p (const program_state &old_state, + const program_state &new_state) +{ + /* Changes in dynamic extents signify creations of heap/alloca regions + and resizings of heap regions; likely to be of interest in + diagnostic paths. */ + if (old_state.m_region_model->get_dynamic_extents () + != new_state.m_region_model->get_dynamic_extents ()) + return true; + + /* Changes in sm-state are of interest. */ + int sm_idx; + sm_state_map *smap; + FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap) + { + const sm_state_map *old_smap = old_state.m_checker_states[sm_idx]; + const sm_state_map *new_smap = new_state.m_checker_states[sm_idx]; + if (*old_smap != *new_smap) + return true; + } + + return false; +} + /* The core of exploded_graph::process_worklist (the main analysis loop), handling one node in the worklist. @@ -3067,7 +3096,8 @@ exploded_graph::process_node (exploded_node *node) next_state = next_state.prune_for_point (*this, next_point, node, &uncertainty); - if (flags.m_sm_changes || flag_analyzer_fine_grained) + if (flag_analyzer_fine_grained + || state_change_requires_new_enode_p (old_state, next_state)) { program_point split_point = program_point::before_stmt (point.get_supernode (), diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h index c67f7b70605a47394f7310cff0602899b85edd31..eb1baefad692972b011a32cc8cba34fbb653a7df 100644 --- a/gcc/analyzer/exploded-graph.h +++ b/gcc/analyzer/exploded-graph.h @@ -198,33 +198,21 @@ class exploded_node : public dnode<eg_traits> /* The result of on_stmt. */ struct on_stmt_flags { - on_stmt_flags (bool sm_changes) - : m_sm_changes (sm_changes), - m_terminate_path (false) + on_stmt_flags () : m_terminate_path (false) {} static on_stmt_flags terminate_path () { - return on_stmt_flags (true, true); + return on_stmt_flags (true); } - static on_stmt_flags state_change (bool any_sm_changes) - { - return on_stmt_flags (any_sm_changes, false); - } - - /* Did any sm-changes occur handling the stmt. */ - bool m_sm_changes : 1; - /* Should we stop analyzing this path (on_stmt may have already added nodes/edges, e.g. when handling longjmp). */ bool m_terminate_path : 1; private: - on_stmt_flags (bool sm_changes, - bool terminate_path) - : m_sm_changes (sm_changes), - m_terminate_path (terminate_path) + on_stmt_flags (bool terminate_path) + : m_terminate_path (terminate_path) {} }; diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index 76959c135db30785ab8df4fd7f2bfbbcef510c3f..67dd785297ef1d48930a56e096c66513cd7e47f6 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -1270,6 +1270,15 @@ program_state::detect_leaks (const program_state &src_state, /* Purge dead svals from constraints. */ dest_state.m_region_model->get_constraints ()->on_liveness_change (maybe_dest_svalues, dest_state.m_region_model); + + /* Purge dead heap-allocated regions from dynamic extents. */ + for (const svalue *sval : dead_svals) + if (const region_svalue *region_sval = sval->dyn_cast_region_svalue ()) + { + const region *reg = region_sval->get_pointee (); + if (reg->get_kind () == RK_HEAP_ALLOCATED) + dest_state.m_region_model->unset_dynamic_extents (reg); + } } #if CHECKING_P @@ -1426,7 +1435,7 @@ test_program_state_1 () program_state s (ext_state); region_model *model = s.m_region_model; const svalue *size_in_bytes - = mgr->get_or_create_unknown_svalue (integer_type_node); + = mgr->get_or_create_unknown_svalue (size_type_node); const region *new_reg = model->create_region_for_heap_alloc (size_in_bytes); const svalue *ptr_sval = mgr->get_ptr_svalue (ptr_type_node, new_reg); model->set_value (model->get_lvalue (p, NULL), @@ -1482,7 +1491,7 @@ test_program_state_merging () region_model *model0 = s0.m_region_model; const svalue *size_in_bytes - = mgr->get_or_create_unknown_svalue (integer_type_node); + = mgr->get_or_create_unknown_svalue (size_type_node); const region *new_reg = model0->create_region_for_heap_alloc (size_in_bytes); const svalue *ptr_sval = mgr->get_ptr_svalue (ptr_type_node, new_reg); model0->set_value (model0->get_lvalue (p, &ctxt), diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc index 4052bb393f82dcb68f740429ced42369d1095a6b..099520a95b0da7c6ae6d9a16a89ef124aa0d768e 100644 --- a/gcc/analyzer/region-model-impl-calls.cc +++ b/gcc/analyzer/region-model-impl-calls.cc @@ -206,6 +206,25 @@ region_model::impl_call_analyzer_describe (const gcall *call, warning_at (call->location, 0, "svalue: %qs", desc.m_buffer); } +/* Handle a call to "__analyzer_dump_capacity". + + Emit a warning describing the capacity of the base region of + the region pointed to by the 1st argument. + This is for use when debugging, and may be of use in DejaGnu tests. */ + +void +region_model::impl_call_analyzer_dump_capacity (const gcall *call, + region_model_context *ctxt) +{ + tree t_ptr = gimple_call_arg (call, 0); + const svalue *sval_ptr = get_rvalue (t_ptr, ctxt); + const region *reg = deref_rvalue (sval_ptr, t_ptr, ctxt); + const region *base_reg = reg->get_base_region (); + const svalue *capacity = get_capacity (base_reg); + label_text desc = capacity->get_desc (true); + warning_at (call->location, 0, "capacity: %qs", desc.m_buffer); +} + /* Handle a call to "__analyzer_eval" by evaluating the input and dumping as a dummy warning, so that test cases can use dg-warning to validate the result (and so unexpected warnings will @@ -312,6 +331,7 @@ region_model::impl_call_free (const call_details &cd) poisoning pointers. */ const region *freed_reg = ptr_to_region_sval->get_pointee (); unbind_region_and_descendents (freed_reg, POISON_KIND_FREED); + m_dynamic_extents.remove (freed_reg); } } diff --git a/gcc/analyzer/region-model-reachability.h b/gcc/analyzer/region-model-reachability.h index c6a21e98e61ecb1dfe5cb798c29e7aed67a4349c..57daf7255fbd8579cd923bc47f0c60b099b3d84d 100644 --- a/gcc/analyzer/region-model-reachability.h +++ b/gcc/analyzer/region-model-reachability.h @@ -89,6 +89,14 @@ public: { return m_mutable_svals.end (); } + hash_set<const region *>::iterator begin_mutable_base_regs () + { + return m_mutable_base_regs.begin (); + } + hash_set<const region *>::iterator end_mutable_base_regs () + { + return m_mutable_base_regs.end (); + } void dump_to_pp (pretty_printer *pp) const; diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 43f991a2a29c81291ea06790980e9237a8f6351a..e02a89765f0c79104e8aadb6f6332c9a8df336a2 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -66,6 +66,7 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/analyzer-selftests.h" #include "stor-layout.h" #include "attribs.h" +#include "tree-object-size.h" #if ENABLE_ANALYZER @@ -225,7 +226,8 @@ region_to_value_map::can_merge_with_p (const region_to_value_map &other, /* Ctor for region_model: construct an "empty" model. */ region_model::region_model (region_model_manager *mgr) -: m_mgr (mgr), m_store (), m_current_frame (NULL) +: m_mgr (mgr), m_store (), m_current_frame (NULL), + m_dynamic_extents () { m_constraints = new constraint_manager (mgr); } @@ -235,7 +237,8 @@ region_model::region_model (region_model_manager *mgr) region_model::region_model (const region_model &other) : m_mgr (other.m_mgr), m_store (other.m_store), m_constraints (new constraint_manager (*other.m_constraints)), - m_current_frame (other.m_current_frame) + m_current_frame (other.m_current_frame), + m_dynamic_extents (other.m_dynamic_extents) { } @@ -261,6 +264,8 @@ region_model::operator= (const region_model &other) m_current_frame = other.m_current_frame; + m_dynamic_extents = other.m_dynamic_extents; + return *this; } @@ -285,6 +290,9 @@ region_model::operator== (const region_model &other) const if (m_current_frame != other.m_current_frame) return false; + if (m_dynamic_extents != other.m_dynamic_extents) + return false; + gcc_checking_assert (hash () == other.hash ()); return true; @@ -346,6 +354,13 @@ region_model::dump_to_pp (pretty_printer *pp, bool simple, m_constraints->dump_to_pp (pp, multiline); if (!multiline) pp_string (pp, "}"); + + /* Dump sizes of dynamic regions, if any are known. */ + if (!m_dynamic_extents.is_empty ()) + { + pp_string (pp, "dynamic_extents:"); + m_dynamic_extents.dump_to_pp (pp, simple, multiline); + } } /* Dump a representation of this model to FILE. */ @@ -1140,6 +1155,17 @@ region_model::handle_unrecognized_call (const gcall *call, /* Update bindings for all clusters that have escaped, whether above, or previously. */ m_store.on_unknown_fncall (call, m_mgr->get_store_manager ()); + + /* Purge dynamic extents from any regions that have escaped mutably: + realloc could have been called on them. */ + for (hash_set<const region *>::iterator + iter = reachable_regs.begin_mutable_base_regs (); + iter != reachable_regs.end_mutable_base_regs (); + ++iter) + { + const region *base_reg = (*iter); + unset_dynamic_extents (base_reg); + } } /* Traverse the regions in this model, determining what regions are @@ -1972,6 +1998,41 @@ region_model::check_for_writable_region (const region* dest_reg, } } +/* Get the capacity of REG in bytes. */ + +const svalue * +region_model::get_capacity (const region *reg) const +{ + switch (reg->get_kind ()) + { + default: + break; + case RK_DECL: + { + const decl_region *decl_reg = as_a <const decl_region *> (reg); + tree decl = decl_reg->get_decl (); + if (TREE_CODE (decl) == SSA_NAME) + { + tree type = TREE_TYPE (decl); + tree size = TYPE_SIZE (type); + return get_rvalue (size, NULL); + } + else + { + tree size = decl_init_size (decl, false); + if (size) + return get_rvalue (size, NULL); + } + } + break; + } + + if (const svalue *recorded = get_dynamic_extents (reg)) + return recorded; + + return m_mgr->get_or_create_unknown_svalue (sizetype); +} + /* Set the value of the region given by LHS_REG to the value given by RHS_SVAL. */ @@ -2241,6 +2302,12 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs, if (ctxt) ctxt->on_condition (lhs, op, rhs); + /* If we have ®ION == NULL, then drop dynamic extents for REGION (for + the case where REGION is heap-allocated and thus could be NULL). */ + if (op == EQ_EXPR && zerop (rhs)) + if (const region_svalue *region_sval = lhs_sval->dyn_cast_region_svalue ()) + unset_dynamic_extents (region_sval->get_pointee ()); + return true; } @@ -3146,7 +3213,8 @@ region_model::get_frame_at_index (int index) const /* Unbind svalues for any regions in REG and below. Find any pointers to such regions; convert them to - poisoned values of kind PKIND. */ + poisoned values of kind PKIND. + Also purge any dynamic extents. */ void region_model::unbind_region_and_descendents (const region *reg, @@ -3167,6 +3235,15 @@ region_model::unbind_region_and_descendents (const region *reg, /* Find any pointers to REG or its descendents; convert to poisoned. */ poison_any_pointers_to_descendents (reg, pkind); + + /* Purge dynamic extents of any base regions in REG and below + (e.g. VLAs and alloca stack regions). */ + for (auto iter : m_dynamic_extents) + { + const region *iter_reg = iter.first; + if (iter_reg->descendent_of_p (reg)) + unset_dynamic_extents (iter_reg); + } } /* Implementation of BindingVisitor. @@ -3241,6 +3318,10 @@ region_model::can_merge_with_p (const region_model &other_model, &m)) return false; + if (!m_dynamic_extents.can_merge_with_p (other_model.m_dynamic_extents, + &out_model->m_dynamic_extents)) + return false; + /* Merge constraints. */ constraint_manager::merge (*m_constraints, *other_model.m_constraints, @@ -3322,7 +3403,8 @@ const region * region_model::create_region_for_heap_alloc (const svalue *size_in_bytes) { const region *reg = m_mgr->create_region_for_heap_alloc (); - record_dynamic_extents (reg, size_in_bytes); + assert_compat_types (size_in_bytes->get_type (), size_type_node); + set_dynamic_extents (reg, size_in_bytes); return reg; } @@ -3333,18 +3415,38 @@ const region * region_model::create_region_for_alloca (const svalue *size_in_bytes) { const region *reg = m_mgr->create_region_for_alloca (m_current_frame); - record_dynamic_extents (reg, size_in_bytes); + assert_compat_types (size_in_bytes->get_type (), size_type_node); + set_dynamic_extents (reg, size_in_bytes); return reg; } -/* Placeholder hook for recording that the size of REG is SIZE_IN_BYTES. - Currently does nothing. */ +/* Record that the size of REG is SIZE_IN_BYTES. */ void -region_model:: -record_dynamic_extents (const region *reg ATTRIBUTE_UNUSED, - const svalue *size_in_bytes ATTRIBUTE_UNUSED) +region_model::set_dynamic_extents (const region *reg, + const svalue *size_in_bytes) +{ + assert_compat_types (size_in_bytes->get_type (), size_type_node); + m_dynamic_extents.put (reg, size_in_bytes); +} + +/* Get the recording of REG in bytes, or NULL if no dynamic size was + recorded. */ + +const svalue * +region_model::get_dynamic_extents (const region *reg) const { + if (const svalue * const *slot = m_dynamic_extents.get (reg)) + return *slot; + return NULL; +} + +/* Unset any recorded dynamic size of REG. */ + +void +region_model::unset_dynamic_extents (const region *reg) +{ + m_dynamic_extents.remove (reg); } /* struct model_merger. */ @@ -4644,7 +4746,7 @@ test_state_merging () { test_region_model_context ctxt; region_model model0 (&mgr); - tree size = build_int_cst (integer_type_node, 1024); + tree size = build_int_cst (size_type_node, 1024); const svalue *size_sval = mgr.get_or_create_constant_svalue (size); const region *new_reg = model0.create_region_for_heap_alloc (size_sval); const svalue *ptr_sval = mgr.get_ptr_svalue (ptr_type_node, new_reg); @@ -5034,7 +5136,7 @@ test_malloc_constraints () tree null_ptr = build_int_cst (ptr_type_node, 0); const svalue *size_in_bytes - = mgr.get_or_create_unknown_svalue (integer_type_node); + = mgr.get_or_create_unknown_svalue (size_type_node); const region *reg = model.create_region_for_heap_alloc (size_in_bytes); const svalue *sval = mgr.get_ptr_svalue (ptr_type_node, reg); model.set_value (model.get_lvalue (p, NULL), sval, NULL); @@ -5259,7 +5361,7 @@ test_malloc () const region *reg = model.create_region_for_heap_alloc (size_sval); const svalue *ptr = mgr.get_ptr_svalue (int_star, reg); model.set_value (model.get_lvalue (p, &ctxt), ptr, &ctxt); - // TODO: verify dynamic extents + ASSERT_EQ (model.get_capacity (reg), size_sval); } /* Verify that alloca works. */ @@ -5294,7 +5396,7 @@ test_alloca () ASSERT_EQ (reg->get_parent_region (), frame_reg); const svalue *ptr = mgr.get_ptr_svalue (int_star, reg); model.set_value (model.get_lvalue (p, &ctxt), ptr, &ctxt); - // TODO: verify dynamic extents + ASSERT_EQ (model.get_capacity (reg), size_sval); /* Verify that the pointers to the alloca region are replaced by poisoned values when the frame is popped. */ diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index 0afcb8635a9dd531db4dbedb3ee9b14d0ad298f0..8b669df00beffbba6848ef7a8961c991b5398908 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -163,6 +163,8 @@ public: m_hash_map.remove (reg); } + bool is_empty () const { return m_hash_map.is_empty (); } + void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const; void dump (bool simple) const; @@ -450,12 +452,16 @@ private: a tree of regions, along with their associated values. The representation is graph-like because values can be pointers to regions. - It also stores a constraint_manager, capturing relationships between - the values. */ + It also stores: + - a constraint_manager, capturing relationships between the values, and + - dynamic extents, mapping dynamically-allocated regions to svalues (their + capacities). */ class region_model { public: + typedef region_to_value_map dynamic_extents_t; + region_model (region_model_manager *mgr); region_model (const region_model &other); ~region_model (); @@ -495,6 +501,8 @@ class region_model bool impl_call_alloca (const call_details &cd); void impl_call_analyzer_describe (const gcall *call, region_model_context *ctxt); + void impl_call_analyzer_dump_capacity (const gcall *call, + region_model_context *ctxt); void impl_call_analyzer_eval (const gcall *call, region_model_context *ctxt); bool impl_call_builtin_expect (const call_details &cd); @@ -606,6 +614,16 @@ class region_model store *get_store () { return &m_store; } const store *get_store () const { return &m_store; } + const dynamic_extents_t & + get_dynamic_extents () const + { + return m_dynamic_extents; + } + const svalue *get_dynamic_extents (const region *reg) const; + void set_dynamic_extents (const region *reg, + const svalue *size_in_bytes); + void unset_dynamic_extents (const region *reg); + region_model_manager *get_manager () const { return m_mgr; } void unbind_region_and_descendents (const region *reg, @@ -629,6 +647,8 @@ class region_model void loop_replay_fixup (const region_model *dst_state); + const svalue *get_capacity (const region *reg) const; + private: const region *get_lvalue_1 (path_var pv, region_model_context *ctxt) const; const svalue *get_rvalue_1 (path_var pv, region_model_context *ctxt) const; @@ -676,9 +696,6 @@ class region_model void on_top_level_param (tree param, region_model_context *ctxt); - void record_dynamic_extents (const region *reg, - const svalue *size_in_bytes); - bool called_from_main_p () const; const svalue *get_initial_value_for_global (const region *reg) const; @@ -693,6 +710,12 @@ class region_model constraint_manager *m_constraints; // TODO: embed, rather than dynalloc? const frame_region *m_current_frame; + + /* Map from base region to size in bytes, for tracking the sizes of + dynamically-allocated regions. + This is part of the region_model rather than the region to allow for + memory regions to be resized (e.g. by realloc). */ + dynamic_extents_t m_dynamic_extents; }; /* Some region_model activity could lead to warnings (e.g. attempts to use an diff --git a/gcc/doc/analyzer.texi b/gcc/doc/analyzer.texi index 26808ff5d22b57a281d32fbcb24d00130d7c8c7c..2ca4bf61352fd03a399e042ae7f19c8eef7658cb 100644 --- a/gcc/doc/analyzer.texi +++ b/gcc/doc/analyzer.texi @@ -479,6 +479,13 @@ __analyzer_dump (); will dump the copious information about the analyzer's state each time it reaches the call in its traversal of the source. +@smallexample +extern void __analyzer_dump_capacity (const void *ptr); +@end smallexample + +will emit a warning describing the capacity of the base region of +the region pointed to by the 1st argument. + @smallexample __analyzer_dump_path (); @end smallexample diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h b/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h index d96b3f2f29a3fae192369f1c6203efdb3c30293f..24466939882a5a2e89262fc32287f22c02bc9908 100644 --- a/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h +++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h @@ -15,6 +15,9 @@ extern void __analyzer_describe (int verbosity, ...); /* Dump copious information about the analyzer’s state when reached. */ extern void __analyzer_dump (void); +/* Emit a warning describing the size of the base region of (*ptr). */ +extern void __analyzer_dump_capacity (const void *ptr); + /* Dump information after analysis on all of the exploded nodes at this program point. diff --git a/gcc/testsuite/gcc.dg/analyzer/capacity-1.c b/gcc/testsuite/gcc.dg/analyzer/capacity-1.c new file mode 100644 index 0000000000000000000000000000000000000000..9ea41f72e1d286f0bb46bd8cff869e0ede92e094 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/capacity-1.c @@ -0,0 +1,106 @@ +#include <stdlib.h> +#include "analyzer-decls.h" + +typedef unsigned __INT32_TYPE__ u32; + +void +test_1 (void) +{ + char buf[16]; + __analyzer_dump_capacity (buf); /* { dg-warning "capacity: '\\(sizetype\\)16'" } */ +} + +void +test_2 (void) +{ + char ch; + __analyzer_dump_capacity (&ch); /* { dg-warning "capacity: '\\(sizetype\\)1'" } */ +} + +struct s3 { char buf[100]; }; + +void +test_3 (void) +{ + struct s3 s; + __analyzer_dump_capacity (&s); /* { dg-warning "capacity: '\\(sizetype\\)100'" } */ +} + +/* Capacity refers to the base region, not any offset within it. */ + +void +test_4 (void) +{ + char buf[1024]; + __analyzer_dump_capacity (buf + 100); /* { dg-warning "capacity: '\\(sizetype\\)1024'" } */ +} + +void +test_5 (void *p) +{ + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'UNKNOWN\\(sizetype\\)'" } */ +} + +void +test_malloc (void) +{ + void *p = malloc (1024); + + __analyzer_dump_capacity (p); /* { dg-warning "capacity: '\\(size_t\\)1024'" } */ + free (p); +} + +void +test_alloca (size_t sz) +{ + void *p = alloca (sz); + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'INIT_VAL\\(sz_\[^\n\r\]*\\)'" } */ +} + +void +test_vla (size_t sz) +{ + char buf[sz]; + __analyzer_dump_capacity (buf); /* { dg-warning "capacity: 'INIT_VAL\\(sz_\[^\n\r\]*\\)'" } */ +} + +static void * __attribute__((noinline)) +called_by_test_interproc_malloc (size_t a) +{ + return malloc (a); +} + +void * +test_interproc_malloc (size_t sz) +{ + void *p = called_by_test_interproc_malloc (sz); + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'INIT_VAL\\(sz_\[^\n\r\]*\\)'" } */ + return p; +} + +struct s +{ + u32 f1; + char arr[]; +}; + +static struct s * __attribute__((noinline)) +alloc_s (size_t num) +{ + struct s *p = malloc (sizeof(struct s) + num); + return p; +} + +struct s * +test_trailing_array (void) +{ + struct s *p = alloc_s (5); + __analyzer_dump_capacity (p); /* { dg-warning "capacity: '\\(\[^\n\r\]*\\)9'" } */ + return p; +} + +void +test_unknown_arr (int p[]) +{ + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'UNKNOWN\\(sizetype\\)'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/capacity-2.c b/gcc/testsuite/gcc.dg/analyzer/capacity-2.c new file mode 100644 index 0000000000000000000000000000000000000000..9f92bcfc0a4cafe58e1206e6b010338e7febdada --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/capacity-2.c @@ -0,0 +1,53 @@ +#include <stdlib.h> +#include "analyzer-decls.h" + +extern void might_realloc (void *); +extern void cant_realloc (const void *); + +void * +test_realloc_1 (void *p, size_t new_sz) +{ + void *q = realloc (p, new_sz); + __analyzer_dump_capacity (q); /* { dg-warning "capacity: 'UNKNOWN\\(sizetype\\)'" } */ + return q; +} + +void * +test_realloc_2 (size_t sz_a, size_t sz_b) +{ + void *p = malloc (sz_a); + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'INIT_VAL\\(sz_a_\[^\n\r\]*\\)'" } */ + void *q = realloc (p, sz_b); + __analyzer_dump_capacity (q); /* { dg-warning "capacity: 'UNKNOWN\\(sizetype\\)'" } */ + return p; +} + +void * +test_might_realloc (void) +{ + void *p = malloc (1024); + + __analyzer_dump_capacity (p); /* { dg-warning "capacity: '\\(size_t\\)1024'" } */ + + might_realloc (p); + + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'UNKNOWN\\(sizetype\\)'" } */ + + return p; +} + +void * +test_cant_realloc (void) +{ + void *p = malloc (1024); + + __analyzer_dump_capacity (p); /* { dg-warning "capacity: '\\(size_t\\)1024'" } */ + + cant_realloc (p); + + __analyzer_dump_capacity (p); /* { dg-warning "capacity: '\\(size_t\\)1024'" } */ + + return p; +} + + diff --git a/gcc/testsuite/gcc.dg/analyzer/capacity-3.c b/gcc/testsuite/gcc.dg/analyzer/capacity-3.c new file mode 100644 index 0000000000000000000000000000000000000000..41e282cee92deb35b99d450e04c63c3f451800f2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/capacity-3.c @@ -0,0 +1,82 @@ +#include <stdlib.h> +#include "analyzer-decls.h" + +static void __attribute__((noinline)) +__analyzer_callee_1 (size_t inner_sz) +{ + void *p = alloca (inner_sz); + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'INIT_VAL\\(outer_sz_\[^\n\r\]*\\)'" } */ +} + +void +test_1 (int flag, size_t outer_sz) +{ + if (flag) + __analyzer_callee_1 (outer_sz); + + /* Verify that we merge state; in particular, the dynamic size of "p" + in the called frame should have been purged. */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */ +} + +void +test_2 (int flag, size_t sz) +{ + if (flag) + { + void *p = malloc (sz); + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'INIT_VAL\\(sz_\[^\n\r\]*\\)'" } */ + free (p); + /* The dynamic size of "p" should have been purged. */ + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'UNKNOWN\\(sizetype\\)'" } */ + } + + /* Verify that we merge state. */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */ +} +/* Verify that we purge state on the NULL branch when a malloc result is + tested against NULL. */ + +void +test_3 (size_t sz) +{ + void *p = malloc (sz); + + if (p) + { + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'INIT_VAL\\(sz_\[^\n\r\]*\\)'" } */ + } + else + { + /* The dynamic size of "p" should have been purged + due to "p" being equal to NULL. */ + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'UNKNOWN\\(sizetype\\)'" } */ + } + + free (p); + + /* The dynamic size of "p" should have been purged. */ + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'UNKNOWN\\(sizetype\\)'" } */ + + /* Verify that we merge state. */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */ +} + +/* Verify that we purge dynamic extent of a pointer when it leaks. */ + +static void __attribute__((noinline)) +__analyzer_callee_4 (size_t inner_sz) +{ + void *p = malloc (inner_sz); + __analyzer_dump_capacity (p); /* { dg-warning "capacity: 'INIT_VAL\\(outer_sz_\[^\n\r\]*\\)'" } */ +} /* { dg-warning "leak of 'p'" } */ + +void +test_4 (int flag, size_t outer_sz) +{ + if (flag) + __analyzer_callee_4 (outer_sz); + + /* Verify that we merge state. */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */ +}