From 160798ab9be87a9be323966c5f5816a5b7ca818f Mon Sep 17 00:00:00 2001 From: Gabor Marton Date: Thu, 26 May 2022 17:41:51 +0200 Subject: [PATCH] [analyzer] Handle SymbolCast in SValBuilder Make the SimpleSValBuilder to be able to look up and use a constraint for an operand of a SymbolCast, when the operand is constrained to a const value. This part of the SValBuilder is responsible for constant folding. We need this constant folding, so the engine can work with less symbols, this way it can be more efficient. Whenever a symbol is constrained with a constant then we substitute the symbol with the corresponding integer. If a symbol is constrained with a range, then the symbol is kept and we fall-back to use the range based constraint manager, which is not that efficient. This patch is the natural extension of the existing constant folding machinery with the support of SymbolCast symbols. Differential Revision: https://reviews.llvm.org/D126481 --- .../lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp | 14 ++++-- clang/test/Analysis/svalbuilder-casts.cpp | 58 ++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 clang/test/Analysis/svalbuilder-casts.cpp diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 1b0c377..1232dcc 100644 --- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -1278,8 +1278,6 @@ SVal SimpleSValBuilder::simplifySValOnce(ProgramStateRef State, SVal V) { return SVB.makeSymbolVal(S); } - // TODO: Support SymbolCast. - SVal VisitSymIntExpr(const SymIntExpr *S) { auto I = Cached.find(S); if (I != Cached.end()) @@ -1349,7 +1347,17 @@ SVal SimpleSValBuilder::simplifySValOnce(ProgramStateRef State, SVal V) { S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType())); } - // FIXME add VisitSymbolCast + SVal VisitSymbolCast(const SymbolCast *S) { + auto I = Cached.find(S); + if (I != Cached.end()) + return I->second; + const SymExpr *OpSym = S->getOperand(); + SVal OpVal = getConstOrVisit(OpSym); + if (isUnchanged(OpSym, OpVal)) + return skip(S); + + return cache(S, SVB.evalCast(OpVal, S->getType(), OpSym->getType())); + } SVal VisitUnarySymExpr(const UnarySymExpr *S) { auto I = Cached.find(S); diff --git a/clang/test/Analysis/svalbuilder-casts.cpp b/clang/test/Analysis/svalbuilder-casts.cpp new file mode 100644 index 0000000..4cee989 --- /dev/null +++ b/clang/test/Analysis/svalbuilder-casts.cpp @@ -0,0 +1,58 @@ +// RUN: %clang_analyze_cc1 %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=debug.ExprInspection \ +// RUN: -analyzer-config support-symbolic-integer-casts=true \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -triple x86_64-unknown-linux-gnu \ +// RUN: -verify + +// Test that the SValBuilder is able to look up and use a constraint for an +// operand of a SymbolCast, when the operand is constrained to a const value. + +void clang_analyzer_eval(bool); + +extern void abort() __attribute__((__noreturn__)); +#define assert(expr) ((expr) ? (void)(0) : abort()) + +void test(int x) { + // Constrain a SymSymExpr to a constant value. + assert(x * x == 1); + // It is expected to be able to get the constraint for the operand of the + // cast. + clang_analyzer_eval((char)(x * x) == 1); // expected-warning{{TRUE}} + clang_analyzer_eval((long)(x * x) == 1); // expected-warning{{TRUE}} +} + +void test1(int x, int y) { + // Even if two lower bytes of `x` equal to zero, it doesn't mean that + // the entire `x` is zero. We are not able to know the exact value of x. + // It can be one of 65536 possible values like + // [0, 65536, -65536, 131072, -131072, ...]. To avoid huge range sets we + // still assume `x` in the range [INT_MIN, INT_MAX]. + assert((short)x == 0); // Lower two bytes are set to 0. + + static_assert((short)65536 == 0, ""); + static_assert((short)-65536 == 0, ""); + static_assert((short)131072 == 0, ""); + static_assert((short)-131072 == 0, ""); + clang_analyzer_eval(x == 0); // expected-warning{{UNKNOWN}} + + // These are not truncated to short as zero. + static_assert((short)1 != 0, ""); + clang_analyzer_eval(x == 1); // expected-warning{{FALSE}} + static_assert((short)-1 != 0, ""); + clang_analyzer_eval(x == -1); // expected-warning{{FALSE}} + static_assert((short)65537 != 0, ""); + clang_analyzer_eval(x == 65537); // expected-warning{{FALSE}} + static_assert((short)-65537 != 0, ""); + clang_analyzer_eval(x == -65537); // expected-warning{{FALSE}} + static_assert((short)131073 != 0, ""); + clang_analyzer_eval(x == 131073); // expected-warning{{FALSE}} + static_assert((short)-131073 != 0, ""); + clang_analyzer_eval(x == -131073); // expected-warning{{FALSE}} + + // Check for implicit cast. + short s = y; + assert(s == 0); + clang_analyzer_eval(y == 0); // expected-warning{{UNKNOWN}} +} -- 2.7.4