aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer/engine.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/analyzer/engine.cc')
-rw-r--r--gcc/analyzer/engine.cc736
1 files changed, 547 insertions, 189 deletions
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 71d7ed7..c3e4800 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. */
@@ -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);
+ NULL, &call);
region_model *model = m_new_state->m_region_model;
return model->get_fndecl_for_call (call, &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)
@@ -1207,10 +1196,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 +1207,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 +1285,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 +1429,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 +1511,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,
@@ -1579,8 +1568,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
@@ -1609,6 +1599,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 +1634,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 +1644,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 +1659,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 +1670,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 +1692,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 +1704,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 +1721,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 +1761,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,7 +1836,7 @@ 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)
@@ -1849,8 +1859,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 +1913,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 +1927,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 +1952,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 +1960,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;
}
@@ -1986,8 +1996,8 @@ exploded_node::on_longjmp (exploded_graph &eg,
{
exploded_edge *eedge
= eg.add_edge (const_cast<exploded_node *> (this), next, NULL, true,
- make_unique<rewind_info_t> (tmp_setjmp_record,
- longjmp_call));
+ 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 +2035,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, NULL, 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,
+ NULL,
+ 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, NULL, 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, NULL, 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.
@@ -2131,20 +2467,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. */
@@ -2189,19 +2521,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 +2626,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 +2752,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;
}
@@ -2518,7 +2850,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);
}
@@ -2641,7 +2973,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 ());
@@ -2791,8 +3123,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:
@@ -2833,7 +3165,7 @@ exploded_graph::add_function_entry (const function &fun)
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)
@@ -2851,7 +3183,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 +3193,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);
@@ -3035,7 +3369,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;
}
@@ -3238,16 +3575,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:
@@ -3304,7 +3641,7 @@ 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));
+ std::make_unique<tainted_args_call_info> (field, fndecl, loc));
}
/* Callback for walk_tree for finding callbacks within initializers;
@@ -3397,7 +3734,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 +3754,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);
@@ -3462,7 +3799,7 @@ exploded_graph::process_worklist ()
/* 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. */
}
@@ -3472,7 +3809,7 @@ exploded_graph::process_worklist ()
in the worklist, to be processed on the next
iteration. */
add_edge (node, node_2, NULL, false);
- node->set_status (exploded_node::STATUS_MERGER);
+ node->set_status (exploded_node::status::merger);
continue;
}
else
@@ -3517,7 +3854,7 @@ exploded_graph::process_worklist ()
else
{
add_edge (node, merged_enode, NULL, false);
- node->set_status (exploded_node::STATUS_MERGER);
+ node->set_status (exploded_node::status::merger);
}
if (merged_enode == node_2)
@@ -3525,7 +3862,7 @@ exploded_graph::process_worklist ()
else
{
add_edge (node_2, merged_enode, NULL, false);
- node_2->set_status (exploded_node::STATUS_MERGER);
+ node_2->set_status (exploded_node::status::merger);
}
continue;
@@ -3575,7 +3912,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 +3951,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 +3971,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 ();
@@ -3761,7 +4098,7 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode)
if (next)
add_edge (it->m_input_enode, next, NULL,
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 +4116,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 +4187,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,
@@ -3904,7 +4242,7 @@ exploded_graph::maybe_create_dynamic_call (const gcall *call,
if (enode)
add_edge (node,enode, NULL,
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 +4330,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 +4341,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 +4362,7 @@ public:
}
private:
- const gcall *m_call;
+ const gcall &m_call;
};
/* The core of exploded_graph::process_worklist (the main analysis loop),
@@ -4041,7 +4379,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 ();
@@ -4269,12 +4607,18 @@ exploded_graph::process_node (exploded_node *node)
NULL, /* 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, NULL,
+ true /* assume that work could be done */,
+ std::move (edge_info));
+ }
}
else
{
@@ -4344,8 +4688,8 @@ 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,
@@ -4369,12 +4713,13 @@ exploded_graph::process_node (exploded_node *node)
if (!call_discovered)
{
/* Check for jump through NULL. */
- if (tree fn_ptr = gimple_call_fn (call))
+ 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 +4739,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))
{
@@ -4431,7 +4788,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)
{
@@ -4441,7 +4798,7 @@ exploded_graph::process_node (exploded_node *node)
node);
if (enode)
add_edge (node, enode, NULL, false,
- make_unique<dynamic_call_info_t> (call, true));
+ std::make_unique<dynamic_call_info_t> (*call, true));
}
}
}
@@ -4660,11 +5017,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 +5031,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 +5137,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 +5343,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);
}
@@ -5037,8 +5394,8 @@ feasibility_state::update_for_stmt (const gimple *stmt)
m_model.on_asm_stmt (asm_stmt, NULL);
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, NULL);
+ m_model.on_call_post (*call, unknown_side_effects, NULL);
}
else if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
m_model.on_return (return_, NULL);
@@ -5499,7 +5856,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 +5877,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;
}
@@ -6007,15 +6364,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;
}
@@ -6094,7 +6454,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 +6475,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 +6486,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 +6502,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;
};
@@ -6197,27 +6557,25 @@ impl_run_checkers (logger *logger)
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);