From 111dd2b8253acc8471d74e672dadcc12cb779041 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 22 Apr 2020 09:39:52 -0400 Subject: [PATCH 069/179] FIXME: WIP on malloc and alloca --- gcc/analyzer/region-model2.cc | 202 ++++++++++++++++++++++++++++++++-- gcc/analyzer/region-model2.h | 90 ++++++++++++--- 2 files changed, 268 insertions(+), 24 deletions(-) diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc index 84a168756a8..f9125d68dfd 100644 --- a/gcc/analyzer/region-model2.cc +++ b/gcc/analyzer/region-model2.cc @@ -2428,6 +2428,16 @@ heap_region2::compare_fields (const heap_region2 &) const } #endif +void +heap_region2::dump_to_pp (pretty_printer *pp, bool simple) const +{ + if (simple) + { + } + else + pp_string (pp, "heap_region2()"); +} + /* class root_region2 : public region2. */ /* root_region2's default ctor. */ @@ -2895,12 +2905,37 @@ element_region2::add_to_hash (inchash::hash &hstate) const hstate.add_ptr (m_index); } +/* class heap_allocated_region2 : public region2. */ + +void +heap_allocated_region2::dump_to_pp (pretty_printer *pp, bool simple) const +{ + // TODO: + if (simple) + pp_string (pp, "HEAP_ALLOCATED_REGION"); + else + pp_string (pp, "heap_allocated_region2()"); +} + +/* class alloca_region2 : public region2. */ + +void +alloca_region2::dump_to_pp (pretty_printer *pp, bool simple) const +{ + // TODO: + if (simple) + pp_string (pp, "ALLOCA_REGION"); + else + pp_string (pp, "alloca_region2()"); +} + /* class region_model2_manager. */ region_model2_manager::region_model2_manager () : m_next_region_id (0), m_root_region (alloc_region_id ()), m_stack_region (alloc_region_id (), &m_root_region), + m_heap_region (alloc_region_id (), &m_root_region), m_code_region (alloc_region_id (), &m_root_region), m_fndecls_map (), m_globals_region (alloc_region_id (), &m_root_region), @@ -3097,6 +3132,32 @@ region_model2_manager::get_symbolic_region (svalue2 *sval) return const_cast (m_symbolic_regions.consolidate (reg)); } +/* FIXME. */ + +region2 * +region_model2_manager::create_region_for_heap_alloc () +{ + // FIXME: should this be a symbolic region?? + region2 *reg + = new heap_allocated_region2 (alloc_region_id (), &m_heap_region); + m_managed_dynamic_regions.safe_push (reg); + // TODO: selftest; verify no leak under valgrind + return reg; + /* TODO: maybe have an ID to model the different allocators? + (are they different parent regions? or some kind of discriminant var?) */ +} + +/* FIXME. */ + +region2 * +region_model2_manager::create_region_for_alloca (frame_region2 *frame) +{ + region2 *reg = new alloca_region2 (alloc_region_id (), frame); + m_managed_dynamic_regions.safe_push (reg); + // TODO: selftest; verify no leak under valgrind + return reg; +} + /* class region_model2. */ /* FIXME: region_model2's default ctor. */ @@ -3882,6 +3943,7 @@ region_model2::on_gcall (const supernode *snode, if (tree callee_fndecl = get_fndecl_for_call (call, ctxt)) { + /* Get any left-hand side for the destination of the result. */ region2 *lhs_reg = NULL; tree lhs_type = NULL_TREE; if (tree lhs = gimple_call_lhs (call)) @@ -3892,17 +3954,30 @@ region_model2::on_gcall (const supernode *snode, if (is_named_call_p (callee_fndecl, "malloc", call, 1)) { - // TODO: capture size as a svalue2? - gcc_unreachable (); -#if 0 - region2 *new_reg = add_new_malloc_region2 (); + tree size_arg = gimple_call_arg (call, 0); + svalue2 *size_sval = get_rvalue (size_arg, NULL); + region2 *new_reg = create_region_for_heap_alloc (size_sval); if (lhs_reg) { - svalue2 *ptr_sval - = get_or_create_ptr_svalue2 (lhs_type, new_reg); + svalue2 *ptr_sval = m_mgr->get_ptr_svalue2 (lhs_type, new_reg); + set_value (lhs_reg, ptr_sval, ctxt); + } + else + { + // FIXME: report a leak here? + } + return true; + } + else if (is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1)) + { + tree size_arg = gimple_call_arg (call, 0); + svalue2 *size_sval = get_rvalue (size_arg, NULL); + region2 *new_reg = create_region_for_alloca (size_sval); + if (lhs_reg) + { + svalue2 *ptr_sval = m_mgr->get_ptr_svalue2 (lhs_type, new_reg); set_value (lhs_reg, ptr_sval, ctxt); } -#endif return true; } } @@ -4706,6 +4781,20 @@ region_model2::get_rvalue_1 (path_var pv, return sval_binop; } + /* Binary ops. */ + case MULT_EXPR: + { + tree expr = pv.m_tree; + tree arg0 = TREE_OPERAND (expr, 0); + tree arg1 = TREE_OPERAND (expr, 1); + svalue2 *arg0_sval = get_rvalue (arg0, ctxt); + svalue2 *arg1_sval = get_rvalue (arg1, ctxt); + svalue2 *sval_binop + = m_mgr->get_or_create_binop (TREE_TYPE (expr), TREE_CODE (expr), + arg0_sval, arg1_sval); + return sval_binop; + } + case COMPONENT_REF: case MEM_REF: { @@ -6610,6 +6699,44 @@ region_model2::get_fndecl_for_call (const gcall *call, return NULL_TREE; } +/* FIXME. */ + +region2 * +region_model2::create_region_for_heap_alloc (svalue2 *size_in_bytes) +{ + region2 *reg = m_mgr->create_region_for_heap_alloc (); + record_dynamic_extents (reg, size_in_bytes); + return reg; +} + +/* FIXME. */ + +region2 * +region_model2::create_region_for_alloca (svalue2 *size_in_bytes) +{ + region2 *reg = m_mgr->create_region_for_alloca (m_current_frame); + record_dynamic_extents (reg, size_in_bytes); + return reg; +} + +/* FIXME. */ + +void +region_model2::record_dynamic_extents (region2 *reg, svalue2 *size_in_bytes) +{ +#if 0 + svalue2 *extents_sval = m_mgr->get_or_create_extents_svalue2 (reg); + add_constraint (extents_sval, EQ_EXPR, size_in_bytes); + /* TODO: This needs the ability to add constraints in the model based on an + svalue2. */ + /* TODO: should be capture where we got the extents from? (so that we can + display that to the user) Perhaps we can just walk backwards along + the exploded_path to find where the constraint was added. */ + /* FIXME: or should the dynamic extents instead be recorded in a special + data map? */ +#endif +} + /* struct model_merger. */ /* Dump a multiline representation of this merger to PP. */ @@ -8543,6 +8670,65 @@ test_POINTER_PLUS_EXPR_then_MEM_REF () m.set_value (mem_ref, int_42, NULL); } +/* FIXME. */ + +static void +test_malloc () +{ + tree int_star = build_pointer_type (integer_type_node); + tree p = build_global_decl ("p", int_star); + tree n = build_global_decl ("n", integer_type_node); + tree n_times_4 = build2 (MULT_EXPR, size_type_node, + n, build_int_cst (size_type_node, 4)); + + region_model2_manager mgr; + test_region_model2_context ctxt; + region_model2 model (&mgr); + + /* "p = malloc (n * 4);". */ + svalue2 *size_sval = model.get_rvalue (n_times_4, &ctxt); + region2 *reg = model.create_region_for_heap_alloc (size_sval); + svalue2 *ptr = mgr.get_ptr_svalue2 (int_star, reg); + model.set_value (model.get_lvalue (p, &ctxt), ptr, &ctxt); + // TODO: verify dynamic extents + +} + +/* FIXME. */ + +static void +test_alloca () +{ + auto_vec param_types; + tree fndecl = make_fndecl (integer_type_node, + "test_fn", + param_types); + allocate_struct_function (fndecl, true); + + + tree int_star = build_pointer_type (integer_type_node); + tree p = build_global_decl ("p", int_star); + tree n = build_global_decl ("n", integer_type_node); + tree n_times_4 = build2 (MULT_EXPR, size_type_node, + n, build_int_cst (size_type_node, 4)); + + region_model2_manager mgr; + test_region_model2_context ctxt; + region_model2 model (&mgr); + + /* Push stack frame. */ + region2 *frame_reg + = model.push_frame (DECL_STRUCT_FUNCTION (fndecl), + NULL, &ctxt); + /* "p = alloca (n * 4);". */ + svalue2 *size_sval = model.get_rvalue (n_times_4, &ctxt); + region2 *reg = model.create_region_for_alloca (size_sval); + svalue2 *ptr = mgr.get_ptr_svalue2 (int_star, reg); + model.set_value (model.get_lvalue (p, &ctxt), ptr, &ctxt); + // TODO: verify dynamic extents + // TODO: try popping the frame; verify that the alloca region is poisoned +} + /* Run all of the selftests within this file. */ void @@ -8583,6 +8769,8 @@ analyzer_region_model2_cc_tests () test_array (); test_mem_ref (); test_POINTER_PLUS_EXPR_then_MEM_REF (); + test_malloc (); + test_alloca (); } } // namespace selftest diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h index 1b6442855e9..f01541de334 100644 --- a/gcc/analyzer/region-model2.h +++ b/gcc/analyzer/region-model2.h @@ -753,7 +753,9 @@ namespace ana { label_region (RK_LABEL) symbolic_region (RK_SYMBOLIC) field_region2 (RK_FIELD) - element_region2 (RK_ELEMENT). */ + element_region2 (RK_ELEMENT) + heap_allocated_region2 (RK_HEAP_ALLOCATED) + alloca_region2 (RK_ALLOCA). */ /* Abstract base class representing a chunk of memory. @@ -785,7 +787,9 @@ public: RK_SYMBOLIC, RK_DECL, RK_FIELD, - RK_ELEMENT + RK_ELEMENT, + RK_HEAP_ALLOCATED, + RK_ALLOCA }; static const char *kind_to_str (enum kind); @@ -1230,8 +1234,6 @@ is_a_helper ::test (region2 *reg) return reg->get_kind () == region2::RK_STACK; } -#if 0 - namespace ana { /* Concrete region2 subclass: a region2 within which region2s can be @@ -1240,25 +1242,22 @@ namespace ana { class heap_region2 : public region2 { public: - heap_region2 (region2 *parent) - : region2 (parent, NULL_TREE) + heap_region2 (unsigned id, region2 *parent) + : region2 (id, parent, NULL_TREE) {} - heap_region2 (const heap_region2 &other); - - bool compare_fields (const heap_region2 &other) const; #if 0 - region2 *clone () const FINAL OVERRIDE; + bool compare_fields (const heap_region2 &other) const; #endif enum kind get_kind () const FINAL OVERRIDE { return RK_HEAP; } + void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; #if 0 static bool can_merge_p (const heap_region2 *heap_a, region2_id heap_a_rid, const heap_region2 *heap_b, region2_id heap_b_rid, heap_region2 *merged_heap, region2_id merged_heap_rid, model_merger *merger); #endif - }; } // namespace ana @@ -1271,8 +1270,6 @@ is_a_helper ::test (region2 *reg) return reg->get_kind () == region2::RK_HEAP; } -#endif - namespace ana { /* Concrete region2 subclass. The root region2, containing all region2s @@ -1286,10 +1283,6 @@ public: bool compare_fields (const root_region2 &other) const; -#if 0 - region2 *clone () const FINAL OVERRIDE; -#endif - enum kind get_kind () const FINAL OVERRIDE { return RK_ROOT; } void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; @@ -1470,6 +1463,52 @@ private: svalue2 *m_index; }; +/* An untyped region dynamically allocated on the heap via "malloc" + or similar. */ +// FIXME: ...but could be NULL + +class heap_allocated_region2 : public region2 +{ +public: + heap_allocated_region2 (unsigned id, region2 *parent) + : region2 (id, parent, NULL_TREE) + {} + +#if 0 + bool compare_fields (const element_region2 &other) const; +#endif + enum region2::kind get_kind () const FINAL OVERRIDE { return RK_HEAP_ALLOCATED; } + + void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; + +protected: +#if 0 + void add_to_hash (inchash::hash &hstate) const OVERRIDE; +#endif +}; + +/* An untyped region dynamically allocated on the stack via "alloca". */ + +class alloca_region2 : public region2 +{ +public: + alloca_region2 (unsigned id, frame_region2 *parent) + : region2 (id, parent, NULL_TREE) + {} + +#if 0 + bool compare_fields (const element_region2 &other) const; +#endif + enum region2::kind get_kind () const FINAL OVERRIDE { return RK_ALLOCA; } + + void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; + +protected: +#if 0 + void add_to_hash (inchash::hash &hstate) const OVERRIDE; +#endif +}; + /* A class responsible for owning and consolidating region2 and svalue2 instances. */ @@ -1509,10 +1548,17 @@ public: store2_manager *get_store2_manager () { return &m_store2_mgr; } + /* Dynamically-allocated region2 instances. + FIXME: for now, the number of these within the analysis can grow + arbitrarily. They are still owned by the manager. */ + region2 *create_region_for_heap_alloc (); + region2 *create_region_for_alloca (frame_region2 *frame); + private: unsigned m_next_region_id; root_region2 m_root_region; stack_region2 m_stack_region; + heap_region2 m_heap_region; /* svalue2 consolidation. */ typedef hash_map constants_map_t; @@ -1554,6 +1600,11 @@ private: uniq_manager m_symbolic_regions; store2_manager m_store2_mgr; + + /* "Dynamically-allocated" region2 instances. + FIXME: for now, the number of these within the analysis can grow + arbitrarily. They are still owned by the manager. */ + auto_delete_vec m_managed_dynamic_regions; }; /* A region_model2 encapsulates a representation of the state of memory, with @@ -1703,7 +1754,11 @@ class region_model2 tree maybe_get_constant (svalue2_id sid) const; region2_id add_new_malloc_region2 (); +#endif + region2 *create_region_for_heap_alloc (svalue2 *size_in_bytes); + region2 *create_region_for_alloca (svalue2 *size_in_bytes); +#if 0 tree get_representative_tree (svalue2_id sid) const; path_var get_representative_path_var (region2_id rid) const; void get_path_vars_for_svalue2 (svalue2_id sid, vec *out) const; @@ -1809,6 +1864,7 @@ class region_model2 bool *is_first); #endif + void record_dynamic_extents (region2 *reg, svalue2 *size_in_bytes); /* Storing this here to avoid passing it around everywhere. */ region_model2_manager *const m_mgr; -- 2.21.0