diff options
Diffstat (limited to 'gcc/analyzer/region-model.cc')
-rw-r--r-- | gcc/analyzer/region-model.cc | 1310 |
1 files changed, 935 insertions, 375 deletions
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 84b81e9..618d96b 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -18,70 +18,53 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ -#include "config.h" #define INCLUDE_ALGORITHM -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "diagnostic-core.h" -#include "graphviz.h" +#include "analyzer/common.h" + +#include "ordered-hash-map.h" #include "options.h" #include "cgraph.h" -#include "tree-dfa.h" +#include "cfg.h" +#include "sbitmap.h" +#include "diagnostics/event-id.h" +#include "stor-layout.h" #include "stringpool.h" -#include "convert.h" +#include "attribs.h" +#include "tree-object-size.h" +#include "gimple-ssa.h" +#include "tree-phinodes.h" +#include "tree-ssa-operands.h" +#include "ssa-iterators.h" #include "target.h" -#include "fold-const.h" +#include "calls.h" +#include "is-a.h" +#include "gcc-rich-location.h" +#include "gcc-urlifier.h" +#include "diagnostics/sarif-sink.h" #include "tree-pretty-print.h" -#include "diagnostic-color.h" -#include "bitmap.h" -#include "selftest.h" +#include "fold-const.h" #include "selftest-tree.h" -#include "analyzer/analyzer.h" + +#include "text-art/tree-widget.h" + #include "analyzer/analyzer-logging.h" -#include "ordered-hash-map.h" -#include "options.h" -#include "cgraph.h" -#include "cfg.h" #include "analyzer/supergraph.h" -#include "sbitmap.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/constraint-manager.h" -#include "diagnostic-event-id.h" -#include "analyzer/sm.h" -#include "diagnostic-event-id.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/region-model-reachability.h" #include "analyzer/analyzer-selftests.h" #include "analyzer/program-state.h" #include "analyzer/call-summary.h" -#include "stor-layout.h" -#include "attribs.h" -#include "tree-object-size.h" -#include "gimple-ssa.h" -#include "tree-phinodes.h" -#include "tree-ssa-operands.h" -#include "ssa-iterators.h" -#include "calls.h" -#include "is-a.h" -#include "gcc-rich-location.h" #include "analyzer/checker-event.h" #include "analyzer/checker-path.h" #include "analyzer/feasible-graph.h" #include "analyzer/record-layout.h" -#include "diagnostic-format-sarif.h" -#include "text-art/tree-widget.h" -#include "gcc-urlifier.h" +#include "analyzer/function-set.h" #if ENABLE_ANALYZER @@ -176,7 +159,7 @@ region_to_value_map::operator== (const region_to_value_map &other) const const region *reg = iter.first; const svalue *sval = iter.second; const svalue * const *other_slot = other.get (reg); - if (other_slot == NULL) + if (other_slot == nullptr) return false; if (sval != *other_slot) return false; @@ -235,7 +218,7 @@ region_to_value_map::dump (bool simple) const std::unique_ptr<json::object> region_to_value_map::to_json () const { - auto map_obj = ::make_unique<json::object> (); + auto map_obj = std::make_unique<json::object> (); auto_vec<const region *> regs; for (iterator iter = begin (); iter != end (); ++iter) @@ -332,12 +315,97 @@ region_to_value_map::purge_state_involving (const svalue *sval) m_hash_map.remove (iter); } +// struct exception_node + +bool +exception_node::operator== (const exception_node &other) const +{ + return (m_exception_sval == other.m_exception_sval + && m_typeinfo_sval == other.m_typeinfo_sval + && m_destructor_sval == other.m_destructor_sval); +} + +void +exception_node::dump_to_pp (pretty_printer *pp, + bool simple) const +{ + pp_printf (pp, "{exception: "); + m_exception_sval->dump_to_pp (pp, simple); + pp_string (pp, ", typeinfo: "); + m_typeinfo_sval->dump_to_pp (pp, simple); + pp_string (pp, ", destructor: "); + m_destructor_sval->dump_to_pp (pp, simple); + pp_string (pp, "}"); +} + +void +exception_node::dump (FILE *fp, bool simple) const +{ + tree_dump_pretty_printer pp (fp); + dump_to_pp (&pp, simple); + pp_newline (&pp); +} + +/* Dump a multiline representation of this model to stderr. */ + +DEBUG_FUNCTION void +exception_node::dump (bool simple) const +{ + dump (stderr, simple); +} + +DEBUG_FUNCTION void +exception_node::dump () const +{ + text_art::dump (*this); +} + +std::unique_ptr<json::object> +exception_node::to_json () const +{ + auto obj = std::make_unique<json::object> (); + obj->set ("exception", m_exception_sval->to_json ()); + obj->set ("typeinfo", m_typeinfo_sval->to_json ()); + obj->set ("destructor", m_destructor_sval->to_json ()); + return obj; +} + +std::unique_ptr<text_art::tree_widget> +exception_node::make_dump_widget (const text_art::dump_widget_info &dwi) const +{ + using text_art::tree_widget; + std::unique_ptr<tree_widget> w + (tree_widget::from_fmt (dwi, nullptr, "Exception Node")); + + w->add_child (m_exception_sval->make_dump_widget (dwi, "exception")); + w->add_child (m_typeinfo_sval->make_dump_widget (dwi, "typeinfo")); + w->add_child (m_destructor_sval->make_dump_widget (dwi, "destructor")); + + return w; +} + +tree +exception_node::maybe_get_type () const +{ + return m_typeinfo_sval->maybe_get_type_from_typeinfo (); +} + +void +exception_node::add_to_reachable_regions (reachable_regions ®s) const +{ + regs.handle_sval (m_exception_sval); + regs.handle_sval (m_typeinfo_sval); + regs.handle_sval (m_destructor_sval); +} + /* class region_model. */ /* 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 (nullptr), + m_thrown_exceptions_stack (), + m_caught_exceptions_stack (), m_dynamic_extents () { m_constraints = new constraint_manager (mgr); @@ -349,6 +417,8 @@ 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_thrown_exceptions_stack (other.m_thrown_exceptions_stack), + m_caught_exceptions_stack (other.m_caught_exceptions_stack), m_dynamic_extents (other.m_dynamic_extents) { } @@ -375,6 +445,9 @@ region_model::operator= (const region_model &other) m_current_frame = other.m_current_frame; + m_thrown_exceptions_stack = other.m_thrown_exceptions_stack; + m_caught_exceptions_stack = other.m_caught_exceptions_stack; + m_dynamic_extents = other.m_dynamic_extents; return *this; @@ -401,6 +474,11 @@ region_model::operator== (const region_model &other) const if (m_current_frame != other.m_current_frame) return false; + if (m_thrown_exceptions_stack != other.m_thrown_exceptions_stack) + return false; + if (m_caught_exceptions_stack != other.m_caught_exceptions_stack) + return false; + if (m_dynamic_extents != other.m_dynamic_extents) return false; @@ -427,7 +505,7 @@ void region_model::dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const { - /* Dump stack. */ + /* Dump frame stack. */ pp_printf (pp, "stack depth: %i", get_stack_depth ()); if (multiline) pp_newline (pp); @@ -448,6 +526,50 @@ region_model::dump_to_pp (pretty_printer *pp, bool simple, if (!multiline) pp_string (pp, "}"); + /* Dump exception stacks. */ + if (m_thrown_exceptions_stack.size () > 0) + { + pp_printf (pp, "thrown exceptions: %i", (int)m_thrown_exceptions_stack.size ()); + if (multiline) + pp_newline (pp); + else + pp_string (pp, " {"); + for (size_t idx = 0; idx < m_thrown_exceptions_stack.size (); ++idx) + { + if (multiline) + pp_string (pp, " "); + else if (idx > 0) + pp_string (pp, ", "); + pp_printf (pp, "exception (index %i): ", (int)idx); + m_thrown_exceptions_stack[idx].dump_to_pp (pp, simple); + if (multiline) + pp_newline (pp); + } + if (!multiline) + pp_string (pp, "}"); + } + if (m_caught_exceptions_stack.size () > 0) + { + pp_printf (pp, "caught exceptions: %i", (int)m_caught_exceptions_stack.size ()); + if (multiline) + pp_newline (pp); + else + pp_string (pp, " {"); + for (size_t idx = 0; idx < m_caught_exceptions_stack.size (); ++idx) + { + if (multiline) + pp_string (pp, " "); + else if (idx > 0) + pp_string (pp, ", "); + pp_printf (pp, "exception (index %i): ", (int)idx); + m_caught_exceptions_stack[idx].dump_to_pp (pp, simple); + if (multiline) + pp_newline (pp); + } + if (!multiline) + pp_string (pp, "}"); + } + /* Dump store. */ if (!multiline) pp_string (pp, ", {"); @@ -515,11 +637,22 @@ region_model::debug () const std::unique_ptr<json::object> region_model::to_json () const { - auto model_obj = ::make_unique<json::object> (); + auto model_obj = std::make_unique<json::object> (); model_obj->set ("store", m_store.to_json ()); model_obj->set ("constraints", m_constraints->to_json ()); if (m_current_frame) model_obj->set ("current_frame", m_current_frame->to_json ()); + + auto thrown_exceptions_arr = std::make_unique<json::array> (); + for (auto &node : m_thrown_exceptions_stack) + thrown_exceptions_arr->append (node.to_json ()); + model_obj->set ("thrown_exception_stack", std::move (thrown_exceptions_arr)); + + auto caught_exceptions_arr = std::make_unique<json::array> (); + for (auto &node : m_caught_exceptions_stack) + caught_exceptions_arr->append (node.to_json ()); + model_obj->set ("caught_exception_stack", std::move (caught_exceptions_arr)); + model_obj->set ("dynamic_extents", m_dynamic_extents.to_json ()); return model_obj; } @@ -543,6 +676,26 @@ region_model::make_dump_widget (const text_art::dump_widget_info &dwi) const m_current_frame->dump_to_pp (pp, simple); model_widget->add_child (tree_widget::make (dwi, pp)); } + + if (m_thrown_exceptions_stack.size () > 0) + { + auto thrown_exceptions_widget + = tree_widget::make (dwi, "Thrown Exceptions"); + for (auto &thrown_exception : m_thrown_exceptions_stack) + thrown_exceptions_widget->add_child + (thrown_exception.make_dump_widget (dwi)); + model_widget->add_child (std::move (thrown_exceptions_widget)); + } + if (m_caught_exceptions_stack.size () > 0) + { + auto caught_exceptions_widget + = tree_widget::make (dwi, "Caught Exceptions"); + for (auto &caught_exception : m_caught_exceptions_stack) + caught_exceptions_widget->add_child + (caught_exception.make_dump_widget (dwi)); + model_widget->add_child (std::move (caught_exceptions_widget)); + } + model_widget->add_child (m_store.make_dump_widget (dwi, m_mgr->get_store_manager ())); @@ -606,7 +759,7 @@ public: bool use_of_uninit_p () const final override { - return m_pkind == POISON_KIND_UNINIT; + return m_pkind == poison_kind::uninit; } bool operator== (const poisoned_value_diagnostic &other) const @@ -622,12 +775,12 @@ public: { default: gcc_unreachable (); - case POISON_KIND_UNINIT: + case poison_kind::uninit: return OPT_Wanalyzer_use_of_uninitialized_value; - case POISON_KIND_FREED: - case POISON_KIND_DELETED: + case poison_kind::freed: + case poison_kind::deleted: return OPT_Wanalyzer_use_after_free; - case POISON_KIND_POPPED_STACK: + case poison_kind::popped_stack: return OPT_Wanalyzer_use_of_pointer_in_stale_stack_frame; } } @@ -640,28 +793,28 @@ public: { default: gcc_unreachable (); - case POISON_KIND_UNINIT: + case poison_kind::uninit: { ctxt.add_cwe (457); /* "CWE-457: Use of Uninitialized Variable". */ return ctxt.warn ("use of uninitialized value %qE", m_expr); } break; - case POISON_KIND_FREED: + case poison_kind::freed: { ctxt.add_cwe (416); /* "CWE-416: Use After Free". */ return ctxt.warn ("use after %<free%> of %qE", m_expr); } break; - case POISON_KIND_DELETED: + case poison_kind::deleted: { ctxt.add_cwe (416); /* "CWE-416: Use After Free". */ return ctxt.warn ("use after %<delete%> of %qE", m_expr); } break; - case POISON_KIND_POPPED_STACK: + case poison_kind::popped_stack: { /* TODO: which CWE? */ return ctxt.warn @@ -680,28 +833,28 @@ public: { default: gcc_unreachable (); - case POISON_KIND_UNINIT: + case poison_kind::uninit: { pp_printf (&pp, "use of uninitialized value %qE here", m_expr); return true; } - case POISON_KIND_FREED: + case poison_kind::freed: { pp_printf (&pp, "use after %<free%> of %qE here", m_expr); return true; } - case POISON_KIND_DELETED: + case poison_kind::deleted: { pp_printf (&pp, "use after %<delete%> of %qE here", m_expr); return true; } - case POISON_KIND_POPPED_STACK: + case poison_kind::popped_stack: { pp_printf (&pp, "dereferencing pointer %qE to within stale stack frame", @@ -742,7 +895,7 @@ public: /* Couldn't get state; accept this diagnostic. */ return true; - const svalue *fsval = emission_model.get_rvalue (m_check_expr, NULL); + const svalue *fsval = emission_model.get_rvalue (m_check_expr, nullptr); /* Check to see if the expr is also poisoned in FNODE (and in the same way). */ const poisoned_svalue * fspval = fsval->dyn_cast_poisoned_svalue (); @@ -754,9 +907,10 @@ public: } void - maybe_add_sarif_properties (sarif_object &result_obj) const final override + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) + const final override { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/poisoned_value_diagnostic/" props.set (PROPERTY_PREFIX "expr", tree_to_json (m_expr)); props.set_string (PROPERTY_PREFIX "kind", poison_kind_to_str (m_pkind)); @@ -956,10 +1110,10 @@ public: { if (reg == m_base_reg_a) emission_path.add_event - (make_unique<ptrdiff_region_creation_event> (loc_info, true)); + (std::make_unique<ptrdiff_region_creation_event> (loc_info, true)); else if (reg == m_base_reg_b) emission_path.add_event - (make_unique<ptrdiff_region_creation_event> (loc_info, false)); + (std::make_unique<ptrdiff_region_creation_event> (loc_info, false)); } bool @@ -1009,17 +1163,18 @@ check_for_invalid_ptrdiff (const gassign *assign, if (base_reg_b->get_kind () == RK_SYMBOLIC) return; - ctxt.warn (make_unique<undefined_ptrdiff_diagnostic> (assign, - sval_a, - sval_b, - base_reg_a, - base_reg_b)); + ctxt.warn + (std::make_unique<undefined_ptrdiff_diagnostic> (assign, + sval_a, + sval_b, + base_reg_a, + base_reg_b)); } /* If ASSIGN is a stmt that can be modelled via set_value (lhs_reg, SVALUE, CTXT) for some SVALUE, get the SVALUE. - Otherwise return NULL. */ + Otherwise return nullptr. */ const svalue * region_model::get_gassign_result (const gassign *assign, @@ -1042,7 +1197,7 @@ region_model::get_gassign_result (const gassign *assign, switch (op) { default: - return NULL; + return nullptr; case POINTER_PLUS_EXPR: { @@ -1192,13 +1347,13 @@ region_model::get_gassign_result (const gassign *assign, { if (tree_int_cst_sgn (rhs2_cst) < 0) ctxt->warn - (make_unique<shift_count_negative_diagnostic> + (std::make_unique<shift_count_negative_diagnostic> (assign, rhs2_cst)); else if (compare_tree_int (rhs2_cst, TYPE_PRECISION (TREE_TYPE (rhs1))) >= 0) ctxt->warn - (make_unique<shift_count_overflow_diagnostic> + (std::make_unique<shift_count_overflow_diagnostic> (assign, int (TYPE_PRECISION (TREE_TYPE (rhs1))), rhs2_cst)); @@ -1307,8 +1462,8 @@ within_short_circuited_stmt_p (const region_model *model, that implies that the value of the second arg doesn't matter, i.e. 1 for bitwise or, 0 for bitwise and. */ tree other_arg = gimple_assign_rhs1 (use_assign); - /* Use a NULL ctxt here to avoid generating warnings. */ - const svalue *other_arg_sval = model->get_rvalue (other_arg, NULL); + /* Use a nullptr ctxt here to avoid generating warnings. */ + const svalue *other_arg_sval = model->get_rvalue (other_arg, nullptr); tree other_arg_cst = other_arg_sval->maybe_get_constant (); if (!other_arg_cst) return false; @@ -1375,7 +1530,7 @@ due_to_ifn_deferred_init_p (const gassign *assign_stmt) /* Check for SVAL being poisoned, adding a warning to CTXT. Return SVAL, or, if a warning is added, another value, to avoid repeatedly complaining about the same poisoned value in followup code. - SRC_REGION is a hint about where SVAL came from, and can be NULL. */ + SRC_REGION is a hint about where SVAL came from, and can be nullptr. */ const svalue * region_model::check_for_poison (const svalue *sval, @@ -1392,12 +1547,12 @@ region_model::check_for_poison (const svalue *sval, /* Ignore uninitialized uses of empty types; there's nothing to initialize. */ - if (pkind == POISON_KIND_UNINIT + if (pkind == poison_kind::uninit && sval->get_type () && is_empty_type (sval->get_type ())) return sval; - if (pkind == POISON_KIND_UNINIT) + if (pkind == poison_kind::uninit) if (const gimple *curr_stmt = ctxt->get_stmt ()) if (const gassign *assign_stmt = dyn_cast <const gassign *> (curr_stmt)) @@ -1418,7 +1573,7 @@ region_model::check_for_poison (const svalue *sval, the tree other than via the def stmts, using fixup_tree_for_diagnostic. */ tree diag_arg = fixup_tree_for_diagnostic (expr); - if (src_region == NULL && pkind == POISON_KIND_UNINIT) + if (src_region == nullptr && pkind == poison_kind::uninit) src_region = get_region_for_poisoned_expr (expr); /* Can we reliably get the poisoned value from "expr"? @@ -1427,15 +1582,16 @@ region_model::check_for_poison (const svalue *sval, Hence we only query its value now, and only use it if we get the poisoned value back again. */ tree check_expr = expr; - const svalue *foo_sval = get_rvalue (expr, NULL); + const svalue *foo_sval = get_rvalue (expr, nullptr); if (foo_sval == sval) check_expr = expr; else - check_expr = NULL; - if (ctxt->warn (make_unique<poisoned_value_diagnostic> (diag_arg, - pkind, - src_region, - check_expr))) + check_expr = nullptr; + if (ctxt->warn + (std::make_unique<poisoned_value_diagnostic> (diag_arg, + pkind, + src_region, + check_expr))) { /* We only want to report use of a poisoned value at the first place it gets used; return an unknown value to avoid generating @@ -1451,7 +1607,7 @@ region_model::check_for_poison (const svalue *sval, /* Attempt to get a region for describing EXPR, the source of region of a poisoned_svalue for use in a poisoned_value_diagnostic. - Return NULL if there is no good region to use. */ + Return nullptr if there is no good region to use. */ const region * region_model::get_region_for_poisoned_expr (tree expr) const @@ -1462,9 +1618,9 @@ region_model::get_region_for_poisoned_expr (tree expr) const if (decl && DECL_P (decl)) expr = decl; else - return NULL; + return nullptr; } - return get_lvalue (expr, NULL); + return get_lvalue (expr, nullptr); } /* Update this model for the ASSIGN stmt, using CTXT to report any @@ -1493,7 +1649,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt) if (const svalue *sval = get_gassign_result (assign, ctxt)) { tree expr = get_diagnostic_tree_for_gassign (assign); - check_for_poison (sval, expr, NULL, ctxt); + check_for_poison (sval, expr, nullptr, ctxt); set_value (lhs_reg, sval, ctxt); return; } @@ -1553,7 +1709,7 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt) /* e.g. "struct s2 x = {{'A', 'B', 'C', 'D'}};". */ const svalue *rhs_sval = get_rvalue (rhs1, ctxt); m_store.set_value (m_mgr->get_store_manager(), lhs_reg, rhs_sval, - ctxt ? ctxt->get_uncertainty () : NULL); + ctxt ? ctxt->get_uncertainty () : nullptr); } break; } @@ -1570,13 +1726,15 @@ region_model::on_stmt_pre (const gimple *stmt, { switch (gimple_code (stmt)) { - default: - /* No-op for now. */ - break; - - case GIMPLE_DEBUG: - /* We should have stripped these out when building the supergraph. */ - gcc_unreachable (); + case GIMPLE_COND: + case GIMPLE_EH_DISPATCH: + case GIMPLE_GOTO: + case GIMPLE_LABEL: + case GIMPLE_NOP: + case GIMPLE_PREDICT: + case GIMPLE_RESX: + case GIMPLE_SWITCH: + /* No-ops here. */ break; case GIMPLE_ASSIGN: @@ -1601,7 +1759,7 @@ region_model::on_stmt_pre (const gimple *stmt, anything, for which we don't have a function body, or for which we don't know the fndecl. */ const gcall *call = as_a <const gcall *> (stmt); - *out_unknown_side_effects = on_call_pre (call, ctxt); + *out_unknown_side_effects = on_call_pre (*call, ctxt); } break; @@ -1611,6 +1769,13 @@ region_model::on_stmt_pre (const gimple *stmt, on_return (return_, ctxt); } break; + + /* We don't expect to see any other statement kinds in the analyzer. */ + case GIMPLE_DEBUG: // should have stripped these out when building the supergraph + default: + internal_error ("unexpected gimple stmt code: %qs", + gimple_code_name[gimple_code (stmt)]); + break; } } @@ -1689,7 +1854,7 @@ region_model::check_call_format_attr (const call_details &cd, }; call_arg_details arg_details (m_cd, m_fmt_param_idx); - add_note (make_unique<reason_format_attr> (arg_details)); + add_note (std::make_unique<reason_format_attr> (arg_details)); } private: const call_details &m_cd; @@ -1775,7 +1940,7 @@ region_model::update_for_nonzero_return (const call_details &cd) to set an upper bound on the size of a copy_to_user. Attempt to simplify such sizes by trying to get the upper bound as a constant. - Return the simplified svalue if possible, or NULL otherwise. */ + Return the simplified svalue if possible, or nullptr otherwise. */ static const svalue * maybe_simplify_upper_bound (const svalue *num_bytes_sval, @@ -1793,7 +1958,7 @@ maybe_simplify_upper_bound (const svalue *num_bytes_sval, when recording the diagnostic, or note that we're using the upper bound. */ } - return NULL; + return nullptr; } /* Attempt to get an upper bound for the size of a copy when simulating a @@ -1804,7 +1969,7 @@ maybe_simplify_upper_bound (const svalue *num_bytes_sval, that, use the size of SRC_REG if constant. Return a symbolic value for an upper limit on the number of bytes - copied, or NULL if no such value could be determined. */ + copied, or nullptr if no such value could be determined. */ const svalue * region_model::maybe_get_copy_bounds (const region *src_reg, @@ -1830,7 +1995,7 @@ region_model::maybe_get_copy_bounds (const region *src_reg, return num_bytes_sval; /* Non-constant: give up. */ - return NULL; + return nullptr; } /* Get any known_function for FNDECL for call CD. @@ -1838,7 +2003,7 @@ region_model::maybe_get_copy_bounds (const region *src_reg, The call must match all assumptions made by the known_function (such as e.g. "argument 1's type must be a pointer type"). - Return NULL if no known_function is found, or it does not match the + Return nullptr if no known_function is found, or it does not match the assumption(s). */ const known_function * @@ -1848,7 +2013,7 @@ region_model::get_known_function (tree fndecl, const call_details &cd) const return known_fn_mgr->get_match (fndecl, cd); } -/* Get any known_function for IFN, or NULL. */ +/* Get any known_function for IFN, or nullptr. */ const known_function * region_model::get_known_function (enum internal_fn ifn) const @@ -1858,12 +2023,12 @@ region_model::get_known_function (enum internal_fn ifn) const } /* Get any builtin_known_function for CALL and emit any warning to CTXT - if not NULL. + if not nullptr. The call must match all assumptions made by the known_function (such as e.g. "argument 1's type must be a pointer type"). - Return NULL if no builtin_known_function is found, or it does + Return nullptr if no builtin_known_function is found, or it does not match the assumption(s). Internally calls get_known_function to find a known_function and cast it @@ -1898,19 +2063,183 @@ region_model::get_known_function (enum internal_fn ifn) const attributes. */ const builtin_known_function * -region_model::get_builtin_kf (const gcall *call, - region_model_context *ctxt /* = NULL */) const +region_model::get_builtin_kf (const gcall &call, + region_model_context *ctxt /* = nullptr */) const { region_model *mut_this = const_cast <region_model *> (this); tree callee_fndecl = mut_this->get_fndecl_for_call (call, ctxt); if (! callee_fndecl) - return NULL; + return nullptr; call_details cd (call, mut_this, ctxt); if (const known_function *kf = get_known_function (callee_fndecl, cd)) return kf->dyn_cast_builtin_kf (); - return NULL; + return nullptr; +} + +/* Subclass of custom_edge_info for use by exploded_edges that represent + an exception being thrown from a call we don't have the code for. */ + +class exception_thrown_from_unrecognized_call : public custom_edge_info +{ +public: + exception_thrown_from_unrecognized_call (const gcall &call, + tree fndecl) + : m_call (call), + m_fndecl (fndecl) + { + } + + void print (pretty_printer *pp) const final override + { + if (m_fndecl) + pp_printf (pp, "if %qD throws an exception...", m_fndecl); + else + pp_printf (pp, "if the called function throws an exception..."); + }; + + bool + update_model (region_model *model, + const exploded_edge *, + region_model_context *ctxt) const final override + { + /* Allocate an exception and set it as the current exception. */ + const region *exception_reg + = model->get_or_create_region_for_heap_alloc + (nullptr, /* We don't know the size of the region. */ + ctxt); + + region_model_manager *mgr = model->get_manager (); + conjured_purge p (model, ctxt); + + /* The contents of the region are some conjured svalue. */ + const svalue *exception_sval + = mgr->get_or_create_conjured_svalue (NULL_TREE, + &m_call, + exception_reg, p, 0); + model->set_value (exception_reg, exception_sval, ctxt); + const svalue *exception_ptr_sval + = mgr->get_ptr_svalue (ptr_type_node, exception_reg); + const svalue *tinfo_sval + = mgr->get_or_create_conjured_svalue (ptr_type_node, + &m_call, + exception_reg, p, 1); + const svalue *destructor_sval + = mgr->get_or_create_conjured_svalue (ptr_type_node, + &m_call, + exception_reg, p, 2); + + /* Push a new exception_node on the model's thrown exception stack. */ + exception_node eh_node (exception_ptr_sval, tinfo_sval, destructor_sval); + model->push_thrown_exception (eh_node); + + return true; + } + + void + add_events_to_path (checker_path *emission_path, + const exploded_edge &eedge) const final override + { + const exploded_node *dst_node = eedge.m_dest; + const program_point &dst_point = dst_node->get_point (); + const int dst_stack_depth = dst_point.get_stack_depth (); + + emission_path->add_event + (std::make_unique<throw_from_call_to_external_fn_event> + (event_loc_info (m_call.location, + dst_point.get_fndecl (), + dst_stack_depth), + dst_node, + m_call, + m_fndecl)); + } + + exploded_node * + create_enode (exploded_graph &eg, + const program_point &point, + program_state &&state, + exploded_node *enode_for_diag, + region_model_context *ctxt) const final override + { + exploded_node *thrown_enode + = eg.get_or_create_node (point, state, enode_for_diag, + /* Don't add to worklist. */ + false); + if (!thrown_enode) + return nullptr; + + /* Add successor edges for thrown_enode "by hand" for the exception. */ + eg.unwind_from_exception (*thrown_enode, + &m_call, + ctxt); + return thrown_enode; + } + +private: + const gcall &m_call; + tree m_fndecl; // could be null +}; + +/* Get a set of functions that are assumed to not throw exceptions. */ + +static function_set +get_fns_assumed_not_to_throw () +{ + // TODO: populate this list more fully + static const char * const fn_names[] = { + /* This array must be kept sorted. */ + + "fclose" + }; + const size_t count = ARRAY_SIZE (fn_names); + function_set fs (fn_names, count); + return fs; +} + +/* Return true if CALL could throw an exception. + FNDECL could be NULL_TREE. */ + +static bool +can_throw_p (const gcall &call, tree fndecl) +{ + if (!flag_exceptions) + return false; + + if (gimple_call_nothrow_p (&call)) + return false; + + if (fndecl) + { + const function_set fs = get_fns_assumed_not_to_throw (); + if (fs.contains_decl_p (fndecl)) + return false; + } + + return true; +} + +/* Given CALL where we don't know what code is being called + (by not having the body of FNDECL, or having NULL_TREE for FNDECL), + potentially bifurcate control flow to simulate the call throwing + an exception. */ + +void +region_model::check_for_throw_inside_call (const gcall &call, + tree fndecl, + region_model_context *ctxt) +{ + if (!ctxt) + return; + + /* Could this function throw an exception? + If so, add an extra e-edge for that. */ + if (!can_throw_p (call, fndecl)) + return; + + auto throws_exception + = std::make_unique<exception_thrown_from_unrecognized_call> (call, fndecl); + ctxt->bifurcate (std::move (throws_exception)); } /* Update this model for the CALL stmt, using CTXT to report any @@ -1925,7 +2254,7 @@ region_model::get_builtin_kf (const gcall *call, fndecl it is). */ bool -region_model::on_call_pre (const gcall *call, region_model_context *ctxt) +region_model::on_call_pre (const gcall &call, region_model_context *ctxt) { call_details cd (call, this, ctxt); @@ -1935,8 +2264,8 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) Handle IFN_DEFERRED_INIT by treating it as no-op: don't touch the lhs of the call, so that it is still uninitialized from the point of view of the analyzer. */ - if (gimple_call_internal_p (call) - && gimple_call_internal_fn (call) == IFN_DEFERRED_INIT) + if (gimple_call_internal_p (&call) + && gimple_call_internal_fn (&call) == IFN_DEFERRED_INIT) return false; /* No side effects. */ /* Get svalues for all of the arguments at the callsite, to ensure that we @@ -1948,9 +2277,9 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) tree callee_fndecl = get_fndecl_for_call (call, ctxt); - if (gimple_call_internal_p (call)) + if (gimple_call_internal_p (&call)) if (const known_function *kf - = get_known_function (gimple_call_internal_fn (call))) + = get_known_function (gimple_call_internal_fn (&call))) { kf->impl_call_pre (cd); return false; /* No further side effects. */ @@ -1958,6 +2287,7 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) if (!callee_fndecl) { + check_for_throw_inside_call (call, NULL_TREE, ctxt); cd.set_any_lhs_with_defaults (); return true; /* Unknown side effects. */ } @@ -1978,7 +2308,10 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) return true; /* Unknown side effects. */ if (!fndecl_has_gimple_body_p (callee_fndecl)) - return true; /* Unknown side effects. */ + { + check_for_throw_inside_call (call, callee_fndecl, ctxt); + return true; /* Unknown side effects. */ + } return false; /* No side effects. */ } @@ -1994,7 +2327,7 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) to purge state. */ void -region_model::on_call_post (const gcall *call, +region_model::on_call_post (const gcall &call, bool unknown_side_effects, region_model_context *ctxt) { @@ -2102,12 +2435,11 @@ private: attribute. */ void -region_model::check_function_attr_access (const gcall *call, +region_model::check_function_attr_access (const gcall &call, tree callee_fndecl, region_model_context *ctxt, rdwr_map &rdwr_idx) const { - gcc_assert (call); gcc_assert (callee_fndecl); gcc_assert (ctxt); @@ -2145,8 +2477,8 @@ region_model::check_function_attr_access (const gcall *call, } void add_annotations () final override { - add_note (make_unique<reason_attr_access> - (m_callee_fndecl, m_access)); + add_note (std::make_unique<reason_attr_access> + (m_callee_fndecl, m_access)); } private: tree m_callee_fndecl; @@ -2157,7 +2489,7 @@ region_model::check_function_attr_access (const gcall *call, note added to them. */ annotating_ctxt my_ctxt (callee_fndecl, *access, ctxt); - tree ptr_tree = gimple_call_arg (call, access->ptrarg); + tree ptr_tree = gimple_call_arg (&call, access->ptrarg); const svalue *ptr_sval = get_rvalue (ptr_tree, &my_ctxt); const region *reg = deref_rvalue (ptr_sval, ptr_tree, &my_ctxt); check_region_for_write (reg, nullptr, &my_ctxt); @@ -2171,13 +2503,12 @@ region_model::check_function_attr_access (const gcall *call, void region_model:: -check_one_function_attr_null_terminated_string_arg (const gcall *call, +check_one_function_attr_null_terminated_string_arg (const gcall &call, tree callee_fndecl, region_model_context *ctxt, rdwr_map &rdwr_idx, tree attr) { - gcc_assert (call); gcc_assert (callee_fndecl); gcc_assert (ctxt); gcc_assert (attr); @@ -2245,12 +2576,11 @@ check_one_function_attr_null_terminated_string_arg (const gcall *call, void region_model:: -check_function_attr_null_terminated_string_arg (const gcall *call, +check_function_attr_null_terminated_string_arg (const gcall &call, tree callee_fndecl, region_model_context *ctxt, rdwr_map &rdwr_idx) { - gcc_assert (call); gcc_assert (callee_fndecl); gcc_assert (ctxt); @@ -2275,11 +2605,10 @@ check_function_attr_null_terminated_string_arg (const gcall *call, function attributes, complaining to CTXT about any issues. */ void -region_model::check_function_attrs (const gcall *call, +region_model::check_function_attrs (const gcall &call, tree callee_fndecl, region_model_context *ctxt) { - gcc_assert (call); gcc_assert (callee_fndecl); gcc_assert (ctxt); @@ -2310,7 +2639,7 @@ region_model::check_function_attrs (const gcall *call, from their values, and from values that point to them. */ void -region_model::handle_unrecognized_call (const gcall *call, +region_model::handle_unrecognized_call (const gcall &call, region_model_context *ctxt) { tree fndecl = get_fndecl_for_call (call, ctxt); @@ -2331,7 +2660,8 @@ region_model::handle_unrecognized_call (const gcall *call, tree iter_param_types = NULL_TREE; if (fndecl) iter_param_types = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); - for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++) + for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (&call); + arg_idx++) { /* Track expected param type, where available. */ tree param_type = NULL_TREE; @@ -2342,13 +2672,13 @@ region_model::handle_unrecognized_call (const gcall *call, iter_param_types = TREE_CHAIN (iter_param_types); } - tree parm = gimple_call_arg (call, arg_idx); + tree parm = gimple_call_arg (&call, arg_idx); const svalue *parm_sval = get_rvalue (parm, ctxt); reachable_regs.handle_parm (parm_sval, param_type); } } - uncertainty_t *uncertainty = ctxt ? ctxt->get_uncertainty () : NULL; + uncertainty_t *uncertainty = ctxt ? ctxt->get_uncertainty () : nullptr; /* Purge sm-state for the svalues that were reachable, both in non-mutable and mutable form. */ @@ -2466,11 +2796,11 @@ region_model::on_return (const greturn *return_stmt, region_model_context *ctxt) 0), as opposed to any second return due to longjmp/sigsetjmp. */ void -region_model::on_setjmp (const gcall *call, const exploded_node *enode, +region_model::on_setjmp (const gcall &call, const exploded_node *enode, region_model_context *ctxt) { - const svalue *buf_ptr = get_rvalue (gimple_call_arg (call, 0), ctxt); - const region *buf_reg = deref_rvalue (buf_ptr, gimple_call_arg (call, 0), + const svalue *buf_ptr = get_rvalue (gimple_call_arg (&call, 0), ctxt); + const region *buf_reg = deref_rvalue (buf_ptr, gimple_call_arg (&call, 0), ctxt); /* Create a setjmp_svalue for this call and store it in BUF_REG's @@ -2484,7 +2814,7 @@ region_model::on_setjmp (const gcall *call, const exploded_node *enode, } /* Direct calls to setjmp return 0. */ - if (tree lhs = gimple_call_lhs (call)) + if (tree lhs = gimple_call_lhs (&call)) { const svalue *new_sval = m_mgr->get_or_create_int_cst (TREE_TYPE (lhs), 0); @@ -2499,23 +2829,23 @@ region_model::on_setjmp (const gcall *call, const exploded_node *enode, done, and should be done by the caller. */ void -region_model::on_longjmp (const gcall *longjmp_call, const gcall *setjmp_call, +region_model::on_longjmp (const gcall &longjmp_call, const gcall &setjmp_call, int setjmp_stack_depth, region_model_context *ctxt) { /* Evaluate the val, using the frame of the "longjmp". */ - tree fake_retval = gimple_call_arg (longjmp_call, 1); + tree fake_retval = gimple_call_arg (&longjmp_call, 1); const svalue *fake_retval_sval = get_rvalue (fake_retval, ctxt); /* Pop any frames until we reach the stack depth of the function where setjmp was called. */ gcc_assert (get_stack_depth () >= setjmp_stack_depth); while (get_stack_depth () > setjmp_stack_depth) - pop_frame (NULL, NULL, ctxt, nullptr, false); + pop_frame (nullptr, nullptr, ctxt, nullptr, false); gcc_assert (get_stack_depth () == setjmp_stack_depth); /* Assign to LHS of "setjmp" in new_state. */ - if (tree lhs = gimple_call_lhs (setjmp_call)) + if (tree lhs = gimple_call_lhs (&setjmp_call)) { /* Passing 0 as the val to longjmp leads to setjmp returning 1. */ const svalue *zero_sval @@ -2711,7 +3041,7 @@ const region * region_model::get_lvalue (path_var pv, region_model_context *ctxt) const { if (pv.m_tree == NULL_TREE) - return NULL; + return nullptr; const region *result_reg = get_lvalue_1 (pv, ctxt); assert_compat_types (result_reg->get_type (), TREE_TYPE (pv.m_tree)); @@ -2856,13 +3186,13 @@ const svalue * region_model::get_rvalue (path_var pv, region_model_context *ctxt) const { if (pv.m_tree == NULL_TREE) - return NULL; + return nullptr; const svalue *result_sval = get_rvalue_1 (pv, ctxt); assert_compat_types (result_sval->get_type (), TREE_TYPE (pv.m_tree)); - result_sval = check_for_poison (result_sval, pv.m_tree, NULL, ctxt); + result_sval = check_for_poison (result_sval, pv.m_tree, nullptr, ctxt); return result_sval; } @@ -3092,7 +3422,7 @@ region_model::deref_rvalue (const svalue *ptr_sval, tree ptr_tree, const poisoned_svalue *poisoned_sval = as_a <const poisoned_svalue *> (ptr_sval); enum poison_kind pkind = poisoned_sval->get_poison_kind (); - ctxt->warn (::make_unique<poisoned_value_diagnostic> + ctxt->warn (std::make_unique<poisoned_value_diagnostic> (ptr, pkind, nullptr, nullptr)); } } @@ -3250,7 +3580,7 @@ void region_model::check_for_writable_region (const region* dest_reg, region_model_context *ctxt) const { - /* Fail gracefully if CTXT is NULL. */ + /* Fail gracefully if CTXT is nullptr. */ if (!ctxt) return; @@ -3263,16 +3593,18 @@ region_model::check_for_writable_region (const region* dest_reg, { const function_region *func_reg = as_a <const function_region *> (base_reg); tree fndecl = func_reg->get_fndecl (); - ctxt->warn (make_unique<write_to_const_diagnostic> - (func_reg, fndecl)); + ctxt->warn + (std::make_unique<write_to_const_diagnostic> + (func_reg, fndecl)); } break; case RK_LABEL: { const label_region *label_reg = as_a <const label_region *> (base_reg); tree label = label_reg->get_label (); - ctxt->warn (make_unique<write_to_const_diagnostic> - (label_reg, label)); + ctxt->warn + (std::make_unique<write_to_const_diagnostic> + (label_reg, label)); } break; case RK_DECL: @@ -3285,11 +3617,13 @@ region_model::check_for_writable_region (const region* dest_reg, "this" param is "T* const"). */ if (TREE_READONLY (decl) && is_global_var (decl)) - ctxt->warn (make_unique<write_to_const_diagnostic> (dest_reg, decl)); + ctxt->warn + (std::make_unique<write_to_const_diagnostic> (dest_reg, decl)); } break; case RK_STRING: - ctxt->warn (make_unique<write_to_string_literal_diagnostic> (dest_reg)); + ctxt->warn + (std::make_unique<write_to_string_literal_diagnostic> (dest_reg)); break; } } @@ -3311,13 +3645,13 @@ region_model::get_capacity (const region *reg) const { tree type = TREE_TYPE (decl); tree size = TYPE_SIZE (type); - return get_rvalue (size, NULL); + return get_rvalue (size, nullptr); } else { tree size = decl_init_size (decl, false); if (size) - return get_rvalue (size, NULL); + return get_rvalue (size, nullptr); } } break; @@ -3367,10 +3701,10 @@ region_model::check_region_access (const region *reg, { default: gcc_unreachable (); - case DIR_READ: + case access_direction::read: /* Currently a no-op. */ break; - case DIR_WRITE: + case access_direction::write: check_for_writable_region (reg, ctxt); break; } @@ -3384,7 +3718,7 @@ region_model::check_region_for_write (const region *dest_reg, const svalue *sval_hint, region_model_context *ctxt) const { - check_region_access (dest_reg, DIR_WRITE, sval_hint, ctxt); + check_region_access (dest_reg, access_direction::write, sval_hint, ctxt); } /* If CTXT is non-NULL, use it to warn about any problems reading from REG. @@ -3394,7 +3728,7 @@ bool region_model::check_region_for_read (const region *src_reg, region_model_context *ctxt) const { - return check_region_access (src_reg, DIR_READ, NULL, ctxt); + return check_region_access (src_reg, access_direction::read, nullptr, ctxt); } /* Concrete subclass for casts of pointers that lead to trailing bytes. */ @@ -3491,7 +3825,8 @@ public: checker_path &emission_path) final override { emission_path.add_event - (make_unique<region_creation_event_allocation_size> (capacity, loc_info)); + (std::make_unique<region_creation_event_allocation_size> + (capacity, loc_info)); m_has_allocation_event = true; } @@ -3501,10 +3836,11 @@ public: interest->add_region_creation (m_rhs); } - void maybe_add_sarif_properties (sarif_object &result_obj) + void + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const final override { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/dubious_allocation_size/" props.set (PROPERTY_PREFIX "lhs", m_lhs->to_json ()); props.set (PROPERTY_PREFIX "rhs", m_rhs->to_json ()); @@ -3781,7 +4117,7 @@ void region_model::check_region_size (const region *lhs_reg, const svalue *rhs_sval, region_model_context *ctxt) const { - if (!ctxt || ctxt->get_stmt () == NULL) + if (!ctxt || ctxt->get_stmt () == nullptr) return; /* Only report warnings on assignments that actually change the type. */ if (!is_any_cast_p (ctxt->get_stmt ())) @@ -3827,9 +4163,10 @@ region_model::check_region_size (const region *lhs_reg, const svalue *rhs_sval, if (TREE_CODE (cst_cap) == INTEGER_CST && !capacity_compatible_with_type (cst_cap, pointee_size_tree, is_struct)) - ctxt->warn (make_unique <dubious_allocation_size> (lhs_reg, rhs_reg, - capacity, cst_cap, - ctxt->get_stmt ())); + ctxt->warn + (std::make_unique <dubious_allocation_size> (lhs_reg, rhs_reg, + capacity, cst_cap, + ctxt->get_stmt ())); } break; default: @@ -3841,10 +4178,11 @@ region_model::check_region_size (const region *lhs_reg, const svalue *rhs_sval, m_constraints)) { tree expr = get_representative_tree (capacity); - ctxt->warn (make_unique <dubious_allocation_size> (lhs_reg, - rhs_reg, - capacity, expr, - ctxt->get_stmt ())); + ctxt->warn + (std::make_unique <dubious_allocation_size> (lhs_reg, + rhs_reg, + capacity, expr, + ctxt->get_stmt ())); } } break; @@ -3872,7 +4210,7 @@ region_model::set_value (const region *lhs_reg, const svalue *rhs_sval, check_region_for_write (lhs_reg, rhs_sval, ctxt); m_store.set_value (m_mgr->get_store_manager(), lhs_reg, rhs_sval, - ctxt ? ctxt->get_uncertainty () : NULL); + ctxt ? ctxt->get_uncertainty () : nullptr); } /* Set the value of the region given by LHS to the value given by RHS. */ @@ -4509,7 +4847,7 @@ region_model::scan_for_null_terminator (const region *reg, Simulate scanning through the buffer, reading until we find a 0 byte (equivalent to calling strlen). - Complain and return NULL if: + Complain and return nullptr if: - the buffer pointed to isn't null-terminated - the buffer pointed to has any uninitalized bytes before any 0-terminator - any of the reads aren't within the bounds of the underlying base region @@ -4537,7 +4875,7 @@ region_model::check_for_null_terminated_string_arg (const call_details &cd, Simulate scanning through the buffer, reading until we find a 0 byte (equivalent to calling strlen). - Complain and return NULL if: + Complain and return nullptr if: - the buffer pointed to isn't null-terminated - the buffer pointed to has any uninitalized bytes before any 0-terminator - any of the reads aren't within the bounds of the underlying base region @@ -4546,7 +4884,7 @@ region_model::check_for_null_terminated_string_arg (const call_details &cd, (including the null terminator) if INCLUDE_TERMINATOR is true, or strlen of the buffer (not including the null terminator) if it is false. - Also, when returning an svalue, if OUT_SVAL is non-NULL, write to + Also, when returning an svalue, if OUT_SVAL is non-nullptr, write to *OUT_SVAL with an svalue representing the content of the buffer up to and including the terminator. @@ -4637,9 +4975,11 @@ region_model::check_for_null_terminated_string_arg (const call_details &cd, m_cd.get_model ()->get_current_function ()->decl, m_cd.get_model ()->get_stack_depth ()); - add_event (make_unique<null_terminator_check_event> (loc_info, - arg_details)); - add_note (make_unique <null_terminator_check_decl_note> (arg_details)); + add_event + (std::make_unique<null_terminator_check_event> (loc_info, + arg_details)); + add_note + (std::make_unique <null_terminator_check_decl_note> (arg_details)); } private: const call_details &m_cd; @@ -5385,7 +5725,7 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs, { bool sat = add_constraint (lhs, op, rhs, ctxt); if (!sat && out) - *out = make_unique <rejected_op_constraint> (*this, lhs, op, rhs); + *out = std::make_unique <rejected_op_constraint> (*this, lhs, op, rhs); return sat; } @@ -5507,7 +5847,7 @@ region_model::get_representative_path_var (const svalue *sval, svalue_set *visited, logger *logger) const { - if (sval == NULL) + if (sval == nullptr) return path_var (NULL_TREE, 0); LOG_SCOPE (logger); @@ -5866,7 +6206,7 @@ region_model::maybe_update_for_edge (const superedge &edge, break; } - if (last_stmt == NULL) + if (last_stmt == nullptr) return true; /* Apply any constraints for conditionals/switch/computed-goto statements. */ @@ -5885,17 +6225,22 @@ region_model::maybe_update_for_edge (const superedge &edge, ctxt, out); } + if (const geh_dispatch *eh_dispatch_stmt + = dyn_cast <const geh_dispatch *> (last_stmt)) + { + const eh_dispatch_cfg_superedge *eh_dispatch_cfg_sedge + = as_a <const eh_dispatch_cfg_superedge *> (&edge); + return apply_constraints_for_eh_dispatch (*eh_dispatch_cfg_sedge, + eh_dispatch_stmt, + ctxt, out); + } + if (const ggoto *goto_stmt = dyn_cast <const ggoto *> (last_stmt)) { const cfg_superedge *cfg_sedge = as_a <const cfg_superedge *> (&edge); return apply_constraints_for_ggoto (*cfg_sedge, goto_stmt, ctxt); } - /* Apply any constraints due to an exception being thrown. */ - if (const cfg_superedge *cfg_sedge = dyn_cast <const cfg_superedge *> (&edge)) - if (cfg_sedge->get_flags () & EDGE_EH) - return apply_constraints_for_exception (last_stmt, ctxt, out); - return true; } @@ -5905,29 +6250,29 @@ region_model::maybe_update_for_edge (const superedge &edge, caller's frame. */ void -region_model::update_for_gcall (const gcall *call_stmt, +region_model::update_for_gcall (const gcall &call_stmt, region_model_context *ctxt, function *callee) { /* Build a vec of argument svalues, using the current top frame for resolving tree expressions. */ - auto_vec<const svalue *> arg_svals (gimple_call_num_args (call_stmt)); + auto_vec<const svalue *> arg_svals (gimple_call_num_args (&call_stmt)); - for (unsigned i = 0; i < gimple_call_num_args (call_stmt); i++) + for (unsigned i = 0; i < gimple_call_num_args (&call_stmt); i++) { - tree arg = gimple_call_arg (call_stmt, i); + tree arg = gimple_call_arg (&call_stmt, i); arg_svals.quick_push (get_rvalue (arg, ctxt)); } if(!callee) { /* Get the function * from the gcall. */ - tree fn_decl = get_fndecl_for_call (call_stmt,ctxt); + tree fn_decl = get_fndecl_for_call (call_stmt, ctxt); callee = DECL_STRUCT_FUNCTION (fn_decl); } gcc_assert (callee); - push_frame (*callee, &arg_svals, ctxt); + push_frame (*callee, &call_stmt, &arg_svals, ctxt); } /* Pop the top-most frame_region from the stack, and copy the return @@ -5935,14 +6280,14 @@ region_model::update_for_gcall (const gcall *call_stmt, the call (if any). */ void -region_model::update_for_return_gcall (const gcall *call_stmt, +region_model::update_for_return_gcall (const gcall &call_stmt, region_model_context *ctxt) { /* Get the lvalue for the result of the call, passing it to pop_frame, so that pop_frame can determine the region with respect to the *caller* frame. */ - tree lhs = gimple_call_lhs (call_stmt); - pop_frame (lhs, NULL, ctxt, call_stmt); + tree lhs = gimple_call_lhs (&call_stmt); + pop_frame (lhs, nullptr, ctxt, &call_stmt); } /* Extract calling information from the superedge and update the model for the @@ -5952,7 +6297,7 @@ void region_model::update_for_call_superedge (const call_superedge &call_edge, region_model_context *ctxt) { - const gcall *call_stmt = call_edge.get_call_stmt (); + const gcall &call_stmt = call_edge.get_call_stmt (); update_for_gcall (call_stmt, ctxt, call_edge.get_callee_function ()); } @@ -5963,7 +6308,7 @@ void region_model::update_for_return_superedge (const return_superedge &return_edge, region_model_context *ctxt) { - const gcall *call_stmt = return_edge.get_call_stmt (); + const gcall &call_stmt = return_edge.get_call_stmt (); update_for_return_gcall (call_stmt, ctxt); } @@ -6018,7 +6363,7 @@ apply_constraints_for_gcond (const cfg_superedge &sedge, std::unique_ptr<rejected_constraint> *out) { ::edge cfg_edge = sedge.get_cfg_edge (); - gcc_assert (cfg_edge != NULL); + gcc_assert (cfg_edge != nullptr); gcc_assert (cfg_edge->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)); enum tree_code op = gimple_cond_code (cond_stmt); @@ -6036,7 +6381,7 @@ static bool has_nondefault_case_for_value_p (const gswitch *switch_stmt, tree int_cst) { /* We expect the initial label to be the default; skip it. */ - gcc_assert (CASE_LOW (gimple_switch_label (switch_stmt, 0)) == NULL); + gcc_assert (CASE_LOW (gimple_switch_label (switch_stmt, 0)) == NULL_TREE); unsigned min_idx = 1; unsigned max_idx = gimple_switch_num_labels (switch_stmt) - 1; @@ -6156,7 +6501,7 @@ apply_constraints_for_gswitch (const switch_cfg_superedge &edge, && !ctxt->possibly_tainted_p (index_sval)) { if (out) - *out = make_unique <rejected_default_case> (*this); + *out = std::make_unique <rejected_default_case> (*this); return false; } @@ -6165,12 +6510,180 @@ apply_constraints_for_gswitch (const switch_cfg_superedge &edge, = ranges_mgr->get_or_create_ranges_for_switch (&edge, switch_stmt); bool sat = m_constraints->add_bounded_ranges (index_sval, all_cases_ranges); if (!sat && out) - *out = make_unique <rejected_ranges_constraint> (*this, index, all_cases_ranges); + *out = std::make_unique <rejected_ranges_constraint> + (*this, index, all_cases_ranges); if (sat && ctxt && !all_cases_ranges->empty_p ()) ctxt->on_bounded_ranges (*index_sval, *all_cases_ranges); return sat; } +class rejected_eh_dispatch : public rejected_constraint +{ +public: + rejected_eh_dispatch (const region_model &model) + : rejected_constraint (model) + {} + + void dump_to_pp (pretty_printer *pp) const final override + { + pp_printf (pp, "rejected_eh_dispatch"); + } +}; + +static bool +exception_matches_type_p (tree exception_type, + tree catch_type) +{ + if (catch_type == exception_type) + return true; + + /* TODO (PR analyzer/119697): we should also handle subclasses etc; + see the rules in https://en.cppreference.com/w/cpp/language/catch + + It looks like we should be calling (or emulating) + can_convert_eh from the C++ FE, but that's specific to the C++ FE. */ + + return false; +} + +static bool +matches_any_exception_type_p (eh_catch ehc, tree exception_type) +{ + if (ehc->type_list == NULL_TREE) + /* All exceptions are caught here. */ + return true; + + for (tree iter = ehc->type_list; iter; iter = TREE_CHAIN (iter)) + if (exception_matches_type_p (TREE_VALUE (iter), + exception_type)) + return true; + return false; +} + +bool +region_model:: +apply_constraints_for_eh_dispatch (const eh_dispatch_cfg_superedge &edge, + const geh_dispatch *, + region_model_context *ctxt, + std::unique_ptr<rejected_constraint> *out) +{ + const exception_node *current_node = get_current_thrown_exception (); + gcc_assert (current_node); + tree curr_exception_type = current_node->maybe_get_type (); + if (!curr_exception_type) + /* We don't know the specific type. */ + return true; + + return edge.apply_constraints (this, ctxt, curr_exception_type, out); +} + +bool +region_model:: +apply_constraints_for_eh_dispatch_try (const eh_dispatch_try_cfg_superedge &edge, + region_model_context */*ctxt*/, + tree exception_type, + std::unique_ptr<rejected_constraint> *out) +{ + /* TODO: can we rely on this ordering? + or do we need to iterate through prev_catch ? */ + /* The exception must not match any of the previous edges. */ + for (auto sibling_sedge : edge.m_src->m_succs) + { + if (sibling_sedge == &edge) + break; + + const eh_dispatch_try_cfg_superedge *sibling_eh_sedge + = as_a <const eh_dispatch_try_cfg_superedge *> (sibling_sedge); + if (eh_catch ehc = sibling_eh_sedge->get_eh_catch ()) + if (matches_any_exception_type_p (ehc, exception_type)) + { + /* The earlier sibling matches, so the "unhandled" edge is + not taken. */ + if (out) + *out = std::make_unique<rejected_eh_dispatch> (*this); + return false; + } + } + + if (eh_catch ehc = edge.get_eh_catch ()) + { + /* We have an edge that tried to match one or more types. */ + + /* The exception must not match any of the previous edges. */ + + /* It must match this type. */ + if (matches_any_exception_type_p (ehc, exception_type)) + return true; + else + { + /* Exception type doesn't match. */ + if (out) + *out = std::make_unique<rejected_eh_dispatch> (*this); + return false; + } + } + else + { + /* This is the "unhandled exception" edge. + If we get here then no sibling edges matched; + we will follow this edge. */ + return true; + } +} + +bool +region_model:: +apply_constraints_for_eh_dispatch_allowed (const eh_dispatch_allowed_cfg_superedge &edge, + region_model_context */*ctxt*/, + tree exception_type, + std::unique_ptr<rejected_constraint> *out) +{ + auto curr_thrown_exception_node = get_current_thrown_exception (); + gcc_assert (curr_thrown_exception_node); + tree curr_exception_type = curr_thrown_exception_node->maybe_get_type (); + eh_region eh_reg = edge.get_eh_region (); + tree type_list = eh_reg->u.allowed.type_list; + + switch (edge.get_eh_kind ()) + { + default: + gcc_unreachable (); + case eh_dispatch_allowed_cfg_superedge::eh_kind::expected: + if (!curr_exception_type) + { + /* We don't know the specific type; + assume we have one of an expected type. */ + return true; + } + for (tree iter = type_list; iter; iter = TREE_CHAIN (iter)) + if (exception_matches_type_p (TREE_VALUE (iter), + exception_type)) + return true; + if (out) + *out = std::make_unique<rejected_eh_dispatch> (*this); + return false; + + case eh_dispatch_allowed_cfg_superedge::eh_kind::unexpected: + if (!curr_exception_type) + { + /* We don't know the specific type; + assume we don't have one of an expected type. */ + if (out) + *out = std::make_unique<rejected_eh_dispatch> (*this); + return false; + } + for (tree iter = type_list; iter; iter = TREE_CHAIN (iter)) + if (exception_matches_type_p (TREE_VALUE (iter), + exception_type)) + { + if (out) + *out = std::make_unique<rejected_eh_dispatch> (*this); + return false; + } + return true; + } +} + /* Given an edge reached by GOTO_STMT, determine appropriate constraints for the edge to be taken. @@ -6202,38 +6715,6 @@ region_model::apply_constraints_for_ggoto (const cfg_superedge &edge, return true; } -/* Apply any constraints due to an exception being thrown at LAST_STMT. - - If they are feasible, add the constraints and return true. - - Return false if the constraints contradict existing knowledge - (and so the edge should not be taken). - When returning false, if OUT is non-NULL, write a new rejected_constraint - to it. */ - -bool -region_model:: -apply_constraints_for_exception (const gimple *last_stmt, - region_model_context *ctxt, - std::unique_ptr<rejected_constraint> *out) -{ - gcc_assert (last_stmt); - if (const gcall *call = dyn_cast <const gcall *> (last_stmt)) - if (tree callee_fndecl = get_fndecl_for_call (call, ctxt)) - if (is_named_call_p (callee_fndecl, "operator new", call, 1) - || is_named_call_p (callee_fndecl, "operator new []", call, 1)) - { - /* We have an exception thrown from operator new. - Add a constraint that the result was NULL, to avoid a false - leak report due to the result being lost when following - the EH edge. */ - if (tree lhs = gimple_call_lhs (call)) - return add_constraint (lhs, EQ_EXPR, null_pointer_node, ctxt, out); - return true; - } - return true; -} - /* For use with push_frame when handling a top-level call within the analysis. PARAM has a defined but unknown initial value. Anything it points to has escaped, since the calling context "knows" @@ -6265,6 +6746,10 @@ region_model::on_top_level_param (tree param, /* Update this region_model to reflect pushing a frame onto the stack for a call to FUN. + If CALL_STMT is non-NULL, this is for the interprocedural case where + we already have an execution path into the caller. It can be NULL for + top-level entrypoints into the analysis, or in selftests. + If ARG_SVALS is non-NULL, use it to populate the parameters in the new frame. Otherwise, the params have their initial_svalues. @@ -6273,14 +6758,32 @@ region_model::on_top_level_param (tree param, const region * region_model::push_frame (const function &fun, + const gcall *call_stmt, const vec<const svalue *> *arg_svals, region_model_context *ctxt) { - m_current_frame = m_mgr->get_frame_region (m_current_frame, fun); + tree fndecl = fun.decl; if (arg_svals) { + /* If the result of the callee is DECL_BY_REFERENCE, then + we'll need to store a reference to the caller's lhs of + CALL_STMT within callee's result. + If so, determine the region of CALL_STMT's lhs within + the caller's frame before updating m_current_frame. */ + const region *caller_return_by_reference_reg = nullptr; + if (tree result = DECL_RESULT (fndecl)) + if (DECL_BY_REFERENCE (result)) + { + gcc_assert (call_stmt); + tree lhs = gimple_call_lhs (call_stmt); + gcc_assert (lhs); + caller_return_by_reference_reg = get_lvalue (lhs, ctxt); + } + + /* Update m_current_frame. */ + m_current_frame = m_mgr->get_frame_region (m_current_frame, fun); + /* Arguments supplied from a caller frame. */ - tree fndecl = fun.decl; unsigned idx = 0; for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm; iter_parm = DECL_CHAIN (iter_parm), ++idx) @@ -6308,13 +6811,39 @@ region_model::push_frame (const function &fun, va_arg_idx); set_value (var_arg_reg, arg_sval, ctxt); } + + /* If the result of the callee is DECL_BY_REFERENCE, then above + we should have determined the region within the + caller's frame that the callee will be writing back to. + Use this now to initialize the reference in callee's frame. */ + if (tree result = DECL_RESULT (fndecl)) + if (DECL_BY_REFERENCE (result)) + { + /* Get reference to the caller lhs. */ + gcc_assert (caller_return_by_reference_reg); + const svalue *ref_sval + = m_mgr->get_ptr_svalue (TREE_TYPE (result), + caller_return_by_reference_reg); + + /* Get region for default val of DECL_RESULT within the + callee. */ + tree result_default_ssa = get_ssa_default_def (fun, result); + gcc_assert (result_default_ssa); + const region *callee_result_reg + = get_lvalue (result_default_ssa, ctxt); + + /* Set the callee's reference to refer to the caller's lhs. */ + set_value (callee_result_reg, ref_sval, ctxt); + } } else { /* Otherwise we have a top-level call within the analysis. The params have defined but unknown initial values. Anything they point to has escaped. */ - tree fndecl = fun.decl; + + /* Update m_current_frame. */ + m_current_frame = m_mgr->get_frame_region (m_current_frame, fun); /* Handle "__attribute__((nonnull))". */ tree fntype = TREE_TYPE (fndecl); @@ -6382,7 +6911,7 @@ public: {} std::unique_ptr<stmt_finder> clone () const override { - return ::make_unique<my_finder> (m_call_stmt, m_caller_frame); + return std::make_unique<my_finder> (m_call_stmt, m_caller_frame); } const gimple *find_stmt (const exploded_path &) override { @@ -6433,7 +6962,7 @@ private: Purge the frame region and all its descendent regions. Convert any pointers that point into such regions into - POISON_KIND_POPPED_STACK svalues. */ + poison_kind::popped_stack svalues. */ void region_model::pop_frame (tree result_lvalue, @@ -6454,7 +6983,7 @@ region_model::pop_frame (tree result_lvalue, /* Evaluate the result, within the callee frame. */ tree fndecl = m_current_frame->get_function ().decl; tree result = DECL_RESULT (fndecl); - const svalue *retval = NULL; + const svalue *retval = nullptr; if (result && TREE_TYPE (result) != void_type_node && eval_return_svalue) @@ -6467,7 +6996,11 @@ region_model::pop_frame (tree result_lvalue, /* Pop the frame. */ m_current_frame = m_current_frame->get_calling_frame (); - if (result_lvalue && retval) + if (result_lvalue + && retval + /* Don't write back for DECL_BY_REFERENCE; the writes + should have happened within the callee already. */ + && !DECL_BY_REFERENCE (result)) { gcc_assert (eval_return_svalue); @@ -6483,7 +7016,7 @@ region_model::pop_frame (tree result_lvalue, set_value (result_dst_reg, retval, call_stmt ? &caller_ctxt : ctxt); } - unbind_region_and_descendents (frame_reg,POISON_KIND_POPPED_STACK); + unbind_region_and_descendents (frame_reg,poison_kind::popped_stack); notify_on_pop_frame (this, &pre_popped_model, retval, ctxt); } @@ -6640,6 +7173,14 @@ region_model::can_merge_with_p (const region_model &other_model, for (auto iter : m.m_svals_changing_meaning) out_model->m_constraints->purge_state_involving (iter); + if (m_thrown_exceptions_stack != other_model.m_thrown_exceptions_stack) + return false; + out_model->m_thrown_exceptions_stack = m_thrown_exceptions_stack; + + if (m_caught_exceptions_stack != other_model.m_caught_exceptions_stack) + return false; + out_model->m_caught_exceptions_stack = m_caught_exceptions_stack; + return true; } @@ -6647,10 +7188,10 @@ region_model::can_merge_with_p (const region_model &other_model, otherwise. */ tree -region_model::get_fndecl_for_call (const gcall *call, +region_model::get_fndecl_for_call (const gcall &call, region_model_context *ctxt) { - tree fn_ptr = gimple_call_fn (call); + tree fn_ptr = gimple_call_fn (&call); if (fn_ptr == NULL_TREE) return NULL_TREE; const svalue *fn_ptr_sval = get_rvalue (fn_ptr, ctxt); @@ -6773,7 +7314,7 @@ private: class contains_floating_point_visitor : public visitor { public: - contains_floating_point_visitor (const svalue *root_sval) : m_result (NULL) + contains_floating_point_visitor (const svalue *root_sval) : m_result (nullptr) { root_sval->accept (this); } @@ -6825,7 +7366,7 @@ region_model::check_dynamic_size_for_floats (const svalue *size_in_bytes, if (const svalue *float_sval = v.get_svalue_to_report ()) { tree diag_arg = get_representative_tree (float_sval); - ctxt->warn (make_unique<float_as_size_arg> (diag_arg)); + ctxt->warn (std::make_unique<float_as_size_arg> (diag_arg)); } } @@ -6894,6 +7435,12 @@ region_model::get_referenced_base_regions (auto_bitmap &out_ids) const reachable_regs.add (base_reg, false); } + for (auto &eh_node : m_thrown_exceptions_stack) + eh_node.add_to_reachable_regions (reachable_regs); + for (auto &eh_node : m_caught_exceptions_stack) + eh_node.add_to_reachable_regions (reachable_regs); + + bitmap_clear (out_ids); for (auto iter_reg : reachable_regs) bitmap_set_bit (out_ids, iter_reg->get_id ()); @@ -6931,7 +7478,7 @@ region_model::set_dynamic_extents (const region *reg, m_dynamic_extents.put (reg, size_in_bytes); } -/* Get the recording of REG in bytes, or NULL if no dynamic size was +/* Get the recording of REG in bytes, or nullptr if no dynamic size was recorded. */ const svalue * @@ -6939,7 +7486,7 @@ region_model::get_dynamic_extents (const region *reg) const { if (const svalue * const *slot = m_dynamic_extents.get (reg)) return *slot; - return NULL; + return nullptr; } /* Unset any recorded dynamic size of REG. */ @@ -7050,9 +7597,10 @@ public: } void - maybe_add_sarif_properties (sarif_object &result_obj) const final override + maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) + const final override { - sarif_property_bag &props = result_obj.get_or_create_properties (); + auto &props = result_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/-Wanalyzer-exposure-through-uninit-copy/" props.set (PROPERTY_PREFIX "src_region", m_src_region->to_json ()); props.set (PROPERTY_PREFIX "dest_region", m_dest_region->to_json ()); @@ -7077,7 +7625,7 @@ private: { const poisoned_svalue *poisoned_sval = as_a <const poisoned_svalue *> (m_copied_sval); - gcc_assert (poisoned_sval->get_poison_kind () == POISON_KIND_UNINIT); + gcc_assert (poisoned_sval->get_poison_kind () == poison_kind::uninit); /* Give up if don't have type information. */ if (m_copied_sval->get_type () == NULL_TREE) @@ -7102,7 +7650,7 @@ private: const svalue *sval = iter.second; if (const poisoned_svalue *psval = sval->dyn_cast_poisoned_svalue ()) - if (psval->get_poison_kind () == POISON_KIND_UNINIT) + if (psval->get_poison_kind () == poison_kind::uninit) { const binding_key *key = iter.first; const concrete_binding *ckey @@ -7154,7 +7702,7 @@ private: const svalue *sval = iter.second; if (const poisoned_svalue *psval = sval->dyn_cast_poisoned_svalue ()) - if (psval->get_poison_kind () == POISON_KIND_UNINIT) + if (psval->get_poison_kind () == poison_kind::uninit) { const binding_key *key = iter.first; const concrete_binding *ckey @@ -7171,8 +7719,7 @@ private: tree type = m_copied_sval->get_type (); if (type && TREE_CODE (type) == RECORD_TYPE) { - // (std::make_unique is C++14) - layout = std::unique_ptr<record_layout> (new record_layout (type)); + layout = std::make_unique<record_layout> (type); if (0) layout->dump (); @@ -7254,7 +7801,7 @@ private: static void complain_about_fully_uninit_item (const record_layout::item &item) { - tree field = item.m_field; + const_tree field = item.m_field; bit_size_t num_bits = item.m_bit_range.m_size_in_bits; if (item.m_is_padding) { @@ -7315,7 +7862,7 @@ private: static void complain_about_partially_uninit_item (const record_layout::item &item) { - tree field = item.m_field; + const_tree field = item.m_field; if (item.m_is_padding) inform (DECL_SOURCE_LOCATION (field), "padding after field %qD is partially uninitialized", @@ -7358,7 +7905,7 @@ contains_uninit_p (const svalue *sval) { const poisoned_svalue *psval = as_a <const poisoned_svalue *> (sval); - return psval->get_poison_kind () == POISON_KIND_UNINIT; + return psval->get_poison_kind () == poison_kind::uninit; } case SK_COMPOUND: { @@ -7370,7 +7917,7 @@ contains_uninit_p (const svalue *sval) const svalue *sval = iter.second; if (const poisoned_svalue *psval = sval->dyn_cast_poisoned_svalue ()) - if (psval->get_poison_kind () == POISON_KIND_UNINIT) + if (psval->get_poison_kind () == poison_kind::uninit) return true; } @@ -7386,7 +7933,7 @@ contains_uninit_p (const svalue *sval) Check that COPIED_SVAL is fully initialized. If not, complain about an infoleak to CTXT. - SRC_REG can be NULL; if non-NULL it is used as a hint in the diagnostic + SRC_REG can be nullptr; if non-NULL it is used as a hint in the diagnostic as to where COPIED_SVAL came from. */ void @@ -7397,9 +7944,10 @@ region_model::maybe_complain_about_infoleak (const region *dst_reg, { /* Check for exposure. */ if (contains_uninit_p (copied_sval)) - ctxt->warn (make_unique<exposure_through_uninit_copy> (src_reg, - dst_reg, - copied_sval)); + ctxt->warn + (std::make_unique<exposure_through_uninit_copy> (src_reg, + dst_reg, + copied_sval)); } /* Set errno to a positive symbolic int, as if some error has occurred. */ @@ -7411,7 +7959,7 @@ region_model::set_errno (const call_details &cd) conjured_purge p (this, cd.get_ctxt ()); const svalue *new_errno_sval = m_mgr->get_or_create_conjured_svalue (integer_type_node, - cd.get_call_stmt (), + &cd.get_call_stmt (), errno_reg, p); const svalue *zero = m_mgr->get_or_create_int_cst (integer_type_node, 0); @@ -7538,8 +8086,8 @@ void rejected_op_constraint::dump_to_pp (pretty_printer *pp) const { region_model m (m_model); - const svalue *lhs_sval = m.get_rvalue (m_lhs, NULL); - const svalue *rhs_sval = m.get_rvalue (m_rhs, NULL); + const svalue *lhs_sval = m.get_rvalue (m_lhs, nullptr); + const svalue *rhs_sval = m.get_rvalue (m_rhs, nullptr); lhs_sval->dump_to_pp (pp, true); pp_printf (pp, " %s ", op_symbol_code (m_op)); rhs_sval->dump_to_pp (pp, true); @@ -7559,7 +8107,7 @@ void rejected_ranges_constraint::dump_to_pp (pretty_printer *pp) const { region_model m (m_model); - const svalue *sval = m.get_rvalue (m_expr, NULL); + const svalue *sval = m.get_rvalue (m_expr, nullptr); sval->dump_to_pp (pp, true); pp_string (pp, " in "); m_ranges->dump_to_pp (pp, true); @@ -7660,7 +8208,7 @@ assert_condition (const location &loc, tree lhs, tree_code op, tree rhs, tristate expected) { - tristate actual = model.eval_condition (lhs, op, rhs, NULL); + tristate actual = model.eval_condition (lhs, op, rhs, nullptr); ASSERT_EQ_AT (loc, actual, expected); } @@ -7749,7 +8297,7 @@ make_test_compound_type (const char *name, bool is_struct, TYPE_NAME (t) = get_identifier (name); TYPE_SIZE (t) = 0; - tree fieldlist = NULL; + tree fieldlist = NULL_TREE; int i; tree field; FOR_EACH_VEC_ELT (*fields, i, field) @@ -7804,22 +8352,22 @@ test_struct () region_model_manager mgr; region_model model (&mgr); - model.set_value (c_x, int_17, NULL); - model.set_value (c_y, int_m3, NULL); + model.set_value (c_x, int_17, nullptr); + model.set_value (c_y, int_m3, nullptr); /* Verify get_offset for "c.x". */ { - const region *c_x_reg = model.get_lvalue (c_x, NULL); + const region *c_x_reg = model.get_lvalue (c_x, nullptr); region_offset offset = c_x_reg->get_offset (&mgr); - ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, NULL)); + ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, nullptr)); ASSERT_EQ (offset.get_bit_offset (), 0); } /* Verify get_offset for "c.y". */ { - const region *c_y_reg = model.get_lvalue (c_y, NULL); + const region *c_y_reg = model.get_lvalue (c_y, nullptr); region_offset offset = c_y_reg->get_offset (&mgr); - ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, NULL)); + ASSERT_EQ (offset.get_base_region (), model.get_lvalue (c, nullptr)); ASSERT_EQ (offset.get_bit_offset (), INT_TYPE_SIZE); } } @@ -7840,7 +8388,7 @@ test_array_1 () tree a_0 = build4 (ARRAY_REF, char_type_node, a, int_0, NULL_TREE, NULL_TREE); tree char_A = build_int_cst (char_type_node, 'A'); - model.set_value (a_0, char_A, NULL); + model.set_value (a_0, char_A, nullptr); } /* Verify that region_model::get_representative_tree works as expected. */ @@ -7854,7 +8402,7 @@ test_get_representative_tree () { tree string_cst = build_string (4, "foo"); region_model m (&mgr); - const svalue *str_sval = m.get_rvalue (string_cst, NULL); + const svalue *str_sval = m.get_rvalue (string_cst, nullptr); tree rep = m.get_representative_tree (str_sval); ASSERT_EQ (rep, string_cst); } @@ -7863,7 +8411,7 @@ test_get_representative_tree () { tree string_cst_ptr = build_string_literal (4, "foo"); region_model m (&mgr); - const svalue *str_sval = m.get_rvalue (string_cst_ptr, NULL); + const svalue *str_sval = m.get_rvalue (string_cst_ptr, nullptr); tree rep = m.get_representative_tree (str_sval); ASSERT_DUMP_TREE_EQ (rep, "&\"foo\"[0]"); } @@ -7988,12 +8536,12 @@ test_unique_unknowns () /* Different types (or the NULL type) should have different unknown_svalues. */ - const svalue *unknown_NULL_type = mgr.get_or_create_unknown_svalue (NULL); + const svalue *unknown_NULL_type = mgr.get_or_create_unknown_svalue (nullptr); ASSERT_NE (unknown_NULL_type, unknown_int); /* Repeated calls with NULL for the type should get the same "unknown" svalue. */ - const svalue *unknown_NULL_type_2 = mgr.get_or_create_unknown_svalue (NULL); + const svalue *unknown_NULL_type_2 = mgr.get_or_create_unknown_svalue (nullptr); ASSERT_EQ (unknown_NULL_type, unknown_NULL_type_2); } @@ -8337,9 +8885,9 @@ test_assignment () region_model model (&mgr); ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0); ASSERT_CONDITION_UNKNOWN (model, y, EQ_EXPR, int_0); - model.set_value (model.get_lvalue (y, NULL), - model.get_rvalue (int_0, NULL), - NULL); + model.set_value (model.get_lvalue (y, nullptr), + model.get_rvalue (int_0, nullptr), + nullptr); ASSERT_CONDITION_TRUE (model, y, EQ_EXPR, int_0); ASSERT_CONDITION_TRUE (model, y, EQ_EXPR, x); } @@ -8367,16 +8915,16 @@ test_compound_assignment () region_model_manager mgr; region_model model (&mgr); - model.set_value (c_x, int_17, NULL); - model.set_value (c_y, int_m3, NULL); + model.set_value (c_x, int_17, nullptr); + model.set_value (c_y, int_m3, nullptr); /* Copy c to d. */ - const svalue *sval = model.get_rvalue (c, NULL); - model.set_value (model.get_lvalue (d, NULL), sval, NULL); + const svalue *sval = model.get_rvalue (c, nullptr); + model.set_value (model.get_lvalue (d, nullptr), sval, nullptr); /* Check that the fields have the same svalues. */ - ASSERT_EQ (model.get_rvalue (c_x, NULL), model.get_rvalue (d_x, NULL)); - ASSERT_EQ (model.get_rvalue (c_y, NULL), model.get_rvalue (d_y, NULL)); + ASSERT_EQ (model.get_rvalue (c_x, nullptr), model.get_rvalue (d_x, nullptr)); + ASSERT_EQ (model.get_rvalue (c_y, nullptr), model.get_rvalue (d_y, nullptr)); } /* Verify the details of pushing and popping stack frames. */ @@ -8432,7 +8980,7 @@ test_stack_frames () /* Push stack frame for "parent_fn". */ const region *parent_frame_reg = model.push_frame (*DECL_STRUCT_FUNCTION (parent_fndecl), - NULL, &ctxt); + nullptr, nullptr, &ctxt); ASSERT_EQ (model.get_current_frame (), parent_frame_reg); ASSERT_TRUE (model.region_exists_p (parent_frame_reg)); const region *a_in_parent_reg = model.get_lvalue (a, &ctxt); @@ -8447,7 +8995,8 @@ test_stack_frames () /* Push stack frame for "child_fn". */ const region *child_frame_reg - = model.push_frame (*DECL_STRUCT_FUNCTION (child_fndecl), NULL, &ctxt); + = model.push_frame (*DECL_STRUCT_FUNCTION (child_fndecl), + nullptr, nullptr, &ctxt); ASSERT_EQ (model.get_current_frame (), child_frame_reg); ASSERT_TRUE (model.region_exists_p (child_frame_reg)); const region *x_in_child_reg = model.get_lvalue (x, &ctxt); @@ -8465,7 +9014,7 @@ test_stack_frames () model.set_value (p_in_globals_reg, mgr.get_ptr_svalue (ptr_type_node, x_in_child_reg), &ctxt); - ASSERT_EQ (p_in_globals_reg->maybe_get_frame_region (), NULL); + ASSERT_EQ (p_in_globals_reg->maybe_get_frame_region (), nullptr); /* Point another global pointer at p: q = &p. */ const region *q_in_globals_reg = model.get_lvalue (q, &ctxt); @@ -8479,16 +9028,16 @@ test_stack_frames () ASSERT_FALSE (a_in_parent_reg->descendent_of_p (child_frame_reg)); /* Pop the "child_fn" frame from the stack. */ - model.pop_frame (NULL, NULL, &ctxt, nullptr); + model.pop_frame (nullptr, nullptr, &ctxt, nullptr); ASSERT_FALSE (model.region_exists_p (child_frame_reg)); ASSERT_TRUE (model.region_exists_p (parent_frame_reg)); /* Verify that p (which was pointing at the local "x" in the popped frame) has been poisoned. */ - const svalue *new_p_sval = model.get_rvalue (p, NULL); + const svalue *new_p_sval = model.get_rvalue (p, nullptr); ASSERT_EQ (new_p_sval->get_kind (), SK_POISONED); ASSERT_EQ (new_p_sval->dyn_cast_poisoned_svalue ()->get_poison_kind (), - POISON_KIND_POPPED_STACK); + poison_kind::popped_stack); /* Verify that q still points to p, in spite of the region renumbering. */ @@ -8540,7 +9089,8 @@ test_get_representative_path_var () for (int depth = 0; depth < 5; depth++) { const region *frame_n_reg - = model.push_frame (*DECL_STRUCT_FUNCTION (fndecl), NULL, &ctxt); + = model.push_frame (*DECL_STRUCT_FUNCTION (fndecl), + nullptr, nullptr, &ctxt); const region *parm_n_reg = model.get_lvalue (path_var (n, depth), &ctxt); parm_regs.safe_push (parm_n_reg); @@ -8562,7 +9112,7 @@ test_get_representative_path_var () } /* ...and that we can lookup lvalues for locals for all frames, not just the top. */ - ASSERT_EQ (model.get_lvalue (path_var (n, depth), NULL), + ASSERT_EQ (model.get_lvalue (path_var (n, depth), nullptr), parm_regs[depth]); /* ...and that we can locate the svalues. */ { @@ -8591,22 +9141,22 @@ test_equality_1 () /* Verify that setting state in model1 makes the models non-equal. */ tree x = build_global_decl ("x", integer_type_node); - model0.set_value (x, int_42, NULL); - ASSERT_EQ (model0.get_rvalue (x, NULL)->maybe_get_constant (), int_42); + model0.set_value (x, int_42, nullptr); + ASSERT_EQ (model0.get_rvalue (x, nullptr)->maybe_get_constant (), int_42); ASSERT_NE (model0, model1); /* Verify the copy-ctor. */ region_model model2 (model0); ASSERT_EQ (model0, model2); - ASSERT_EQ (model2.get_rvalue (x, NULL)->maybe_get_constant (), int_42); + ASSERT_EQ (model2.get_rvalue (x, nullptr)->maybe_get_constant (), int_42); ASSERT_NE (model1, model2); /* Verify that models obtained from copy-ctor are independently editable w/o affecting the original model. */ - model2.set_value (x, int_17, NULL); + model2.set_value (x, int_17, nullptr); ASSERT_NE (model0, model2); - ASSERT_EQ (model2.get_rvalue (x, NULL)->maybe_get_constant (), int_17); - ASSERT_EQ (model0.get_rvalue (x, NULL)->maybe_get_constant (), int_42); + ASSERT_EQ (model2.get_rvalue (x, nullptr)->maybe_get_constant (), int_17); + ASSERT_EQ (model0.get_rvalue (x, nullptr)->maybe_get_constant (), int_42); } /* Verify that region models for @@ -8625,20 +9175,20 @@ test_canonicalization_2 () region_model_manager mgr; region_model model0 (&mgr); - model0.set_value (model0.get_lvalue (x, NULL), - model0.get_rvalue (int_42, NULL), - NULL); - model0.set_value (model0.get_lvalue (y, NULL), - model0.get_rvalue (int_113, NULL), - NULL); + model0.set_value (model0.get_lvalue (x, nullptr), + model0.get_rvalue (int_42, nullptr), + nullptr); + model0.set_value (model0.get_lvalue (y, nullptr), + model0.get_rvalue (int_113, nullptr), + nullptr); region_model model1 (&mgr); - model1.set_value (model1.get_lvalue (y, NULL), - model1.get_rvalue (int_113, NULL), - NULL); - model1.set_value (model1.get_lvalue (x, NULL), - model1.get_rvalue (int_42, NULL), - NULL); + model1.set_value (model1.get_lvalue (y, nullptr), + model1.get_rvalue (int_113, nullptr), + nullptr); + model1.set_value (model1.get_lvalue (x, nullptr), + model1.get_rvalue (int_42, nullptr), + nullptr); ASSERT_EQ (model0, model1); } @@ -8659,12 +9209,12 @@ test_canonicalization_3 () region_model_manager mgr; region_model model0 (&mgr); - model0.add_constraint (x, GT_EXPR, int_3, NULL); - model0.add_constraint (y, GT_EXPR, int_42, NULL); + model0.add_constraint (x, GT_EXPR, int_3, nullptr); + model0.add_constraint (y, GT_EXPR, int_42, nullptr); region_model model1 (&mgr); - model1.add_constraint (y, GT_EXPR, int_42, NULL); - model1.add_constraint (x, GT_EXPR, int_3, NULL); + model1.add_constraint (y, GT_EXPR, int_42, nullptr); + model1.add_constraint (x, GT_EXPR, int_3, nullptr); model0.canonicalize (); model1.canonicalize (); @@ -8684,7 +9234,7 @@ test_canonicalization_4 () region_model model (&mgr); for (tree cst : csts) - model.get_rvalue (cst, NULL); + model.get_rvalue (cst, nullptr); model.canonicalize (); } @@ -8693,7 +9243,7 @@ test_canonicalization_4 () with values VAL_A and VAL_B for EXPR that they are mergable. Write the merged model to *OUT_MERGED_MODEL, and the merged svalue ptr to *OUT_MERGED_SVALUE. - If VAL_A or VAL_B are NULL_TREE, don't populate EXPR + If VAL_A or VAL_B are nullptr_TREE, don't populate EXPR for that region_model. */ static void @@ -8786,9 +9336,11 @@ test_state_merging () region_model model0 (&mgr); region_model model1 (&mgr); ASSERT_EQ (model0.get_stack_depth (), 0); - model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt); + model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, &ctxt); ASSERT_EQ (model0.get_stack_depth (), 1); - model1.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt); + model1.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, &ctxt); placeholder_svalue test_sval (mgr.alloc_symbol_id (), integer_type_node, "test sval"); @@ -8880,9 +9432,10 @@ test_state_merging () /* Pointers: non-NULL and non-NULL: ptr to a local. */ { region_model model0 (&mgr); - model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); - model0.set_value (model0.get_lvalue (p, NULL), - model0.get_rvalue (addr_of_a, NULL), NULL); + model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, nullptr); + model0.set_value (model0.get_lvalue (p, nullptr), + model0.get_rvalue (addr_of_a, nullptr), nullptr); region_model model1 (model0); ASSERT_EQ (model0, model1); @@ -8906,7 +9459,7 @@ test_state_merging () const region_svalue *merged_p_ptr = merged_p_sval->dyn_cast_region_svalue (); const region *merged_p_star_reg = merged_p_ptr->get_pointee (); - ASSERT_EQ (merged_p_star_reg, merged.get_lvalue (y, NULL)); + ASSERT_EQ (merged_p_star_reg, merged.get_lvalue (y, nullptr)); } /* Pointers: non-NULL ptrs to different globals: should be unknown. */ @@ -9019,17 +9572,19 @@ test_state_merging () frame points to a local in a more recent stack frame. */ { region_model model0 (&mgr); - model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); - const region *q_in_first_frame = model0.get_lvalue (q, NULL); + model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, nullptr); + const region *q_in_first_frame = model0.get_lvalue (q, nullptr); /* Push a second frame. */ const region *reg_2nd_frame - = model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); + = model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, nullptr); /* Have a pointer in the older frame point to a local in the more recent frame. */ - const svalue *sval_ptr = model0.get_rvalue (addr_of_a, NULL); - model0.set_value (q_in_first_frame, sval_ptr, NULL); + const svalue *sval_ptr = model0.get_rvalue (addr_of_a, nullptr); + model0.set_value (q_in_first_frame, sval_ptr, nullptr); /* Verify that it's pointing at the newer frame. */ const region *reg_pointee = sval_ptr->maybe_get_region (); @@ -9051,9 +9606,10 @@ test_state_merging () /* Verify that we can merge a model in which a local points to a global. */ { region_model model0 (&mgr); - model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); - model0.set_value (model0.get_lvalue (q, NULL), - model0.get_rvalue (addr_of_y, NULL), NULL); + model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), + nullptr, nullptr, nullptr); + model0.set_value (model0.get_lvalue (q, nullptr), + model0.get_rvalue (addr_of_y, nullptr), nullptr); region_model model1 (model0); ASSERT_EQ (model0, model1); @@ -9085,14 +9641,14 @@ test_constraint_merging () /* model0: 0 <= (x == y) < n. */ region_model model0 (&mgr); model0.add_constraint (x, EQ_EXPR, y, &ctxt); - model0.add_constraint (x, GE_EXPR, int_0, NULL); - model0.add_constraint (x, LT_EXPR, n, NULL); + model0.add_constraint (x, GE_EXPR, int_0, nullptr); + model0.add_constraint (x, LT_EXPR, n, nullptr); /* model1: z != 5 && (0 <= x < n). */ region_model model1 (&mgr); - model1.add_constraint (z, NE_EXPR, int_5, NULL); - model1.add_constraint (x, GE_EXPR, int_0, NULL); - model1.add_constraint (x, LT_EXPR, n, NULL); + model1.add_constraint (z, NE_EXPR, int_5, nullptr); + model1.add_constraint (x, GE_EXPR, int_0, nullptr); + model1.add_constraint (x, LT_EXPR, n, nullptr); /* They should be mergeable; the merged constraints should be: (0 <= x < n). */ @@ -9317,17 +9873,17 @@ test_malloc_constraints () const svalue *size_in_bytes = mgr.get_or_create_unknown_svalue (size_type_node); const region *reg - = model.get_or_create_region_for_heap_alloc (size_in_bytes, NULL); + = model.get_or_create_region_for_heap_alloc (size_in_bytes, nullptr); const svalue *sval = mgr.get_ptr_svalue (ptr_type_node, reg); - model.set_value (model.get_lvalue (p, NULL), sval, NULL); - model.set_value (q, p, NULL); + model.set_value (model.get_lvalue (p, nullptr), sval, nullptr); + model.set_value (q, p, nullptr); ASSERT_CONDITION_UNKNOWN (model, p, NE_EXPR, null_ptr); ASSERT_CONDITION_UNKNOWN (model, p, EQ_EXPR, null_ptr); ASSERT_CONDITION_UNKNOWN (model, q, NE_EXPR, null_ptr); ASSERT_CONDITION_UNKNOWN (model, q, EQ_EXPR, null_ptr); - model.add_constraint (p, NE_EXPR, null_ptr, NULL); + model.add_constraint (p, NE_EXPR, null_ptr, nullptr); ASSERT_CONDITION_TRUE (model, p, NE_EXPR, null_ptr); ASSERT_CONDITION_FALSE (model, p, EQ_EXPR, null_ptr); @@ -9349,25 +9905,25 @@ test_var () region_model_manager mgr; region_model model (&mgr); - const region *i_reg = model.get_lvalue (i, NULL); + const region *i_reg = model.get_lvalue (i, nullptr); ASSERT_EQ (i_reg->get_kind (), RK_DECL); /* Reading "i" should give a symbolic "initial value". */ - const svalue *sval_init = model.get_rvalue (i, NULL); + const svalue *sval_init = model.get_rvalue (i, nullptr); ASSERT_EQ (sval_init->get_kind (), SK_INITIAL); ASSERT_EQ (sval_init->dyn_cast_initial_svalue ()->get_region (), i_reg); /* ..and doing it again should give the same "initial value". */ - ASSERT_EQ (model.get_rvalue (i, NULL), sval_init); + ASSERT_EQ (model.get_rvalue (i, nullptr), sval_init); /* "i = 17;". */ - model.set_value (i, int_17, NULL); - ASSERT_EQ (model.get_rvalue (i, NULL), - model.get_rvalue (int_17, NULL)); + model.set_value (i, int_17, nullptr); + ASSERT_EQ (model.get_rvalue (i, nullptr), + model.get_rvalue (int_17, nullptr)); /* "i = -3;". */ - model.set_value (i, int_m3, NULL); - ASSERT_EQ (model.get_rvalue (i, NULL), - model.get_rvalue (int_m3, NULL)); + model.set_value (i, int_m3, nullptr); + ASSERT_EQ (model.get_rvalue (i, nullptr), + model.get_rvalue (int_m3, nullptr)); /* Verify get_offset for "i". */ { @@ -9406,38 +9962,41 @@ test_array_2 () region_model_manager mgr; region_model model (&mgr); /* "arr[0] = 17;". */ - model.set_value (arr_0, int_17, NULL); + model.set_value (arr_0, int_17, nullptr); /* "arr[1] = -3;". */ - model.set_value (arr_1, int_m3, NULL); + model.set_value (arr_1, int_m3, nullptr); - ASSERT_EQ (model.get_rvalue (arr_0, NULL), model.get_rvalue (int_17, NULL)); - ASSERT_EQ (model.get_rvalue (arr_1, NULL), model.get_rvalue (int_m3, NULL)); + ASSERT_EQ (model.get_rvalue (arr_0, nullptr), + model.get_rvalue (int_17, nullptr)); + ASSERT_EQ (model.get_rvalue (arr_1, nullptr), + model.get_rvalue (int_m3, nullptr)); /* Overwrite a pre-existing binding: "arr[1] = 42;". */ - model.set_value (arr_1, int_42, NULL); - ASSERT_EQ (model.get_rvalue (arr_1, NULL), model.get_rvalue (int_42, NULL)); + model.set_value (arr_1, int_42, nullptr); + ASSERT_EQ (model.get_rvalue (arr_1, nullptr), + model.get_rvalue (int_42, nullptr)); /* Verify get_offset for "arr[0]". */ { - const region *arr_0_reg = model.get_lvalue (arr_0, NULL); + const region *arr_0_reg = model.get_lvalue (arr_0, nullptr); region_offset offset = arr_0_reg->get_offset (&mgr); - ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, NULL)); + ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, nullptr)); ASSERT_EQ (offset.get_bit_offset (), 0); } /* Verify get_offset for "arr[1]". */ { - const region *arr_1_reg = model.get_lvalue (arr_1, NULL); + const region *arr_1_reg = model.get_lvalue (arr_1, nullptr); region_offset offset = arr_1_reg->get_offset (&mgr); - ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, NULL)); + ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, nullptr)); ASSERT_EQ (offset.get_bit_offset (), INT_TYPE_SIZE); } /* Verify get_offset for "arr[i]". */ { - const region *arr_i_reg = model.get_lvalue (arr_i, NULL); + const region *arr_i_reg = model.get_lvalue (arr_i, nullptr); region_offset offset = arr_i_reg->get_offset (&mgr); - ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, NULL)); + ASSERT_EQ (offset.get_base_region (), model.get_lvalue (arr, nullptr)); const svalue *offset_sval = offset.get_symbolic_byte_offset (); if (const svalue *cast = offset_sval->maybe_undo_cast ()) offset_sval = cast; @@ -9445,14 +10004,15 @@ test_array_2 () } /* "arr[i] = i;" - this should remove the earlier bindings. */ - model.set_value (arr_i, i, NULL); - ASSERT_EQ (model.get_rvalue (arr_i, NULL), model.get_rvalue (i, NULL)); - ASSERT_EQ (model.get_rvalue (arr_0, NULL)->get_kind (), SK_UNKNOWN); + model.set_value (arr_i, i, nullptr); + ASSERT_EQ (model.get_rvalue (arr_i, nullptr), model.get_rvalue (i, nullptr)); + ASSERT_EQ (model.get_rvalue (arr_0, nullptr)->get_kind (), SK_UNKNOWN); /* "arr[0] = 17;" - this should remove the arr[i] binding. */ - model.set_value (arr_0, int_17, NULL); - ASSERT_EQ (model.get_rvalue (arr_0, NULL), model.get_rvalue (int_17, NULL)); - ASSERT_EQ (model.get_rvalue (arr_i, NULL)->get_kind (), SK_UNKNOWN); + model.set_value (arr_0, int_17, nullptr); + ASSERT_EQ (model.get_rvalue (arr_0, nullptr), + model.get_rvalue (int_17, nullptr)); + ASSERT_EQ (model.get_rvalue (arr_i, nullptr)->get_kind (), SK_UNKNOWN); } /* Smoketest of dereferencing a pointer via MEM_REF. */ @@ -9479,12 +10039,12 @@ test_mem_ref () region_model model (&mgr); /* "x = 17;". */ - model.set_value (x, int_17, NULL); + model.set_value (x, int_17, nullptr); /* "p = &x;". */ - model.set_value (p, addr_of_x, NULL); + model.set_value (p, addr_of_x, nullptr); - const svalue *sval = model.get_rvalue (star_p, NULL); + const svalue *sval = model.get_rvalue (star_p, nullptr); ASSERT_EQ (sval->maybe_get_constant (), int_17); } @@ -9530,8 +10090,8 @@ test_POINTER_PLUS_EXPR_then_MEM_REF () region_model m (&mgr); tree int_42 = build_int_cst (integer_type_node, 42); - m.set_value (mem_ref, int_42, NULL); - ASSERT_EQ (m.get_rvalue (mem_ref, NULL)->maybe_get_constant (), int_42); + m.set_value (mem_ref, int_42, nullptr); + ASSERT_EQ (m.get_rvalue (mem_ref, nullptr)->maybe_get_constant (), int_42); } /* Verify that malloc works. */ @@ -9583,7 +10143,7 @@ test_alloca () /* Push stack frame. */ const region *frame_reg = model.push_frame (*DECL_STRUCT_FUNCTION (fndecl), - NULL, &ctxt); + nullptr, nullptr, &ctxt); /* "p = alloca (n * 4);". */ const svalue *size_sval = model.get_rvalue (n_times_4, &ctxt); const region *reg = model.create_region_for_alloca (size_sval, &ctxt); @@ -9594,8 +10154,8 @@ test_alloca () /* Verify that the pointers to the alloca region are replaced by poisoned values when the frame is popped. */ - model.pop_frame (NULL, NULL, &ctxt, nullptr); - ASSERT_EQ (model.get_rvalue (p, NULL)->get_kind (), SK_POISONED); + model.pop_frame (nullptr, nullptr, &ctxt, nullptr); + ASSERT_EQ (model.get_rvalue (p, nullptr)->get_kind (), SK_POISONED); } /* Verify that svalue::involves_p works. */ |