Author: vabridgers
Date: 2025-09-22T09:38:16-05:00
New Revision: 78180892d5e869d39152c92438571c56d6e0daef

URL: 
https://github.com/llvm/llvm-project/commit/78180892d5e869d39152c92438571c56d6e0daef
DIFF: 
https://github.com/llvm/llvm-project/commit/78180892d5e869d39152c92438571c56d6e0daef.diff

LOG: [analyzer] Hotfix a boolean conversion crash in the Z3 SMTConv (#158276)

If a UnarySymExpr with an arithmetic negation of a logical operation to
obtain a SMTRefExpr, the Z3 engine will crash. Since an arithmetic
negation of a logical operation makes no sense and has no effect, the
arithmetic negation is detected and removed to avoid the crash in Z3.

This shows up following this C snippet

```c++
1: void bb(int a) {
2:  if (-(&c && a)) {
3:     int *d;
4:     *d; // expected-warning{{Dereference of undefined pointer value}}
5:  }
6: }
```

Line 2 is expressed as SymExpr -((reg_$1<int a>) != 0) , which is then
attempted to be converted to SMTRefExpr (not (= reg_$1 #x00000000)).
This does not make sense to Z3 since a logical expression cannot be
arithmetically negated.

A solution is to detect that the result of a comparison is attempted to
be arithmetically negated and remove that arithmetic negation since the
negation of a bool is the same as the positive of a bool. Bool's have no
sign, they are only True or False.

Co-authored-by: Vince Bridgers <vince.a.bridg...@ericsson.com>

Added: 
    

Modified: 
    clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
    clang/test/Analysis/z3-unarysymexpr.c

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h 
b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
index a6cb6c0f12a8c..7f25223d232cf 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
@@ -455,6 +455,20 @@ class SMTConv {
       QualType OperandTy;
       llvm::SMTExprRef OperandExp =
           getSymExpr(Solver, Ctx, USE->getOperand(), &OperandTy, 
hasComparison);
+
+      if (const BinarySymExpr *BSE =
+              dyn_cast<BinarySymExpr>(USE->getOperand())) {
+        if (USE->getOpcode() == UO_Minus &&
+            BinaryOperator::isComparisonOp(BSE->getOpcode()))
+          // The comparison operator yields a boolean value in the Z3
+          // language and applying the unary minus operator on a boolean
+          // crashes Z3. However, the unary minus does nothing in this
+          // context (a number is truthy if and only if its negative is
+          // truthy), so let's just ignore the unary minus.
+          // TODO: Replace this with a more general solution.
+          return OperandExp;
+      }
+
       llvm::SMTExprRef UnaryExp =
           OperandTy->isRealFloatingType()
               ? fromFloatUnOp(Solver, USE->getOpcode(), OperandExp)

diff  --git a/clang/test/Analysis/z3-unarysymexpr.c 
b/clang/test/Analysis/z3-unarysymexpr.c
index ed9ba72468422..89c043e5befab 100644
--- a/clang/test/Analysis/z3-unarysymexpr.c
+++ b/clang/test/Analysis/z3-unarysymexpr.c
@@ -1,5 +1,8 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify 
%s \
 // RUN:  -analyzer-constraints=z3 
+// RUN: %clang_analyze_cc1 -verify %s \
+// RUN:  -analyzer-checker=core,debug.ExprInspection \
+// RUN:  -analyzer-config crosscheck-with-z3=true
 
 // REQUIRES: Z3
 //
@@ -7,9 +10,40 @@
 // that this no longer happens.
 //
 
-// expected-no-diagnostics
 int negate(int x, int y) {
   if ( ~(x && y))
     return 0;
   return 1;
 }
+
+// Z3 is presented with a SymExpr like this : -((reg_$0<int a>) != 0) :
+// from the Z3 refutation wrapper, that it attempts to convert to a
+// SMTRefExpr, then crashes inside of Z3. The "not zero" portion
+// of that expression is converted to the SMTRefExpr
+// "(not (= reg_$1 #x00000000))", which is a boolean result then the
+// "negative" operator (unary '-', UO_Minus) is attempted to be applied which
+// then causes Z3 to crash. The accompanying patch just strips the negative
+// operator before submitting to Z3 to avoid the crash.
+//
+// TODO: Find the root cause of this and fix it in symbol manager
+//
+void c();
+
+int z3crash(int a, int b) {
+  b = a || b;
+  return (-b == a) / a; // expected-warning{{Division by zero 
[core.DivideZero]}}
+}
+
+// Floats are handled specifically, and 
diff erently in the Z3 refutation layer
+// Just cover that code path
+int z3_nocrash(float a, float b) {
+  b = a || b;
+  return (-b == a) / a;
+}
+
+int z3_crash2(int a) {
+  int *d;
+  if (-(&c && a))
+    return *d; // expected-warning{{Dereference of undefined pointer value}}
+  return 0;
+}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to