analyzer: remove add_any_constraints_from_ssa_def_stmt

I'm working on reimplementing -Wanalyzer-use-of-uninitialized-value, but
I ran into issues with
region_model::add_any_constraints_from_ssa_def_stmt.
This function is from the initial commit of the analyzer and walks the
SSA names finding conditions that were missed due to the GCC 10 era
region_model not retaining useful information on how values were
created; as of GCC 11 the symbolic values contain this information,
and so the conditions can be reconstructed from them instead.

region_model::add_any_constraints_from_ssa_def_stmt is a liability
when tracking uninitialized values as it requires looking up SSA
values when those values may have been purged, thus greatly complicating
detection of uses of uninitialized values.

It's simplest to eliminate it and reimplement the condition-finding
via the makeup of the svalues, which this patch does.  Doing so requires
supporting add_condition on svalues rather than just on trees, which
requires some changes to ana::state_machine and its subclasses.

gcc/analyzer/ChangeLog:
	* diagnostic-manager.cc (null_assignment_sm_context::get_state):
	New overload.
	(null_assignment_sm_context::set_next_state): New overload.
	(null_assignment_sm_context::get_diagnostic_tree): New.
	* engine.cc (impl_sm_context::get_state): New overload.
	(impl_sm_context::set_next_state): New overload.
	(impl_sm_context::get_diagnostic_tree): New overload.
	(impl_region_model_context::on_condition): Convert params from
	tree to const svalue *.
	* exploded-graph.h (impl_region_model_context::on_condition):
	Likewise.
	* region-model.cc (region_model::on_call_pre): Move handling of
	internal calls to before checking for get_fndecl_for_call.
	(region_model::add_constraints_from_binop): New.
	(region_model::add_constraint): Split out into a new overload
	working on const svalue * rather than tree.  Call
	add_constraints_from_binop.  Drop call to
	add_any_constraints_from_ssa_def_stmt.
	(region_model::add_any_constraints_from_ssa_def_stmt): Delete.
	(region_model::add_any_constraints_from_gassign): Delete.
	(region_model::add_any_constraints_from_gcall): Delete.
	* region-model.h
	(region_model::add_any_constraints_from_ssa_def_stmt): Delete.
	(region_model::add_any_constraints_from_gassign): Delete.
	(region_model::add_any_constraints_from_gcall): Delete.
	(region_model::add_constraint): Add overload decl.
	(region_model::add_constraints_from_binop): New decl.
	(region_model_context::on_condition): Convert params from tree to
	const svalue *.
	(noop_region_model_context::on_condition): Likewise.
	* sm-file.cc (fileptr_state_machine::condition): Likewise.
	* sm-malloc.cc (malloc_state_machine::on_condition): Likewise.
	* sm-pattern-test.cc: Include tristate.h, selftest.h,
	analyzer/call-string.h, analyzer/program-point.h,
	analyzer/store.h, and analyzer/region-model.h.
	(pattern_test_state_machine::on_condition): Convert params from tree to
	const svalue *.
	* sm-sensitive.cc (sensitive_state_machine::on_condition): Delete.
	* sm-signal.cc (signal_state_machine::on_condition): Delete.
	* sm-taint.cc (taint_state_machine::on_condition): Convert params
	from tree to const svalue *.
	* sm.cc: Include tristate.h, selftest.h, analyzer/call-string.h,
	analyzer/program-point.h, analyzer/store.h, and
	analyzer/region-model.h.
	(any_pointer_p): Add overload taking const svalue *sval.
	* sm.h (any_pointer_p): Add overload taking const svalue *sval.
	(state_machine::on_condition): Convert params from tree to
	const svalue *.  Provide no-op default implementation.
	(sm_context::get_state): Add overload taking const svalue *sval.
	(sm_context::set_next_state): Likewise.
	(sm_context::on_transition): Likewise.
	(sm_context::get_diagnostic_tree): Likewise.
	* svalue.cc (svalue::all_zeroes_p): New.
	(constant_svalue::all_zeroes_p): New.
	(repeated_svalue::all_zeroes_p): Convert to vfunc.
	* svalue.h (svalue::all_zeroes_p): New decl.
	(constant_svalue::all_zeroes_p): New decl.
	(repeated_svalue::all_zeroes_p): Convert decl to vfunc.

gcc/testsuite/ChangeLog:
	* gcc.dg/analyzer/pattern-test-2.c: Update expected results.
	* gcc.dg/plugin/analyzer_gil_plugin.c
	(gil_state_machine::on_condition): Remove.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm 2021-07-07 19:29:30 -04:00
parent a110855667
commit 48e8a7a677
17 changed files with 361 additions and 270 deletions

View File

