diff options
Diffstat (limited to 'gcc/analyzer/engine.cc')
-rw-r--r-- | gcc/analyzer/engine.cc | 956 |
1 files changed, 663 insertions, 293 deletions
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 71d7ed7..67024e9 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -18,23 +18,27 @@ 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_VECTOR -#include "system.h" -#include "coretypes.h" -#include "make-unique.h" -#include "tree.h" -#include "fold-const.h" +#include "analyzer/common.h" + +#include <zlib.h> + +#include "cfg.h" +#include "basic-block.h" #include "gcc-rich-location.h" -#include "diagnostic-core.h" -#include "diagnostic-event-id.h" -#include "diagnostic-path.h" -#include "function.h" -#include "pretty-print.h" -#include "sbitmap.h" -#include "bitmap.h" -#include "ordered-hash-map.h" -#include "analyzer/analyzer.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "gimple-pretty-print.h" +#include "cgraph.h" +#include "fold-const.h" +#include "digraph.h" +#include "plugin.h" +#include "target.h" +#include "stringpool.h" +#include "attribs.h" +#include "tree-dfa.h" + +#include "text-art/dump.h" + #include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" @@ -44,13 +48,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" -#include "cfg.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "gimple-pretty-print.h" -#include "cgraph.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "analyzer/program-state.h" #include "analyzer/exploded-graph.h" @@ -59,16 +56,8 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/state-purge.h" #include "analyzer/bar-chart.h" #include "analyzer/call-info.h" -#include <zlib.h> -#include "plugin.h" -#include "target.h" -#include <memory> -#include "stringpool.h" -#include "attribs.h" -#include "tree-dfa.h" #include "analyzer/known-function-manager.h" #include "analyzer/call-summary.h" -#include "text-art/dump.h" /* For an overview, see gcc/doc/analyzer.texi. */ @@ -106,14 +95,14 @@ impl_region_model_context (program_state *state, const extrinsic_state &ext_state, uncertainty_t *uncertainty, logger *logger) -: m_eg (NULL), m_logger (logger), m_enode_for_diag (NULL), - m_old_state (NULL), +: m_eg (nullptr), m_logger (logger), m_enode_for_diag (nullptr), + m_old_state (nullptr), m_new_state (state), - m_stmt (NULL), - m_stmt_finder (NULL), + m_stmt (nullptr), + m_stmt_finder (nullptr), m_ext_state (ext_state), m_uncertainty (uncertainty), - m_path_ctxt (NULL), + m_path_ctxt (nullptr), m_out_could_have_done_work (nullptr) { } @@ -124,7 +113,7 @@ impl_region_model_context::warn (std::unique_ptr<pending_diagnostic> d, { LOG_FUNC (get_logger ()); auto curr_stmt_finder = custom_finder ? custom_finder : m_stmt_finder; - if (m_stmt == NULL && curr_stmt_finder == NULL) + if (m_stmt == nullptr && curr_stmt_finder == nullptr) { if (get_logger ()) get_logger ()->log ("rejecting diagnostic: no stmt"); @@ -307,7 +296,7 @@ public: const sm_state_map *old_smap, sm_state_map *new_smap, path_context *path_ctxt, - const stmt_finder *stmt_finder = NULL, + const stmt_finder *stmt_finder = nullptr, bool unknown_side_effects = false) : sm_context (sm_idx, sm), m_logger (eg.get_logger ()), @@ -322,11 +311,11 @@ public: logger *get_logger () const { return m_logger.get_logger (); } - tree get_fndecl_for_call (const gcall *call) final override + tree get_fndecl_for_call (const gcall &call) final override { impl_region_model_context old_ctxt - (m_eg, m_enode_for_diag, NULL, NULL, NULL/*m_enode->get_state ()*/, - NULL, call); + (m_eg, m_enode_for_diag, nullptr, nullptr, nullptr/*m_enode->get_state ()*/, + nullptr, &call); region_model *model = m_new_state->m_region_model; return model->get_fndecl_for_call (call, &old_ctxt); } @@ -336,10 +325,10 @@ public: { logger * const logger = get_logger (); LOG_FUNC (logger); - /* Use NULL ctxt on this get_rvalue call to avoid triggering + /* Use nullptr ctxt on this get_rvalue call to avoid triggering uninitialized value warnings. */ const svalue *var_old_sval - = m_old_state->m_region_model->get_rvalue (var, NULL); + = m_old_state->m_region_model->get_rvalue (var, nullptr); state_machine::state_t current = m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ()); @@ -364,9 +353,9 @@ public: logger * const logger = get_logger (); LOG_FUNC (logger); const svalue *var_new_sval - = m_new_state->m_region_model->get_rvalue (var, NULL); + = m_new_state->m_region_model->get_rvalue (var, nullptr); const svalue *origin_new_sval - = m_new_state->m_region_model->get_rvalue (origin, NULL); + = m_new_state->m_region_model->get_rvalue (origin, nullptr); /* We use the new sval here to avoid issues with uninitialized values. */ state_machine::state_t current @@ -389,11 +378,11 @@ public: logger * const logger = get_logger (); LOG_FUNC (logger); impl_region_model_context old_ctxt - (m_eg, m_enode_for_diag, NULL, NULL, NULL/*m_enode->get_state ()*/, - NULL, stmt); + (m_eg, m_enode_for_diag, nullptr, nullptr, nullptr/*m_enode->get_state ()*/, + nullptr, stmt); const svalue *origin_new_sval - = m_new_state->m_region_model->get_rvalue (origin, NULL); + = m_new_state->m_region_model->get_rvalue (origin, nullptr); state_machine::state_t current = m_old_smap->get_state (sval, m_eg.get_ext_state ()); @@ -419,7 +408,7 @@ public: LOG_FUNC (get_logger ()); gcc_assert (d); const svalue *var_old_sval - = m_old_state->m_region_model->get_rvalue (var, NULL); + = m_old_state->m_region_model->get_rvalue (var, nullptr); state_machine::state_t current = (var ? m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ()) @@ -472,7 +461,7 @@ public: return expr; gcc_assert (m_new_state); - const svalue *sval = m_new_state->m_region_model->get_rvalue (expr, NULL); + const svalue *sval = m_new_state->m_region_model->get_rvalue (expr, nullptr); /* Find trees for all regions storing the value. */ if (tree t = m_new_state->m_region_model->get_representative_tree (sval)) return t; @@ -513,7 +502,7 @@ public: if (!assign_stmt) return NULL_TREE; impl_region_model_context old_ctxt - (m_eg, m_enode_for_diag, m_old_state, m_new_state, NULL, NULL, stmt); + (m_eg, m_enode_for_diag, m_old_state, m_new_state, nullptr, nullptr, stmt); if (const svalue *sval = m_new_state->m_region_model->get_gassign_result (assign_stmt, &old_ctxt)) @@ -583,17 +572,17 @@ get_state_map_by_name (const char *name, { const sm_state_map *old_smap = m_old_state->m_checker_states[sm_idx]; *out_sm_context - = make_unique<impl_sm_context> (*m_eg, - sm_idx, - *sm, - m_enode_for_diag, - m_old_state, - m_new_state, - old_smap, - new_smap, - m_path_ctxt, - m_stmt_finder, - false); + = std::make_unique<impl_sm_context> (*m_eg, + sm_idx, + *sm, + m_enode_for_diag, + m_old_state, + m_new_state, + old_smap, + new_smap, + m_path_ctxt, + m_stmt_finder, + false); } return true; } @@ -609,7 +598,7 @@ public: std::unique_ptr<stmt_finder> clone () const final override { - return make_unique<leak_stmt_finder> (m_eg, m_var); + return std::make_unique<leak_stmt_finder> (m_eg, m_var); } const gimple *find_stmt (const exploded_path &epath) @@ -677,7 +666,7 @@ public: } gcc_unreachable (); - return NULL; + return nullptr; } void update_event_loc_info (event_loc_info &) final override @@ -936,7 +925,9 @@ impl_region_model_context::on_state_leak (const state_machine &sm, } tree leaked_tree_for_diag = fixup_tree_for_diagnostic (leaked_tree); - std::unique_ptr<pending_diagnostic> pd = sm.on_leak (leaked_tree_for_diag); + std::unique_ptr<pending_diagnostic> pd = sm.on_leak (leaked_tree_for_diag, + m_old_state, + m_new_state); if (pd) { pending_location ploc (m_enode_for_diag, @@ -971,7 +962,7 @@ impl_region_model_context::on_condition (const svalue *lhs, sm.on_condition (sm_ctxt, (m_enode_for_diag ? m_enode_for_diag->get_supernode () - : NULL), + : nullptr), m_stmt, lhs, op, rhs); } @@ -998,7 +989,7 @@ impl_region_model_context::on_bounded_ranges (const svalue &sval, sm.on_bounded_ranges (sm_ctxt, (m_enode_for_diag ? m_enode_for_diag->get_supernode () - : NULL), + : nullptr), m_stmt, sval, ranges); } } @@ -1207,10 +1198,10 @@ exploded_node::status_to_str (enum status s) switch (s) { default: gcc_unreachable (); - case STATUS_WORKLIST: return "WORKLIST"; - case STATUS_PROCESSED: return "PROCESSED"; - case STATUS_MERGER: return "MERGER"; - case STATUS_BULK_MERGED: return "BULK_MERGED"; + case status::worklist: return "worklist"; + case status::processed: return "processed"; + case status::merger: return "merger"; + case status::bulk_merged: return "bulk_merged"; } } @@ -1218,7 +1209,7 @@ exploded_node::status_to_str (enum status s) exploded_node::exploded_node (const point_and_state &ps, int index) -: m_ps (ps), m_status (STATUS_WORKLIST), m_index (index), +: m_ps (ps), m_status (status::worklist), m_index (index), m_num_processed_stmts (0) { gcc_checking_assert (ps.get_state ().m_region_model->canonicalized_p ()); @@ -1296,9 +1287,9 @@ exploded_node::dump_dot (graphviz_out *gv, const dump_args_t &args) const pp_write_text_to_stream (pp); pp_printf (pp, "EN: %i", m_index); - if (m_status == STATUS_MERGER) + if (m_status == status::merger) pp_string (pp, " (merger)"); - else if (m_status == STATUS_BULK_MERGED) + else if (m_status == status::bulk_merged) pp_string (pp, " (bulk merged)"); pp_newline (pp); @@ -1440,7 +1431,7 @@ exploded_node::dump (const extrinsic_state &ext_state) const std::unique_ptr<json::object> exploded_node::to_json (const extrinsic_state &ext_state) const { - auto enode_obj = ::make_unique<json::object> (); + auto enode_obj = std::make_unique<json::object> (); enode_obj->set ("point", get_point ().to_json ()); enode_obj->set ("state", get_state ().to_json (ext_state)); @@ -1522,7 +1513,7 @@ exploded_node::on_stmt (exploded_graph &eg, gcc_assert (called_fn); return replay_call_summaries (eg, snode, - as_a <const gcall *> (stmt), + *as_a <const gcall *> (stmt), state, path_ctxt, *called_fn, @@ -1549,7 +1540,7 @@ exploded_node::on_stmt (exploded_graph &eg, = old_state.m_checker_states[sm_idx]; sm_state_map *new_smap = state->m_checker_states[sm_idx]; impl_sm_context sm_ctxt (eg, sm_idx, sm, this, &old_state, state, - old_smap, new_smap, path_ctxt, NULL, + old_smap, new_smap, path_ctxt, nullptr, unknown_side_effects); /* Allow the state_machine to handle the stmt. */ @@ -1579,8 +1570,9 @@ exploded_node::on_stmt_pre (exploded_graph &eg, region_model_context *ctxt) { /* Handle special-case calls that require the full program_state. */ - if (const gcall *call = dyn_cast <const gcall *> (stmt)) + if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt)) { + const gcall &call = *call_stmt; if (is_special_named_call_p (call, "__analyzer_dump", 0)) { /* Handle the builtin "__analyzer_dump" by dumping state @@ -1588,6 +1580,16 @@ exploded_node::on_stmt_pre (exploded_graph &eg, state->dump (eg.get_ext_state (), true); return; } + else if (is_special_named_call_p (call, "__analyzer_dump_xml", 0)) + { + state->dump_xml (eg.get_ext_state ()); + return; + } + else if (is_special_named_call_p (call, "__analyzer_dump_dot", 0)) + { + state->dump_dot (eg.get_ext_state ()); + return; + } else if (is_special_named_call_p (call, "__analyzer_dump_state", 2)) { state->impl_call_analyzer_dump_state (call, eg.get_ext_state (), @@ -1609,6 +1611,24 @@ exploded_node::on_stmt_pre (exploded_graph &eg, ctxt->maybe_did_work (); return; } + else if (is_cxa_throw_p (call)) + { + on_throw (eg, call, state, false, ctxt); + *out_terminate_path = true; + return; + } + else if (is_cxa_rethrow_p (call)) + { + on_throw (eg, call, state, true, ctxt); + *out_terminate_path = true; + return; + } + } + else if (const gresx *resx = dyn_cast <const gresx *> (stmt)) + { + on_resx (eg, *resx, state, ctxt); + *out_terminate_path = true; + return; } /* Otherwise, defer to m_region_model. */ @@ -1626,7 +1646,7 @@ exploded_node::on_stmt_post (const gimple *stmt, region_model_context *ctxt) { if (const gcall *call = dyn_cast <const gcall *> (stmt)) - state->m_region_model->on_call_post (call, unknown_side_effects, ctxt); + state->m_region_model->on_call_post (*call, unknown_side_effects, ctxt); } /* A concrete call_info subclass representing a replay of a call summary. */ @@ -1636,7 +1656,7 @@ class call_summary_edge_info : public call_info public: call_summary_edge_info (const call_details &cd, const function &called_fn, - call_summary *summary, + call_summary &summary, const extrinsic_state &ext_state) : call_info (cd, called_fn), m_called_fn (called_fn), @@ -1651,7 +1671,7 @@ public: /* Update STATE based on summary_end_state. */ call_details cd (get_call_details (state->m_region_model, ctxt)); call_summary_replay r (cd, m_called_fn, m_summary, m_ext_state); - const program_state &summary_end_state = m_summary->get_state (); + const program_state &summary_end_state = m_summary.get_state (); return state->replay_call_summary (r, summary_end_state); } @@ -1662,19 +1682,19 @@ public: /* Update STATE based on summary_end_state. */ call_details cd (get_call_details (model, ctxt)); call_summary_replay r (cd, m_called_fn, m_summary, m_ext_state); - const program_state &summary_end_state = m_summary->get_state (); + const program_state &summary_end_state = m_summary.get_state (); model->replay_call_summary (r, *summary_end_state.m_region_model); return true; } void print_desc (pretty_printer &pp) const final override { - pp_string (&pp, m_summary->get_desc ().get ()); + pp_string (&pp, m_summary.get_desc ().get ()); } private: const function &m_called_fn; - call_summary *m_summary; + call_summary &m_summary; const extrinsic_state &m_ext_state; }; @@ -1684,7 +1704,7 @@ private: exploded_node::on_stmt_flags exploded_node::replay_call_summaries (exploded_graph &eg, const supernode *snode, - const gcall *call_stmt, + const gcall &call_stmt, program_state *state, path_context *path_ctxt, const function &called_fn, @@ -1696,8 +1716,11 @@ exploded_node::replay_call_summaries (exploded_graph &eg, /* Each summary will call bifurcate on the PATH_CTXT. */ for (auto summary : called_fn_data.m_summaries) - replay_call_summary (eg, snode, call_stmt, state, - path_ctxt, called_fn, summary, ctxt); + { + gcc_assert (summary); + replay_call_summary (eg, snode, call_stmt, state, + path_ctxt, called_fn, *summary, ctxt); + } path_ctxt->terminate_path (); return on_stmt_flags (); @@ -1710,27 +1733,25 @@ exploded_node::replay_call_summaries (exploded_graph &eg, void exploded_node::replay_call_summary (exploded_graph &eg, const supernode *snode, - const gcall *call_stmt, + const gcall &call_stmt, program_state *old_state, path_context *path_ctxt, const function &called_fn, - call_summary *summary, + call_summary &summary, region_model_context *ctxt) { logger *logger = eg.get_logger (); LOG_SCOPE (logger); gcc_assert (snode); - gcc_assert (call_stmt); gcc_assert (old_state); - gcc_assert (summary); if (logger) logger->log ("using %s as summary for call to %qE from %qE", - summary->get_desc ().get (), + summary.get_desc ().get (), called_fn.decl, snode->get_function ()->decl); const extrinsic_state &ext_state = eg.get_ext_state (); - const program_state &summary_end_state = summary->get_state (); + const program_state &summary_end_state = summary.get_state (); if (logger) { pretty_printer *pp = logger->get_printer (); @@ -1752,10 +1773,11 @@ exploded_node::replay_call_summary (exploded_graph &eg, call_summary_replay r (cd, called_fn, summary, ext_state); if (path_ctxt) - path_ctxt->bifurcate (make_unique<call_summary_edge_info> (cd, - called_fn, - summary, - ext_state)); + path_ctxt->bifurcate + (std::make_unique<call_summary_edge_info> (cd, + called_fn, + summary, + ext_state)); } @@ -1826,10 +1848,10 @@ valid_longjmp_stack_p (const program_point &longjmp_point, class stale_jmp_buf : public pending_diagnostic_subclass<stale_jmp_buf> { public: - stale_jmp_buf (const gcall *setjmp_call, const gcall *longjmp_call, + stale_jmp_buf (const gcall &setjmp_call, const gcall &longjmp_call, const program_point &setjmp_point) : m_setjmp_call (setjmp_call), m_longjmp_call (longjmp_call), - m_setjmp_point (setjmp_point), m_stack_pop_event (NULL) + m_setjmp_point (setjmp_point), m_stack_pop_event (nullptr) {} int get_controlling_option () const final override @@ -1849,8 +1871,8 @@ public: bool operator== (const stale_jmp_buf &other) const { - return (m_setjmp_call == other.m_setjmp_call - && m_longjmp_call == other.m_longjmp_call); + return (&m_setjmp_call == &other.m_setjmp_call + && &m_longjmp_call == &other.m_longjmp_call); } bool @@ -1903,8 +1925,8 @@ public: private: - const gcall *m_setjmp_call; - const gcall *m_longjmp_call; + const gcall &m_setjmp_call; + const gcall &m_longjmp_call; program_point m_setjmp_point; custom_event *m_stack_pop_event; }; @@ -1917,11 +1939,11 @@ private: void exploded_node::on_longjmp (exploded_graph &eg, - const gcall *longjmp_call, + const gcall &longjmp_call, program_state *new_state, region_model_context *ctxt) { - tree buf_ptr = gimple_call_arg (longjmp_call, 0); + tree buf_ptr = gimple_call_arg (&longjmp_call, 0); gcc_assert (POINTER_TYPE_P (TREE_TYPE (buf_ptr))); region_model *new_region_model = new_state->m_region_model; @@ -1942,7 +1964,7 @@ exploded_node::on_longjmp (exploded_graph &eg, call back to the setjmp/sigsetjmp. */ rewind_info_t rewind_info (tmp_setjmp_record, longjmp_call); - const gcall *setjmp_call = rewind_info.get_setjmp_call (); + const gcall &setjmp_call = rewind_info.get_setjmp_call (); const program_point &setjmp_point = rewind_info.get_setjmp_point (); const program_point &longjmp_point = get_point (); @@ -1950,9 +1972,9 @@ exploded_node::on_longjmp (exploded_graph &eg, /* Verify that the setjmp's call_stack hasn't been popped. */ if (!valid_longjmp_stack_p (longjmp_point, setjmp_point)) { - ctxt->warn (make_unique<stale_jmp_buf> (setjmp_call, - longjmp_call, - setjmp_point)); + ctxt->warn (std::make_unique<stale_jmp_buf> (setjmp_call, + longjmp_call, + setjmp_point)); return; } @@ -1971,7 +1993,7 @@ exploded_node::on_longjmp (exploded_graph &eg, setjmp_point.get_stack_depth (), ctxt); /* Detect leaks in the new state relative to the old state. */ - program_state::detect_leaks (get_state (), *new_state, NULL, + program_state::detect_leaks (get_state (), *new_state, nullptr, eg.get_ext_state (), ctxt); program_point next_point @@ -1985,9 +2007,9 @@ exploded_node::on_longjmp (exploded_graph &eg, if (next) { exploded_edge *eedge - = eg.add_edge (const_cast<exploded_node *> (this), next, NULL, true, - make_unique<rewind_info_t> (tmp_setjmp_record, - longjmp_call)); + = eg.add_edge (const_cast<exploded_node *> (this), next, nullptr, true, + std::make_unique<rewind_info_t> (tmp_setjmp_record, + longjmp_call)); /* For any diagnostics that were queued here (such as leaks) we want the checker_path to show the rewinding events after the "final event" @@ -2025,6 +2047,332 @@ exploded_node::on_longjmp (exploded_graph &eg, } } +/* Subclass of call_info for exploded edges that express + a throw or rethrow of an exception (actually a call + to __cxa_throw or __cxa_rethrow). */ + +class throw_custom_edge : public call_info +{ +public: + throw_custom_edge (const call_details &cd, + tree type, + bool is_rethrow) + : call_info (cd), + m_type (type), + m_is_rethrow (is_rethrow) + { + } + + void print (pretty_printer *pp) const final override + { + if (m_is_rethrow) + { + if (m_type) + pp_printf (pp, "rethrowing %qT", m_type); + else + pp_printf (pp, "rethrowing"); + } + else + { + if (m_type) + pp_printf (pp, "throwing %qT", m_type); + else + pp_printf (pp, "throwing"); + } + } + + void print_desc (pretty_printer &pp) const final override + { + print (&pp); + } + + bool update_model (region_model *model, + const exploded_edge *, + region_model_context *ctxt) const final override + { + if (m_is_rethrow) + { + auto eh_node = model->get_current_caught_exception (); + gcc_assert (eh_node); + model->push_thrown_exception (*eh_node); + } + else + { + call_details cd (get_call_details (model, ctxt)); + + const svalue *exception_sval = cd.get_arg_svalue (0); + const svalue *tinfo_sval = cd.get_arg_svalue (1); + const svalue *destructor_sval = cd.get_arg_svalue (2); + + /* Push a new exception_node on the model's m_exception_stack. */ + exception_node eh_node (exception_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 (); + + const gcall &call = get_call_stmt (); + + emission_path->add_event + (std::make_unique<explicit_throw_event> + (event_loc_info (call.location, + dst_point.get_fndecl (), + dst_stack_depth), + dst_node, + call, + m_type, + m_is_rethrow)); + } + +private: + tree m_type; + bool m_is_rethrow; +}; + +/* Subclass of custom_edge_info for an exploded edge that expresses + unwinding one stack frame during exception handling. */ + +class unwind_custom_edge : public custom_edge_info +{ +public: + unwind_custom_edge (location_t loc) + : m_loc (loc) + { + } + + void print (pretty_printer *pp) const final override + { + pp_printf (pp, "unwinding frame"); + } + + bool update_model (region_model *model, + const exploded_edge *, + region_model_context *ctxt) const final override + { + model->pop_frame (NULL_TREE, nullptr, ctxt, nullptr, false); + return true; + } + + void add_events_to_path (checker_path *emission_path, + const exploded_edge &eedge) const final override + { + const exploded_node *src_node = eedge.m_src; + const program_point &src_point = src_node->get_point (); + const int src_stack_depth = src_point.get_stack_depth (); + emission_path->add_event + (std::make_unique<unwind_event> (event_loc_info (m_loc, + src_point.get_fndecl (), + src_stack_depth))); + } + +private: + location_t m_loc; +}; + +/* Locate an SNODE that's a CFG edge with the EH flag, + or return nullptr. */ + +static const superedge * +get_eh_outedge (const supernode &snode) +{ + for (auto out_sedge : snode.m_succs) + if (::edge cfg_edge = out_sedge->get_any_cfg_edge ()) + if (cfg_edge->flags & EDGE_EH) + return out_sedge; + + // Not found + return nullptr; +} + +/* Given THROWN_ENODE, which expreses a throw or rethrow occurring at + THROW_STMT, unwind intraprocedurally and interprocedurally to find + the next eh_dispatch statement to handle exceptions, if any. + + Add eedges and enodes to this graph expressing the actions taken + to reach an enode containing the eh_dispatch stmt, if any. + Only the final enode is added to this graph's worklist. + + Use CTXT to warn about problems e.g. memory leaks due to stack frames + being unwound. */ + +void +exploded_graph::unwind_from_exception (exploded_node &thrown_enode, + const gimple *throw_stmt, + region_model_context *ctxt) +{ + logger * const logger = get_logger (); + LOG_FUNC_1 (logger, "thrown EN: %i", thrown_enode.m_index); + + /* Iteratively unwind the stack looking for an out-cfg-edge + flagged EH. */ + exploded_node *iter_enode = &thrown_enode; + while (iter_enode) + { + /* If we have an out-cfg-edge flagged EH, follow that, + presumably to a bb with a label and an eh_dispatch stmt. + Otherwise assume no out-cfgs-edges, and we are unwinding to the + caller. */ + if (auto sedge = get_eh_outedge (*iter_enode->get_supernode ())) + { + /* Intraprocedural case. + Assume we have an out-edge flagged with EH leading to + code for dispatch to catch handlers. */ + const program_point next_point + = program_point::before_supernode (sedge->m_dest, + sedge, + iter_enode->get_point ().get_call_string ()); + exploded_node *next_enode + = get_or_create_node (next_point, + iter_enode->get_state (), + iter_enode, + /* Add this enode to the worklist. */ + true); + if (!next_enode) + return; + + add_edge (iter_enode, next_enode, nullptr, false, nullptr); + return; + } + else + { + /* Interprocedural case. + No out-cfg-edge. Unwind one stack frame. */ + program_state unwound_state (iter_enode->get_state ()); + location_t loc = throw_stmt ? throw_stmt->location : UNKNOWN_LOCATION; + auto unwind_edge_info + = std::make_unique<unwind_custom_edge> (loc); + unwind_edge_info->update_model (unwound_state.m_region_model, nullptr, + ctxt); + + /* Detect leaks in the new state relative to the old state. + Use an alternate ctxt that uses the original enode and the stmt + (if any) for the location of any diagnostics. */ + { + uncertainty_t uncertainty; + impl_region_model_context ctxt (*this, + &thrown_enode, + &iter_enode->get_state (), + &unwound_state, + &uncertainty, + nullptr, + throw_stmt); + program_state::detect_leaks (iter_enode->get_state (), + unwound_state, + nullptr, + get_ext_state (), &ctxt); + } + const call_string &cs = iter_enode->get_point ().get_call_string (); + if (cs.empty_p ()) + { + /* Top-level stack frame in analysis: unwinding + to the outside world that called us. */ + return; + } + else + { + /* Nested function in analysis: unwinding to + the callsite in the analysis (or beyond). */ + program_point unwound_point + = program_point::after_supernode (cs.get_caller_node (), cs); + unwound_point.pop_from_call_stack (); + + exploded_node *after_unwind_enode + = get_or_create_node (unwound_point, + std::move (unwound_state), + iter_enode, + /* Don't add this enode to the + worklist; we will process it + on the next iteration. */ + false); + + if (!after_unwind_enode) + return; + + add_edge (iter_enode, after_unwind_enode, nullptr, true, + std::move (unwind_edge_info)); + iter_enode = after_unwind_enode; + } + } + } +} + +/* Handle THROW_CALL, a call to __cxa_throw or __cxa_rethrow. + + Create an eedge and destination enode for the throw/rethrow, adding + them to this egraph. The new enode isn't added to the worklist, but + instead exploded_graph::unwind_from_exception is immediately called + on it, potentially creating more eedges and enodes leading to an + eh_handler stmt. */ + +void +exploded_node::on_throw (exploded_graph &eg, + const gcall &throw_call, + program_state *new_state, + bool is_rethrow, + region_model_context *ctxt) +{ + region_model *model = new_state->m_region_model; + call_details cd (throw_call, model, ctxt); + + /* Create an enode and eedge for the "throw". */ + tree type = NULL_TREE; + if (is_rethrow) + { + const exception_node *eh_node = model->get_current_caught_exception (); + gcc_assert (eh_node); + type = eh_node->maybe_get_type (); + } + else + { + const svalue *tinfo_sval = cd.get_arg_svalue (1); + type = tinfo_sval->maybe_get_type_from_typeinfo (); + } + auto throw_edge_info + = std::make_unique<throw_custom_edge> (cd, type, is_rethrow); + throw_edge_info->update_model (model, nullptr, ctxt); + + program_point after_throw_point = get_point ().get_next (); + + exploded_node *after_throw_enode + = eg.get_or_create_node (after_throw_point, *new_state, this, + /* Don't add to worklist; we process + this immediately below. */ + false); + + if (!after_throw_enode) + return; + + /* Create custom exploded_edge for a throw. */ + eg.add_edge (this, after_throw_enode, nullptr, true, + std::move (throw_edge_info)); + + eg.unwind_from_exception (*after_throw_enode, &throw_call, ctxt); +} + +/* Handle a gimple "resx" statement by adding eedges and enode. + that unwind to the next eh_dispatch statement, if any. Only + the final enode is added to the worklist. */ + +void +exploded_node::on_resx (exploded_graph &eg, + const gresx &/*resx*/, + program_state */*new_state*/, + region_model_context *ctxt) +{ + eg.unwind_from_exception (*this, + nullptr, + ctxt); +} + + /* Subroutine of exploded_graph::process_node for finding the successors of the supernode for a function exit basic block. @@ -2056,10 +2404,10 @@ exploded_node::detect_leaks (exploded_graph &eg) uncertainty_t uncertainty; impl_region_model_context ctxt (eg, this, - &old_state, &new_state, &uncertainty, NULL, + &old_state, &new_state, &uncertainty, nullptr, get_stmt ()); - const svalue *result = NULL; - new_state.m_region_model->pop_frame (NULL, &result, &ctxt, nullptr); + const svalue *result = nullptr; + new_state.m_region_model->pop_frame (nullptr, &result, &ctxt, nullptr); program_state::detect_leaks (old_state, new_state, result, eg.get_ext_state (), &ctxt); } @@ -2131,20 +2479,16 @@ dynamic_call_info_t::add_events_to_path (checker_path *emission_path, if (m_is_returning_call) emission_path->add_event - (make_unique<return_event> (eedge, - event_loc_info (m_dynamic_call - ? m_dynamic_call->location - : UNKNOWN_LOCATION, - dest_point.get_fndecl (), - dest_stack_depth))); + (std::make_unique<return_event> (eedge, + event_loc_info (m_dynamic_call.location, + dest_point.get_fndecl (), + dest_stack_depth))); else emission_path->add_event - (make_unique<call_event> (eedge, - event_loc_info (m_dynamic_call - ? m_dynamic_call->location - : UNKNOWN_LOCATION, - src_point.get_fndecl (), - src_stack_depth))); + (std::make_unique<call_event> (eedge, + event_loc_info (m_dynamic_call.location, + src_point.get_fndecl (), + src_stack_depth))); } /* class rewind_info_t : public custom_edge_info. */ @@ -2170,7 +2514,7 @@ rewind_info_t::update_model (region_model *model, model->on_longjmp (get_longjmp_call (), get_setjmp_call (), - setjmp_point.get_stack_depth (), NULL); + setjmp_point.get_stack_depth (), nullptr); return true; } @@ -2189,19 +2533,19 @@ rewind_info_t::add_events_to_path (checker_path *emission_path, const int dst_stack_depth = dst_point.get_stack_depth (); emission_path->add_event - (make_unique<rewind_from_longjmp_event> - (&eedge, - event_loc_info (get_longjmp_call ()->location, - src_point.get_fndecl (), - src_stack_depth), - this)); + (std::make_unique<rewind_from_longjmp_event> + (&eedge, + event_loc_info (get_longjmp_call ().location, + src_point.get_fndecl (), + src_stack_depth), + this)); emission_path->add_event - (make_unique<rewind_to_setjmp_event> - (&eedge, - event_loc_info (get_setjmp_call ()->location, - dst_point.get_fndecl (), - dst_stack_depth), - this)); + (std::make_unique<rewind_to_setjmp_event> + (&eedge, + event_loc_info (get_setjmp_call ().location, + dst_point.get_fndecl (), + dst_stack_depth), + this)); } /* class exploded_edge : public dedge<eg_traits>. */ @@ -2294,7 +2638,7 @@ exploded_edge::dump_dot_label (pretty_printer *pp) const std::unique_ptr<json::object> exploded_edge::to_json () const { - auto eedge_obj = ::make_unique<json::object> (); + auto eedge_obj = std::make_unique<json::object> (); eedge_obj->set_integer ("src_idx", m_src->m_index); eedge_obj->set_integer ("dst_idx", m_dest->m_index); if (m_sedge) @@ -2420,9 +2764,9 @@ strongly_connected_components::dump () const std::unique_ptr<json::array> strongly_connected_components::to_json () const { - auto scc_arr = ::make_unique<json::array> (); + auto scc_arr = std::make_unique<json::array> (); for (int i = 0; i < m_sg.num_nodes (); i++) - scc_arr->append (::make_unique<json::integer_number> (get_scc_id (i))); + scc_arr->append (std::make_unique<json::integer_number> (get_scc_id (i))); return scc_arr; } @@ -2485,7 +2829,7 @@ strongly_connected_components::strong_connect (unsigned index) worklist::worklist (const exploded_graph &eg, const analysis_plan &plan) : m_scc (eg.get_supergraph (), eg.get_logger ()), m_plan (plan), - m_queue (key_t (*this, NULL)) + m_queue (key_t (*this, nullptr)) { } @@ -2518,7 +2862,7 @@ worklist::peek_next () void worklist::add_node (exploded_node *enode) { - gcc_assert (enode->get_status () == exploded_node::STATUS_WORKLIST); + gcc_assert (enode->get_status () == exploded_node::status::worklist); m_queue.insert (key_t (*this, enode), enode); } @@ -2547,8 +2891,8 @@ worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb) if (flag_analyzer_call_summaries && call_string_a.empty_p () && call_string_b.empty_p () - && point_a.get_function () != NULL - && point_b.get_function () != NULL + && point_a.get_function () != nullptr + && point_b.get_function () != nullptr && point_a.get_function () != point_b.get_function ()) { if (int cmp = ka.m_worklist.m_plan.cmp_function (point_a.get_function (), @@ -2584,19 +2928,19 @@ worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb) ordering). */ const supernode *snode_a = ka.m_enode->get_supernode (); const supernode *snode_b = kb.m_enode->get_supernode (); - if (snode_a == NULL) + if (snode_a == nullptr) { - if (snode_b != NULL) - /* One is NULL. */ + if (snode_b != nullptr) + /* One is nullptr. */ return -1; else - /* Both are NULL. */ + /* Both are nullptr. */ return 0; } - if (snode_b == NULL) - /* One is NULL. */ + if (snode_b == nullptr) + /* One is nullptr. */ return 1; - /* Neither are NULL. */ + /* Neither are nullptr. */ gcc_assert (snode_a && snode_b); if (snode_a->m_index != snode_b->m_index) return snode_a->m_index - snode_b->m_index; @@ -2641,7 +2985,7 @@ worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb) std::unique_ptr<json::object> worklist::to_json () const { - auto worklist_obj = ::make_unique<json::object> (); + auto worklist_obj = std::make_unique<json::object> (); worklist_obj->set ("scc", m_scc.to_json ()); @@ -2670,7 +3014,7 @@ exploded_graph::exploded_graph (const supergraph &sg, logger *logger, { m_origin = get_or_create_node (program_point::origin (*ext_state.get_model_manager ()), - program_state (ext_state), NULL); + program_state (ext_state), nullptr); for (int i = 0; i < m_sg.num_nodes (); i++) m_PK_AFTER_SUPERNODE_per_snode.quick_push (i); } @@ -2722,10 +3066,10 @@ mark_params_as_tainted (program_state *state, tree fndecl, tree param = iter_parm; if (tree parm_default_ssa = ssa_default_def (fun, iter_parm)) param = parm_default_ssa; - const region *param_reg = state->m_region_model->get_lvalue (param, NULL); + const region *param_reg = state->m_region_model->get_lvalue (param, nullptr); const svalue *init_sval = mgr->get_or_create_initial_value (param_reg); smap->set_state (state->m_region_model, init_sval, - tainted, NULL /*origin_new_sval*/, ext_state); + tainted, nullptr /*origin_new_sval*/, ext_state); if (POINTER_TYPE_P (TREE_TYPE (param))) { const region *pointee_reg = mgr->get_symbolic_region (init_sval); @@ -2733,7 +3077,7 @@ mark_params_as_tainted (program_state *state, tree fndecl, const svalue *init_pointee_sval = mgr->get_or_create_initial_value (pointee_reg); smap->set_state (state->m_region_model, init_pointee_sval, - tainted, NULL /*origin_new_sval*/, ext_state); + tainted, nullptr /*origin_new_sval*/, ext_state); } } @@ -2791,8 +3135,8 @@ public: const exploded_edge &) const final override { emission_path->add_event - (make_unique<tainted_args_function_custom_event> - (event_loc_info (DECL_SOURCE_LOCATION (m_fndecl), m_fndecl, 0))); + (std::make_unique<tainted_args_function_custom_event> + (event_loc_info (DECL_SOURCE_LOCATION (m_fndecl), m_fndecl, 0))); } private: @@ -2819,7 +3163,7 @@ exploded_graph::add_function_entry (const function &fun) logger * const logger = get_logger (); if (logger) logger->log ("entrypoint for %qE already exists", fun.decl); - return NULL; + return nullptr; } program_point point @@ -2828,22 +3172,22 @@ exploded_graph::add_function_entry (const function &fun) program_state state (m_ext_state); state.push_frame (m_ext_state, fun); - std::unique_ptr<custom_edge_info> edge_info = NULL; + std::unique_ptr<custom_edge_info> edge_info = nullptr; if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (fun.decl))) { if (mark_params_as_tainted (&state, fun.decl, m_ext_state)) - edge_info = make_unique<tainted_args_function_info> (fun.decl); + edge_info = std::make_unique<tainted_args_function_info> (fun.decl); } if (!state.m_valid) - return NULL; + return nullptr; - exploded_node *enode = get_or_create_node (point, state, NULL); + exploded_node *enode = get_or_create_node (point, state, nullptr); if (!enode) - return NULL; + return nullptr; - add_edge (m_origin, enode, NULL, false, std::move (edge_info)); + add_edge (m_origin, enode, nullptr, false, std::move (edge_info)); m_functions_with_enodes.add (key); @@ -2851,7 +3195,8 @@ exploded_graph::add_function_entry (const function &fun) } /* Get or create an exploded_node for (POINT, STATE). - If a new node is created, it is added to the worklist. + If a new node is created and ADD_TO_WORKLIST is true, + it is added to the worklist. Use ENODE_FOR_DIAG, a pre-existing enode, for any diagnostics that need to be emitted (e.g. when purging state *before* we have @@ -2860,7 +3205,8 @@ exploded_graph::add_function_entry (const function &fun) exploded_node * exploded_graph::get_or_create_node (const program_point &point, const program_state &state, - exploded_node *enode_for_diag) + exploded_node *enode_for_diag, + bool add_to_worklist) { logger * const logger = get_logger (); LOG_FUNC (logger); @@ -2884,7 +3230,7 @@ exploded_graph::get_or_create_node (const program_point &point, { if (logger) logger->log ("invalid state; not creating node"); - return NULL; + return nullptr; } auto_cfun sentinel (point.get_function ()); @@ -2998,7 +3344,7 @@ exploded_graph::get_or_create_node (const program_point &point, "terminating analysis for this program point: %s", pp_formatted_text (&pp)); per_point_data->m_excess_enodes++; - return NULL; + return nullptr; } ps.validate (m_ext_state); @@ -3035,7 +3381,10 @@ exploded_graph::get_or_create_node (const program_point &point, } /* Add the new node to the worlist. */ - m_worklist.add_node (node); + if (add_to_worklist) + m_worklist.add_node (node); + else + node->set_status (exploded_node::status::special); return node; } @@ -3075,7 +3424,7 @@ get_or_create_per_program_point_data (const program_point &point) } /* Get this graph's per-program-point-data for POINT if there is any, - otherwise NULL. */ + otherwise nullptr. */ per_program_point_data * exploded_graph::get_per_program_point_data (const program_point &point) const @@ -3084,7 +3433,7 @@ exploded_graph::get_per_program_point_data (const program_point &point) const = const_cast <point_map_t &> (m_per_point_data).get (&point)) return *slot; - return NULL; + return nullptr; } /* Ensure that this graph has per-call_string-data for CS; @@ -3117,7 +3466,7 @@ exploded_graph::get_or_create_per_function_data (function *fun) } /* Get this graph's per-function-data for FUN if there is any, - otherwise NULL. */ + otherwise nullptr. */ per_function_data * exploded_graph::get_per_function_data (function *fun) const @@ -3126,7 +3475,7 @@ exploded_graph::get_per_function_data (function *fun) const = const_cast <per_function_data_t &> (m_per_function_data).get (fun)) return *slot; - return NULL; + return nullptr; } /* Return true if FUN should be traversed directly, rather than only as @@ -3238,16 +3587,16 @@ public: /* Show the field in the struct declaration, e.g. "(1) field 'store' is marked with '__attribute__((tainted_args))'" */ emission_path->add_event - (make_unique<tainted_args_field_custom_event> (m_field)); + (std::make_unique<tainted_args_field_custom_event> (m_field)); /* Show the callback in the initializer e.g. "(2) function 'gadget_dev_desc_UDC_store' used as initializer for field 'store' marked with '__attribute__((tainted_args))'". */ emission_path->add_event - (make_unique<tainted_args_callback_custom_event> - (event_loc_info (m_loc, m_fndecl, 0), - m_field)); + (std::make_unique<tainted_args_callback_custom_event> + (event_loc_info (m_loc, m_fndecl, 0), + m_field)); } private: @@ -3289,7 +3638,7 @@ add_tainted_args_callback (exploded_graph *eg, tree field, tree fndecl, if (!state.m_valid) return; - exploded_node *enode = eg->get_or_create_node (point, state, NULL); + exploded_node *enode = eg->get_or_create_node (point, state, nullptr); if (logger) { if (enode) @@ -3303,8 +3652,8 @@ add_tainted_args_callback (exploded_graph *eg, tree field, tree fndecl, } } - eg->add_edge (eg->get_origin (), enode, NULL, false, - make_unique<tainted_args_call_info> (field, fndecl, loc)); + eg->add_edge (eg->get_origin (), enode, nullptr, false, + std::make_unique<tainted_args_call_info> (field, fndecl, loc)); } /* Callback for walk_tree for finding callbacks within initializers; @@ -3377,7 +3726,7 @@ exploded_graph::build_initial_worklist () tree init = DECL_INITIAL (decl); if (!init) continue; - walk_tree (&init, add_any_callbacks, this, NULL); + walk_tree (&init, add_any_callbacks, this, nullptr); } } @@ -3397,7 +3746,7 @@ exploded_graph::process_worklist () while (m_worklist.length () > 0) { exploded_node *node = m_worklist.take_next (); - gcc_assert (node->get_status () == exploded_node::STATUS_WORKLIST); + gcc_assert (node->get_status () == exploded_node::status::worklist); gcc_assert (node->m_succs.length () == 0 || node == m_origin); @@ -3417,7 +3766,7 @@ exploded_graph::process_worklist () if (exploded_node *node_2 = m_worklist.peek_next ()) { gcc_assert (node_2->get_status () - == exploded_node::STATUS_WORKLIST); + == exploded_node::status::worklist); gcc_assert (node->m_succs.length () == 0); gcc_assert (node_2->m_succs.length () == 0); @@ -3458,11 +3807,11 @@ exploded_graph::process_worklist () if (merged_state == state) { /* Then merge node_2 into node by adding an edge. */ - add_edge (node_2, node, NULL, false); + add_edge (node_2, node, nullptr, false); /* Remove node_2 from the worklist. */ m_worklist.take_next (); - node_2->set_status (exploded_node::STATUS_MERGER); + node_2->set_status (exploded_node::status::merger); /* Continue processing "node" below. */ } @@ -3471,8 +3820,8 @@ exploded_graph::process_worklist () /* Then merge node into node_2, and leave node_2 in the worklist, to be processed on the next iteration. */ - add_edge (node, node_2, NULL, false); - node->set_status (exploded_node::STATUS_MERGER); + add_edge (node, node_2, nullptr, false); + node->set_status (exploded_node::status::merger); continue; } else @@ -3488,7 +3837,7 @@ exploded_graph::process_worklist () exploded_node *merged_enode = get_or_create_node (node->get_point (), merged_state, node); - if (merged_enode == NULL) + if (merged_enode == nullptr) continue; if (logger) @@ -3516,16 +3865,16 @@ exploded_graph::process_worklist () m_worklist.add_node (merged_enode); else { - add_edge (node, merged_enode, NULL, false); - node->set_status (exploded_node::STATUS_MERGER); + add_edge (node, merged_enode, nullptr, false); + node->set_status (exploded_node::status::merger); } if (merged_enode == node_2) m_worklist.add_node (merged_enode); else { - add_edge (node_2, merged_enode, NULL, false); - node_2->set_status (exploded_node::STATUS_MERGER); + add_edge (node_2, merged_enode, nullptr, false); + node_2->set_status (exploded_node::status::merger); } continue; @@ -3575,7 +3924,7 @@ exploded_graph::process_worklist () If ENODE's point is of the form (before-supernode, SNODE) and the next nodes in the worklist are a consecutive run of enodes of the same form, for the same supernode as ENODE (but potentially from different in-edges), - process them all together, setting their status to STATUS_BULK_MERGED, + process them all together, setting their status to status::bulk_merged, and return true. Otherwise, return false, in which case ENODE must be processed in the normal way. @@ -3614,7 +3963,7 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) int m_merger_idx; }; - gcc_assert (enode->get_status () == exploded_node::STATUS_WORKLIST); + gcc_assert (enode->get_status () == exploded_node::status::worklist); gcc_assert (enode->m_succs.length () == 0); const program_point &point = enode->get_point (); @@ -3634,7 +3983,7 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) while (exploded_node *enode_2 = m_worklist.peek_next ()) { gcc_assert (enode_2->get_status () - == exploded_node::STATUS_WORKLIST); + == exploded_node::status::worklist); gcc_assert (enode_2->m_succs.length () == 0); const program_point &point_2 = enode_2->get_point (); @@ -3679,7 +4028,7 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) uncertainty_t uncertainty; impl_region_model_context ctxt (*this, iter_enode, &state, next_state, - &uncertainty, NULL, NULL); + &uncertainty, nullptr, nullptr); const cfg_superedge *last_cfg_superedge = iter_sedge->dyn_cast_cfg_superedge (); if (last_cfg_superedge) @@ -3742,7 +4091,7 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) = first_item_for_each_merged_state[i]->m_input_enode; exploded_node *next = get_or_create_node (next_point, *merged_state, src_enode); - /* "next" could be NULL; we handle that when adding the edges below. */ + /* "next" could be nullptr; we handle that when adding the edges below. */ next_enodes.quick_push (next); if (logger) { @@ -3759,9 +4108,9 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) { exploded_node *next = next_enodes[it->m_merger_idx]; if (next) - add_edge (it->m_input_enode, next, NULL, + add_edge (it->m_input_enode, next, nullptr, false); /* no "work" is done during merger. */ - it->m_input_enode->set_status (exploded_node::STATUS_BULK_MERGED); + it->m_input_enode->set_status (exploded_node::status::bulk_merged); } if (logger) @@ -3779,8 +4128,9 @@ static bool stmt_requires_new_enode_p (const gimple *stmt, const gimple *prev_stmt) { - if (const gcall *call = dyn_cast <const gcall *> (stmt)) + if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt)) { + const gcall &call = *call_stmt; /* Stop consolidating at calls to "__analyzer_dump_exploded_nodes", so they always appear at the start of an exploded_node. */ @@ -3849,7 +4199,7 @@ state_change_requires_new_enode_p (const program_state &old_state, functions or calls that happen via function pointer. */ bool -exploded_graph::maybe_create_dynamic_call (const gcall *call, +exploded_graph::maybe_create_dynamic_call (const gcall &call, tree fn_decl, exploded_node *node, program_state next_state, @@ -3869,7 +4219,7 @@ exploded_graph::maybe_create_dynamic_call (const gcall *call, program_point new_point = program_point::before_supernode (sn_entry, - NULL, + nullptr, this_point->get_call_string ()); new_point.push_to_call_stack (sn_exit, @@ -3902,9 +4252,9 @@ exploded_graph::maybe_create_dynamic_call (const gcall *call, next_state, node); if (enode) - add_edge (node,enode, NULL, + add_edge (node,enode, nullptr, false, /* No work is done by the call itself. */ - make_unique<dynamic_call_info_t> (call)); + std::make_unique<dynamic_call_info_t> (call)); return true; } } @@ -3992,7 +4342,7 @@ private: class jump_through_null : public pending_diagnostic_subclass<jump_through_null> { public: - jump_through_null (const gcall *call) + jump_through_null (const gcall &call) : m_call (call) {} @@ -4003,7 +4353,7 @@ public: bool operator== (const jump_through_null &other) const { - return m_call == other.m_call; + return &m_call == &other.m_call; } int get_controlling_option () const final override @@ -4024,7 +4374,7 @@ public: } private: - const gcall *m_call; + const gcall &m_call; }; /* The core of exploded_graph::process_worklist (the main analysis loop), @@ -4041,7 +4391,7 @@ exploded_graph::process_node (exploded_node *node) logger * const logger = get_logger (); LOG_FUNC_1 (logger, "EN: %i", node->m_index); - node->set_status (exploded_node::STATUS_PROCESSED); + node->set_status (exploded_node::status::processed); const program_point &point = node->get_point (); @@ -4082,7 +4432,7 @@ exploded_graph::process_node (exploded_node *node) { impl_region_model_context ctxt (*this, node, &state, &next_state, - &uncertainty, NULL, NULL); + &uncertainty, nullptr, nullptr); const cfg_superedge *last_cfg_superedge = point.get_from_edge ()->dyn_cast_cfg_superedge (); if (last_cfg_superedge) @@ -4090,14 +4440,14 @@ exploded_graph::process_node (exploded_node *node) (node->get_supernode (), last_cfg_superedge, &ctxt); - program_state::detect_leaks (state, next_state, NULL, + program_state::detect_leaks (state, next_state, nullptr, get_ext_state (), &ctxt); } program_point next_point (point.get_next ()); exploded_node *next = get_or_create_node (next_point, next_state, node); if (next) - add_edge (node, next, NULL, + add_edge (node, next, nullptr, false); /* Assume no work is done at phi nodes. */ } break; @@ -4130,7 +4480,7 @@ exploded_graph::process_node (exploded_node *node) uncertainty_t uncertainty; const supernode *snode = point.get_supernode (); unsigned stmt_idx; - const gimple *prev_stmt = NULL; + const gimple *prev_stmt = nullptr; for (stmt_idx = point.get_stmt_idx (); stmt_idx < snode->m_stmts.length (); stmt_idx++) @@ -4162,8 +4512,8 @@ exploded_graph::process_node (exploded_node *node) { impl_region_model_context ctxt (*this, node, &old_state, &next_state, - &uncertainty, NULL, stmt); - program_state::detect_leaks (old_state, next_state, NULL, + &uncertainty, nullptr, stmt); + program_state::detect_leaks (old_state, next_state, nullptr, get_ext_state (), &ctxt); } @@ -4207,7 +4557,7 @@ exploded_graph::process_node (exploded_node *node) node->m_num_processed_stmts--; if (logger) logger->log ("creating edge to split_enode"); - add_edge (node, split_enode, NULL, could_have_done_work); + add_edge (node, split_enode, nullptr, could_have_done_work); return; } else @@ -4234,7 +4584,7 @@ exploded_graph::process_node (exploded_node *node) exploded_node *next = get_or_create_node (next_point, next_state, node); if (next) - add_edge (node, next, NULL, could_have_done_work); + add_edge (node, next, nullptr, could_have_done_work); } /* If we have custom edge infos, "bifurcate" the state @@ -4262,19 +4612,25 @@ exploded_graph::process_node (exploded_node *node) node, // enode_for_diag &path_ctxt.get_state_at_bifurcation (), &bifurcated_new_state, - NULL, // uncertainty_t *uncertainty - NULL, // path_context *path_ctxt + nullptr, // uncertainty_t *uncertainty + nullptr, // path_context *path_ctxt stmt); if (edge_info->update_state (&bifurcated_new_state, - NULL, /* no exploded_edge yet. */ + nullptr, /* no exploded_edge yet. */ &bifurcation_ctxt)) { - exploded_node *next2 - = get_or_create_node (next_point, bifurcated_new_state, node); - if (next2) - add_edge (node, next2, NULL, - true /* assume that work could be done */, - std::move (edge_info)); + if (exploded_node *next2 + = edge_info->create_enode + (*this, + next_point, + std::move (bifurcated_new_state), + node, + &bifurcation_ctxt)) + { + add_edge (node, next2, nullptr, + true /* assume that work could be done */, + std::move (edge_info)); + } } else { @@ -4344,15 +4700,15 @@ exploded_graph::process_node (exploded_node *node) if (succ->m_kind == SUPEREDGE_INTRAPROCEDURAL_CALL && !(succ->get_any_callgraph_edge ())) { - const gcall *call - = point.get_supernode ()->get_final_call (); + const gcall &call + = *point.get_supernode ()->get_final_call (); impl_region_model_context ctxt (*this, node, &state, &next_state, &uncertainty, - NULL, + nullptr, point.get_stmt()); region_model *model = state.m_region_model; @@ -4368,13 +4724,14 @@ exploded_graph::process_node (exploded_node *node) logger); if (!call_discovered) { - /* Check for jump through NULL. */ - if (tree fn_ptr = gimple_call_fn (call)) + /* Check for jump through nullptr. */ + if (tree fn_ptr = gimple_call_fn (&call)) { const svalue *fn_ptr_sval = model->get_rvalue (fn_ptr, &ctxt); if (fn_ptr_sval->all_zeroes_p ()) - ctxt.warn (make_unique<jump_through_null> (call)); + ctxt.warn + (std::make_unique<jump_through_null> (call)); } /* An unknown function or a special function was called @@ -4394,6 +4751,18 @@ exploded_graph::process_node (exploded_node *node) } } + /* Ignore CFG edges in the sgraph flagged with EH whilst + we're exploring the egraph. + We only use these sedges in special-case logic for + dealing with exception-handling. */ + if (auto cfg_sedge = succ->dyn_cast_cfg_superedge ()) + if (cfg_sedge->get_flags () & EDGE_EH) + { + if (logger) + logger->log ("rejecting EH edge"); + continue; + } + if (!node->on_edge (*this, succ, &next_point, &next_state, &uncertainty)) { @@ -4422,7 +4791,7 @@ exploded_graph::process_node (exploded_node *node) const call_string &cs = point.get_call_string (); program_point next_point = program_point::before_supernode (cs.get_caller_node (), - NULL, + nullptr, cs); program_state next_state (state); uncertainty_t uncertainty; @@ -4431,7 +4800,7 @@ exploded_graph::process_node (exploded_node *node) = next_point.get_supernode ()->get_returning_call (); if (call) - next_state.returning_call (*this, node, call, &uncertainty); + next_state.returning_call (*this, node, *call, &uncertainty); if (next_state.m_valid) { @@ -4440,8 +4809,8 @@ exploded_graph::process_node (exploded_node *node) next_state, node); if (enode) - add_edge (node, enode, NULL, false, - make_unique<dynamic_call_info_t> (call, true)); + add_edge (node, enode, nullptr, false, + std::make_unique<dynamic_call_info_t> (*call, true)); } } } @@ -4450,7 +4819,7 @@ exploded_graph::process_node (exploded_node *node) } /* Ensure that this graph has a stats instance for FN, return it. - FN can be NULL, in which case a stats instances is returned covering + FN can be nullptr, in which case a stats instances is returned covering "functionless" parts of the graph (the origin node). */ stats * @@ -4660,11 +5029,11 @@ exploded_graph::dump_states_for_supernode (FILE *out, std::unique_ptr<json::object> exploded_graph::to_json () const { - auto egraph_obj = ::make_unique<json::object> (); + auto egraph_obj = std::make_unique<json::object> (); /* Nodes. */ { - auto nodes_arr = ::make_unique<json::array> (); + auto nodes_arr = std::make_unique<json::array> (); unsigned i; exploded_node *n; FOR_EACH_VEC_ELT (m_nodes, i, n) @@ -4674,7 +5043,7 @@ exploded_graph::to_json () const /* Edges. */ { - auto edges_arr = ::make_unique<json::array> (); + auto edges_arr = std::make_unique<json::array> (); unsigned i; exploded_edge *n; FOR_EACH_VEC_ELT (m_edges, i, n) @@ -4780,9 +5149,9 @@ exploded_path::feasible_p (logger *logger, const program_point &src_point = src_enode.get_point (); const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt (); - *out = ::make_unique<feasibility_problem> (edge_idx, *eedge, - last_stmt, - std::move (rc)); + *out = std::make_unique<feasibility_problem> (edge_idx, *eedge, + last_stmt, + std::move (rc)); } return false; } @@ -4986,7 +5355,7 @@ maybe_update_for_edge (logger *logger, == PK_BEFORE_SUPERNODE); function *fun = eedge->m_dest->get_function (); gcc_assert (fun); - m_model.push_frame (*fun, NULL, ctxt); + m_model.push_frame (*fun, nullptr, nullptr, ctxt); if (logger) logger->log (" pushing frame for %qD", fun->decl); } @@ -5032,16 +5401,16 @@ void feasibility_state::update_for_stmt (const gimple *stmt) { if (const gassign *assign = dyn_cast <const gassign *> (stmt)) - m_model.on_assignment (assign, NULL); + m_model.on_assignment (assign, nullptr); else if (const gasm *asm_stmt = dyn_cast <const gasm *> (stmt)) - m_model.on_asm_stmt (asm_stmt, NULL); + m_model.on_asm_stmt (asm_stmt, nullptr); else if (const gcall *call = dyn_cast <const gcall *> (stmt)) { - bool unknown_side_effects = m_model.on_call_pre (call, NULL); - m_model.on_call_post (call, unknown_side_effects, NULL); + bool unknown_side_effects = m_model.on_call_pre (*call, nullptr); + m_model.on_call_post (*call, unknown_side_effects, nullptr); } else if (const greturn *return_ = dyn_cast <const greturn *> (stmt)) - m_model.on_return (return_, NULL); + m_model.on_return (return_, nullptr); } /* Dump this object to PP. */ @@ -5256,7 +5625,7 @@ template <> inline void pod_hash_traits<function_call_string>::mark_empty (value_type &v) { - v.m_fun = NULL; + v.m_fun = nullptr; } template <> inline bool @@ -5268,7 +5637,7 @@ template <> inline bool pod_hash_traits<function_call_string>::is_empty (value_type v) { - return v.m_fun == NULL; + return v.m_fun == nullptr; } namespace ana { @@ -5424,7 +5793,7 @@ exploded_graph::dump_exploded_nodes () const { auto_timevar tv (TV_ANALYZER_DUMP); char *filename - = concat (dump_base_name, ".eg.txt", NULL); + = concat (dump_base_name, ".eg.txt", nullptr); FILE *outf = fopen (filename, "w"); if (!outf) error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename); @@ -5499,7 +5868,7 @@ exploded_graph::dump_exploded_nodes () const if (const gimple *stmt = enode->get_stmt ()) if (const gcall *call = dyn_cast <const gcall *> (stmt)) - if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes", + if (is_special_named_call_p (*call, "__analyzer_dump_exploded_nodes", 1)) { if (seen.contains (stmt)) @@ -5520,13 +5889,13 @@ exploded_graph::dump_exploded_nodes () const { default: gcc_unreachable (); - case exploded_node::STATUS_WORKLIST: + case exploded_node::status::worklist: worklist_enodes.safe_push (other_enode); break; - case exploded_node::STATUS_PROCESSED: + case exploded_node::status::processed: processed_enodes.safe_push (other_enode); break; - case exploded_node::STATUS_MERGER: + case exploded_node::status::merger: merger_enodes.safe_push (other_enode); break; } @@ -5851,7 +6220,7 @@ dump_callgraph (const supergraph &sg, const char *filename, // TODO viz_callgraph vcg (sg); - vcg.dump_dot (filename, NULL, viz_callgraph_traits::dump_args_t (eg)); + vcg.dump_dot (filename, nullptr, viz_callgraph_traits::dump_args_t (eg)); fclose (outf); } @@ -5862,7 +6231,7 @@ static void dump_callgraph (const supergraph &sg, const exploded_graph *eg) { auto_timevar tv (TV_ANALYZER_DUMP); - char *filename = concat (dump_base_name, ".callgraph.dot", NULL); + char *filename = concat (dump_base_name, ".callgraph.dot", nullptr); dump_callgraph (sg, filename, eg); free (filename); } @@ -6007,15 +6376,18 @@ private: { default: gcc_unreachable (); - case exploded_node::STATUS_WORKLIST: + case exploded_node::status::worklist: pp_string (pp, "(W)"); break; - case exploded_node::STATUS_PROCESSED: + case exploded_node::status::processed: + break; + case exploded_node::status::special: + pp_string (pp, "(S)"); break; - case exploded_node::STATUS_MERGER: + case exploded_node::status::merger: pp_string (pp, "(M)"); break; - case exploded_node::STATUS_BULK_MERGED: + case exploded_node::status::bulk_merged: pp_string (pp, "(BM)"); break; } @@ -6085,7 +6457,7 @@ dump_analyzer_json (const supergraph &sg, const exploded_graph &eg) { auto_timevar tv (TV_ANALYZER_DUMP); - char *filename = concat (dump_base_name, ".analyzer.json.gz", NULL); + char *filename = concat (dump_base_name, ".analyzer.json.gz", nullptr); gzFile output = gzopen (filename, "w"); if (!output) { @@ -6094,7 +6466,7 @@ dump_analyzer_json (const supergraph &sg, return; } - auto toplev_obj = ::make_unique<json::object> (); + auto toplev_obj = std::make_unique<json::object> (); toplev_obj->set ("sgraph", sg.to_json ()); toplev_obj->set ("egraph", eg.to_json ()); @@ -6115,8 +6487,8 @@ dump_analyzer_json (const supergraph &sg, class plugin_analyzer_init_impl : public plugin_analyzer_init_iface { public: - plugin_analyzer_init_impl (auto_delete_vec <state_machine> *checkers, - known_function_manager *known_fn_mgr, + plugin_analyzer_init_impl (std::vector<std::unique_ptr<state_machine>> &checkers, + known_function_manager &known_fn_mgr, logger *logger) : m_checkers (checkers), m_known_fn_mgr (known_fn_mgr), @@ -6126,14 +6498,14 @@ public: void register_state_machine (std::unique_ptr<state_machine> sm) final override { LOG_SCOPE (m_logger); - m_checkers->safe_push (sm.release ()); + m_checkers.push_back (std::move (sm)); } void register_known_function (const char *name, std::unique_ptr<known_function> kf) final override { LOG_SCOPE (m_logger); - m_known_fn_mgr->add (name, std::move (kf)); + m_known_fn_mgr.add (name, std::move (kf)); } logger *get_logger () const final override @@ -6142,8 +6514,8 @@ public: } private: - auto_delete_vec <state_machine> *m_checkers; - known_function_manager *m_known_fn_mgr; + std::vector<std::unique_ptr<state_machine>> &m_checkers; + known_function_manager &m_known_fn_mgr; logger *m_logger; }; @@ -6172,7 +6544,7 @@ impl_run_checkers (logger *logger) engine eng (&sg, logger); - state_purge_map *purge_map = NULL; + state_purge_map *purge_map = nullptr; if (flag_analyzer_state_purge) purge_map = new state_purge_map (sg, eng.get_model_manager (), logger); @@ -6181,8 +6553,8 @@ impl_run_checkers (logger *logger) { /* Dump supergraph pre-analysis. */ auto_timevar tv (TV_ANALYZER_DUMP); - char *filename = concat (dump_base_name, ".supergraph.dot", NULL); - supergraph::dump_args_t args ((enum supergraph_dot_flags)0, NULL); + char *filename = concat (dump_base_name, ".supergraph.dot", nullptr); + supergraph::dump_args_t args ((enum supergraph_dot_flags)0, nullptr); sg.dump_dot (filename, args); free (filename); } @@ -6191,33 +6563,31 @@ impl_run_checkers (logger *logger) { auto_timevar tv (TV_ANALYZER_DUMP); state_purge_annotator a (purge_map); - char *filename = concat (dump_base_name, ".state-purge.dot", NULL); + char *filename = concat (dump_base_name, ".state-purge.dot", nullptr); supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a); sg.dump_dot (filename, args); free (filename); } - auto_delete_vec <state_machine> checkers; - make_checkers (checkers, logger); + auto checkers = make_checkers (logger); register_known_functions (*eng.get_known_function_manager (), *eng.get_model_manager ()); - plugin_analyzer_init_impl data (&checkers, - eng.get_known_function_manager (), + plugin_analyzer_init_impl data (checkers, + *eng.get_known_function_manager (), logger); invoke_plugin_callbacks (PLUGIN_ANALYZER_INIT, &data); if (logger) { - int i; - state_machine *sm; - FOR_EACH_VEC_ELT (checkers, i, sm) - logger->log ("checkers[%i]: %s", i, sm->get_name ()); + int i = 0; + for (auto &sm : checkers) + logger->log ("checkers[%i]: %s", ++i, sm->get_name ()); } /* Extrinsic state shared by nodes in the graph. */ - const extrinsic_state ext_state (checkers, &eng, logger); + const extrinsic_state ext_state (std::move (checkers), &eng, logger); const analysis_plan plan (sg, logger); @@ -6238,7 +6608,7 @@ impl_run_checkers (logger *logger) { auto_timevar tv (TV_ANALYZER_DUMP); char *filename - = concat (dump_base_name, ".eg.dot", NULL); + = concat (dump_base_name, ".eg.dot", nullptr); exploded_graph::dump_args_t args (eg); root_cluster c; eg.dump_dot (filename, &c, args); @@ -6259,7 +6629,7 @@ impl_run_checkers (logger *logger) { /* Dump post-analysis form of supergraph. */ auto_timevar tv (TV_ANALYZER_DUMP); - char *filename = concat (dump_base_name, ".supergraph-eg.dot", NULL); + char *filename = concat (dump_base_name, ".supergraph-eg.dot", nullptr); exploded_graph_annotator a (eg); supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a); sg.dump_dot (filename, args); @@ -6283,7 +6653,7 @@ impl_run_checkers (logger *logger) } /* Handle -fdump-analyzer and -fdump-analyzer-stderr. */ -static FILE *dump_fout = NULL; +static FILE *dump_fout = nullptr; /* Track if we're responsible for closing dump_fout. */ static bool owns_dump_fout = false; @@ -6300,7 +6670,7 @@ get_or_create_any_logfile () dump_fout = stderr; else if (flag_dump_analyzer) { - char *dump_filename = concat (dump_base_name, ".analyzer.txt", NULL); + char *dump_filename = concat (dump_base_name, ".analyzer.txt", nullptr); dump_fout = fopen (dump_filename, "w"); free (dump_filename); if (dump_fout) @@ -6320,7 +6690,7 @@ run_checkers () location_t saved_input_location = input_location; { - log_user the_logger (NULL); + log_user the_logger (nullptr); get_or_create_any_logfile (); if (dump_fout) the_logger.set_logger (new logger (dump_fout, 0, 0, @@ -6337,7 +6707,7 @@ run_checkers () { fclose (dump_fout); owns_dump_fout = false; - dump_fout = NULL; + dump_fout = nullptr; } /* Restore input_location. Subsequent passes may assume that input_location |