mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-03 16:03:21 +00:00
[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:
parent
31ad5c14fe
commit
89d210fe1a
@ -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
|
||||
==========
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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}}
|
||||
|
157
clang/test/Analysis/memory-model.cpp
Normal file
157
clang/test/Analysis/memory-model.cpp
Normal 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));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user