@ -1377,6 +1377,14 @@ struct null_assignment_sm_context : public sm_context
return current;
}
state_machine::state_t get_state (const gimple *stmt ATTRIBUTE_UNUSED,
const svalue *sval) FINAL OVERRIDE
{
const sm_state_map *old_smap = m_old_state->m_checker_states[m_sm_idx];
state_machine::state_t current = old_smap->get_state (sval, m_ext_state);
return current;
}
void set_next_state (const gimple *stmt,
tree var,
state_machine::state_t to,
@ -1401,6 +1409,28 @@ struct null_assignment_sm_context : public sm_context
*m_new_state));
}
void set_next_state (const gimple *stmt,
const svalue *sval,
state_machine::state_t to,
tree origin ATTRIBUTE_UNUSED) FINAL OVERRIDE
{
state_machine::state_t from = get_state (stmt, sval);
if (from != m_sm.get_start_state ())
return;
const supernode *supernode = m_point->get_supernode ();
int stack_depth = m_point->get_stack_depth ();
m_emission_path->add_event (new state_change_event (supernode,
m_stmt,
stack_depth,
m_sm,
sval,
from, to,
NULL,
*m_new_state));
}
void warn (const supernode *, const gimple *,
tree, pending_diagnostic *d) FINAL OVERRIDE
{
@ -1412,6 +1442,11 @@ struct null_assignment_sm_context : public sm_context
return expr;
}
tree get_diagnostic_tree (const svalue *sval) FINAL OVERRIDE
{
return m_new_state->m_region_model->get_representative_tree (sval);
}
state_machine::state_t get_global_state () const FINAL OVERRIDE
{
return 0;

View File

@ -245,6 +245,16 @@ public:
= m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ());
return current;
}
state_machine::state_t get_state (const gimple *stmt ATTRIBUTE_UNUSED,
const svalue *sval)
{
logger * const logger = get_logger ();
LOG_FUNC (logger);
state_machine::state_t current
= m_old_smap->get_state (sval, m_eg.get_ext_state ());
return current;
}
void set_next_state (const gimple *stmt,
tree var,
@ -280,6 +290,41 @@ public:
to, origin_new_sval, m_eg.get_ext_state ());
}
void set_next_state (const gimple *stmt,
const svalue *sval,
state_machine::state_t to,
tree origin)
{
logger * const logger = get_logger ();
LOG_FUNC (logger);
impl_region_model_context old_ctxt
(m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/,
NULL, stmt);
impl_region_model_context new_ctxt (m_eg, m_enode_for_diag,
m_old_state, m_new_state,
NULL,
stmt);
const svalue *origin_new_sval
= m_new_state->m_region_model->get_rvalue (origin, &new_ctxt);
state_machine::state_t current
= m_old_smap->get_state (sval, m_eg.get_ext_state ());
if (logger)
{
logger->start_log_line ();
logger->log_partial ("%s: state transition of ",
m_sm.get_name ());
sval->dump_to_pp (logger->get_printer (), true);
logger->log_partial (": %s -> %s",
current->get_name (),
to->get_name ());
logger->end_log_line ();
}
m_new_smap->set_state (m_new_state->m_region_model, sval,
to, origin_new_sval, m_eg.get_ext_state ());
}
void warn (const supernode *snode, const gimple *stmt,
tree var, pending_diagnostic *d) FINAL OVERRIDE
{
@ -323,6 +368,11 @@ public:
return expr;
}
tree get_diagnostic_tree (const svalue *sval) FINAL OVERRIDE
{
return m_new_state->m_region_model->get_representative_tree (sval);
}
state_machine::state_t get_global_state () const FINAL OVERRIDE
{
return m_old_state->m_checker_states[m_sm_idx]->get_global_state ();
@ -654,7 +704,9 @@ impl_region_model_context::on_state_leak (const state_machine &sm,
state transitions. */
void
impl_region_model_context::on_condition (tree lhs, enum tree_code op, tree rhs)
impl_region_model_context::on_condition (const svalue *lhs,
enum tree_code op,
const svalue *rhs)
{
int sm_idx;
sm_state_map *smap;

View File

@ -59,7 +59,9 @@ class impl_region_model_context : public region_model_context
const svalue *sval,
state_machine::state_t state);
void on_condition (tree lhs, enum tree_code op, tree rhs) FINAL OVERRIDE;
void on_condition (const svalue *lhs,
enum tree_code op,
const svalue *rhs) FINAL OVERRIDE;
void on_unknown_change (const svalue *sval, bool is_mutable) FINAL OVERRIDE;

View File

@ -881,12 +881,23 @@ bool
region_model::on_call_pre (const gcall *call, region_model_context *ctxt,
bool *out_terminate_path)
{
call_details cd (call, this, ctxt);
bool unknown_side_effects = false;
if (gimple_call_internal_p (call))
{
switch (gimple_call_internal_fn (call))
{
default:
break;
case IFN_BUILTIN_EXPECT:
return impl_call_builtin_expect (cd);
}
}
if (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
{
call_details cd (call, this, ctxt);
/* The various impl_call_* member functions are implemented
in region-model-impl-calls.cc.
Having them split out into separate functions makes it easier
@ -958,16 +969,6 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt,
on the return value. */
break;
}
else if (gimple_call_internal_p (call))
switch (gimple_call_internal_fn (call))
{
default:
if (!DECL_PURE_P (callee_fndecl))
unknown_side_effects = true;
break;
case IFN_BUILTIN_EXPECT:
return impl_call_builtin_expect (cd);
}
else if (is_named_call_p (callee_fndecl, "malloc", call, 1))
return impl_call_malloc (cd);
else if (is_named_call_p (callee_fndecl, "calloc", call, 2))
@ -2251,6 +2252,123 @@ region_model::compare_initial_and_pointer (const initial_svalue *init,
return tristate::TS_UNKNOWN;
}
/* Handle various constraints of the form:
LHS: ((bool)INNER_LHS INNER_OP INNER_RHS))
OP : == or !=
RHS: zero
and (with a cast):
LHS: CAST([long]int, ((bool)INNER_LHS INNER_OP INNER_RHS))
OP : == or !=
RHS: zero
by adding constraints for INNER_LHS INNEROP INNER_RHS.
Return true if this function can fully handle the constraint; if
so, add the implied constraint(s) and write true to *OUT if they
are consistent with existing constraints, or write false to *OUT
if they contradicts existing constraints.
Return false for cases that this function doeesn't know how to handle.
For example, if we're checking a stored conditional, we'll have
something like:
LHS: CAST(long int, (&HEAP_ALLOCATED_REGION(8)!=(int *)0B))
OP : NE_EXPR
RHS: zero
which this function can turn into an add_constraint of:
(&HEAP_ALLOCATED_REGION(8) != (int *)0B)
Similarly, optimized && and || conditionals lead to e.g.
if (p && q)
becoming gimple like this:
_1 = p_6 == 0B;
_2 = q_8 == 0B
_3 = _1 | _2
On the "_3 is false" branch we can have constraints of the form:
((&HEAP_ALLOCATED_REGION(8)!=(int *)0B)
| (&HEAP_ALLOCATED_REGION(10)!=(int *)0B))
== 0
which implies that both _1 and _2 are false,
which this function can turn into a pair of add_constraints of
(&HEAP_ALLOCATED_REGION(8)!=(int *)0B)
and:
(&HEAP_ALLOCATED_REGION(10)!=(int *)0B). */
bool
region_model::add_constraints_from_binop (const svalue *outer_lhs,
enum tree_code outer_op,
const svalue *outer_rhs,
bool *out,
region_model_context *ctxt)
{
while (const svalue *cast = outer_lhs->maybe_undo_cast ())
outer_lhs = cast;
const binop_svalue *binop_sval = outer_lhs->dyn_cast_binop_svalue ();
if (!binop_sval)
return false;
if (!outer_rhs->all_zeroes_p ())
return false;
const svalue *inner_lhs = binop_sval->get_arg0 ();
enum tree_code inner_op = binop_sval->get_op ();
const svalue *inner_rhs = binop_sval->get_arg1 ();
if (outer_op != NE_EXPR && outer_op != EQ_EXPR)
return false;
/* We have either
- "OUTER_LHS != false" (i.e. OUTER is true), or
- "OUTER_LHS == false" (i.e. OUTER is false). */
bool is_true = outer_op == NE_EXPR;
switch (inner_op)
{
default:
return false;
case EQ_EXPR:
case NE_EXPR:
{
/* ...and "(inner_lhs OP inner_rhs) == 0"
then (inner_lhs OP inner_rhs) must have the same
logical value as LHS. */
if (!is_true)
inner_op = invert_tree_comparison (inner_op, false /* honor_nans */);
*out = add_constraint (inner_lhs, inner_op, inner_rhs, ctxt);
return true;
}
break;
case BIT_AND_EXPR:
if (is_true)
{
/* ...and "(inner_lhs & inner_rhs) != 0"
then both inner_lhs and inner_rhs must be true. */
const svalue *false_sval
= m_mgr->get_or_create_constant_svalue (boolean_false_node);
bool sat1 = add_constraint (inner_lhs, NE_EXPR, false_sval, ctxt);
bool sat2 = add_constraint (inner_rhs, NE_EXPR, false_sval, ctxt);
*out = sat1 && sat2;
return true;
}
return false;
case BIT_IOR_EXPR:
if (!is_true)
{
/* ...and "(inner_lhs | inner_rhs) == 0"
i.e. "(inner_lhs | inner_rhs)" is false
then both inner_lhs and inner_rhs must be false. */
const svalue *false_sval
= m_mgr->get_or_create_constant_svalue (boolean_false_node);
bool sat1 = add_constraint (inner_lhs, EQ_EXPR, false_sval, ctxt);
bool sat2 = add_constraint (inner_rhs, EQ_EXPR, false_sval, ctxt);
*out = sat1 && sat2;
return true;
}
return false;
}
}
/* Attempt to add the constraint "LHS OP RHS" to this region_model.
If it is consistent with existing constraints, add it, and return true.
Return false if it contradicts existing constraints.
@ -2268,7 +2386,21 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs,
const svalue *lhs_sval = get_rvalue (lhs, ctxt);
const svalue *rhs_sval = get_rvalue (rhs, ctxt);
tristate t_cond = eval_condition (lhs_sval, op, rhs_sval);
return add_constraint (lhs_sval, op, rhs_sval, ctxt);
}
/* Attempt to add the constraint "LHS OP RHS" to this region_model.
If it is consistent with existing constraints, add it, and return true.
Return false if it contradicts existing constraints.
Use CTXT for reporting any diagnostics associated with the accesses. */
bool
region_model::add_constraint (const svalue *lhs,
enum tree_code op,
const svalue *rhs,
region_model_context *ctxt)
{
tristate t_cond = eval_condition (lhs, op, rhs);
/* If we already have the condition, do nothing. */
if (t_cond.is_true ())
@ -2279,10 +2411,12 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs,
if (t_cond.is_false ())
return false;
/* Store the constraint. */
m_constraints->add_constraint (lhs_sval, op, rhs_sval);
bool out;
if (add_constraints_from_binop (lhs, op, rhs, &out, ctxt))
return out;
add_any_constraints_from_ssa_def_stmt (lhs, op, rhs, ctxt);
/* Store the constraint. */
m_constraints->add_constraint (lhs, op, rhs);
/* Notify the context, if any. This exists so that the state machines
in a program_state can be notified about the condition, and so can
@ -2293,9 +2427,10 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs,
/* If we have &REGION == NULL, then drop dynamic extents for REGION (for
the case where REGION is heap-allocated and thus could be NULL). */
if (op == EQ_EXPR && zerop (rhs))
if (const region_svalue *region_sval = lhs_sval->dyn_cast_region_svalue ())
unset_dynamic_extents (region_sval->get_pointee ());
if (tree rhs_cst = rhs->maybe_get_constant ())
if (op == EQ_EXPR && zerop (rhs_cst))
if (const region_svalue *region_sval = lhs->dyn_cast_region_svalue ())
unset_dynamic_extents (region_sval->get_pointee ());
return true;
}
@ -2314,137 +2449,6 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs,
return sat;
}
/* Subroutine of region_model::add_constraint for handling optimized
&& and || conditionals.
If we have an SSA_NAME for a boolean compared against 0,
look at anything implied by the def stmt and call add_constraint
for it (which could recurse).
For example, if we have
_1 = p_6 == 0B;
_2 = p_8 == 0B
_3 = _1 | _2
and add the constraint
(_3 == 0),
then the def stmt for _3 implies that _1 and _2 are both false,
and hence we can add the constraints:
p_6 != 0B
p_8 != 0B. */
void
region_model::add_any_constraints_from_ssa_def_stmt (tree lhs,
enum tree_code op,
tree rhs,
region_model_context *ctxt)
{
if (TREE_CODE (lhs) != SSA_NAME)
return;
if (!zerop (rhs))
return;
if (op != NE_EXPR && op != EQ_EXPR)
return;
gimple *def_stmt = SSA_NAME_DEF_STMT (lhs);
if (const gassign *assign = dyn_cast<gassign *> (def_stmt))
add_any_constraints_from_gassign (op, rhs, assign, ctxt);
else if (gcall *call = dyn_cast<gcall *> (def_stmt))
add_any_constraints_from_gcall (op, rhs, call, ctxt);
}
/* Add any constraints for an SSA_NAME defined by ASSIGN
where the result OP RHS. */
void
region_model::add_any_constraints_from_gassign (enum tree_code op,
tree rhs,
const gassign *assign,
region_model_context *ctxt)
{
/* We have either
- "LHS != false" (i.e. LHS is true), or
- "LHS == false" (i.e. LHS is false). */
bool is_true = op == NE_EXPR;
enum tree_code rhs_code = gimple_assign_rhs_code (assign);
switch (rhs_code)
{
default:
break;
case NOP_EXPR:
case VIEW_CONVERT_EXPR:
{
add_constraint (gimple_assign_rhs1 (assign), op, rhs, ctxt);
}
break;
case BIT_AND_EXPR:
{
if (is_true)
{
/* ...and "LHS == (rhs1 & rhs2) i.e. "(rhs1 & rhs2)" is true
then both rhs1 and rhs2 must be true. */
tree rhs1 = gimple_assign_rhs1 (assign);
tree rhs2 = gimple_assign_rhs2 (assign);
add_constraint (rhs1, NE_EXPR, boolean_false_node, ctxt);
add_constraint (rhs2, NE_EXPR, boolean_false_node, ctxt);
}
}
break;
case BIT_IOR_EXPR:
{
if (!is_true)
{
/* ...and "LHS == (rhs1 | rhs2)
i.e. "(rhs1 | rhs2)" is false
then both rhs1 and rhs2 must be false. */
tree rhs1 = gimple_assign_rhs1 (assign);
tree rhs2 = gimple_assign_rhs2 (assign);
add_constraint (rhs1, EQ_EXPR, boolean_false_node, ctxt);
add_constraint (rhs2, EQ_EXPR, boolean_false_node, ctxt);
}
}
break;
case EQ_EXPR:
case NE_EXPR:
{
/* ...and "LHS == (rhs1 OP rhs2)"
then rhs1 OP rhs2 must have the same logical value as LHS. */
tree rhs1 = gimple_assign_rhs1 (assign);
tree rhs2 = gimple_assign_rhs2 (assign);
if (!is_true)
rhs_code
= invert_tree_comparison (rhs_code, false /* honor_nans */);
add_constraint (rhs1, rhs_code, rhs2, ctxt);
}
break;
}
}
/* Add any constraints for an SSA_NAME defined by CALL
where the result OP RHS. */
void
region_model::add_any_constraints_from_gcall (enum tree_code op,
tree rhs,
const gcall *call,
region_model_context *ctxt)
{
if (gimple_call_builtin_p (call, BUILT_IN_EXPECT)
|| gimple_call_builtin_p (call, BUILT_IN_EXPECT_WITH_PROBABILITY)
|| gimple_call_internal_p (call, IFN_BUILTIN_EXPECT))
{
/* __builtin_expect's return value is its initial argument. */
add_constraint (gimple_call_arg (call, 0), op, rhs, ctxt);
}
}
/* Determine what is known about the condition "LHS OP RHS" within
this model.
Use CTXT for reporting any diagnostics associated with the accesses. */

View File

@ -691,18 +691,15 @@ class region_model
get_representative_path_var_1 (const region *reg,
svalue_set *visited) const;
void add_any_constraints_from_ssa_def_stmt (tree lhs,
enum tree_code op,
tree rhs,
region_model_context *ctxt);
void add_any_constraints_from_gassign (enum tree_code op,
tree rhs,
const gassign *assign,
region_model_context *ctxt);
void add_any_constraints_from_gcall (enum tree_code op,
tree rhs,
const gcall *call,
region_model_context *ctxt);
bool add_constraint (const svalue *lhs,
enum tree_code op,
const svalue *rhs,
region_model_context *ctxt);
bool add_constraints_from_binop (const svalue *outer_lhs,
enum tree_code outer_op,
const svalue *outer_rhs,
bool *out,
region_model_context *ctxt);
void update_for_call_superedge (const call_superedge &call_edge,
region_model_context *ctxt);
@ -781,7 +778,9 @@ class region_model_context
and use them to trigger sm-state transitions (e.g. transitions due
to ptrs becoming known to be NULL or non-NULL, rather than just
"unchecked") */
virtual void on_condition (tree lhs, enum tree_code op, tree rhs) = 0;
virtual void on_condition (const svalue *lhs,
enum tree_code op,
const svalue *rhs) = 0;
/* Hooks for clients to be notified when an unknown change happens
to SVAL (in response to a call to an unknown function). */
@ -812,9 +811,9 @@ public:
void on_liveness_change (const svalue_set &,
const region_model *) OVERRIDE {}
logger *get_logger () OVERRIDE { return NULL; }
void on_condition (tree lhs ATTRIBUTE_UNUSED,
void on_condition (const svalue *lhs ATTRIBUTE_UNUSED,
enum tree_code op ATTRIBUTE_UNUSED,
tree rhs ATTRIBUTE_UNUSED) OVERRIDE
const svalue *rhs ATTRIBUTE_UNUSED) OVERRIDE
{
}
void on_unknown_change (const svalue *sval ATTRIBUTE_UNUSED,

View File

@ -77,9 +77,9 @@ public:
void on_condition (sm_context *sm_ctxt,
const supernode *node,
const gimple *stmt,
tree lhs,
const svalue *lhs,
enum tree_code op,
tree rhs) const FINAL OVERRIDE;
const svalue *rhs) const FINAL OVERRIDE;
bool can_purge_p (state_t s) const FINAL OVERRIDE;
pending_diagnostic *on_leak (tree var) const FINAL OVERRIDE;
@ -381,19 +381,18 @@ void
fileptr_state_machine::on_condition (sm_context *sm_ctxt,
const supernode *node,
const gimple *stmt,
tree lhs,
const svalue *lhs,
enum tree_code op,
tree rhs) const
const svalue *rhs) const
{
if (!zerop (rhs))
if (!rhs->all_zeroes_p ())
return;
// TODO: has to be a FILE *, specifically
if (TREE_CODE (TREE_TYPE (lhs)) != POINTER_TYPE)
if (!any_pointer_p (lhs))
return;
// TODO: has to be a FILE *, specifically
if (TREE_CODE (TREE_TYPE (rhs)) != POINTER_TYPE)
if (!any_pointer_p (rhs))
return;
if (op == NE_EXPR)

View File

@ -375,9 +375,9 @@ public:
void on_condition (sm_context *sm_ctxt,
const supernode *node,
const gimple *stmt,
tree lhs,
const svalue *lhs,
enum tree_code op,
tree rhs) const FINAL OVERRIDE;
const svalue *rhs) const FINAL OVERRIDE;
bool can_purge_p (state_t s) const FINAL OVERRIDE;
pending_diagnostic *on_leak (tree var) const FINAL OVERRIDE;
@ -1863,11 +1863,11 @@ void
malloc_state_machine::on_condition (sm_context *sm_ctxt,
const supernode *node ATTRIBUTE_UNUSED,
const gimple *stmt,
tree lhs,
const svalue *lhs,
enum tree_code op,
tree rhs) const
const svalue *rhs) const
{
if (!zerop (rhs))
if (!rhs->all_zeroes_p ())
return;
if (!any_pointer_p (lhs))

View File

@ -37,6 +37,12 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/analyzer-logging.h"
#include "analyzer/sm.h"
#include "analyzer/pending-diagnostic.h"
#include "tristate.h"
#include "selftest.h"
#include "analyzer/call-string.h"
#include "analyzer/program-point.h"
#include "analyzer/store.h"
#include "analyzer/region-model.h"
#if ENABLE_ANALYZER
@ -61,9 +67,9 @@ public:
void on_condition (sm_context *sm_ctxt,
const supernode *node,
const gimple *stmt,
tree lhs,
const svalue *lhs,
enum tree_code op,
tree rhs) const FINAL OVERRIDE;
const svalue *rhs) const FINAL OVERRIDE;
bool can_purge_p (state_t s) const FINAL OVERRIDE;
};
@ -118,18 +124,22 @@ void
pattern_test_state_machine::on_condition (sm_context *sm_ctxt,
const supernode *node,
const gimple *stmt,
tree lhs,
const svalue *lhs,
enum tree_code op,
tree rhs) const
const svalue *rhs) const
{
if (stmt == NULL)
return;
if (!CONSTANT_CLASS_P (rhs))
tree rhs_cst = rhs->maybe_get_constant ();
if (!rhs_cst)
return;
pending_diagnostic *diag = new pattern_match (lhs, op, rhs);
sm_ctxt->warn (node, stmt, lhs, diag);
if (tree lhs_expr = sm_ctxt->get_diagnostic_tree (lhs))
{
pending_diagnostic *diag = new pattern_match (lhs_expr, op, rhs_cst);
sm_ctxt->warn (node, stmt, lhs_expr, diag);
}
}
bool

