Skip to content

Commit 7818089

Browse files
vabridgersVince Bridgers
andauthored
[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 <[email protected]>
1 parent 56b6624 commit 7818089

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,20 @@ class SMTConv {
455455
QualType OperandTy;
456456
llvm::SMTExprRef OperandExp =
457457
getSymExpr(Solver, Ctx, USE->getOperand(), &OperandTy, hasComparison);
458+
459+
if (const BinarySymExpr *BSE =
460+
dyn_cast<BinarySymExpr>(USE->getOperand())) {
461+
if (USE->getOpcode() == UO_Minus &&
462+
BinaryOperator::isComparisonOp(BSE->getOpcode()))
463+
// The comparison operator yields a boolean value in the Z3
464+
// language and applying the unary minus operator on a boolean
465+
// crashes Z3. However, the unary minus does nothing in this
466+
// context (a number is truthy if and only if its negative is
467+
// truthy), so let's just ignore the unary minus.
468+
// TODO: Replace this with a more general solution.
469+
return OperandExp;
470+
}
471+
458472
llvm::SMTExprRef UnaryExp =
459473
OperandTy->isRealFloatingType()
460474
? fromFloatUnOp(Solver, USE->getOpcode(), OperandExp)
Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,49 @@
11
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s \
22
// RUN: -analyzer-constraints=z3
3+
// RUN: %clang_analyze_cc1 -verify %s \
4+
// RUN: -analyzer-checker=core,debug.ExprInspection \
5+
// RUN: -analyzer-config crosscheck-with-z3=true
36

47
// REQUIRES: Z3
58
//
69
// Previously Z3 analysis crashed when it encountered an UnarySymExpr, validate
710
// that this no longer happens.
811
//
912

10-
// expected-no-diagnostics
1113
int negate(int x, int y) {
1214
if ( ~(x && y))
1315
return 0;
1416
return 1;
1517
}
18+
19+
// Z3 is presented with a SymExpr like this : -((reg_$0<int a>) != 0) :
20+
// from the Z3 refutation wrapper, that it attempts to convert to a
21+
// SMTRefExpr, then crashes inside of Z3. The "not zero" portion
22+
// of that expression is converted to the SMTRefExpr
23+
// "(not (= reg_$1 #x00000000))", which is a boolean result then the
24+
// "negative" operator (unary '-', UO_Minus) is attempted to be applied which
25+
// then causes Z3 to crash. The accompanying patch just strips the negative
26+
// operator before submitting to Z3 to avoid the crash.
27+
//
28+
// TODO: Find the root cause of this and fix it in symbol manager
29+
//
30+
void c();
31+
32+
int z3crash(int a, int b) {
33+
b = a || b;
34+
return (-b == a) / a; // expected-warning{{Division by zero [core.DivideZero]}}
35+
}
36+
37+
// Floats are handled specifically, and differently in the Z3 refutation layer
38+
// Just cover that code path
39+
int z3_nocrash(float a, float b) {
40+
b = a || b;
41+
return (-b == a) / a;
42+
}
43+
44+
int z3_crash2(int a) {
45+
int *d;
46+
if (-(&c && a))
47+
return *d; // expected-warning{{Dereference of undefined pointer value}}
48+
return 0;
49+
}

0 commit comments

Comments
 (0)