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,
*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
{
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;
= 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,
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
{
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 ();
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;
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;
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 (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
+ if (gimple_call_internal_p (call))
{
- call_details cd (call, this, ctxt);
+ 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))
+ {
/* The various impl_call_* member functions are implemented
in region-model-impl-calls.cc.
Having them split out into separate functions makes it easier
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))
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.
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 ())
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
/* If we have ®ION == 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;
}
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. */
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);
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). */
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,
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;
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)
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;
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))
#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
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;
};
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
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. */
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
{
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. */
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
{
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;
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;
#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
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. */
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
{
}
- 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).
/* 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. */
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
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,
{
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;
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. */
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. */
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
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)
const bit_range &subrange,
region_model_manager *mgr) const FINAL OVERRIDE;
+ bool all_zeroes_p () const FINAL OVERRIDE;
+
private:
tree m_cst_expr;
};
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,
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 } */
}
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 } */
}
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,
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
{