View File

@ -58,13 +58,6 @@ public:
const supernode *node,
const gimple *stmt) const FINAL OVERRIDE;
void on_condition (sm_context *sm_ctxt,
const supernode *node,
const gimple *stmt,
tree lhs,
enum tree_code op,
tree rhs) const FINAL OVERRIDE;
bool can_purge_p (state_t s) const FINAL OVERRIDE;
/* State for "sensitive" data, such as a password. */
@ -222,17 +215,6 @@ sensitive_state_machine::on_stmt (sm_context *sm_ctxt,
return false;
}
void
sensitive_state_machine::on_condition (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
const supernode *node ATTRIBUTE_UNUSED,
const gimple *stmt ATTRIBUTE_UNUSED,
tree lhs ATTRIBUTE_UNUSED,
enum tree_code op ATTRIBUTE_UNUSED,
tree rhs ATTRIBUTE_UNUSED) const
{
/* Empty. */
}
bool
sensitive_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const
{

View File

@ -81,13 +81,6 @@ public:
const supernode *node,
const gimple *stmt) const FINAL OVERRIDE;
void on_condition (sm_context *sm_ctxt,
const supernode *node,
const gimple *stmt,
tree lhs,
enum tree_code op,
tree rhs) const FINAL OVERRIDE;
bool can_purge_p (state_t s) const FINAL OVERRIDE;
/* These states are "global", rather than per-expression. */
@ -363,20 +356,6 @@ signal_state_machine::on_stmt (sm_context *sm_ctxt,
return false;
}
/* Implementation of state_machine::on_condition vfunc for
signal_state_machine. */
void
signal_state_machine::on_condition (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
const supernode *node ATTRIBUTE_UNUSED,
const gimple *stmt ATTRIBUTE_UNUSED,
tree lhs ATTRIBUTE_UNUSED,
enum tree_code op ATTRIBUTE_UNUSED,
tree rhs ATTRIBUTE_UNUSED) const
{
// Empty
}
bool
signal_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const
{

View File

@ -61,9 +61,9 @@ public:
void on_condition (sm_context *sm_ctxt,
const supernode *node,
const gimple *stmt,
tree lhs,
const svalue *lhs,
enum tree_code op,
tree rhs) const FINAL OVERRIDE;
const svalue *rhs) const FINAL OVERRIDE;
bool can_purge_p (state_t s) const FINAL OVERRIDE;
@ -281,9 +281,9 @@ void
taint_state_machine::on_condition (sm_context *sm_ctxt,
const supernode *node,
const gimple *stmt,
tree lhs,
const svalue *lhs,
enum tree_code op,
tree rhs ATTRIBUTE_UNUSED) const
const svalue *rhs ATTRIBUTE_UNUSED) const
{
if (stmt == NULL)
return;

View File

@ -35,6 +35,11 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "analyzer/sm.h"
#include "tristate.h"
#include "analyzer/call-string.h"
#include "analyzer/program-point.h"
#include "analyzer/store.h"
#include "analyzer/svalue.h"
#if ENABLE_ANALYZER
@ -48,6 +53,15 @@ any_pointer_p (tree var)
return POINTER_TYPE_P (TREE_TYPE (var));
}
/* Return true if SVAL has pointer or reference type. */
bool
any_pointer_p (const svalue *sval)
{
if (!sval->get_type ())
return false;
return POINTER_TYPE_P (sval->get_type ());
}
/* class state_machine::state. */

