]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
analyzer: remove add_any_constraints_from_ssa_def_stmt
authorDavid Malcolm <dmalcolm@redhat.com>
Wed, 7 Jul 2021 23:29:30 +0000 (19:29 -0400)
committerDavid Malcolm <dmalcolm@redhat.com>
Wed, 7 Jul 2021 23:29:30 +0000 (19:29 -0400)
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>
17 files changed:
gcc/analyzer/diagnostic-manager.cc
gcc/analyzer/engine.cc
gcc/analyzer/exploded-graph.h
gcc/analyzer/region-model.cc
gcc/analyzer/region-model.h
gcc/analyzer/sm-file.cc
gcc/analyzer/sm-malloc.cc
gcc/analyzer/sm-pattern-test.cc
gcc/analyzer/sm-sensitive.cc
gcc/analyzer/sm-signal.cc
gcc/analyzer/sm-taint.cc
gcc/analyzer/sm.cc
gcc/analyzer/sm.h
gcc/analyzer/svalue.cc
gcc/analyzer/svalue.h
gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c
gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c

index 7eb4ed8a4f2d9148b84ae676a423908d73d73738..b7d263b42179d92340bed9fb133e9bc90419a6d7 100644 (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;
index 4456d9b828bfa4d1a4e858f2fa889a5d063ed3f7..01b83a4ef282ec4feb8544477a84d7e00a0e80d6 100644 (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;
index eb1baefad692972b011a32cc8cba34fbb653a7df..2d25e5e51678514479a73539f85e7fa374782498 100644 (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;
 
index 4fb6bc9f747088ae65e484079334b9cdce0c92c3..acbbd112543e2464ad0c5bde85f593e2bfef7800 100644 (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 (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
@@ -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.  */
index b42852b3db9c9de6ec2fc208948e99428720f348..cf5232dfab8e21dde03202be6521f87e0428fa32 100644 (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,
index 3a5f95def345bc22b8c7612e6f405047b62ad19f..b40a9a1edb939252d529ddd56df98ed2731d2e2b 100644 (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)
index a1582ca2f1fe0bde26efc9188a4df60a49b40e05..40e64b3630f938cc875b8b86bdea8b5bb3f874d0 100644 (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))
index 43b847559f8d5862cac2271d61ae800660a58aee..4e285492d048de88ab895ef05766dfc77803628d 100644 (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
index 9703f7e791680e91feb454c3d2da639c8b1d2aa0..4add55e91fb9a78a8e5a3031449e7b6ee4da502d 100644 (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
 {
index 42be8094997f59a6e43c496ed16acce37ac576e0..fcbf322f502c9a8b68e8ff1453f85ede72cb9a52 100644 (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
 {
index e2460f9cf5c0a275ae5f000991cb50090d62712d..721d3eabb8ff4c5db62887eb2bdd33614d45a54a 100644 (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;
index 2d227dd1be034344d1fb1a9f5b20d036ca4f8b12..db07bf31d295aaabc7aa3df1415bd705327d163b 100644 (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.  */
 
index 8d4d030394a9b86aa2fefe88605f765a97fd1e3c..6bb036e343b6a009a69612c1dd37b8e71d00a9fe 100644 (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;
index 6c8afef461ba52c8fbf6aea69efa5ea8b6273477..70c23f016f9c8b95c2c3b2814dc2b8179dd459fd 100644 (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
index 3965a5f805db1eee71bf17036d9136186e473543..5552fcf4c992eb949b7581737bf2c4dacbf08428 100644 (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,
index f5424f526f74caf6bc4902e29eebea54519ef9c7..7c8d1b33fc98d3e9d24ce4d975a7fdfaa39ed41c 100644 (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 } */
 }
index 05133d5250e881aa74229ce789e96c3aa083daff..61dd490436a8db4764f91b6a2181bc933ad47b6e 100644 (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
 {