@@ -1383,6 +1383,7 @@ ANALYZER_OBJS = \
analyzer/sm-signal.o \
analyzer/sm-taint.o \
analyzer/state-purge.o \
+ analyzer/state-transition.o \
analyzer/store.o \
analyzer/supergraph.o \
analyzer/supergraph-fixup-locations.o \
@@ -29,6 +29,16 @@ along with GCC; see the file COPYING3. If not see
namespace ana {
+bool
+printable_expr_p (const_tree expr)
+{
+ if (TREE_CODE (expr) == SSA_NAME
+ && !SSA_NAME_VAR (expr))
+ return false;
+
+ return true;
+}
+
/* Workaround for missing location information for some stmts,
which ultimately should be solved by fixing the frontends
to provide the locations (TODO). */
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/exploded-graph.h"
#include "analyzer/call-details.h"
#include "analyzer/call-info.h"
+#include "analyzer/state-transition.h"
#if ENABLE_ANALYZER
@@ -96,7 +97,8 @@ call_info::print (pretty_printer *pp) const
void
call_info::add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &) const
+ pending_diagnostic &,
+ const state_transition *) const
{
class call_event : public custom_event
{
@@ -121,6 +123,10 @@ call_info::add_events_to_path (checker_path *emission_path,
tree caller_fndecl = src_point.get_fndecl ();
const int stack_depth = src_point.get_stack_depth ();
+ /* TODO: we don't yet make use of the state_transition (if any), as
+ doing so presumably requires a combinatorial explosion of
+ known functions vs pending diagnostics. */
+
emission_path->add_event
(std::make_unique<call_event> (event_loc_info (get_call_stmt ().location,
caller_fndecl,
@@ -33,7 +33,8 @@ public:
void print (pretty_printer *pp) const override;
void add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &pd) const override;
+ pending_diagnostic &pd,
+ const state_transition *state_trans) const override;
const gcall &get_call_stmt () const { return m_call_stmt; }
tree get_fndecl () const { return m_fndecl; }
new file mode 100644
@@ -0,0 +1,104 @@
+/* User-facing descriptions of expressions at call sites.
+ Copyright (C) 2019-2026 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+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/>. */
+
+#ifndef GCC_ANALYZER_CALLSITE_EXPR_H
+#define GCC_ANALYZER_CALLSITE_EXPR_H
+
+#include "pretty-print-markup.h"
+
+namespace ana {
+
+/* An ID representing an expression at a callsite:
+ either a parameter index, or the return value (or unknown). */
+
+class callsite_expr
+{
+ public:
+ callsite_expr () : m_val (-1) {}
+
+ static callsite_expr from_zero_based_param (int idx)
+ {
+ return callsite_expr (idx + 1);
+ }
+
+ static callsite_expr from_return_value ()
+ {
+ return callsite_expr (0);
+ }
+
+ bool param_p () const
+ {
+ return m_val > 0;
+ }
+
+ /* Get 1-based param number. */
+ int param_num () const
+ {
+ gcc_assert (param_p ());
+ return m_val;
+ }
+
+ tree get_param_tree (tree fndecl) const;
+
+ bool
+ maybe_get_param_location (tree fndecl,
+ location_t *out_loc) const;
+
+ bool return_value_p () const
+ {
+ return m_val == 0;
+ }
+
+ private:
+ callsite_expr (int val) : m_val (val) {}
+
+ int m_val; /* 1-based parm, 0 for return value, or -1 for "unknown". */
+};
+
+class callsite_expr_element : public pp_element
+{
+public:
+ callsite_expr_element (callsite_expr expr) : m_expr (expr) {}
+
+ void
+ add_to_phase_2 (pp_markup::context &ctxt) final override
+ {
+
+ if (m_expr.return_value_p ())
+ pp_string (&ctxt.m_pp, "return value");
+ else if (m_expr.param_p ())
+ {
+ /* We can't call pp_printf directly on ctxt.m_pp from within
+ formatting. As a workaround, work with a clone of the pp. */
+ std::unique_ptr<pretty_printer> pp (ctxt.m_pp.clone ());
+ pp_printf (pp.get (), "parameter %i", m_expr.param_num ());
+ pp_string (&ctxt.m_pp, pp_formatted_text (pp.get ()));
+ }
+ else
+ pp_string (&ctxt.m_pp, "unknown");
+ }
+
+private:
+ callsite_expr m_expr;
+};
+
+} // namespace ana
+
+#endif /* GCC_ANALYZER_CALLSITE_EXPR_H */
@@ -45,6 +45,8 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/constraint-manager.h"
#include "analyzer/checker-event.h"
#include "analyzer/exploded-graph.h"
+#include "analyzer/callsite-expr.h"
+#include "analyzer/state-transition.h"
#if ENABLE_ANALYZER
@@ -67,6 +69,8 @@ event_kind_to_string (enum event_kind ek)
return "stmt";
case event_kind::region_creation:
return "region_creation";
+ case event_kind::state_transition:
+ return "state_transition";
case event_kind::function_entry:
return "function_entry";
case event_kind::state_change:
@@ -301,6 +305,94 @@ statement_event::print_desc (pretty_printer &pp) const
pp_gimple_stmt_1 (&pp, m_stmt, 0, (dump_flags_t)0);
}
+/* class state_transition_event : public checker_event. */
+
+void
+state_transition_event::print_desc (pretty_printer &pp) const
+{
+ gcc_assert (m_state_trans);
+ switch (m_state_trans->get_kind ())
+ {
+ default:
+ gcc_unreachable ();
+ case state_transition::kind::origin:
+ {
+ const state_transition_origin &state_trans
+ = *static_cast <const state_transition_origin *> (m_state_trans);
+ if (m_pending_diagnostic)
+ {
+ evdesc::origin_of_state evd (state_trans.m_dst_reg_expr);
+ if (m_pending_diagnostic->describe_origin_of_state (pp, evd))
+ return;
+ }
+ pp_printf (&pp, "value originates here");
+ }
+ break;
+
+ case state_transition::kind::at_call:
+ case state_transition::kind::at_return:
+ // These should be handled by call_event and return_event
+ gcc_unreachable ();
+ break;
+
+ case state_transition::kind::copy:
+ {
+ const state_transition_copy &state_trans
+ = *static_cast <const state_transition_copy *> (m_state_trans);
+ if (m_pending_diagnostic)
+ {
+ evdesc::copy_of_state evd (state_trans.m_src_reg_expr,
+ state_trans.get_src_event_id (),
+ state_trans.m_dst_reg_expr);
+ if (m_pending_diagnostic->describe_copy_of_state (pp, evd))
+ return;
+ }
+ auto event_id = state_trans.get_src_event_id ();
+ if (event_id.known_p ())
+ pp_printf (&pp, "copying value from %@ from %qE to %qE",
+ &event_id,
+ state_trans.m_src_reg_expr,
+ state_trans.m_dst_reg_expr);
+ else
+ pp_printf (&pp, "copying value from %qE to %qE",
+ state_trans.m_src_reg_expr,
+ state_trans.m_dst_reg_expr);
+ }
+ break;
+ case state_transition::kind::use:
+ {
+ const state_transition_use &state_trans
+ = *static_cast <const state_transition_use *> (m_state_trans);
+ if (m_pending_diagnostic)
+ {
+ evdesc::use_of_state evd (state_trans.m_src_reg_expr,
+ state_trans.get_src_event_id ());
+ if (m_pending_diagnostic->describe_use_of_state (pp, evd))
+ return;
+ }
+ auto event_id = state_trans.get_src_event_id ();
+ if (state_trans.get_src_event_id ().known_p ())
+ pp_printf (&pp, "using value from %@ from %qE",
+ &event_id,
+ state_trans.m_src_reg_expr);
+ else
+ pp_printf (&pp, "using value from %qE",
+ state_trans.m_src_reg_expr);
+ }
+ break;
+ }
+}
+
+void
+state_transition_event::
+prepare_for_emission (checker_path *path,
+ pending_diagnostic *pd,
+ diagnostics::paths::event_id_t emission_id)
+{
+ checker_event::prepare_for_emission (path, pd, emission_id);
+ const_cast<state_transition *> (m_state_trans)->m_event_id = emission_id;
+}
+
/* class region_creation_event : public checker_event. */
region_creation_event::region_creation_event (const event_loc_info &loc_info)
@@ -376,16 +468,6 @@ region_creation_event_debug::print_desc (pretty_printer &pp) const
/* class function_entry_event : public checker_event. */
-function_entry_event::function_entry_event (const program_point &dst_point,
- const program_state &state)
-: checker_event (event_kind::function_entry,
- event_loc_info (dst_point.get_location (),
- dst_point.get_fndecl (),
- dst_point.get_stack_depth ())),
- m_state (state)
-{
-}
-
/* Implementation of diagnostics::paths::event::print_desc vfunc for
function_entry_event.
@@ -394,6 +476,25 @@ function_entry_event::function_entry_event (const program_point &dst_point,
void
function_entry_event::print_desc (pretty_printer &pp) const
{
+ if (m_state_trans)
+ {
+ callsite_expr expr = m_state_trans->get_callsite_expr ();
+ if (tree parm = expr.get_param_tree (m_effective_fndecl))
+ {
+ auto src_event_id = m_state_trans->get_src_event_id ();
+ if (src_event_id.known_p ())
+ pp_printf (&pp,
+ "entry to %qE with problematic value from %@ for %qE",
+ m_effective_fndecl,
+ &src_event_id,
+ parm);
+ else
+ pp_printf (&pp, "entry to %qE with problematic value for %qE",
+ m_effective_fndecl, parm);
+ return;
+ }
+ }
+
pp_printf (&pp, "entry to %qE", m_effective_fndecl);
}
@@ -406,6 +507,17 @@ function_entry_event::get_meaning () const
return meaning (verb::enter, noun::function);
}
+void
+function_entry_event::
+prepare_for_emission (checker_path *path,
+ pending_diagnostic *pd,
+ diagnostics::paths::event_id_t emission_id)
+{
+ checker_event::prepare_for_emission (path, pd, emission_id);
+ if (m_state_trans)
+ const_cast<state_transition_at_call *> (m_state_trans)->m_event_id = emission_id;
+}
+
/* class state_change_event : public checker_event. */
/* state_change_event's ctor. */
@@ -730,8 +842,10 @@ catch_cfg_edge_event::get_meaning () const
/* call_event's ctor. */
call_event::call_event (const exploded_edge &eedge,
- const event_loc_info &loc_info)
-: superedge_event (event_kind::call_, eedge, loc_info)
+ const event_loc_info &loc_info,
+ const state_transition_at_call *state_trans)
+: superedge_event (event_kind::call_, eedge, loc_info),
+ m_state_trans (state_trans)
{
m_src_snode = eedge.m_src->get_supernode ();
m_dest_snode = eedge.m_dest->get_supernode ();
@@ -751,16 +865,50 @@ call_event::call_event (const exploded_edge &eedge,
void
call_event::print_desc (pretty_printer &pp) const
{
- if (m_critical_state.m_state && m_pending_diagnostic)
+ if (m_pending_diagnostic)
{
- gcc_assert (m_critical_state.m_var);
- tree var = fixup_tree_for_diagnostic (m_critical_state.m_var);
- evdesc::call_with_state evd (m_src_snode->m_fun->decl,
- m_dest_snode->m_fun->decl,
- var,
- m_critical_state.m_state);
- if (m_pending_diagnostic->describe_call_with_state (pp, evd))
- return;
+ if (m_critical_state.m_state)
+ {
+ gcc_assert (m_critical_state.m_var);
+ tree var = fixup_tree_for_diagnostic (m_critical_state.m_var);
+ evdesc::call_with_state evd (m_src_snode->m_fun->decl,
+ m_dest_snode->m_fun->decl,
+ var,
+ m_critical_state.m_state,
+ m_state_trans);
+ if (m_pending_diagnostic->describe_call_with_state (pp, evd))
+ return;
+ }
+ else if (m_state_trans)
+ {
+ evdesc::call_with_state evd (m_src_snode->m_fun->decl,
+ m_dest_snode->m_fun->decl,
+ NULL_TREE, nullptr,
+ m_state_trans);
+ if (m_pending_diagnostic->describe_call_with_state (pp, evd))
+ return;
+ callsite_expr expr = m_state_trans->get_callsite_expr ();
+ if (expr.param_p ())
+ {
+ auto src_event_id = m_state_trans->get_src_event_id ();
+ if (src_event_id.known_p ())
+ pp_printf (&pp,
+ "passing problematic value from %@ from %qE to %qE"
+ " via parameter %i",
+ &src_event_id,
+ get_caller_fndecl (),
+ get_callee_fndecl (),
+ expr.param_num ());
+ else
+ pp_printf (&pp,
+ "passing problematic value from %qE to %qE"
+ " via parameter %i",
+ get_caller_fndecl (),
+ get_callee_fndecl (),
+ expr.param_num ());
+ return;
+ }
+ }
}
pp_printf (&pp,
@@ -806,14 +954,26 @@ call_event::get_program_state () const
return &m_eedge.m_src->get_state ();
}
+void
+call_event::prepare_for_emission (checker_path *path,
+ pending_diagnostic *pd,
+ diagnostics::paths::event_id_t emission_id)
+{
+ checker_event::prepare_for_emission (path, pd, emission_id);
+ if (m_state_trans)
+ const_cast<state_transition_at_call *> (m_state_trans)->m_event_id = emission_id;
+}
+
/* class return_event : public checker_event. */
/* return_event's ctor. */
return_event::return_event (const exploded_edge &eedge,
- const event_loc_info &loc_info)
+ const event_loc_info &loc_info,
+ const state_transition_at_return *state_trans)
: checker_event (event_kind::return_, loc_info),
- m_eedge (eedge)
+ m_eedge (eedge),
+ m_state_trans (state_trans)
{
m_src_snode = eedge.m_src->get_supernode ();
m_dest_snode = eedge.m_dest->get_supernode ();
@@ -839,14 +999,28 @@ return_event::print_desc (pretty_printer &pp) const
state involved in the pending diagnostic, give the pending
diagnostic a chance to describe this return (in terms of
itself). */
- if (m_critical_state.m_state && m_pending_diagnostic)
+ if (m_pending_diagnostic)
{
- evdesc::return_of_state evd (m_dest_snode->m_fun->decl,
- m_src_snode->m_fun->decl,
- m_critical_state.m_state);
- if (m_pending_diagnostic->describe_return_of_state (pp, evd))
- return;
+ if (m_critical_state.m_state)
+ {
+ evdesc::return_of_state evd (m_dest_snode->m_fun->decl,
+ m_src_snode->m_fun->decl,
+ m_critical_state.m_state,
+ nullptr);
+ if (m_pending_diagnostic->describe_return_of_state (pp, evd))
+ return;
+ }
+ else if (m_state_trans)
+ {
+ evdesc::return_of_state evd (m_dest_snode->m_fun->decl,
+ m_src_snode->m_fun->decl,
+ nullptr,
+ m_state_trans);
+ if (m_pending_diagnostic->describe_return_of_state (pp, evd))
+ return;
+ }
}
+
pp_printf (&pp,
"returning to %qE from %qE",
m_dest_snode->m_fun->decl,
@@ -876,6 +1050,16 @@ return_event::get_program_state () const
return &m_eedge.m_dest->get_state ();
}
+void
+return_event::prepare_for_emission (checker_path *path,
+ pending_diagnostic *pd,
+ diagnostics::paths::event_id_t emission_id)
+{
+ checker_event::prepare_for_emission (path, pd, emission_id);
+ if (m_state_trans)
+ const_cast<state_transition_at_return *> (m_state_trans)->m_event_id = emission_id;
+}
+
/* class start_consolidated_cfg_edges_event : public checker_event. */
void
@@ -37,6 +37,7 @@ enum class event_kind
custom,
stmt,
region_creation,
+ state_transition,
function_entry,
state_change,
start_cfg_edge,
@@ -68,6 +69,7 @@ extern const char *event_kind_to_string (enum event_kind ek);
custom_event (event_kind::custom)
precanned_custom_event
statement_event (event_kind::stmt)
+ state_transition_event (event_kind::data_flow)
region_creation_event (event_kind::region_creation)
function_entry_event (event_kind::function_entry)
state_change_event (event_kind::state_change)
@@ -245,6 +247,31 @@ public:
const program_state m_dst_state;
};
+/* A concrete checker_event subclass referencing a state_transition,
+ for cases where the state_transition doesn't already have its own event. */
+
+class state_transition_event : public checker_event
+{
+public:
+ state_transition_event (const event_loc_info &loc_info,
+ const state_transition *state_trans)
+ : checker_event (event_kind::state_transition, loc_info),
+ m_state_trans (state_trans)
+ {
+ gcc_assert (m_state_trans);
+ }
+
+ void print_desc (pretty_printer &) const final override;
+
+ void prepare_for_emission (checker_path *path,
+ pending_diagnostic *pd,
+ diagnostics::paths::event_id_t emission_id) final override;
+
+private:
+ // borrowed from the exploded_path
+ const state_transition *m_state_trans;
+};
+
/* An abstract event subclass describing the creation of a region that
is significant for a diagnostic.
@@ -352,15 +379,14 @@ class function_entry_event : public checker_event
{
public:
function_entry_event (const event_loc_info &loc_info,
- const program_state &state)
+ const program_state &state,
+ const state_transition_at_call *state_trans)
: checker_event (event_kind::function_entry, loc_info),
- m_state (state)
+ m_state (state),
+ m_state_trans (state_trans)
{
}
- function_entry_event (const program_point &dst_point,
- const program_state &state);
-
void print_desc (pretty_printer &pp) const override;
meaning get_meaning () const override;
@@ -372,8 +398,17 @@ public:
return &m_state;
}
+ void
+ prepare_for_emission (checker_path *path,
+ pending_diagnostic *pd,
+ diagnostics::paths::event_id_t emission_id) final override;
+
+ const state_transition_at_call *
+ get_state_transition_at_call () const { return m_state_trans; }
+
private:
const program_state &m_state;
+ const state_transition_at_call *m_state_trans;
};
/* Subclass of checker_event describing a state change. */
@@ -558,7 +593,8 @@ class call_event : public superedge_event
{
public:
call_event (const exploded_edge &eedge,
- const event_loc_info &loc_info);
+ const event_loc_info &loc_info,
+ const state_transition_at_call *state_trans);
void print_desc (pretty_printer &pp) const override;
meaning get_meaning () const override;
@@ -568,6 +604,11 @@ public:
const program_state *
get_program_state () const final override;
+ void
+ prepare_for_emission (checker_path *path,
+ pending_diagnostic *pd,
+ diagnostics::paths::event_id_t emission_id) final override;
+
/* Mark this edge event as being either an interprocedural call or
return in which VAR is in STATE, and that this is critical to the
diagnostic (so that print_desc can attempt to get a better description
@@ -577,6 +618,9 @@ public:
m_critical_state = critical_state (var, state);
}
+ const state_transition_at_call *
+ get_state_transition_at_call () const { return m_state_trans; }
+
protected:
tree get_caller_fndecl () const;
tree get_callee_fndecl () const;
@@ -584,6 +628,7 @@ protected:
const supernode *m_src_snode;
const supernode *m_dest_snode;
critical_state m_critical_state;
+ const state_transition_at_call *m_state_trans;
};
/* A concrete event subclass for an interprocedural return. */
@@ -592,7 +637,8 @@ class return_event : public checker_event
{
public:
return_event (const exploded_edge &eedge,
- const event_loc_info &loc_info);
+ const event_loc_info &loc_info,
+ const state_transition_at_return *state_trans);
void print_desc (pretty_printer &pp) const final override;
meaning get_meaning () const override;
@@ -608,6 +654,11 @@ public:
const program_state *
get_program_state () const override;
+ void
+ prepare_for_emission (checker_path *path,
+ pending_diagnostic *pd,
+ diagnostics::paths::event_id_t emission_id) final override;
+
/* Mark this edge event as being either an interprocedural call or
return in which VAR is in STATE, and that this is critical to the
diagnostic (so that print_desc can attempt to get a better description
@@ -622,6 +673,7 @@ public:
const supernode *m_dest_snode;
const call_and_return_op *m_call_and_return_op;
critical_state m_critical_state;
+ const state_transition_at_return *m_state_trans;
};
/* A concrete event subclass for the start of a consolidated run of CFG
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
#define GCC_ANALYZER_COMMON_H
#include "config.h"
+#define INCLUDE_ALGORITHM
#define INCLUDE_LIST
#define INCLUDE_MAP
#define INCLUDE_SET
@@ -139,6 +140,9 @@ class call_summary;
class call_summary_replay;
struct per_function_data;
struct interesting_t;
+class state_transition;
+ class state_transition_at_call;
+ class state_transition_at_return;
class uncertainty_t;
class feasible_node;
@@ -155,6 +159,7 @@ extern void dump_tree (pretty_printer *pp, tree t);
extern void dump_quoted_tree (pretty_printer *pp, tree t);
extern void print_quoted_type (pretty_printer *pp, tree t);
extern void print_expr_for_user (pretty_printer *pp, tree t);
+extern bool printable_expr_p (const_tree expr);
extern int readability_comparator (const void *p1, const void *p2);
extern int tree_cmp (const void *p1, const void *p2);
extern tree fixup_tree_for_diagnostic (tree);
@@ -374,6 +379,44 @@ enum class access_direction
write
};
+/* State tracked along an execution path that's pertinent to a specific
+ diagnostic (e.g. for a divide-by-zero warning where the zero value
+ comes from). */
+
+struct diagnostic_state
+{
+ diagnostic_state ()
+ : m_region_holding_value (nullptr)
+ {
+ }
+
+ diagnostic_state (std::string debug_desc,
+ const region *region_holding_value)
+ : m_debug_desc (std::move (debug_desc)),
+ m_region_holding_value (region_holding_value)
+ {
+ }
+
+ void dump_to_pp (pretty_printer *) const;
+ void dump () const;
+
+ bool
+ operator== (const diagnostic_state &other) const
+ {
+ return m_region_holding_value == other.m_region_holding_value;
+ }
+ bool
+ operator!= (const diagnostic_state &other) const
+ {
+ return !(*this == other);
+ }
+
+ std::string m_debug_desc;
+ const region *m_region_holding_value;
+};
+
+struct rewind_context;
+
/* Abstract base class for associating custom data with an
exploded_edge, for handling non-standard edges such as
rewinding from a longjmp, signal handlers, etc.
@@ -406,13 +449,20 @@ public:
virtual void add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &pd) const = 0;
+ pending_diagnostic &pd,
+ const state_transition *state_trans) const = 0;
virtual exploded_node *create_enode (exploded_graph &eg,
const program_point &point,
program_state &&state,
exploded_node *enode_for_diag,
region_model_context *ctxt) const;
+
+ virtual bool
+ try_to_rewind_data_flow (rewind_context &) const
+ {
+ return false;
+ }
};
/* Abstract base class for splitting state.
@@ -917,7 +917,7 @@ compatible_epath_p (const exploded_path *lhs_path,
while (lhs_eedge_idx >= 0)
{
/* Find LHS_PATH's next superedge. */
- lhs_eedge = lhs_path->m_edges[lhs_eedge_idx];
+ lhs_eedge = lhs_path->m_elements[lhs_eedge_idx].m_eedge;
if (lhs_eedge->m_sedge)
break;
else
@@ -926,7 +926,7 @@ compatible_epath_p (const exploded_path *lhs_path,
while (rhs_eedge_idx >= 0)
{
/* Find RHS_PATH's next superedge. */
- rhs_eedge = rhs_path->m_edges[rhs_eedge_idx];
+ rhs_eedge = rhs_path->m_elements[rhs_eedge_idx].m_eedge;
if (rhs_eedge->m_sedge)
break;
else
@@ -1555,13 +1555,20 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
sd.get_index (), sd.m_d->get_kind (), sd.get_supernode ()->m_id);
log ("num dupes: %i", sd.get_num_dupes ());
- const exploded_path *epath = sd.get_best_epath ();
+ exploded_path *epath = sd.get_best_epath ();
gcc_assert (epath);
+ epath->maybe_log (get_logger (), "best epath");
+
/* Precompute all enodes from which the diagnostic is reachable. */
path_builder pb (eg, *epath, sd.get_feasibility_problem (), sd);
- /* This is the diagnostics::paths::path subclass that will be built for
+ /* Annotate EPATH with information specific to the diagnostic, such
+ as pertinent data flow events. */
+ annotate_exploded_path (pb, *epath);
+ epath->maybe_log (get_logger (), "best epath with annotations");
+
+ /* This is the diagnostics::paths::path instance that will be built for
the diagnostic. */
checker_path emission_path (get_logical_location_manager (),
eg.get_ext_state (),
@@ -1589,7 +1596,8 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
trailing eedge stashed, add any events for it. This is for use
in handling longjmp, to show where a longjmp is rewinding to. */
if (sd.m_trailing_eedge)
- add_events_for_eedge (pb, *sd.m_trailing_eedge, &emission_path, nullptr);
+ add_events_for_eedge (pb, *sd.m_trailing_eedge, &emission_path, nullptr,
+ nullptr);
emission_path.inject_any_inlined_call_events (get_logger ());
@@ -1640,6 +1648,149 @@ diagnostic_manager::get_logical_location_manager () const
return *mgr;
}
+class epath_rewind_context : public rewind_context
+{
+public:
+ epath_rewind_context (const exploded_edge &eedge,
+ logger *logger,
+ diagnostic_state input_state,
+ state_transition *&last_state_transition,
+ exploded_path::element_t &epath_element)
+ : rewind_context (eedge, logger, input_state),
+ m_last_state_transition (last_state_transition),
+ m_epath_element (epath_element)
+ {
+ }
+
+ bool
+ could_be_affected_by_write_p (tree lhs) final override
+ {
+ if (!m_input.m_region_holding_value)
+ return false;
+
+ if (TREE_CODE (lhs) == SSA_NAME)
+ if (tree decl = m_input.m_region_holding_value->maybe_get_decl ())
+ return decl == lhs;
+
+ return true;
+ }
+
+ void
+ add_state_transition (std::unique_ptr<state_transition> st) final override
+ {
+ gcc_assert (st.get ());
+ if (m_logger)
+ {
+ m_logger->start_log_line ();
+ m_logger->log_partial ("adding state transition: ");
+ st->dump_to_pp (m_logger->get_printer ());
+ m_logger->end_log_line ();
+ }
+
+ /* Chain up the state_transition instances, so that each state transition
+ has a pointer to the one that occurred before it (but was created after
+ it, since we are rewinding the epath). */
+ if (m_last_state_transition)
+ m_last_state_transition->m_prev_state_transition = st.get ();
+ m_last_state_transition = st.get ();
+
+ m_epath_element.m_state_transition = std::move (st);
+ }
+
+private:
+ state_transition *&m_last_state_transition;
+ exploded_path::element_t &m_epath_element;
+};
+
+/* Populate the elements of EPATH with diagnostic_state and state_transition
+ information pertinent to the pending diagnostic. */
+
+void
+diagnostic_manager::annotate_exploded_path (const path_builder &pb,
+ exploded_path &epath) const
+{
+ auto logger = get_logger ();
+ LOG_SCOPE (logger);
+
+ // TODO: consolidate this with build_emission_path?
+ interesting_t interest;
+ pb.get_pending_diagnostic ()->mark_interesting_stuff (&interest);
+
+ gcc_assert (epath.m_elements.size () > 0);
+
+ diagnostic_state curr_state;
+ state_transition *last_state_transition = nullptr;
+
+ if (interest.m_read_regions.size () > 0)
+ curr_state = interest.m_read_regions[0];
+
+ // Walk epath backwards, propagating annotation information
+ for (int idx = epath.m_elements.size () - 1; idx >= 0; --idx)
+ {
+ exploded_path::element_t &iter_element = epath.m_elements[idx];
+ if (logger)
+ {
+ logger->log ("edge[%i]: considering rewinding EN %i -> EN %i",
+ idx,
+ iter_element.m_eedge->m_src->m_index,
+ iter_element.m_eedge->m_dest->m_index);
+ logger->start_log_line ();
+ logger->log_partial ("curr_state: ");
+ curr_state.dump_to_pp (logger->get_printer ());
+ logger->end_log_line ();
+ }
+ iter_element.m_state_at_dst = curr_state;
+ const exploded_edge *eedge = iter_element.m_eedge;
+ gcc_assert (eedge);
+
+ epath_rewind_context ctxt (*eedge, logger, curr_state,
+ last_state_transition, iter_element);
+ if (eedge->m_custom_info)
+ {
+ if (logger)
+ {
+ logger->start_log_line ();
+ logger->log_partial ("custom_edge_info: ");
+ eedge->m_custom_info->print (logger->get_printer ());
+ logger->end_log_line ();
+ }
+ if (!eedge->m_custom_info->try_to_rewind_data_flow (ctxt))
+ {
+ if (logger)
+ logger->log ("could not rewind custom info");
+ return;
+ }
+ }
+ else if (const operation *op = eedge->maybe_get_op ())
+ {
+ if (logger)
+ {
+ logger->start_log_line ();
+ logger->log_partial ("op: ");
+ op->print_as_edge_label (logger->get_printer (), false);
+ logger->end_log_line ();
+ }
+ if (!op->try_to_rewind_data_flow (ctxt))
+ {
+ if (logger)
+ logger->log ("could not rewind op");
+ return;
+ }
+ }
+
+ iter_element.m_state_at_src = ctxt.m_output;
+ curr_state = ctxt.m_output;
+ if (logger)
+ {
+ logger->log ("rewound");
+ logger->start_log_line ();
+ logger->log_partial ("curr_state: ");
+ curr_state.dump_to_pp (logger->get_printer ());
+ logger->end_log_line ();
+ }
+ }
+}
+
/* Emit a "path" of events to EMISSION_PATH describing the exploded path
EPATH within EG. */
@@ -1683,10 +1834,12 @@ diagnostic_manager::build_emission_path (const path_builder &pb,
}
/* Walk EPATH, adding events as appropriate. */
- for (unsigned i = 0; i < epath.m_edges.length (); i++)
+ for (unsigned i = 0; i < epath.m_elements.size (); ++i)
{
- const exploded_edge *eedge = epath.m_edges[i];
- add_events_for_eedge (pb, *eedge, emission_path, &interest);
+ const exploded_edge *eedge = epath.m_elements[i].m_eedge;
+ gcc_assert (eedge);
+ add_events_for_eedge (pb, *eedge, emission_path, &interest,
+ epath.m_elements[i].m_state_transition.get ());
}
add_event_on_final_node (pb, epath.get_final_enode (),
emission_path, &interest);
@@ -1895,7 +2048,8 @@ void
diagnostic_manager::add_events_for_eedge (const path_builder &pb,
const exploded_edge &eedge,
checker_path *emission_path,
- interesting_t *interest) const
+ interesting_t *interest,
+ const state_transition *state_trans) const
{
const exploded_node *src_node = eedge.m_src;
const program_point &src_point = src_node->get_point ();
@@ -1913,11 +2067,19 @@ diagnostic_manager::add_events_for_eedge (const path_builder &pb,
src_point.print (pp, format (false));
pp_string (pp, "-> ");
dst_point.print (pp, format (false));
+ if (state_trans)
+ {
+ pp_string (pp, " {");
+ state_trans->dump_to_pp (pp);
+ pp_string (pp, "}");
+ }
get_logger ()->end_log_line ();
}
const program_state &src_state = src_node->get_state ();
const program_state &dst_state = dst_node->get_state ();
+ bool created_event_for_state_trans = false;
+
/* Add state change events for the states that have changed.
We add these before events for superedges, so that if we have a
state_change_event due to following an edge, we'll get this sequence
@@ -1946,11 +2108,15 @@ diagnostic_manager::add_events_for_eedge (const path_builder &pb,
/* Allow non-standard edges to add events, e.g. when rewinding from
longjmp to a setjmp. */
if (eedge.m_custom_info)
- eedge.m_custom_info->add_events_to_path (emission_path, eedge, *pd);
+ {
+ eedge.m_custom_info->add_events_to_path (emission_path, eedge, *pd,
+ state_trans);
+ created_event_for_state_trans = true;
+ }
/* Don't add events for insignificant edges at verbosity levels below 3. */
if (m_verbosity < 3)
- if (!significant_edge_p (pb, eedge))
+ if (!significant_edge_p (pb, eedge) && !state_trans)
return;
/* Add events for operations. */
@@ -1962,7 +2128,11 @@ diagnostic_manager::add_events_for_eedge (const path_builder &pb,
if (dst_point.get_supernode ()->entry_p ())
{
pb.get_pending_diagnostic ()->add_function_entry_event
- (eedge, emission_path);
+ (eedge, emission_path,
+ (state_trans
+ ? state_trans->dyn_cast_state_transition_at_call ()
+ : nullptr));
+ created_event_for_state_trans = true;
/* Create region_creation_events for on-stack regions within
this frame. */
if (interest)
@@ -2028,6 +2198,14 @@ diagnostic_manager::add_events_for_eedge (const path_builder &pb,
}
}
+ /* If we have a state transition and haven't yet created an
+ event that describes it, do so now. */
+ if (state_trans && !created_event_for_state_trans)
+ emission_path->add_event
+ (std::make_unique<state_transition_event>
+ (eedge.m_src->get_point (),
+ state_trans));
+
if (pb.get_feasibility_problem ()
&& &pb.get_feasibility_problem ()->m_eedge == &eedge)
{
@@ -2241,6 +2419,21 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
/* Don't filter these. */
break;
+ case event_kind::state_transition:
+ /* Prune these if they have an empty description. */
+ {
+ tree_dump_pretty_printer pp (nullptr);
+ base_event->print_desc (pp);
+ if (strlen (pp_formatted_text (&pp)) == 0)
+ {
+ log (("filtering event %i:"
+ " state_transition_event with empty description"),
+ idx);
+ path->delete_event (idx);
+ }
+ }
+ break;
+
case event_kind::function_entry:
if (m_verbosity < 1)
{
@@ -101,7 +101,7 @@ public:
}
bool calc_best_epath (epath_finder *pf);
- const exploded_path *get_best_epath () const { return m_best_epath.get (); }
+ exploded_path *get_best_epath () const { return m_best_epath.get (); }
unsigned get_epath_length () const;
void add_duplicate (saved_diagnostic *other);
@@ -201,6 +201,10 @@ private:
const diagnostics::logical_locations::manager &
get_logical_location_manager () const;
+ void
+ annotate_exploded_path (const path_builder &pb,
+ exploded_path &epath) const;
+
void build_emission_path (const path_builder &pb,
const exploded_path &epath,
checker_path *emission_path) const;
@@ -213,7 +217,8 @@ private:
void add_events_for_eedge (const path_builder &pb,
const exploded_edge &eedge,
checker_path *emission_path,
- interesting_t *interest) const;
+ interesting_t *interest,
+ const state_transition *state_trans) const;
bool significant_edge_p (const path_builder &pb,
const exploded_edge &eedge) const;
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see
#include "gimple-predict.h"
#include "context.h"
#include "channels.h"
+#include "pretty-print-markup.h"
#include "text-art/dump.h"
@@ -308,10 +309,10 @@ public:
after the SSA name was set? (if any). */
for (unsigned idx = idx_of_def_stmt + 1;
- idx < epath.m_edges.length ();
+ idx < epath.m_elements.size ();
++idx)
{
- const exploded_edge *eedge = epath.m_edges[idx];
+ const exploded_edge *eedge = epath.m_elements[idx].m_eedge;
if (logger)
logger->log ("eedge[%i]: EN %i -> EN %i",
idx,
@@ -352,10 +353,10 @@ public:
retval = gimple_return_retval (return_stmt);
log_scope sentinel (logger, "walking backward along epath");
- int idx;
- const exploded_edge *eedge;
- FOR_EACH_VEC_ELT_REVERSE (epath.m_edges, idx, eedge)
+ for (int idx = epath.m_elements.size () - 1; idx >= 0; --idx)
{
+ const exploded_path::element_t &element = epath.m_elements[idx];
+ const exploded_edge *eedge = element.m_eedge;
if (logger)
{
logger->log ("eedge[%i]: EN %i -> EN %i",
@@ -403,10 +404,10 @@ private:
{
LOG_SCOPE (logger);
- int idx;
- const exploded_edge *eedge;
- FOR_EACH_VEC_ELT_REVERSE (epath.m_edges, idx, eedge)
+ for (int idx = epath.m_elements.size () - 1; idx >= 0; --idx)
{
+ const exploded_path::element_t &element = epath.m_elements[idx];
+ const exploded_edge *eedge = element.m_eedge;
if (eedge->m_src->get_stack_depth ()
!= eedge->m_dest->get_stack_depth ())
{
@@ -1319,7 +1320,8 @@ public:
void add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &) const final override
+ pending_diagnostic &,
+ const state_transition *) const final override
{
const exploded_node *dst_node = eedge.m_dest;
const program_point &dst_point = dst_node->get_point ();
@@ -1369,7 +1371,8 @@ public:
void add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &) const final override
+ pending_diagnostic &,
+ const state_transition *) const final override
{
const exploded_node *src_node = eedge.m_src;
const program_point &src_point = src_node->get_point ();
@@ -1644,7 +1647,7 @@ void
interprocedural_call::print (pretty_printer *pp) const
{
pp_string (pp, "call to ");
- pp_gimple_stmt_1 (pp, &m_call_stmt, 0, (dump_flags_t)0);
+ pp_gimple_stmt_1 (pp, &get_gcall (), 0, (dump_flags_t)0);
}
void
@@ -1667,16 +1670,70 @@ interprocedural_call::update_model (region_model *model,
const exploded_edge */*eedge*/,
region_model_context *ctxt) const
{
- model->update_for_gcall (m_call_stmt, ctxt, &m_callee_fun);
+ model->update_for_gcall (get_gcall (), ctxt, &m_callee_fun);
return true;
}
void
interprocedural_call::add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &pd) const
+ pending_diagnostic &pd,
+ const state_transition *state_trans) const
+{
+ pd.add_call_event (eedge, get_gcall (), *emission_path,
+ (state_trans
+ ? state_trans->dyn_cast_state_transition_at_call ()
+ : nullptr));
+}
+
+bool
+interprocedural_call::try_to_rewind_data_flow (rewind_context &ctxt) const
+{
+ auto logger = ctxt.m_logger;
+
+ // Rewind from params to arguments
+ if (ctxt.m_input.m_region_holding_value)
+ {
+ const region_model *dst_enode_model
+ = ctxt.m_eedge.m_dest->get_state ().m_region_model;
+ tree dst_tree
+ = dst_enode_model->get_representative_tree
+ (ctxt.m_input.m_region_holding_value);
+ if (dst_tree)
+ {
+ callsite_expr expr;
+ tree src_tree
+ = m_op.map_expr_from_callee_to_caller (m_callee_fun.decl,
+ dst_tree,
+ &expr);
+ if (src_tree)
+ {
+ const region_model *src_enode_model
+ = ctxt.m_eedge.m_src->get_state ().m_region_model;
+ ctxt.m_output.m_region_holding_value
+ = src_enode_model->get_lvalue (src_tree, nullptr);
+
+ ctxt.add_state_transition
+ (std::make_unique<state_transition_at_call> (expr));
+
+ if (logger)
+ {
+ callsite_expr_element e (expr);
+ logger->log ("updating m_region_holding_value from %qE to %qE"
+ " (callsite_expr: %e)",
+ dst_tree, src_tree, &e);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+const gcall &
+interprocedural_call::get_gcall () const
{
- pd.add_call_event (eedge, m_call_stmt, *emission_path);
+ return m_op.get_gcall ();
}
// class interprocedural_return : public custom_edge_info
@@ -1715,7 +1772,8 @@ interprocedural_return::update_model (region_model *model,
void
interprocedural_return::add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &) const
+ pending_diagnostic &,
+ const state_transition *state_trans) const
{
const program_point &dst_point = eedge.m_dest->get_point ();
emission_path->add_event
@@ -1723,7 +1781,29 @@ interprocedural_return::add_events_to_path (checker_path *emission_path,
(eedge,
event_loc_info (m_call_stmt.location,
dst_point.get_fndecl (),
- dst_point.get_stack_depth ())));
+ dst_point.get_stack_depth ()),
+ (state_trans
+ ? state_trans->dyn_cast_state_transition_at_return ()
+ : nullptr)));
+}
+
+bool
+interprocedural_return::try_to_rewind_data_flow (rewind_context &ctxt) const
+{
+ auto logger = ctxt.m_logger;
+
+ tree lhs = gimple_call_lhs (&m_call_stmt);
+ if (!lhs)
+ return true;
+
+ const region_model *src_enode_model
+ = ctxt.m_eedge.m_src->get_state ().m_region_model;
+ tree fndecl = src_enode_model->get_current_function ()->decl;
+ tree fn_result = DECL_RESULT (fndecl);
+
+ ctxt.on_data_flow (DECL_RESULT (fndecl), lhs);
+
+ return true;
}
/* class exploded_edge : public dedge<eg_traits>. */
@@ -2311,7 +2391,8 @@ public:
void add_events_to_path (checker_path *emission_path,
const exploded_edge &,
- pending_diagnostic &) const final override
+ pending_diagnostic &,
+ const state_transition *) const final override
{
emission_path->add_event
(std::make_unique<tainted_args_function_custom_event>
@@ -2767,7 +2848,8 @@ public:
void add_events_to_path (checker_path *emission_path,
const exploded_edge &,
- pending_diagnostic &) const final override
+ pending_diagnostic &,
+ const state_transition *) const final override
{
/* Show the field in the struct declaration, e.g.
"(1) field 'store' is marked with '__attribute__((tainted_args))'" */
@@ -39,6 +39,10 @@ struct event_loc_info
int m_depth;
};
+extern event_loc_info
+event_loc_info_for_function_entry (const program_point &point,
+ const state_transition_at_call *state_trans);
+
} // namespace ana
#endif /* GCC_ANALYZER_EVENT_LOC_INFO_H */
@@ -381,9 +381,9 @@ private:
class interprocedural_call : public custom_edge_info
{
public:
- interprocedural_call (const gcall &call_stmt,
+ interprocedural_call (const call_and_return_op &op,
function &callee_fun)
- : m_call_stmt (call_stmt),
+ : m_op (op),
m_callee_fun (callee_fun)
{}
@@ -402,10 +402,16 @@ public:
void add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &pd) const final override;
+ pending_diagnostic &pd,
+ const state_transition *state_trans) const final override;
+
+ bool
+ try_to_rewind_data_flow (rewind_context &) const final override;
+
+ const gcall &get_gcall () const;
private:
- const gcall &m_call_stmt;
+ const call_and_return_op &m_op;
function &m_callee_fun;
};
@@ -434,7 +440,11 @@ public:
void add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &pd) const final override;
+ pending_diagnostic &pd,
+ const state_transition *state_trans) const final override;
+
+ bool
+ try_to_rewind_data_flow (rewind_context &) const final override;
private:
const gcall &m_call_stmt;
@@ -463,7 +473,8 @@ public:
void add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &pd) const final override;
+ pending_diagnostic &pd,
+ const state_transition *state_trans) const final override;
program_point
get_point_before_setjmp () const
@@ -26,19 +26,30 @@ along with GCC; see the file COPYING3. If not see
namespace ana {
-/* class exploded_path. */
+// struct diagnostic_state
-/* Copy ctor. */
+void
+diagnostic_state::dump_to_pp (pretty_printer *pp) const
+{
+ pp_printf (pp, "%s: {", m_debug_desc.c_str ());
+ if (m_region_holding_value)
+ {
+ pp_string (pp, "region holding value: ");
+ m_region_holding_value->dump_to_pp (pp, false);
+ }
+ pp_string (pp, "}");
+}
-exploded_path::exploded_path (const exploded_path &other)
-: m_edges (other.m_edges.length ())
+void
+diagnostic_state::dump () const
{
- int i;
- const exploded_edge *eedge;
- FOR_EACH_VEC_ELT (other.m_edges, i, eedge)
- m_edges.quick_push (eedge);
+ tree_dump_pretty_printer pp (stderr);
+ dump_to_pp (&pp);
+ pp_newline (&pp);
}
+/* class exploded_path. */
+
/* Look for the last use of SEARCH_STMT within this path.
If found write the edge's index to *OUT_IDX and return true, otherwise
return false. */
@@ -47,31 +58,33 @@ bool
exploded_path::find_stmt_backwards (const gimple *search_stmt,
int *out_idx) const
{
- int i;
- const exploded_edge *eedge;
- FOR_EACH_VEC_ELT_REVERSE (m_edges, i, eedge)
- if (search_stmt->code == GIMPLE_PHI)
- {
- /* Each phis_for_edge_op instance handles multiple phi stmts
- at once, so we have to special-case the search for a phi stmt. */
- if (auto op = eedge->maybe_get_op ())
- if (auto phis_op = op->dyn_cast_phis_for_edge_op ())
- if (phis_op->defines_ssa_name_p (gimple_phi_result (search_stmt)))
+ for (int i = m_elements.size () - 1; i >= 0; --i)
+ {
+ const element_t *element = &m_elements[i];
+ const exploded_edge *eedge = element->m_eedge;
+ if (search_stmt->code == GIMPLE_PHI)
+ {
+ /* Each phis_for_edge_op instance handles multiple phi stmts
+ at once, so we have to special-case the search for a phi stmt. */
+ if (auto op = eedge->maybe_get_op ())
+ if (auto phis_op = op->dyn_cast_phis_for_edge_op ())
+ if (phis_op->defines_ssa_name_p (gimple_phi_result (search_stmt)))
+ {
+ *out_idx = i;
+ return true;
+ }
+ }
+ else
+ {
+ /* Non-phi stmt. */
+ if (const gimple *stmt = eedge->maybe_get_stmt ())
+ if (stmt == search_stmt)
{
*out_idx = i;
return true;
}
- }
- else
- {
- /* Non-phi stmt. */
- if (const gimple *stmt = eedge->maybe_get_stmt ())
- if (stmt == search_stmt)
- {
- *out_idx = i;
- return true;
- }
- }
+ }
+ }
return false;
}
@@ -80,8 +93,8 @@ exploded_path::find_stmt_backwards (const gimple *search_stmt,
exploded_node *
exploded_path::get_final_enode () const
{
- gcc_assert (m_edges.length () > 0);
- return m_edges[m_edges.length () - 1]->m_dest;
+ gcc_assert (m_elements.size () > 0);
+ return m_elements.back ().m_eedge->m_dest;
}
/* Check state along this path, returning true if it is feasible.
@@ -99,9 +112,9 @@ exploded_path::feasible_p (logger *logger,
eg->get_supergraph ());
/* Traverse the path, updating this state. */
- for (unsigned edge_idx = 0; edge_idx < m_edges.length (); edge_idx++)
+ for (unsigned edge_idx = 0; edge_idx < m_elements.size (); ++edge_idx)
{
- const exploded_edge *eedge = m_edges[edge_idx];
+ const exploded_edge *eedge = m_elements[edge_idx].m_eedge;
if (logger)
logger->log ("considering edge %i: EN:%i -> EN:%i",
edge_idx,
@@ -140,13 +153,20 @@ void
exploded_path::dump_to_pp (pretty_printer *pp,
const extrinsic_state *ext_state) const
{
- for (unsigned i = 0; i < m_edges.length (); i++)
+ for (unsigned i = 0; i < m_elements.size (); ++i)
{
- const exploded_edge *eedge = m_edges[i];
- pp_printf (pp, "m_edges[%i]: EN %i -> EN %i",
+ const element_t &element = m_elements[i];
+ const exploded_edge *eedge = element.m_eedge;
+ pp_printf (pp, "m_elements[%i]: EN %i -> EN %i",
i,
eedge->m_src->m_index,
eedge->m_dest->m_index);
+ if (element.m_state_transition)
+ {
+ pp_string (pp, " {");
+ element.m_state_transition->dump_to_pp (pp);
+ pp_string (pp, "}");
+ }
pp_newline (pp);
if (ext_state)
@@ -188,6 +208,49 @@ exploded_path::dump_to_file (const char *filename,
fclose (fp);
}
+/* Print a multiline form of this path to LOGGER, prefixing it with DESC. */
+
+void
+exploded_path::maybe_log (logger *logger, const char *desc) const
+{
+ if (!logger)
+ return;
+ logger->start_log_line ();
+ logger->log_partial ("%s: ", desc);
+ logger->end_log_line ();
+ for (unsigned idx = 0; idx < m_elements.size (); idx++)
+ {
+ const exploded_edge &eedge = *m_elements[idx].m_eedge;
+ const exploded_node *src_node = eedge.m_src;
+ const program_point &src_point = src_node->get_point ();
+ const exploded_node *dst_node = eedge.m_dest;
+ const program_point &dst_point = dst_node->get_point ();
+
+ pretty_printer *pp = logger->get_printer ();
+ logger->start_log_line ();
+ pp_printf (pp, " [%i] EN %i -> EN %i: ",
+ idx,
+ src_node->m_index,
+ dst_node->m_index);
+ src_point.print (pp, format (false));
+ pp_string (pp, " -> ");
+ dst_point.print (pp, format (false));
+ if (auto state_trans = m_elements[idx].m_state_transition.get ())
+ {
+ pp_string (pp, " {");
+ state_trans->dump_to_pp (pp);
+ pp_string (pp, "}");
+ }
+ logger->end_log_line ();
+ }
+}
+
+void
+exploded_path::reverse ()
+{
+ std::reverse (m_elements.begin (), m_elements.end ());
+}
+
} // namespace ana
#endif /* #if ENABLE_ANALYZER */
@@ -22,6 +22,8 @@ along with GCC; see the file COPYING3. If not see
#define GCC_ANALYZER_EXPLODED_PATH_H
#include "analyzer/exploded-graph.h"
+#include "analyzer/checker-event.h"
+#include "analyzer/state-transition.h"
namespace ana {
@@ -30,10 +32,45 @@ namespace ana {
class exploded_path
{
public:
- exploded_path () : m_edges () {}
- exploded_path (const exploded_path &other);
-
- unsigned length () const { return m_edges.length (); }
+ struct element_t
+ {
+ element_t (const exploded_edge *eedge)
+ : m_eedge (eedge)
+ {
+ }
+ element_t (const element_t &other)
+ : m_eedge (other.m_eedge),
+ m_state_at_src (other.m_state_at_src),
+ m_state_at_dst (other.m_state_at_dst),
+ m_state_transition (nullptr)
+ {
+ if (other.m_state_transition)
+ m_state_transition = other.m_state_transition->clone ();
+ }
+
+ element_t (element_t &&other) = default;
+
+ element_t &operator= (const element_t &other)
+ {
+ m_eedge = other.m_eedge;
+ m_state_at_src = other.m_state_at_src;
+ m_state_at_dst = other.m_state_at_dst;
+ m_state_transition = (other.m_state_transition
+ ? other.m_state_transition->clone ()
+ : nullptr);
+ return *this;
+ }
+
+ const exploded_edge *m_eedge;
+ diagnostic_state m_state_at_src;
+ diagnostic_state m_state_at_dst;
+ std::unique_ptr<state_transition> m_state_transition;
+ };
+
+ exploded_path () = default;
+ exploded_path (const exploded_path &other) = default;
+
+ unsigned length () const { return m_elements.size (); }
bool find_stmt_backwards (const gimple *search_stmt,
int *out_idx) const;
@@ -47,10 +84,21 @@ public:
void dump_to_file (const char *filename,
const extrinsic_state &ext_state) const;
+ void maybe_log (logger *logger, const char *desc) const;
+
bool feasible_p (logger *logger, std::unique_ptr<feasibility_problem> *out,
engine *eng, const exploded_graph *eg) const;
- auto_vec<const exploded_edge *> m_edges;
+ void
+ append_edge (const exploded_edge *edge)
+ {
+ m_elements.push_back (edge);
+ }
+
+ void
+ reverse ();
+
+ std::vector<element_t> m_elements;
};
/* Finding the shortest exploded_path within an exploded_graph. */
@@ -187,12 +187,12 @@ feasible_graph::make_epath (feasible_node *fnode) const
gcc_assert (fnode->m_preds.length () == 1);
feasible_edge *pred_fedge
= static_cast <feasible_edge *> (fnode->m_preds[0]);
- epath->m_edges.safe_push (pred_fedge->get_inner_edge ());
+ epath->m_elements.push_back (pred_fedge->get_inner_edge ());
fnode = static_cast <feasible_node *> (pred_fedge->m_src);
}
/* Now reverse it. */
- epath->m_edges.reverse ();
+ epath->reverse ();
return epath;
}
@@ -98,7 +98,8 @@ public:
void
add_function_entry_event (const exploded_edge &eedge,
- checker_path *emission_path) final override
+ checker_path *emission_path,
+ const state_transition_at_call *state_trans) final override
{
/* Subclass of function_entry_event for use when reporting both
the initial and subsequent entries to the function of interest,
@@ -111,7 +112,9 @@ public:
const program_state &dst_state,
const infinite_recursion_diagnostic &pd,
bool topmost)
- : function_entry_event (dst_point, dst_state),
+ : function_entry_event (event_loc_info (dst_point),
+ dst_state,
+ nullptr),
m_pd (pd),
m_topmost (topmost)
{
@@ -161,7 +164,8 @@ public:
(std::make_unique<recursive_function_entry_event>
(dst_point, dst_node->get_state (), *this, true));
else
- pending_diagnostic::add_function_entry_event (eedge, emission_path);
+ pending_diagnostic::add_function_entry_event (eedge, emission_path,
+ state_trans);
}
/* Customize the location where the warning_event appears, putting
@@ -38,6 +38,8 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/call-summary.h"
#include "analyzer/call-info.h"
#include "analyzer/analysis-plan.h"
+#include "analyzer/callsite-expr.h"
+#include "analyzer/state-transition.h"
#if ENABLE_ANALYZER
@@ -66,6 +68,25 @@ event_loc_info::event_loc_info (const program_point &point)
m_depth = point.get_stack_depth ();
}
+/* Make an event_loc_info suitable for a function_entry_event at POINT.
+ If STATE_TRANS is non-null, then try to extract the pertinent parameter
+ from it and use the location of that parameter, rather than that of the
+ function name. */
+
+event_loc_info
+event_loc_info_for_function_entry (const program_point &point,
+ const state_transition_at_call *state_trans)
+{
+ event_loc_info result (point);
+ if (state_trans)
+ {
+ callsite_expr expr = state_trans->get_callsite_expr ();
+ expr.maybe_get_param_location (point.get_fndecl (),
+ &result.m_loc);
+ }
+ return result;
+}
+
// struct operation_context
void
@@ -190,6 +211,56 @@ private:
} m_path_context;
};
+// struct rewind_context
+
+void
+rewind_context::on_data_origin (tree dst_tree)
+{
+ gcc_assert (dst_tree);
+ const region_model *dst_enode_model
+ = m_eedge.m_dest->get_state ().m_region_model;
+ const region *dst_reg_in_dst_enode
+ = dst_enode_model->get_lvalue (dst_tree, nullptr);
+ if (m_input.m_region_holding_value == dst_reg_in_dst_enode)
+ {
+ if (m_logger)
+ m_logger->log ("data origin, into %qE", dst_tree);
+ m_output.m_region_holding_value = nullptr;
+ add_state_transition
+ (std::make_unique<state_transition_origin> (dst_tree));
+ }
+}
+
+void
+rewind_context::on_data_flow (tree src_tree, tree dst_tree)
+{
+ gcc_assert (src_tree);
+ gcc_assert (dst_tree);
+ const region_model *dst_enode_model
+ = m_eedge.m_dest->get_state ().m_region_model;
+ const region *dst_reg_in_dst_enode
+ = dst_enode_model->get_lvalue (dst_tree, nullptr);
+ if (m_input.m_region_holding_value == dst_reg_in_dst_enode)
+ {
+ if (m_logger)
+ m_logger->log ("rewinding from %qE to %qE", dst_tree, src_tree);
+ const region_model *src_enode_model
+ = m_eedge.m_src->get_state ().m_region_model;
+ const region *src_reg_in_src_enode
+ = src_enode_model->get_lvalue (src_tree, nullptr);
+ m_output.m_region_holding_value = src_reg_in_src_enode;
+
+ if (TREE_CODE (src_tree) == RESULT_DECL)
+ add_state_transition (std::make_unique<state_transition_at_return> ());
+ else if (auto state_trans
+ = state_transition::make (m_output.m_region_holding_value,
+ src_tree,
+ m_input.m_region_holding_value,
+ dst_tree))
+ add_state_transition (std::move (state_trans));
+ }
+}
+
// class gimple_stmt_op : public operation
void
@@ -663,8 +734,58 @@ gimple_stmt_op::add_any_events_for_eedge (const exploded_edge &eedge,
}
}
+// class gasm_op : public gimple_stmt_op
+
// class gassign_op : public gimple_stmt_op
+bool
+gassign_op::try_to_rewind_data_flow (rewind_context &ctxt) const
+{
+ auto logger = ctxt.m_logger;
+ LOG_SCOPE (logger);
+ if (logger)
+ {
+ logger->start_log_line ();
+ pp_gimple_stmt_1 (logger->get_printer (), &get_stmt (), 0,
+ (dump_flags_t)0);
+ logger->end_log_line ();
+ }
+
+ const gassign &assign = get_gassign ();
+ tree lhs = gimple_assign_lhs (&assign);
+
+ if (!ctxt.could_be_affected_by_write_p (lhs))
+ return true;
+
+ tree rhs1 = gimple_assign_rhs1 (&assign);
+ enum tree_code op = gimple_assign_rhs_code (&assign);
+
+ switch (op)
+ {
+ default:
+ return false;
+
+ case NOP_EXPR:
+ case SSA_NAME:
+ case VAR_DECL:
+ case PARM_DECL:
+ case COMPONENT_REF:
+ ctxt.on_data_flow (rhs1, lhs);
+ break;
+
+ case INTEGER_CST:
+ case REAL_CST:
+ if (logger)
+ logger->log ("value comes from here");
+ ctxt.on_data_origin (lhs);
+ break;
+ }
+
+ return true;
+}
+
+// class predict_op : public gimple_stmt_op
+
// class greturn_op : public gimple_stmt_op
void
@@ -734,6 +855,24 @@ greturn_op::add_any_events_for_eedge (const exploded_edge &,
// No-op.
}
+
+bool
+greturn_op::try_to_rewind_data_flow (rewind_context &ctxt) const
+{
+ auto logger = ctxt.m_logger;
+ LOG_SCOPE (logger);
+
+ if (get_retval ())
+ {
+ const region_model *src_enode_model
+ = ctxt.m_eedge.m_src->get_state ().m_region_model;
+ tree fndecl = src_enode_model->get_current_function ()->decl;
+ ctxt.on_data_flow (get_retval (), DECL_RESULT (fndecl));
+ }
+
+ return true;
+}
+
// class call_and_return_op : public gimple_stmt_op
std::unique_ptr<operation>
@@ -839,7 +978,7 @@ call_and_return_op::execute (operation_context &op_ctxt) const
const program_point dst_point
(callee_entry_snode, *dst_call_string);
auto edge_info
- = std::make_unique<interprocedural_call> (get_gcall (),
+ = std::make_unique<interprocedural_call> (*this,
*callee_fun);
edge_info->update_state (&dst_state, nullptr, &ctxt);
op_ctxt.add_outcome (dst_point, dst_state, false, nullptr,
@@ -1023,6 +1162,13 @@ replay_call_summaries (operation_context &op_ctxt,
}
}
+bool
+call_and_return_op::try_to_rewind_data_flow (rewind_context &ctxt) const
+{
+ LOG_SCOPE (ctxt.m_logger);
+ return true;
+}
+
/* A concrete call_info subclass representing a replay of a call summary. */
class call_summary_edge_info : public call_info
@@ -2350,6 +2496,16 @@ phis_for_edge_op::add_any_events_for_eedge (const exploded_edge &,
// No-op
}
+bool
+phis_for_edge_op::try_to_rewind_data_flow (rewind_context &ctxt) const
+{
+ auto logger = ctxt.m_logger;
+ LOG_SCOPE (logger);
+ for (auto iter : m_pairs)
+ ctxt.on_data_flow (iter.m_src, iter.m_dst);
+ return true;
+}
+
// class resx_op : public gimple_stmt_op
void
@@ -74,6 +74,36 @@ struct operation_context
const superedge &m_sedge;
};
+struct rewind_context
+{
+ rewind_context (const exploded_edge &eedge,
+ logger *logger,
+ diagnostic_state input_state)
+ : m_eedge (eedge),
+ m_logger (logger),
+ m_input (input_state),
+ m_output (input_state)
+ {
+ }
+
+ void
+ on_data_origin (tree dst);
+
+ void
+ on_data_flow (tree src, tree dst);
+
+ virtual bool
+ could_be_affected_by_write_p (tree lhs) = 0;
+
+ virtual void
+ add_state_transition (std::unique_ptr<state_transition>) = 0;
+
+ const exploded_edge &m_eedge;
+ logger *m_logger;
+ diagnostic_state m_input;
+ diagnostic_state m_output;
+};
+
/* Abstract base class for an operation along a superedge. */
class operation
@@ -163,6 +193,12 @@ class operation
enum kind get_kind () const { return m_kind; }
+ virtual bool
+ try_to_rewind_data_flow (rewind_context &) const
+ {
+ return false;
+ }
+
protected:
operation (enum kind kind_)
: m_kind (kind_)
@@ -279,6 +315,9 @@ public:
{
return *as_a <const gassign *> (&get_stmt ());
}
+
+ bool
+ try_to_rewind_data_flow (rewind_context &ctxt) const final override;
};
/* An operation subclass for a GIMPLE_PREDICT stmt.
@@ -300,6 +339,12 @@ public:
{
return std::make_unique<predict_op> (get_stmt ());
}
+
+ bool
+ try_to_rewind_data_flow (rewind_context &) const final override
+ {
+ return true;
+ }
};
/* An operation subclass representing both:
@@ -350,6 +395,9 @@ public:
{
return gimple_return_retval (&get_greturn ());
}
+
+ bool
+ try_to_rewind_data_flow (rewind_context &ctxt) const final override;
};
/* A concrete operation subclass representing the effect of a GIMPLE_CALL stmt.
@@ -428,6 +476,9 @@ public:
const known_function *
maybe_get_known_function (const call_details &cd) const;
+ bool
+ try_to_rewind_data_flow (rewind_context &ctxt) const final override;
+
private:
cgraph_edge *
get_any_cgraph_edge (operation_context &op_ctxt) const;
@@ -638,6 +689,12 @@ public:
const gimple &get_ctrlflow_stmt () const { return m_ctrlflow_stmt; }
+ bool
+ try_to_rewind_data_flow (rewind_context &) const final override
+ {
+ return true;
+ }
+
protected:
control_flow_op (enum kind kind_,
::edge cfg_edge,
@@ -983,6 +1040,9 @@ public:
add_any_events_for_eedge (const exploded_edge &eedge,
checker_path &out_path) const final override;
+ bool
+ try_to_rewind_data_flow (rewind_context &ctxt) const final override;
+
const std::vector<pair> &get_pairs () const { return m_pairs; }
private:
@@ -58,6 +58,15 @@ interesting_t::add_region_creation (const region *reg)
m_region_creation.safe_push (reg);
}
+/* Mark the value read from REG as being interesting. */
+
+void
+interesting_t::add_read_region (const region *reg, std::string debug_desc)
+{
+ gcc_assert (reg);
+ m_read_regions.push_back (diagnostic_state (std::move (debug_desc), reg));
+}
+
void
interesting_t::dump_to_pp (pretty_printer *pp, bool simple) const
{
@@ -70,6 +79,14 @@ interesting_t::dump_to_pp (pretty_printer *pp, bool simple) const
pp_string (pp, ", ");
reg->dump_to_pp (pp, simple);
}
+ pp_string (pp, "], read regions: [");
+ for (i = 0; i < m_read_regions.size (); ++i)
+ {
+ auto &ann = m_read_regions[i];
+ if (i > 0)
+ pp_string (pp, ", ");
+ ann.dump_to_pp (pp);
+ }
pp_string (pp, "]}");
}
@@ -199,14 +216,21 @@ pending_diagnostic::fixup_location (location_t loc, bool) const
void
pending_diagnostic::add_function_entry_event (const exploded_edge &eedge,
- checker_path *emission_path)
+ checker_path *emission_path,
+ const state_transition_at_call *state_trans)
{
const exploded_node *dst_node = eedge.m_dest;
const program_point &dst_point = dst_node->get_point ();
const program_state &dst_state = dst_node->get_state ();
+
+ /* If we have STATE_TRANS with a specific param, put the event on
+ that parameter, otherwise put in on the function name. */
+ auto loc_info {event_loc_info_for_function_entry (dst_point, state_trans)};
+
emission_path->add_event
- (std::make_unique<function_entry_event> (dst_point,
- dst_state));
+ (std::make_unique<function_entry_event> (loc_info,
+ dst_state,
+ state_trans));
}
/* Base implementation of pending_diagnostic::add_call_event.
@@ -215,11 +239,13 @@ pending_diagnostic::add_function_entry_event (const exploded_edge &eedge,
void
pending_diagnostic::add_call_event (const exploded_edge &eedge,
const gcall &,
- checker_path &emission_path)
+ checker_path &emission_path,
+ const state_transition_at_call *state_trans)
{
emission_path.add_event
(std::make_unique<call_event> (eedge,
- event_loc_info (eedge.m_src)));
+ event_loc_info (eedge.m_src),
+ state_trans));
}
/* Base implementation of pending_diagnostic::add_region_creation_events.
@@ -23,23 +23,31 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostics/metadata.h"
#include "analyzer/sm.h"
+#include "analyzer/state-transition.h"
namespace ana {
/* A bundle of information about things that are of interest to a
- pending_diagnostic.
+ pending_diagnostic:
- For now, merely the set of regions that are pertinent to the
+ * a set of regions that are pertinent to the
diagnostic, so that we can notify the user about when they
- were created. */
+ were created.
+
+ * a set of regions that a pertinent value for the diagnostic was
+ read from, so that we can notify the user about where those values
+ came from. */
struct interesting_t
{
void add_region_creation (const region *reg);
+ void add_read_region (const region *reg, std::string debug_desc);
+
void dump_to_pp (pretty_printer *pp, bool simple) const;
auto_vec<const region *> m_region_creation;
+ std::vector<diagnostic_state> m_read_regions;
};
/* Various bundles of information used for generating more precise
@@ -74,23 +82,42 @@ struct state_change
const state_change_event &m_event;
};
+/* For use by pending_diagnostic::describe_origin_of_state. */
+
+struct origin_of_state
+{
+ origin_of_state (tree dst_reg_expr)
+ : m_dst_reg_expr (dst_reg_expr)
+ {
+ gcc_assert (m_dst_reg_expr);
+ }
+
+ tree m_dst_reg_expr;
+};
+
/* For use by pending_diagnostic::describe_call_with_state. */
struct call_with_state
{
call_with_state (tree caller_fndecl, tree callee_fndecl,
- tree expr, state_machine::state_t state)
+ tree expr, state_machine::state_t state,
+ const state_transition_at_call *state_trans)
: m_caller_fndecl (caller_fndecl),
m_callee_fndecl (callee_fndecl),
m_expr (expr),
- m_state (state)
+ m_state (state),
+ m_state_trans (state_trans)
{
+ if (state_trans)
+ m_src_event_id = state_trans->get_src_event_id ();
}
tree m_caller_fndecl;
tree m_callee_fndecl;
tree m_expr;
state_machine::state_t m_state;
+ const state_transition_at_call *m_state_trans;
+ diagnostics::paths::event_id_t m_src_event_id;
};
/* For use by pending_diagnostic::describe_return_of_state. */
@@ -98,16 +125,58 @@ struct call_with_state
struct return_of_state
{
return_of_state (tree caller_fndecl, tree callee_fndecl,
- state_machine::state_t state)
+ state_machine::state_t state,
+ const state_transition_at_return *state_trans)
: m_caller_fndecl (caller_fndecl),
m_callee_fndecl (callee_fndecl),
- m_state (state)
+ m_state (state),
+ m_state_trans (state_trans)
{
+ if (state_trans)
+ m_src_event_id = state_trans->get_src_event_id ();
}
tree m_caller_fndecl;
tree m_callee_fndecl;
state_machine::state_t m_state;
+ const state_transition_at_return *m_state_trans;
+ diagnostics::paths::event_id_t m_src_event_id;
+};
+
+/* For use by pending_diagnostic::describe_copy_of_state. */
+
+struct copy_of_state
+{
+ copy_of_state (tree src_reg_expr,
+ diagnostics::paths::event_id_t src_event_id,
+ tree dst_reg_expr)
+ : m_src_reg_expr (src_reg_expr),
+ m_src_event_id (src_event_id),
+ m_dst_reg_expr (dst_reg_expr)
+ {
+ gcc_assert (m_src_reg_expr);
+ gcc_assert (m_dst_reg_expr);
+ }
+
+ tree m_src_reg_expr;
+ diagnostics::paths::event_id_t m_src_event_id;
+ tree m_dst_reg_expr;
+};
+
+/* For use by pending_diagnostic::describe_use_of_state. */
+
+struct use_of_state
+{
+ use_of_state (tree src_reg_expr,
+ diagnostics::paths::event_id_t src_event_id)
+ : m_src_reg_expr (src_reg_expr),
+ m_src_event_id (src_event_id)
+ {
+ gcc_assert (m_src_reg_expr);
+ }
+
+ tree m_src_reg_expr;
+ diagnostics::paths::event_id_t m_src_event_id;
};
/* For use by pending_diagnostic::describe_final_event. */
@@ -268,6 +337,20 @@ class pending_diagnostic
return diagnostics::paths::event::meaning ();
}
+ /* Precision-of-wording vfunc for use in describing state_transition_event
+ instances of state_transition::kind::origin.
+ Return true if a description of the event was printed to the
+ pretty-printer, or false otherwise.
+ For example, a divide-by-zero diagnostic might use:
+ "zero value originates here"
+ at the point where the zero comes from. */
+ virtual bool describe_origin_of_state (pretty_printer &,
+ const evdesc::origin_of_state &)
+ {
+ /* Default no-op implementation. */
+ return false;
+ }
+
/* Precision-of-wording vfunc for describing an interprocedural call
carrying critial state for the diagnostic, from caller to callee.
@@ -299,6 +382,32 @@ class pending_diagnostic
return false;
}
+ /* Precision-of-wording vfunc for use in describing state_transition_event
+ instances of state_transition::kind::copy.
+ Return true if a description of the event was printed to the
+ pretty-printer, or false otherwise.
+ For example, a divide-by-zero diagnostic might use:
+ "copying zero value from (3) from 'x' to 'y'". */
+ virtual bool describe_copy_of_state (pretty_printer &,
+ const evdesc::copy_of_state &)
+ {
+ /* Default no-op implementation. */
+ return false;
+ }
+
+ /* Precision-of-wording vfunc for use in describing state_transition_event
+ instances of state_transition::kind::use.
+ Return true if a description of the event was printed to the
+ pretty-printer, or false otherwise.
+ For example, a divide-by-zero diagnostic might use:
+ "using zero value from (7) from 'y'". */
+ virtual bool describe_use_of_state (pretty_printer &,
+ const evdesc::use_of_state &)
+ {
+ /* Default no-op implementation. */
+ return false;
+ }
+
/* Precision-of-wording vfunc for describing the final event within a
diagnostic path.
@@ -322,7 +431,8 @@ class pending_diagnostic
virtual void
add_function_entry_event (const exploded_edge &eedge,
- checker_path *emission_path);
+ checker_path *emission_path,
+ const state_transition_at_call *state_trans);
/* Vfunc for extending/overriding creation of the events for an
exploded_edge, allowing for custom events to be created that are
@@ -343,7 +453,8 @@ class pending_diagnostic
the variadic arguments. */
virtual void add_call_event (const exploded_edge &,
const gcall &call_stmt,
- checker_path &emission_path);
+ checker_path &emission_path,
+ const state_transition_at_call *state_trans);
/* Vfunc for adding any events for the creation of regions identified
by the mark_interesting_stuff vfunc.
@@ -155,7 +155,10 @@ public:
void mark_interesting_stuff (interesting_t *interest) final override
{
if (m_src_region)
- interest->add_region_creation (m_src_region);
+ {
+ interest->add_region_creation (m_src_region);
+ interest->add_read_region (m_src_region, "poisoned value");
+ }
}
/* Attempt to suppress false positives.
@@ -69,6 +69,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/feasible-graph.h"
#include "analyzer/record-layout.h"
#include "analyzer/function-set.h"
+#include "analyzer/state-transition.h"
#if ENABLE_ANALYZER
@@ -855,12 +856,57 @@ private:
const region *m_base_reg_b;
};
+/* Locate the parameter with the given index within FNDECL.
+ ARGNUM is zero based, -1 indicates the `this' argument of a method.
+ Return the location of the FNDECL itself if there are problems. */
+
+bool
+callsite_expr::maybe_get_param_location (tree fndecl,
+ location_t *out_loc) const
+{
+ gcc_assert (fndecl);
+
+ if (DECL_ARTIFICIAL (fndecl))
+ return false;
+
+ tree param = get_param_tree (fndecl);
+ if (!param)
+ return false;
+
+ *out_loc = DECL_SOURCE_LOCATION (param);
+ return true;
+}
+
+/* If this callsite_expr refers to a parameter, get the PARM_DECL from
+ FNDECL.
+ Return NULL_TREE on any problems. */
+
+tree
+callsite_expr::get_param_tree (tree fndecl) const
+{
+ if (!param_p ())
+ return NULL_TREE;
+
+ int i;
+ tree param;
+
+ /* Locate param by index within DECL_ARGUMENTS (fndecl). */
+ for (i = 1, param = DECL_ARGUMENTS (fndecl);
+ i < param_num () && param;
+ i++, param = TREE_CHAIN (param))
+ ;
+
+ return param;
+}
+
class div_by_zero_diagnostic
: public pending_diagnostic_subclass<div_by_zero_diagnostic>
{
public:
- div_by_zero_diagnostic (const gassign *assign)
- : m_assign (assign)
+ div_by_zero_diagnostic (const gassign *assign,
+ const region *divisor_reg)
+ : m_assign (assign),
+ m_divisor_reg (divisor_reg)
{}
const char *get_kind () const final override
@@ -891,8 +937,142 @@ public:
return true;
}
+ void
+ mark_interesting_stuff (interesting_t *interest)
+ {
+ interest->add_read_region (m_divisor_reg, "divisor zero value");
+ }
+
+ void
+ add_function_entry_event (const exploded_edge &eedge,
+ checker_path *emission_path,
+ const state_transition_at_call *state_trans)
+ {
+ class custom_function_entry_event : public function_entry_event
+ {
+ public:
+ custom_function_entry_event (const event_loc_info &loc_info,
+ const program_state &state,
+ const state_transition_at_call *state_trans)
+ : function_entry_event (loc_info,
+ state,
+ state_trans)
+ {
+ }
+
+ void print_desc (pretty_printer &pp) const override
+ {
+ if (auto state_trans = get_state_transition_at_call ())
+ {
+ auto expr = state_trans->get_callsite_expr ();
+ if (tree parm = expr.get_param_tree (m_effective_fndecl))
+ {
+ auto src_event_id = state_trans->get_src_event_id ();
+ if (src_event_id.known_p ())
+ pp_printf (&pp, "entry to %qE with zero from %@ for %qE",
+ m_effective_fndecl,
+ &src_event_id,
+ parm);
+ else
+ pp_printf (&pp, "entry to %qE with zero for %qE",
+ m_effective_fndecl, parm);
+ return;
+ }
+ }
+ return function_entry_event::print_desc (pp);
+ }
+ };
+
+ const exploded_node *dst_node = eedge.m_dest;
+ const program_point &dst_point = dst_node->get_point ();
+ const program_state &dst_state = dst_node->get_state ();
+ auto loc_info {event_loc_info_for_function_entry (dst_point, state_trans)};
+ emission_path->add_event
+ (std::make_unique<custom_function_entry_event> (loc_info,
+ dst_state,
+ state_trans));
+ }
+
+ bool
+ describe_origin_of_state (pretty_printer &pp,
+ const evdesc::origin_of_state &) final override
+ {
+ pp_printf (&pp, "zero value originates here");
+ return true;
+ }
+
+ bool
+ describe_call_with_state (pretty_printer &pp,
+ const evdesc::call_with_state &evd) final override
+ {
+ if (evd.m_state_trans)
+ {
+ callsite_expr expr = evd.m_state_trans->get_callsite_expr ();
+ if (expr.param_p ())
+ {
+ if (evd.m_src_event_id.known_p ())
+ pp_printf (&pp, "passing zero from %@ from %qE to %qE via parameter %i",
+ &evd.m_src_event_id,
+ evd.m_caller_fndecl,
+ evd.m_callee_fndecl,
+ expr.param_num ());
+ else
+ pp_printf (&pp, "passing zero from %qE to %qE via parameter %i",
+ evd.m_caller_fndecl,
+ evd.m_callee_fndecl,
+ expr.param_num ());
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool
+ describe_return_of_state (pretty_printer &pp,
+ const evdesc::return_of_state &evd) final override
+ {
+ if (evd.m_src_event_id.known_p ())
+ pp_printf (&pp, "returning zero from %@ from %qE here",
+ &evd.m_src_event_id,
+ evd.m_callee_fndecl);
+ else
+ pp_printf (&pp, "returning zero from %qE here",
+ evd.m_callee_fndecl);
+ return true;
+ }
+
+ bool
+ describe_copy_of_state (pretty_printer &pp,
+ const evdesc::copy_of_state &evd) final override
+ {
+ if (evd.m_src_event_id.known_p ())
+ pp_printf (&pp, "copying zero value from %@ from %qE to %qE",
+ &evd.m_src_event_id,
+ evd.m_src_reg_expr, evd.m_dst_reg_expr);
+ else
+ pp_printf (&pp, "copying zero value from %qE to %qE",
+ evd.m_src_reg_expr, evd.m_dst_reg_expr);
+ return true;
+ }
+
+ bool
+ describe_use_of_state (pretty_printer &pp,
+ const evdesc::use_of_state &evd) final override
+ {
+ if (evd.m_src_event_id.known_p ())
+ pp_printf (&pp, "using zero value from %@ from %qE",
+ &evd.m_src_event_id,
+ evd.m_src_reg_expr);
+ else
+ pp_printf (&pp, "using zero value from %qE",
+ evd.m_src_reg_expr);
+ return true;
+ }
+
private:
const gassign *m_assign;
+ const region *m_divisor_reg;
};
/* Check the pointer subtraction SVAL_A - SVAL_B at ASSIGN and add
@@ -1101,15 +1281,26 @@ region_model::get_gassign_result (const gassign *assign,
&& INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
{
if (tree_int_cst_sgn (rhs2_cst) < 0)
- ctxt->warn
- (make_shift_count_negative_diagnostic (assign, rhs2_cst));
+ {
+ const region *rhs2_reg
+ = get_lvalue (gimple_assign_rhs2 (assign), nullptr);
+ ctxt->warn
+ (make_shift_count_negative_diagnostic (assign,
+ rhs2_cst,
+ rhs2_reg));
+ }
else if (compare_tree_int (rhs2_cst,
TYPE_PRECISION (TREE_TYPE (rhs1)))
>= 0)
- ctxt->warn (make_shift_count_overflow_diagnostic
- (assign,
- int (TYPE_PRECISION (TREE_TYPE (rhs1))),
- rhs2_cst));
+ {
+ const region *rhs2_reg
+ = get_lvalue (gimple_assign_rhs2 (assign), nullptr);
+ ctxt->warn (make_shift_count_overflow_diagnostic
+ (assign,
+ int (TYPE_PRECISION (TREE_TYPE (rhs1))),
+ rhs2_cst,
+ rhs2_reg));
+ }
}
}
@@ -1130,8 +1321,11 @@ region_model::get_gassign_result (const gassign *assign,
{
if (ctxt)
{
+ const region *rhs2_reg
+ = get_lvalue (gimple_assign_rhs2 (assign), nullptr);
ctxt->warn
- (std::make_unique<div_by_zero_diagnostic> (assign));
+ (std::make_unique<div_by_zero_diagnostic> (assign,
+ rhs2_reg));
ctxt->terminate_path ();
}
return nullptr;
@@ -1935,7 +2129,8 @@ public:
void
add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &) const final override
+ pending_diagnostic &,
+ const state_transition *) const final override
{
const exploded_node *dst_node = eedge.m_dest;
const program_point &dst_point = dst_node->get_point ();
@@ -1338,12 +1338,14 @@ make_poisoned_value_diagnostic (tree expr, enum poison_kind pkind,
extern std::unique_ptr<pending_diagnostic>
make_shift_count_negative_diagnostic (const gassign *assign,
- tree count_cst);
+ tree count_cst,
+ const region *src_region);
extern std::unique_ptr<pending_diagnostic>
make_shift_count_overflow_diagnostic (const gassign *assign,
int operand_precision,
- tree count_cst);
+ tree count_cst,
+ const region *src_region);
extern std::unique_ptr<pending_diagnostic>
make_write_to_const_diagnostic (const region *dest_reg, tree decl);
@@ -22,7 +22,8 @@ along with GCC; see the file COPYING3. If not see
#define GCC_ANALYZER_REGION_H
#include "analyzer/symbol.h"
-#include "text-art/widget.h"
+#include "analyzer/store.h"
+#include "text-art/tree-widget.h"
namespace ana {
@@ -469,7 +469,8 @@ rewind_info_t::update_model (region_model *model,
void
rewind_info_t::add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge,
- pending_diagnostic &) const
+ pending_diagnostic &,
+ const state_transition *) const
{
const exploded_node *src_node = eedge.m_src;
const program_point &src_point = src_node->get_point ();
@@ -35,8 +35,9 @@ class shift_count_negative_diagnostic
: public pending_diagnostic_subclass<shift_count_negative_diagnostic>
{
public:
- shift_count_negative_diagnostic (const gassign *assign, tree count_cst)
- : m_assign (assign), m_count_cst (count_cst)
+ shift_count_negative_diagnostic (const gassign *assign, tree count_cst,
+ const region *src_region)
+ : m_assign (assign), m_count_cst (count_cst), m_src_region (src_region)
{}
const char *get_kind () const final override
@@ -70,15 +71,24 @@ public:
return true;
}
+ void
+ mark_interesting_stuff (interesting_t *interest)
+ {
+ interest->add_read_region (m_src_region, "shift count value");
+ }
+
private:
const gassign *m_assign;
tree m_count_cst;
+ const region *m_src_region;
};
std::unique_ptr<pending_diagnostic>
-make_shift_count_negative_diagnostic (const gassign *assign, tree count_cst)
+make_shift_count_negative_diagnostic (const gassign *assign, tree count_cst,
+ const region *src_region)
{
- return std::make_unique<shift_count_negative_diagnostic> (assign, count_cst);
+ return std::make_unique<shift_count_negative_diagnostic>
+ (assign, count_cst, src_region);
}
/* A subclass of pending_diagnostic for complaining about shifts
@@ -90,9 +100,11 @@ class shift_count_overflow_diagnostic
public:
shift_count_overflow_diagnostic (const gassign *assign,
int operand_precision,
- tree count_cst)
+ tree count_cst,
+ const region *src_region)
: m_assign (assign), m_operand_precision (operand_precision),
- m_count_cst (count_cst)
+ m_count_cst (count_cst),
+ m_src_region (src_region)
{}
const char *get_kind () const final override
@@ -128,19 +140,27 @@ public:
return true;
}
+ void
+ mark_interesting_stuff (interesting_t *interest)
+ {
+ interest->add_read_region (m_src_region, "shift count value");
+ }
+
private:
const gassign *m_assign;
int m_operand_precision;
tree m_count_cst;
+ const region *m_src_region;
};
std::unique_ptr<pending_diagnostic>
make_shift_count_overflow_diagnostic (const gassign *assign,
int operand_precision,
- tree count_cst)
+ tree count_cst,
+ const region *src_region)
{
return std::make_unique<shift_count_overflow_diagnostic>
- (assign, operand_precision, count_cst);
+ (assign, operand_precision, count_cst, src_region);
}
} // namespace ana
@@ -221,7 +221,8 @@ public:
void add_events_to_path (checker_path *emission_path,
const exploded_edge &eedge ATTRIBUTE_UNUSED,
- pending_diagnostic &)
+ pending_diagnostic &,
+ const state_transition *)
const final override
{
emission_path->add_event
new file mode 100644
@@ -0,0 +1,184 @@
+/* Classes for tracking pertinent events that happen along
+ an execution path.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+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 "analyzer/common.h"
+
+#include "tree-diagnostic.h"
+
+#include "gimple-pretty-print.h"
+#include "gimple-iterator.h"
+#include "tree-cfg.h"
+#include "tree-dfa.h"
+#include "fold-const.h"
+#include "cgraph.h"
+#include "text-art/dump.h"
+#include "text-art/tree-widget.h"
+
+#include "analyzer/ops.h"
+#include "analyzer/call-details.h"
+#include "analyzer/exploded-graph.h"
+#include "analyzer/checker-path.h"
+#include "analyzer/impl-sm-context.h"
+#include "analyzer/constraint-manager.h"
+#include "analyzer/call-summary.h"
+#include "analyzer/call-info.h"
+#include "analyzer/analysis-plan.h"
+#include "analyzer/callsite-expr.h"
+#include "analyzer/state-transition.h"
+
+#if ENABLE_ANALYZER
+
+namespace ana {
+
+// class state_transition
+
+DEBUG_FUNCTION void
+state_transition::dump () const
+{
+ tree_dump_pretty_printer pp (stderr);
+ dump_to_pp (&pp);
+ pp_newline (&pp);
+}
+
+std::unique_ptr<state_transition>
+state_transition::make (const region *src_reg,
+ tree src_reg_expr,
+ const region *dst_reg,
+ tree dst_reg_expr)
+{
+ gcc_assert (src_reg != dst_reg);
+ gcc_assert (dst_reg);
+
+ if (!src_reg)
+ return std::make_unique<state_transition_origin> (dst_reg_expr);
+
+ if (src_reg->get_parent_region () == dst_reg->get_parent_region ())
+ if (tree src_decl = src_reg->maybe_get_decl ())
+ if (tree dst_decl = dst_reg->maybe_get_decl ())
+ {
+ if (TREE_CODE (src_decl) == SSA_NAME
+ && TREE_CODE (dst_decl) == SSA_NAME
+ && SSA_NAME_VAR (src_decl)
+ && SSA_NAME_VAR (src_decl) == SSA_NAME_VAR (dst_decl))
+ {
+ /* Avoid printing "copying value from 'y' to 'y'. */
+ return nullptr;
+ }
+ }
+
+ if (printable_expr_p (src_reg_expr))
+ {
+ if (printable_expr_p (dst_reg_expr))
+ return std::make_unique<state_transition_copy> (src_reg_expr,
+ dst_reg_expr);
+ else
+ return std::make_unique<state_transition_use> (src_reg_expr);
+ }
+ else
+ return nullptr;
+}
+
+diagnostics::paths::event_id_t
+state_transition::get_src_event_id () const
+{
+ if (!m_prev_state_transition)
+ return diagnostics::paths::event_id_t ();
+ return m_prev_state_transition->m_event_id;
+}
+// class state_transition_origin : public state_transition
+
+std::unique_ptr<state_transition>
+state_transition_origin::clone () const
+{
+ return std::make_unique<state_transition_origin> (m_dst_reg_expr);
+}
+
+void
+state_transition_origin::dump_to_pp (pretty_printer *pp) const
+{
+ pp_printf (pp, "state_transition_origin (dst: %qE)",
+ m_dst_reg_expr);
+}
+
+// class state_transition_at_call : public state_transition
+
+std::unique_ptr<state_transition>
+state_transition_at_call::clone () const
+{
+ return std::make_unique<state_transition_at_call> (m_expr);
+}
+
+void
+state_transition_at_call::dump_to_pp (pretty_printer *pp) const
+{
+ callsite_expr_element e (m_expr);
+ pp_printf (pp, "state_transition_at_call (callsite_expr: %e)", &e);
+}
+
+// class state_transition_at_return : public state_transition
+
+std::unique_ptr<state_transition>
+state_transition_at_return::clone () const
+{
+ return std::make_unique<state_transition_at_return> ();
+}
+
+void
+state_transition_at_return::dump_to_pp (pretty_printer *pp) const
+{
+ pp_printf (pp, "state_transition_at_return");
+}
+
+// class state_transition_copy : public state_transition
+
+std::unique_ptr<state_transition>
+state_transition_copy::clone () const
+{
+ return std::make_unique<state_transition_copy> (m_src_reg_expr,
+ m_dst_reg_expr);
+}
+
+void
+state_transition_copy::dump_to_pp (pretty_printer *pp) const
+{
+ pp_printf (pp, "state_transition_copy (src: %qE, dst: %qE)",
+ m_src_reg_expr,
+ m_dst_reg_expr);
+}
+
+// class state_transition_use : public state_transition
+
+std::unique_ptr<state_transition>
+state_transition_use::clone () const
+{
+ return std::make_unique<state_transition_use> (m_src_reg_expr);
+}
+
+void
+state_transition_use::dump_to_pp (pretty_printer *pp) const
+{
+ pp_printf (pp, "state_transition_use (src: %qE)",
+ m_src_reg_expr);
+}
+
+} // namespace ana
+
+#endif /* #if ENABLE_ANALYZER */
new file mode 100644
@@ -0,0 +1,191 @@
+/* Classes for tracking pertinent events that happen along
+ an execution path.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+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/>. */
+
+#ifndef GCC_ANALYZER_STATE_TRANSITION_H
+#define GCC_ANALYZER_STATE_TRANSITION_H
+
+#include "diagnostics/event-id.h"
+#include "analyzer/callsite-expr.h"
+
+namespace ana {
+
+class state_transition
+{
+public:
+ enum class kind
+ {
+ origin,
+ at_call,
+ at_return,
+ copy,
+ use
+ };
+
+ state_transition ()
+ : m_prev_state_transition (nullptr)
+ {
+ }
+
+ virtual ~state_transition () {}
+
+ virtual std::unique_ptr<state_transition>
+ clone () const = 0;
+
+ virtual void
+ dump_to_pp (pretty_printer *pp) const = 0;
+
+ virtual enum kind get_kind () const = 0;
+
+ virtual const state_transition_at_call *
+ dyn_cast_state_transition_at_call () const { return nullptr; }
+
+ virtual const state_transition_at_return *
+ dyn_cast_state_transition_at_return () const { return nullptr; }
+
+ void dump () const;
+
+ static std::unique_ptr<state_transition>
+ make (const region *src_reg,
+ tree src_reg_expr,
+ const region *dst_reg,
+ tree dst_reg_expr);
+
+ diagnostics::paths::event_id_t
+ get_src_event_id () const;
+
+ state_transition *m_prev_state_transition;
+ diagnostics::paths::event_id_t m_event_id;
+};
+
+class state_transition_origin : public state_transition
+{
+public:
+ state_transition_origin (tree dst_reg_expr)
+ : m_dst_reg_expr (dst_reg_expr)
+ {
+ }
+
+ std::unique_ptr<state_transition>
+ clone () const final override;
+
+ void
+ dump_to_pp (pretty_printer *pp) const final override;
+
+ enum kind
+ get_kind () const final override { return kind::origin; }
+
+ tree m_dst_reg_expr;
+};
+
+class state_transition_at_call : public state_transition
+{
+public:
+ state_transition_at_call (callsite_expr expr)
+ : m_expr (expr)
+ {
+ }
+
+ std::unique_ptr<state_transition>
+ clone () const final override;
+
+ void
+ dump_to_pp (pretty_printer *pp) const final override;
+
+ enum kind
+ get_kind () const final override { return kind::at_call; }
+
+ const state_transition_at_call *
+ dyn_cast_state_transition_at_call () const final override { return this; }
+
+ callsite_expr
+ get_callsite_expr () const { return m_expr; }
+
+private:
+ callsite_expr m_expr;
+};
+
+class state_transition_at_return : public state_transition
+{
+public:
+ std::unique_ptr<state_transition>
+ clone () const final override;
+
+ void
+ dump_to_pp (pretty_printer *pp) const final override;
+
+ enum kind
+ get_kind () const final override { return kind::at_return; }
+
+ const state_transition_at_return *
+ dyn_cast_state_transition_at_return () const final override { return this; }
+};
+
+class state_transition_copy : public state_transition
+{
+public:
+ state_transition_copy (tree src_reg_expr,
+ tree dst_reg_expr)
+ : m_src_reg_expr (src_reg_expr),
+ m_dst_reg_expr (dst_reg_expr)
+ {
+ gcc_assert (m_src_reg_expr);
+ gcc_assert (printable_expr_p (m_src_reg_expr));
+
+ gcc_assert (m_dst_reg_expr);
+ gcc_assert (printable_expr_p (m_dst_reg_expr));
+ }
+
+ std::unique_ptr<state_transition>
+ clone () const final override;
+
+ void
+ dump_to_pp (pretty_printer *pp) const final override;
+
+ enum kind
+ get_kind () const final override { return kind::copy; }
+
+ tree m_src_reg_expr;
+ tree m_dst_reg_expr;
+};
+
+class state_transition_use : public state_transition
+{
+public:
+ state_transition_use (tree src_reg_expr)
+ : m_src_reg_expr (src_reg_expr)
+ {
+ }
+
+ std::unique_ptr<state_transition>
+ clone () const final override;
+
+ void
+ dump_to_pp (pretty_printer *pp) const final override;
+
+ enum kind
+ get_kind () const final override { return kind::use; }
+
+ tree m_src_reg_expr;
+};
+
+} // namespace ana
+
+#endif /* GCC_ANALYZER_STATE_TRANSITION_H */
@@ -323,40 +323,6 @@ private:
::edge m_cfg_edge;
};
-/* An ID representing an expression at a callsite:
- either a parameter index, or the return value (or unknown). */
-
-class callsite_expr
-{
- public:
- callsite_expr () : m_val (-1) {}
-
- static callsite_expr from_zero_based_param (int idx)
- {
- return callsite_expr (idx + 1);
- }
-
- static callsite_expr from_return_value ()
- {
- return callsite_expr (0);
- }
-
- bool param_p () const
- {
- return m_val > 0;
- }
-
- bool return_value_p () const
- {
- return m_val == 0;
- }
-
- private:
- callsite_expr (int val) : m_val (val) {}
-
- int m_val; /* 1-based parm, 0 for return value, or -1 for "unknown". */
-};
-
/* Base class for adding additional content to the .dot output
for a supergraph. */
@@ -775,7 +775,8 @@ public:
adding a custom call_event subclass. */
void add_call_event (const exploded_edge &eedge,
const gcall &call_stmt,
- checker_path &emission_path) override
+ checker_path &emission_path,
+ const state_transition_at_call *state_trans) override
{
/* As per call_event, but show the number of variadic arguments
in the call. */
@@ -785,7 +786,7 @@ public:
va_arg_call_event (const exploded_edge &eedge,
const event_loc_info &loc_info,
int num_variadic_arguments)
- : call_event (eedge, loc_info),
+ : call_event (eedge, loc_info, nullptr),
m_num_variadic_arguments (num_variadic_arguments)
{
}
@@ -819,7 +820,8 @@ public:
num_variadic_arguments));
}
else
- pending_diagnostic::add_call_event (eedge, call_stmt, emission_path);
+ pending_diagnostic::add_call_event (eedge, call_stmt, emission_path,
+ state_trans);
}
protected:
@@ -96,6 +96,9 @@ struct test_cluster : public cluster<test_graph_traits>
struct test_path
{
+ void append_edge (const test_edge *edge) { m_edges.safe_push (edge); }
+ void reverse () { m_edges.reverse (); }
+
auto_vec<const test_edge *> m_edges;
};
@@ -187,7 +187,7 @@ get_shortest_path (const node_t *other_node) const
while (m_best_edge[other_node->m_index])
{
- result.m_edges.safe_push (m_best_edge[other_node->m_index]);
+ result.append_edge (m_best_edge[other_node->m_index]);
if (m_sense == SPS_FROM_GIVEN_ORIGIN)
other_node = m_best_edge[other_node->m_index]->m_src;
else
@@ -195,7 +195,7 @@ get_shortest_path (const node_t *other_node) const
}
if (m_sense == SPS_FROM_GIVEN_ORIGIN)
- result.m_edges.reverse ();
+ result.reverse ();
return result;
}
@@ -3,7 +3,7 @@
static int __attribute__((noipa))
return_zero (void)
{
- return 0;
+ return 0; /* { dg-message "value originates here" } */
}
void
new file mode 100644
@@ -0,0 +1,14 @@
+/* { dg-additional-options "-fno-analyzer-state-merge" } */
+
+extern int
+get_value (void);
+
+int
+test (int flag)
+{
+ int x = 42;
+ int y = 0; /* { dg-message "value originates here" } */
+ if (flag)
+ y = get_value ();
+ return x / y; /* { dg-warning "division by zero" } */
+}
new file mode 100644
@@ -0,0 +1,19 @@
+/* { dg-additional-options "-fno-analyzer-state-merge" } */
+
+extern int
+get_value (void);
+
+int
+test (int flag, int flag_2, int flag_3)
+{
+ int x = 42; /* { dg-bogus "value originates here" } */
+ int y = 10; /* { dg-bogus "value originates here" } */
+ int z = 0; /* { dg-message "value originates here" } */
+ if (flag)
+ y = get_value ();
+ if (flag_2)
+ z = get_value ();
+ if (flag_3)
+ y = z;
+ return x / y; /* { dg-warning "division by zero" } */
+}
@@ -27,8 +27,10 @@ f2 (void)
f1 (_dl_hwcaps_subdirs_build_bitmask (33, 31));
}
-static int __attribute__((noinline)) op3 (int op, int c) { return op << c; } /* { dg-message "shift by negative count \\('-1'\\)" } */
-int test_3 (void) { return op3 (1, -1); }
+static int __attribute__((noinline)) op3 (int op, int c) { return op << c; } /* { dg-message "55: entry to 'op3' with problematic value for 'c'" } */
+/* { dg-message "shift by negative count \\('-1'\\)" "" { target *-*-* } .-1 } */
+
+int test_3 (void) { return op3 (1, -1); } /* { dg-message "passing problematic value from 'test_3' to 'op3' via parameter 2" } */
static int __attribute__((noinline)) op4 (int op, int c) { return op << c; }
int test_4 (void) { return op4 (1, 0); }
new file mode 100644
@@ -0,0 +1,11 @@
+unsigned char
+do_shift (unsigned char val, int bits) /* { dg-message "34: entry to 'do_shift' with problematic value for 'bits'" } */
+{
+ return val << bits; /* { dg-warning "Wanalyzer-shift-count-overflow" } */
+}
+
+int
+test (unsigned char ch)
+{
+ return do_shift (ch, 1000); /* { dg-message "passing problematic value from 'test' to 'do_shift' via parameter 2" } */
+}
new file mode 100644
@@ -0,0 +1,12 @@
+unsigned char
+do_shift (unsigned char val, int bits) /* { dg-message "34: entry to 'do_shift' with problematic value from \\\(2\\\) for 'bits'" } */
+{
+ return val << bits; /* { dg-warning "Wanalyzer-shift-count-overflow" } */
+}
+
+int
+test (unsigned char ch)
+{
+ int bits = 1000; /* { dg-message "\\\(2\\\) value originates here" } */
+ return do_shift (ch, bits); /* { dg-message "\\\(3\\\) passing problematic value from \\\(2\\\) from 'test' to 'do_shift' via parameter 2" } */
+}
new file mode 100644
@@ -0,0 +1,12 @@
+unsigned char
+do_shift (unsigned char val, int bits) /* { dg-message "34: entry to 'do_shift' with problematic value from \\\(2\\\) for 'bits'" } */
+{
+ return val << bits; /* { dg-warning "Wanalyzer-shift-count-negative" } */
+}
+
+int
+test (unsigned char ch)
+{
+ int bits = -1; /* { dg-message "\\\(2\\\) value originates here" } */
+ return do_shift (ch, bits); /* { dg-message "\\\(3\\\) passing problematic value from \\\(2\\\) from 'test' to 'do_shift' via parameter 2" } */
+}
new file mode 100644
@@ -0,0 +1,28 @@
+/* { dg-additional-options "-fno-analyzer-state-merge" } */
+
+// TODO: we shouldn't need this:
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+
+struct foo
+{
+ foo (int x_, int y_)
+ : x (x_), y (y_) // TODO: should show event here
+ {
+ }
+
+ int divide () const
+ {
+ return x / y; /* { dg-message "using zero value from '\\*this\\.foo::y'" } */
+ /* { dg-warning "division by zero" "" { target *-*-* } .-1 } */
+ }
+
+ int x;
+ int y;
+};
+
+int
+test ()
+{
+ foo f (5, 0); // TODO: should show "origin of zero" event here
+ return f.divide ();
+}
new file mode 100644
@@ -0,0 +1,39 @@
+/* { dg-additional-options "-fno-analyzer-state-merge" } */
+
+/* TODO: we shouldn't need this: */
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+
+int
+get_zero (void)
+{
+ return 0; /* { dg-message "\\\(6\\\) zero value originates here" } */
+}
+
+struct foo { int x; int y; };
+
+void
+init_foo (struct foo *f, int x, int y) /* { dg-message "\\\(9\\\) entry to 'init_foo' with zero from \\\(7\\\) for 'y'" } */
+{
+ f->x = x;
+ f->y = y; /* { dg-message "\\\(10\\\) copying zero value from \\\(9\\\) from 'y' to '\\*f\\.y'" } */
+}
+
+int
+do_divide (struct foo *f)
+{
+ return f->x / f->y; /* { dg-message "using zero value from \\\(10\\\) from '\\*f\\.y'" } */
+ /* { dg-warning "division by zero" "" { target *-*-* } .-1 } */
+}
+
+int
+test (int flag, int flag_2, int flag_3)
+{
+ struct foo f;
+ int a = 42;
+ int b = 10;
+ if (flag)
+ b = get_zero ();
+ /* { dg-message "\\\(7\\\) returning zero from \\\(6\\\) from 'get_zero' here" "" { target *-*-* } .-1 } */
+ init_foo (&f, a, b); /* { dg-message "passing zero from \\\(7\\\) from 'test' to 'init_foo' via parameter 3" } */
+ return do_divide (&f);
+}
new file mode 100644
@@ -0,0 +1,42 @@
+/* { dg-additional-options "-fno-analyzer-state-merge" } */
+
+/* TODO: we shouldn't need this: */
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+
+int
+maybe_get_zero (int flag)
+{
+ if (flag)
+ return 0; /* { dg-message "zero value originates here" } */
+ else
+ return 42;
+}
+
+struct foo { int x; int y; };
+
+void
+init_foo (struct foo *f, int x, int y) /* { dg-message "\\\(11\\\) entry to 'init_foo' with zero from \\\(9\\\) for 'y'" } */
+{
+ f->x = x;
+ f->y = y; /* { dg-message "\\\(12\\\) copying zero value from \\\(11\\\) from 'y' to '\\*f\\.y'" } */
+}
+
+int
+do_divide (struct foo *f)
+{
+ return f->x / f->y; /* { dg-message "using zero value from \\\(12\\\) from '\\*f\\.y'" } */
+ /* { dg-warning "division by zero" "" { target *-*-* } .-1 } */
+}
+
+int
+test (int flag, int flag_2, int flag_3)
+{
+ struct foo f;
+ int a = 42;
+ int b = 10;
+ if (flag)
+ b = maybe_get_zero (flag_2); /* { dg-bogus "value of 'b' unchanged here" } */
+ /* { dg-message "returning zero from \\\(8\\\) from 'maybe_get_zero' here" "" { target *-*-* } .-1 } */
+ init_foo (&f, a, b); /* { dg-message "passing zero from \\\(9\\\) from 'test' to 'init_foo' via parameter 3" } */
+ return do_divide (&f);
+}
new file mode 100644
@@ -0,0 +1,27 @@
+/* { dg-additional-options "-fno-analyzer-state-merge" } */
+
+/* TODO: we shouldn't need this: */
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+
+struct foo { int x; int y; };
+
+void
+init_foo (struct foo *f)
+{
+ __builtin_memset (f, 0, sizeof (f));
+}
+
+int
+do_divide (struct foo *f)
+{
+ return f->x / f->y; /* { dg-message "using zero value from '\\*f\\.y'" } */
+ /* { dg-warning "division by zero" "" { target *-*-* } .-1 } */
+}
+
+int
+test (int flag, int flag_2, int flag_3)
+{
+ struct foo f;
+ init_foo (&f);
+ return do_divide (&f);
+}
@@ -7,7 +7,7 @@ test_1 ()
static float __attribute__((noinline))
get_zero ()
{
- return 0.f;
+ return 0.f; /* { dg-message "value originates here" } */
}
float
@@ -71,7 +71,8 @@ public:
}
~tree_dump_pretty_printer ()
{
- pp_flush (this);
+ if (pp_buffer (this)->m_stream)
+ pp_flush (this);
}
};