diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -108,6 +108,8 @@ /// \note: Currently only support integral casts. SVal simplifySymbolCast(nonloc::SymbolVal V, QualType CastTy); + SVal simplifySymbolCast(SymbolRef SE, QualType CastTy); + public: SValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, ProgramStateManager &stateMgr); diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp --- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -773,6 +773,16 @@ // QualType elemTy = cast(originalTy)->getElementType(); // QualType pointerTy = C.getPointerType(elemTy); } + + AnalyzerOptions &Opts = + StateMgr.getOwningEngine().getAnalysisManager().getAnalyzerOptions(); + if (Opts.ShouldSupportSymbolicIntegerCasts) { + const MemRegion *R = V.getRegion(); + if (R && !OriginalTy.isNull()) + if (const auto *SR = dyn_cast(R)) + return simplifySymbolCast(SR->getSymbol(), CastTy); + } + const unsigned BitWidth = Context.getIntWidth(CastTy); return makeLocAsInteger(Val.castAs(), BitWidth); } @@ -1009,8 +1019,12 @@ return V; } -SVal clang::ento::SValBuilder::simplifySymbolCast(nonloc::SymbolVal V, +SVal clang::ento::SValBuilder::simplifySymbolCast(SymbolRef SE, QualType CastTy) { + QualType T = Context.getCanonicalType(SE->getType()); + if (!isa(SE)) + return makeNonLoc(SE, T, CastTy); + // We use seven conditions to recognize a simplification case. // For the clarity let `CastTy` be `C`, SE->getType() - `T`, root type - `R`, // prefix `u` for unsigned, `s` for signed, no prefix - any sign: @@ -1044,15 +1058,6 @@ // (uint)(ushort)(ushort x) -> (uint)(ushort x) // (llong)(ulong)(uint x) -> (llong)(uint x) (sizeof(ulong) == sizeof(uint)) - SymbolRef SE = V.getSymbol(); - QualType T = Context.getCanonicalType(SE->getType()); - - if (T == CastTy) - return V; - - if (!isa(SE)) - return makeNonLoc(SE, T, CastTy); - SymbolRef RootSym = cast(SE)->getOperand(); QualType RT = RootSym->getType().getCanonicalType(); @@ -1080,3 +1085,14 @@ return makeNonLoc(SE, T, CastTy); } + +SVal clang::ento::SValBuilder::simplifySymbolCast(nonloc::SymbolVal V, + QualType CastTy) { + assert(CastTy == Context.getCanonicalType(CastTy) && + "The CastTy shall be canonical!"); + SymbolRef SE = V.getSymbol(); + QualType T = Context.getCanonicalType(SE->getType()); + if (T == CastTy) + return V; + return simplifySymbolCast(SE, CastTy); +} diff --git a/clang/test/Analysis/produce-ptr-to-integer-symbolcast.cpp b/clang/test/Analysis/produce-ptr-to-integer-symbolcast.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/produce-ptr-to-integer-symbolcast.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_analyze_cc1 %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config support-symbolic-integer-casts=true \ +// RUN: -triple x86_64-pc-linux-gnu \ +// RUN: -verify + +template +void clang_analyzer_dump(T); + +void test_memory_region_to_integer_cast(int *p) { + long p_int = (long)p; + clang_analyzer_dump(p); // expected-warning{{&SymRegion{reg_$0}}} + clang_analyzer_dump(p_int); // expected-warning{{(long) (reg_$0)}} +} + +void test_label_to_integer_cast(bool coin) { + int x = 0; +p: ++x; + if (coin) + goto p; + + // FIXME, below we should produce a SymbolCast instead of the LocAsInteger. + // However, for that we'd need to be able to store a loc::GotoLabel SVal + // inside the SymbolCast. But currently we can put only another SymExpr + // inside a SymbolCast. + + // Use of GNU address-of-label extension. + clang_analyzer_dump((long)&&p); // expected-warning{{&&p [as 64 bit integer]}} +} diff --git a/clang/test/Analysis/symbol-simplification-mem-region-to-int-cast.cpp b/clang/test/Analysis/symbol-simplification-mem-region-to-int-cast.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/symbol-simplification-mem-region-to-int-cast.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_analyze_cc1 %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config support-symbolic-integer-casts=true \ +// RUN: -verify + +template +void clang_analyzer_dump(T); + +void clang_analyzer_eval(bool); + +void test_memory_region_to_integer_cast_and_symbol_simplification(int *p) { + long p_as_integer = (long)p; + if (42 - p_as_integer < 42) + return; + // 42 - p_as_integer >= 42 + // p_as_integer <= 0 + + if (p) + return; + clang_analyzer_eval(p == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(p_as_integer == 0); // expected-warning{{UNKNOWN}} + + if (p_as_integer) + return; + clang_analyzer_eval(p == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(p_as_integer == 0); // expected-warning{{TRUE}} + + (void)p_as_integer; + (void)p; +}