[PATCH] D110910: [analyzer][solver] Fix CmpOpTable handling bug

Gabor Marton via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 1 01:34:36 PDT 2021


martong created this revision.
martong added reviewers: ASDenysPetrov, steakhal, manas, vsavchenko, NoQ.
Herald added subscribers: gamesh411, dkrupp, donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, rnkovacs, szepet, baloghadamsoftware, xazax.hun, whisperity.
Herald added a reviewer: Szelethus.
martong requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

There is an error in the implementation of the logic of reaching the `Unknonw` tristate in CmpOpTable.

  void cmp_op_table_unknownX2(int x, int y, int z) {
    if (x >= y) {
                      // x >= y    [1, 1]
      if (x + z < y)
        return;
                      // x + z < y [0, 0]
      if (z != 0)
        return;
                      // x < y     [0, 0]
      clang_analyzer_eval(x > y);  // expected-warning{{TRUE}} expected-warning{{FALSE}}
    }
  }

We miss the `FALSE` warning because the false branch is infeasible.

We have to exploit simplification to discover the bug. If we had `x < y`
as the second condition then the analyzer would return the parent state
on the false path and the new constraint would not be part of the State.
But adding `z` to the condition makes both paths feasible.

The root cause of the bug is that we reach the `Unknown` tristate
twice, but in both occasions we reach the same `Op` that is `>=` in the
test case. So, we reached `>=` twice, but we never reached `!=`, thus
querying the `Unknonw2x` column with `getCmpOpStateForUnknownX2` is
wrong.

The solution is to ensure that we reached both **different** `Op`s once.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D110910

Files:
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/test/Analysis/constraint_manager_conditions.cpp


Index: clang/test/Analysis/constraint_manager_conditions.cpp
===================================================================
--- clang/test/Analysis/constraint_manager_conditions.cpp
+++ clang/test/Analysis/constraint_manager_conditions.cpp
@@ -211,3 +211,17 @@
       clang_analyzer_eval(y != x); // expected-warning{{FALSE}}
     }
 }
+
+// Test the logic of reaching the `Unknonw` tristate in CmpOpTable.
+void cmp_op_table_unknownX2(int x, int y, int z) {
+  if (x >= y) {
+                    // x >= y    [1, 1]
+    if (x + z < y)
+      return;
+                    // x + z < y [0, 0]
+    if (z != 0)
+      return;
+                    // x < y     [0, 0]
+    clang_analyzer_eval(x > y);  // expected-warning{{TRUE}} expected-warning{{FALSE}}
+  }
+}
Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -1126,7 +1126,7 @@
 
     SymbolManager &SymMgr = State->getSymbolManager();
 
-    int UnknownStates = 0;
+    llvm::SmallSet<BinaryOperatorKind, 2> QueriedToUnknown;
 
     // Loop goes through all of the columns exept the last one ('UnknownX2').
     // We treat `UnknownX2` column separately at the end of the loop body.
@@ -1163,7 +1163,8 @@
           CmpOpTable.getCmpOpState(CurrentOP, QueriedOP);
 
       if (BranchState == OperatorRelationsTable::Unknown) {
-        if (++UnknownStates == 2)
+        QueriedToUnknown.insert(QueriedOP);
+        if (QueriedToUnknown.size() == 2)
           // If we met both Unknown states.
           // if (x <= y)    // assume true
           //   if (x != y)  // assume true


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D110910.376447.patch
Type: text/x-patch
Size: 1749 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20211001/cc0919f4/attachment.bin>


More information about the cfe-commits mailing list