View File

@ -29,7 +29,8 @@ class state_machine;
class sm_context;
class pending_diagnostic;
extern bool any_pointer_p (tree var);
extern bool any_pointer_p (tree expr);
extern bool any_pointer_p (const svalue *sval);
/* An abstract base class for a state machine describing an API.
Manages a set of state objects, and has various virtual functions
@ -89,10 +90,14 @@ public:
{
}
virtual void on_condition (sm_context *sm_ctxt,
const supernode *node,
const gimple *stmt,
tree lhs, enum tree_code op, tree rhs) const = 0;
virtual void on_condition (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
const supernode *node ATTRIBUTE_UNUSED,
const gimple *stmt ATTRIBUTE_UNUSED,
const svalue *lhs ATTRIBUTE_UNUSED,
enum tree_code op ATTRIBUTE_UNUSED,
const svalue *rhs ATTRIBUTE_UNUSED) const
{
}
/* Return true if it safe to discard the given state (to help
when simplifying state objects).
@ -182,6 +187,8 @@ public:
/* Get the old state of VAR at STMT. */
virtual state_machine::state_t get_state (const gimple *stmt,
tree var) = 0;
virtual state_machine::state_t get_state (const gimple *stmt,
const svalue *) = 0;
/* Set the next state of VAR to be TO, recording the "origin" of the
state as ORIGIN.
Use STMT for location information. */
@ -189,6 +196,10 @@ public:
tree var,
state_machine::state_t to,
tree origin = NULL_TREE) = 0;
virtual void set_next_state (const gimple *stmt,
const svalue *var,
state_machine::state_t to,
tree origin = NULL_TREE) = 0;
/* Called by state_machine in response to pattern matches:
if VAR is in state FROM, transition it to state TO, potentially
@ -206,6 +217,18 @@ public:
set_next_state (stmt, var, to, origin);
}
void on_transition (const supernode *node ATTRIBUTE_UNUSED,
const gimple *stmt,
const svalue *var,
state_machine::state_t from,
state_machine::state_t to,
tree origin = NULL_TREE)
{
state_machine::state_t current = get_state (stmt, var);
if (current == from)
set_next_state (stmt, var, to, origin);
}
/* Called by state_machine in response to pattern matches:
issue a diagnostic D using NODE and STMT for location information. */
virtual void warn (const supernode *node, const gimple *stmt,
@ -220,6 +243,7 @@ public:
{
return expr;
}
virtual tree get_diagnostic_tree (const svalue *) = 0;
virtual state_machine::state_t get_global_state () const = 0;
virtual void set_global_state (state_machine::state_t) = 0;

View File

@ -557,6 +557,15 @@ svalue::maybe_fold_bits_within (tree,
return NULL;
}
/* Base implementation of svalue::all_zeroes_p.
Return true if this value is known to be all zeroes. */
bool
svalue::all_zeroes_p () const
{
return false;
}
/* class region_svalue : public svalue. */
/* Implementation of svalue::dump_to_pp vfunc for region_svalue. */
@ -742,6 +751,14 @@ constant_svalue::maybe_fold_bits_within (tree type,
return NULL;
}
/* Implementation of svalue::all_zeroes_p for constant_svalue. */
bool
constant_svalue::all_zeroes_p () const
{
return zerop (m_cst_expr);
}
/* class unknown_svalue : public svalue. */
/* Implementation of svalue::dump_to_pp vfunc for unknown_svalue. */
@ -1154,15 +1171,12 @@ repeated_svalue::accept (visitor *v) const
m_inner_svalue->accept (v);
}
/* Return true if this value is known to be all zeroes. */
/* Implementation of svalue::all_zeroes_p for repeated_svalue. */
bool
repeated_svalue::all_zeroes_p () const
{
if (tree cst = m_inner_svalue->maybe_get_constant ())
if (zerop (cst))
return true;
return false;
return m_inner_svalue->all_zeroes_p ();
}
/* Implementation of svalue::maybe_fold_bits_within vfunc

View File

@ -157,6 +157,8 @@ public:
const bit_range &subrange,
region_model_manager *mgr) const;
virtual bool all_zeroes_p () const;
protected:
svalue (complexity c, tree type)
: m_complexity (c), m_type (type)
@ -277,6 +279,8 @@ public:
const bit_range &subrange,
region_model_manager *mgr) const FINAL OVERRIDE;
bool all_zeroes_p () const FINAL OVERRIDE;
private:
tree m_cst_expr;
};
@ -858,7 +862,7 @@ public:
const svalue *get_outer_size () const { return m_outer_size; }
const svalue *get_inner_svalue () const { return m_inner_svalue; }
bool all_zeroes_p () const;
bool all_zeroes_p () const FINAL OVERRIDE;
const svalue *
maybe_fold_bits_within (tree type,

View File

@ -25,11 +25,8 @@ void test_2 (void *p, void *q)
return;
foo(p);
/* { dg-warning "pattern match on 'tmp1 == 0'" "tmp1 == 0" { target *-*-* } cond_2 } */
/* { dg-warning "pattern match on 'tmp2 == 0'" "tmp2 == 0" { target *-*-* } cond_2 } */
/* { dg-warning "pattern match on '<unknown> == 0'" "<unknown> == 0" { target *-*-* } cond_2 } */
/* { dg-warning "pattern match on '<unknown> != 0'" "<unknown> != 0" { target *-*-* } cond_2 } */
/* { dg-warning "pattern match on 'p != 0'" "p != 0" { target *-*-* } cond_2 } */
/* { dg-warning "pattern match on 'tmp1 | tmp2 != 0'" "tmp1 | tmp2 != 0" { target *-*-* } cond_2 } */
/* { dg-warning "pattern match on 'q != 0'" "q != 0" { target *-*-* } cond_2 } */
}
@ -44,10 +41,7 @@ void test_3 (void *p, void *q)
return;
foo(p);
/* { dg-warning "pattern match on 'tmp1 != 0'" "tmp1 != 0" { target *-*-* } cond_3 } */
/* { dg-warning "pattern match on 'tmp2 != 0'" "tmp2 != 0" { target *-*-* } cond_3 } */
/* { dg-warning "pattern match on '<unknown> == 0'" "<unknown> == 0" { target *-*-* } cond_3 } */
/* { dg-warning "pattern match on '<unknown> != 0'" "<unknown> != 0" { target *-*-* } cond_3 } */
/* { dg-warning "pattern match on 'p == 0'" "p == 0" { target *-*-* } cond_3 } */
/* { dg-warning "pattern match on 'tmp1 & tmp2 == 0'" "tmp1 & tmp2 == 0" { target *-*-* } cond_3 } */
/* { dg-warning "pattern match on 'q == 0'" "q == 0" { target *-*-* } cond_3 } */
}

View File

@ -53,13 +53,6 @@ public:
const supernode *node,
const gimple *stmt) const FINAL OVERRIDE;
void on_condition (sm_context *sm_ctxt,
const supernode *node,
const gimple *stmt,
tree lhs,
enum tree_code op,
tree rhs) const FINAL OVERRIDE;
bool can_purge_p (state_t s) const FINAL OVERRIDE;
void check_for_pyobject_usage_without_gil (sm_context *sm_ctxt,
@ -365,20 +358,6 @@ gil_state_machine::on_stmt (sm_context *sm_ctxt,
return false;
}
/* Implementation of state_machine::on_condition vfunc for
gil_state_machine. */
void
gil_state_machine::on_condition (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
const supernode *node ATTRIBUTE_UNUSED,
const gimple *stmt ATTRIBUTE_UNUSED,
tree lhs ATTRIBUTE_UNUSED,
enum tree_code op ATTRIBUTE_UNUSED,
tree rhs ATTRIBUTE_UNUSED) const
{
// Empty
}
bool
gil_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const
{