Author: Charusso Date: 2021-04-05T19:17:52+02:00 New Revision: 89d210fe1a7a1c6cbf926df0595b6f107bc491d5
URL: https://github.com/llvm/llvm-project/commit/89d210fe1a7a1c6cbf926df0595b6f107bc491d5 DIFF: https://github.com/llvm/llvm-project/commit/89d210fe1a7a1c6cbf926df0595b6f107bc491d5.diff LOG: [analyzer] DynamicSize: Debug facility This patch adds two debug functions to ExprInspectionChecker to dump out the dynamic extent and element count of symbolic values: dumpExtent(), dumpElementCount(). Added: clang/test/Analysis/memory-model.cpp Modified: clang/docs/analyzer/developer-docs/DebugChecks.rst clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp clang/test/Analysis/expr-inspection.cpp Removed: ################################################################################ diff --git a/clang/docs/analyzer/developer-docs/DebugChecks.rst b/clang/docs/analyzer/developer-docs/DebugChecks.rst index 45985a1dfd79..29ab6c89488f 100644 --- a/clang/docs/analyzer/developer-docs/DebugChecks.rst +++ b/clang/docs/analyzer/developer-docs/DebugChecks.rst @@ -297,6 +297,19 @@ ExprInspection checks return n; } +- ``clang_analyzer_dumpExtent(a single argument of any type)`` +- ``clang_analyzer_dumpElementCount(a single argument of any type)`` + + Dumps out the extent and the element count of the argument. + + Example usage:: + + void array() { + int a[] = {1, 3}; + clang_analyzer_dumpExtent(a); // expected-warning {{8 S64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{2 S64b}} + } + Statistics ========== diff --git a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index c0167b53ae26..242815d937df 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -22,8 +22,8 @@ using namespace clang; using namespace ento; namespace { -class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols, - check::EndAnalysis> { +class ExprInspectionChecker + : public Checker<eval::Call, check::DeadSymbols, check::EndAnalysis> { mutable std::unique_ptr<BugType> BT; // These stats are per-analysis, not per-branch, hence they shouldn't @@ -44,6 +44,8 @@ class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols, void analyzerExplain(const CallExpr *CE, CheckerContext &C) const; void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const; void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const; + void analyzerDumpExtent(const CallExpr *CE, CheckerContext &C) const; + void analyzerDumpElementCount(const CallExpr *CE, CheckerContext &C) const; void analyzerHashDump(const CallExpr *CE, CheckerContext &C) const; void analyzerDenote(const CallExpr *CE, CheckerContext &C) const; void analyzerExpress(const CallExpr *CE, CheckerContext &C) const; @@ -55,17 +57,19 @@ class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols, // Optional parameter `ExprVal` for expression value to be marked interesting. ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C, Optional<SVal> ExprVal = None) const; - ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, - ExplodedNode *N, + ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N, Optional<SVal> ExprVal = None) const; + const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const; + const MemRegion *getArgRegion(const CallExpr *CE, CheckerContext &C) const; + public: bool evalCall(const CallEvent &Call, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) const; }; -} +} // namespace REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef) REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *) @@ -90,6 +94,10 @@ bool ExprInspectionChecker::evalCall(const CallEvent &Call, &ExprInspectionChecker::analyzerWarnOnDeadSymbol) .StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain) + .Case("clang_analyzer_dumpExtent", + &ExprInspectionChecker::analyzerDumpExtent) + .Case("clang_analyzer_dumpElementCount", + &ExprInspectionChecker::analyzerDumpElementCount) .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump) .Case("clang_analyzer_getExtent", @@ -131,7 +139,7 @@ static const char *getArgumentValueString(const CallExpr *CE, ProgramStateRef StTrue, StFalse; std::tie(StTrue, StFalse) = - State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>()); + State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>()); if (StTrue) { if (StFalse) @@ -155,8 +163,7 @@ ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, } ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, - BugReporter &BR, - ExplodedNode *N, + BugReporter &BR, ExplodedNode *N, Optional<SVal> ExprVal) const { if (!N) return nullptr; @@ -172,6 +179,30 @@ ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, return N; } +const Expr *ExprInspectionChecker::getArgExpr(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) { + reportBug("Missing argument", C); + return nullptr; + } + return CE->getArg(0); +} + +const MemRegion *ExprInspectionChecker::getArgRegion(const CallExpr *CE, + CheckerContext &C) const { + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) + return nullptr; + + const MemRegion *MR = C.getSVal(Arg).getAsRegion(); + if (!MR) { + reportBug("Cannot obtain the region", C); + return nullptr; + } + + return MR; +} + void ExprInspectionChecker::analyzerEval(const CallExpr *CE, CheckerContext &C) const { const LocationContext *LC = C.getPredecessor()->getLocationContext(); @@ -215,24 +246,22 @@ void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) { - reportBug("Missing argument for explaining", C); + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; - } - SVal V = C.getSVal(CE->getArg(0)); + SVal V = C.getSVal(Arg); SValExplainer Ex(C.getASTContext()); reportBug(Ex.Visit(V), C); } void ExprInspectionChecker::analyzerDump(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) { - reportBug("Missing argument for dumping", C); + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; - } - SVal V = C.getSVal(CE->getArg(0)); + SVal V = C.getSVal(Arg); llvm::SmallString<32> Str; llvm::raw_svector_ostream OS(Str); @@ -242,16 +271,9 @@ void ExprInspectionChecker::analyzerDump(const CallExpr *CE, void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) { - reportBug("Missing region for obtaining extent", C); + const MemRegion *MR = getArgRegion(CE, C); + if (!MR) return; - } - - auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion()); - if (!MR) { - reportBug("Obtaining extent of a non-region", C); - return; - } ProgramStateRef State = C.getState(); DefinedOrUnknownSVal Size = getDynamicSize(State, MR, C.getSValBuilder()); @@ -260,6 +282,46 @@ void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, C.addTransition(State); } +void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE, + CheckerContext &C) const { + const MemRegion *MR = getArgRegion(CE, C); + if (!MR) + return; + + DefinedOrUnknownSVal Size = + getDynamicSize(C.getState(), MR, C.getSValBuilder()); + + SmallString<64> Msg; + llvm::raw_svector_ostream Out(Msg); + Out << Size; + reportBug(Out.str(), C); +} + +void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE, + CheckerContext &C) const { + const MemRegion *MR = getArgRegion(CE, C); + if (!MR) + return; + + QualType ElementTy; + if (const auto *TVR = MR->getAs<TypedValueRegion>()) { + ElementTy = TVR->getValueType(); + } else { + ElementTy = + MR->castAs<SymbolicRegion>()->getSymbol()->getType()->getPointeeType(); + } + + assert(!ElementTy->isPointerType()); + + DefinedOrUnknownSVal ElementCount = + getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy); + + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + Out << ElementCount; + reportBug(Out.str(), C); +} + void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, CheckerContext &C) const { C.getState()->dump(); @@ -267,9 +329,11 @@ void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; - SVal Val = C.getSVal(CE->getArg(0)); + + SVal Val = C.getSVal(Arg); SymbolRef Sym = Val.getAsSymbol(); if (!Sym) return; @@ -306,7 +370,7 @@ void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper, void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) const { - for (auto Item: ReachedStats) { + for (auto Item : ReachedStats) { unsigned NumTimesReached = Item.second.NumTimesReached; ExplodedNode *N = Item.second.ExampleNode; @@ -373,9 +437,7 @@ class SymbolExpressor return None; } - Optional<std::string> VisitSymExpr(const SymExpr *S) { - return lookup(S); - } + Optional<std::string> VisitSymExpr(const SymExpr *S) { return lookup(S); } Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { if (Optional<std::string> Str = lookup(S)) @@ -394,7 +456,8 @@ class SymbolExpressor if (Optional<std::string> Str1 = Visit(S->getLHS())) if (Optional<std::string> Str2 = Visit(S->getRHS())) return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + - " " + *Str2).str(); + " " + *Str2) + .str(); return None; } @@ -410,10 +473,9 @@ class SymbolExpressor void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) { - reportBug("clang_analyzer_express() requires a symbol", C); + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; - } SVal ArgVal = C.getSVal(CE->getArg(0)); SymbolRef Sym = ArgVal.getAsSymbol(); diff --git a/clang/test/Analysis/expr-inspection.cpp b/clang/test/Analysis/expr-inspection.cpp index 609b44ca6d4c..0bf824c2be5d 100644 --- a/clang/test/Analysis/expr-inspection.cpp +++ b/clang/test/Analysis/expr-inspection.cpp @@ -12,7 +12,7 @@ void clang_analyzer_express(); void foo(int x, unsigned y) { clang_analyzer_denote(); // expected-warning{{clang_analyzer_denote() requires a symbol and a string literal}} - clang_analyzer_express(); // expected-warning{{clang_analyzer_express() requires a symbol}} + clang_analyzer_express(); // expected-warning{{Missing argument}} clang_analyzer_denote(x); // expected-warning{{clang_analyzer_denote() requires a symbol and a string literal}} clang_analyzer_express(x); // expected-warning{{Unable to express}} diff --git a/clang/test/Analysis/memory-model.cpp b/clang/test/Analysis/memory-model.cpp new file mode 100644 index 000000000000..7e1d23e43b69 --- /dev/null +++ b/clang/test/Analysis/memory-model.cpp @@ -0,0 +1,157 @@ +// RUN: %clang_analyze_cc1 -std=c++20 \ +// RUN: -analyzer-checker=core,unix,cplusplus,debug.ExprInspection \ +// RUN: -triple x86_64-unknown-linux-gnu \ +// RUN: -verify %s + +#include "Inputs/system-header-simulator-cxx.h" + +typedef __SIZE_TYPE__ size_t; +void *malloc(size_t); +void *alloca(size_t); +void *realloc(void *ptr, size_t size); +void *calloc(size_t number, size_t size); +void free(void *); + +struct S { + int f; +}; + +void clang_analyzer_dump(int); +void clang_analyzer_dump(const void *); +void clang_analyzer_dumpExtent(int); +void clang_analyzer_dumpExtent(const void *); +void clang_analyzer_dumpElementCount(int); +void clang_analyzer_dumpElementCount(const void *); + +int clang_analyzer_getExtent(void *); +void clang_analyzer_eval(bool); + +void var_simple_ref() { + int a = 13; + clang_analyzer_dump(&a); // expected-warning {{a}} + clang_analyzer_dumpExtent(&a); // expected-warning {{4 S64b}} + clang_analyzer_dumpElementCount(&a); // expected-warning {{1 S64b}} +} + +void var_simple_ptr(int *a) { + clang_analyzer_dump(a); // expected-warning {{SymRegion{reg_$0<int * a>}}} + clang_analyzer_dumpExtent(a); // expected-warning {{extent_$1{SymRegion{reg_$0<int * a>}}}} + clang_analyzer_dumpElementCount(a); // expected-warning {{(extent_$1{SymRegion{reg_$0<int * a>}}) / 4}} +} + +void var_array() { + int a[] = {1, 2, 3}; + clang_analyzer_dump(a); // expected-warning {{Element{a,0 S64b,int}}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 S64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} +} + +void string() { + clang_analyzer_dump("foo"); // expected-warning {{Element{"foo",0 S64b,char}}} + clang_analyzer_dumpExtent("foo"); // expected-warning {{4 S64b}} + clang_analyzer_dumpElementCount("foo"); // expected-warning {{4 S64b}} +} + +void struct_simple_ptr(S *a) { + clang_analyzer_dump(a); // expected-warning {{SymRegion{reg_$0<struct S * a>}}} + clang_analyzer_dumpExtent(a); // expected-warning {{extent_$1{SymRegion{reg_$0<struct S * a>}}}} + clang_analyzer_dumpElementCount(a); // expected-warning {{(extent_$1{SymRegion{reg_$0<struct S * a>}}) / 4}} +} + +void field_ref(S a) { + clang_analyzer_dump(&a.f); // expected-warning {{a.f}} + clang_analyzer_dumpExtent(&a.f); // expected-warning {{4 S64b}} + clang_analyzer_dumpElementCount(&a.f); // expected-warning {{1 S64b}} +} + +void field_ptr(S *a) { + clang_analyzer_dump(&a->f); // expected-warning {{SymRegion{reg_$0<struct S * a>}.f}} + clang_analyzer_dumpExtent(&a->f); // expected-warning {{4 S64b}} + clang_analyzer_dumpElementCount(&a->f); // expected-warning {{1 S64b}} +} + +void symbolic_array() { + int *a = new int[3]; + clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 S64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} + delete[] a; +} + +void symbolic_placement_new() { + char *buf = new char[sizeof(int) * 3]; + int *a = new (buf) int(12); + clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 S64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} + delete[] buf; +} + +void symbolic_malloc() { + int *a = (int *)malloc(12); + clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 U64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} + free(a); +} + +void symbolic_alloca() { + int *a = (int *)alloca(12); + clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 U64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} +} + +void symbolic_complex() { + int *a = (int *)malloc(4); + clang_analyzer_dumpExtent(a); // expected-warning {{4 U64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{1 S64b}} + + int *b = (int *)realloc(a, sizeof(int) * 2); + clang_analyzer_dumpExtent(b); // expected-warning {{8 U64b}} + clang_analyzer_dumpElementCount(b); // expected-warning {{2 S64b}} + free(b); + + int *c = (int *)calloc(3, 4); + clang_analyzer_dumpExtent(c); // expected-warning {{12 U64b}} + clang_analyzer_dumpElementCount(c); // expected-warning {{3 S64b}} + free(c); +} + +void signedness_equality() { + char *a = new char[sizeof(char) * 13]; + char *b = (char *)malloc(13); + + clang_analyzer_dump(clang_analyzer_getExtent(a)); // expected-warning {{13 S64b}} + clang_analyzer_dump(clang_analyzer_getExtent(b)); // expected-warning {{13 U64b}} + clang_analyzer_eval(clang_analyzer_getExtent(a) == + clang_analyzer_getExtent(b)); + // expected-warning@-2 {{TRUE}} + + delete[] a; + free(b); +} + +void default_new_aligned() { + struct alignas(32) S {}; + + S *a = new S[10]; + + clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning {{320 S64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{10 S64b}} + + delete[] a; +} + +void *operator new[](std::size_t, std::align_val_t, bool hack) throw(); + +void user_defined_new() { + int *a = new (std::align_val_t(32), true) int[10]; + + clang_analyzer_dump(a); // expected-warning {{Element{SymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning-re {{{{^extent_\$[0-9]\{SymRegion{conj}}}} + clang_analyzer_dumpElementCount(a); // expected-warning-re {{{{^\(extent_\$[0-9]\{SymRegion{conj.*\) / 4}}}} + + operator delete[](a, std::align_val_t(32)); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits