From 0a17896fe6fdbbde1f9d3ffbb10a4f3bfa8960f9 Mon Sep 17 00:00:00 2001 From: Gabor Marton Date: Thu, 11 Nov 2021 17:12:24 +0100 Subject: [PATCH] [Analyzer][Core] Make SValBuilder to better simplify svals with 3 symbols in the tree Add the capability to simplify more complex constraints where there are 3 symbols in the tree. In this change I extend simplifySVal to query constraints of children sub-symbols in a symbol tree. (The constraint for the parent is asked in getKnownValue.) Differential Revision: https://reviews.llvm.org/D103317 --- .../StaticAnalyzer/Core/SimpleSValBuilder.cpp | 32 ++++++-- .../svalbuilder-simplify-compound-svals.cpp | 76 +++++++++++++++++++ clang/test/Analysis/taint-tester.c | 7 +- 3 files changed, 105 insertions(+), 10 deletions(-) create mode 100644 clang/test/Analysis/svalbuilder-simplify-compound-svals.cpp diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index e179ecc33481..4ca35dd06ae5 100644 --- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -1102,7 +1102,6 @@ const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, if (SymbolRef Sym = V.getAsSymbol()) return state->getConstraintManager().getSymVal(state, Sym); - // FIXME: Add support for SymExprs. return nullptr; } @@ -1134,6 +1133,24 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { return cache(Sym, SVB.makeSymbolVal(Sym)); } + // Return the known const value for the Sym if available, or return Undef + // otherwise. + SVal getConst(SymbolRef Sym) { + const llvm::APSInt *Const = + State->getConstraintManager().getSymVal(State, Sym); + if (Const) + return Loc::isLocType(Sym->getType()) ? (SVal)SVB.makeIntLocVal(*Const) + : (SVal)SVB.makeIntVal(*Const); + return UndefinedVal(); + } + + SVal getConstOrVisit(SymbolRef Sym) { + const SVal Ret = getConst(Sym); + if (Ret.isUndef()) + return Visit(Sym); + return Ret; + } + public: Simplifier(ProgramStateRef State) : State(State), SVB(State->getStateManager().getSValBuilder()) {} @@ -1147,15 +1164,14 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { return SVB.makeSymbolVal(S); } - // TODO: Support SymbolCast. Support IntSymExpr when/if we actually - // start producing them. + // TODO: Support SymbolCast. SVal VisitSymIntExpr(const SymIntExpr *S) { auto I = Cached.find(S); if (I != Cached.end()) return I->second; - SVal LHS = Visit(S->getLHS()); + SVal LHS = getConstOrVisit(S->getLHS()); if (isUnchanged(S->getLHS(), LHS)) return skip(S); @@ -1187,9 +1203,10 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { if (I != Cached.end()) return I->second; - SVal RHS = Visit(S->getRHS()); + SVal RHS = getConstOrVisit(S->getRHS()); if (isUnchanged(S->getRHS(), RHS)) return skip(S); + SVal LHS = SVB.makeIntVal(S->getLHS()); return cache( S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType())); @@ -1208,8 +1225,9 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { Loc::isLocType(S->getRHS()->getType())) return skip(S); - SVal LHS = Visit(S->getLHS()); - SVal RHS = Visit(S->getRHS()); + SVal LHS = getConstOrVisit(S->getLHS()); + SVal RHS = getConstOrVisit(S->getRHS()); + if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS)) return skip(S); diff --git a/clang/test/Analysis/svalbuilder-simplify-compound-svals.cpp b/clang/test/Analysis/svalbuilder-simplify-compound-svals.cpp new file mode 100644 index 000000000000..0417507476ee --- /dev/null +++ b/clang/test/Analysis/svalbuilder-simplify-compound-svals.cpp @@ -0,0 +1,76 @@ +// RUN: %clang_analyze_cc1 %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -verify + +// Here we test whether the SValBuilder is capable to simplify existing +// compound SVals (where there are at leaset 3 symbols in the tree) based on +// newly added constraints. + +void clang_analyzer_eval(bool); +void clang_analyzer_warnIfReached(); + +void test_left_tree_constrained(int x, int y, int z) { + if (x + y + z != 0) + return; + if (x + y != 0) + return; + clang_analyzer_eval(x + y + z == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(x + y == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(z == 0); // expected-warning{{TRUE}} + x = y = z = 1; + return; +} + +void test_right_tree_constrained(int x, int y, int z) { + if (x + y * z != 0) + return; + if (y * z != 0) + return; + clang_analyzer_eval(x + y * z == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(y * z == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} + return; +} + +void test_left_tree_constrained_minus(int x, int y, int z) { + if (x - y - z != 0) + return; + if (x - y != 0) + return; + clang_analyzer_eval(x - y - z == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(x - y == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(z == 0); // expected-warning{{TRUE}} + x = y = z = 1; + return; +} + +void test_SymInt_constrained(int x, int y, int z) { + if (x * y * z != 4) + return; + if (z != 2) + return; + if (x * y == 3) { + clang_analyzer_warnIfReached(); // no-warning + return; + } + (void)(x * y * z); +} + +void test_SValBuilder_simplifies_IntSym(int x, int y, int z) { + // Most IntSym BinOps are transformed to SymInt in SimpleSValBuilder. + // Division is one exception. + x = 77 / (y + z); + if (y + z != 1) + return; + clang_analyzer_eval(x == 77); // expected-warning{{TRUE}} + (void)(x * y * z); +} + +void recurring_symbol(int b) { + if (b * b != b) + if ((b * b) * b * b != (b * b) * b) + if (b * b == 1) // no-crash (assert should not fire) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} diff --git a/clang/test/Analysis/taint-tester.c b/clang/test/Analysis/taint-tester.c index 3a8cc1825a02..cbb2ad6c9744 100644 --- a/clang/test/Analysis/taint-tester.c +++ b/clang/test/Analysis/taint-tester.c @@ -59,7 +59,7 @@ void taintTracking(int x) { int tty = xy.y; // expected-warning + {{tainted}} } -void BitwiseOp(int in, char inn) { +void BitwiseOp(int in, char inn, int zz) { // Taint on bitwise operations, integer to integer cast. int m; int x = 0; @@ -67,11 +67,12 @@ void BitwiseOp(int in, char inn) { int y = (in << (x << in)) * 5;// expected-warning + {{tainted}} // The next line tests integer to integer cast. int z = y & inn; // expected-warning + {{tainted}} - if (y == 5) // expected-warning + {{tainted}} + if (y == zz) { // expected-warning + {{tainted}} m = z | z;// expected-warning + {{tainted}} + } else m = inn; - int mm = m; // expected-warning + {{tainted}} + int mm = m; // expected-warning 1 {{tainted}} } // Test getenv.