[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().
This commit is contained in:
Charusso 2021-04-05 19:17:48 +02:00
parent 31ad5c14fe
commit 89d210fe1a
4 changed files with 268 additions and 36 deletions

View File

@ -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
==========

View File

@ -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 @@ public:
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 @@ public:
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 @@ public:
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();

View File

@ -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}}

View File

@ -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));
}