1 //==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/IssueHash.h"
10 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
11 #include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
12 #include "clang/StaticAnalyzer/Checkers/Taint.h"
13 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
14 #include "clang/StaticAnalyzer/Core/Checker.h"
15 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
18 #include "llvm/ADT/StringSwitch.h"
19 #include "llvm/Support/ScopedPrinter.h"
22 using namespace clang
;
26 class ExprInspectionChecker
27 : public Checker
<eval::Call
, check::DeadSymbols
, check::EndAnalysis
> {
28 mutable std::unique_ptr
<BugType
> BT
;
30 // These stats are per-analysis, not per-branch, hence they shouldn't
31 // stay inside the program state.
33 ExplodedNode
*ExampleNode
;
34 unsigned NumTimesReached
;
36 mutable llvm::DenseMap
<const CallExpr
*, ReachedStat
> ReachedStats
;
38 void analyzerEval(const CallExpr
*CE
, CheckerContext
&C
) const;
39 void analyzerCheckInlined(const CallExpr
*CE
, CheckerContext
&C
) const;
40 void analyzerWarnIfReached(const CallExpr
*CE
, CheckerContext
&C
) const;
41 void analyzerNumTimesReached(const CallExpr
*CE
, CheckerContext
&C
) const;
42 void analyzerCrash(const CallExpr
*CE
, CheckerContext
&C
) const;
43 void analyzerWarnOnDeadSymbol(const CallExpr
*CE
, CheckerContext
&C
) const;
44 void analyzerValue(const CallExpr
*CE
, CheckerContext
&C
) const;
45 void analyzerDumpSValType(const CallExpr
*CE
, CheckerContext
&C
) const;
46 void analyzerDump(const CallExpr
*CE
, CheckerContext
&C
) const;
47 void analyzerExplain(const CallExpr
*CE
, CheckerContext
&C
) const;
48 void analyzerPrintState(const CallExpr
*CE
, CheckerContext
&C
) const;
49 void analyzerGetExtent(const CallExpr
*CE
, CheckerContext
&C
) const;
50 void analyzerDumpExtent(const CallExpr
*CE
, CheckerContext
&C
) const;
51 void analyzerDumpElementCount(const CallExpr
*CE
, CheckerContext
&C
) const;
52 void analyzerHashDump(const CallExpr
*CE
, CheckerContext
&C
) const;
53 void analyzerDenote(const CallExpr
*CE
, CheckerContext
&C
) const;
54 void analyzerExpress(const CallExpr
*CE
, CheckerContext
&C
) const;
55 void analyzerIsTainted(const CallExpr
*CE
, CheckerContext
&C
) const;
57 typedef void (ExprInspectionChecker::*FnCheck
)(const CallExpr
*,
58 CheckerContext
&C
) const;
60 // Optional parameter `ExprVal` for expression value to be marked interesting.
61 ExplodedNode
*reportBug(llvm::StringRef Msg
, CheckerContext
&C
,
62 std::optional
<SVal
> ExprVal
= std::nullopt
) const;
63 ExplodedNode
*reportBug(llvm::StringRef Msg
, BugReporter
&BR
, ExplodedNode
*N
,
64 std::optional
<SVal
> ExprVal
= std::nullopt
) const;
65 template <typename T
> void printAndReport(CheckerContext
&C
, T What
) const;
67 const Expr
*getArgExpr(const CallExpr
*CE
, CheckerContext
&C
) const;
68 const MemRegion
*getArgRegion(const CallExpr
*CE
, CheckerContext
&C
) const;
71 bool evalCall(const CallEvent
&Call
, CheckerContext
&C
) const;
72 void checkDeadSymbols(SymbolReaper
&SymReaper
, CheckerContext
&C
) const;
73 void checkEndAnalysis(ExplodedGraph
&G
, BugReporter
&BR
,
74 ExprEngine
&Eng
) const;
78 REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols
, SymbolRef
)
79 REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols
, SymbolRef
, const StringLiteral
*)
81 bool ExprInspectionChecker::evalCall(const CallEvent
&Call
,
82 CheckerContext
&C
) const {
83 const auto *CE
= dyn_cast_or_null
<CallExpr
>(Call
.getOriginExpr());
87 // These checks should have no effect on the surrounding environment
88 // (globals should not be invalidated, etc), hence the use of evalCall.
90 llvm::StringSwitch
<FnCheck
>(C
.getCalleeName(CE
))
91 .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval
)
92 .Case("clang_analyzer_checkInlined",
93 &ExprInspectionChecker::analyzerCheckInlined
)
94 .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash
)
95 .Case("clang_analyzer_warnIfReached",
96 &ExprInspectionChecker::analyzerWarnIfReached
)
97 .Case("clang_analyzer_warnOnDeadSymbol",
98 &ExprInspectionChecker::analyzerWarnOnDeadSymbol
)
99 .StartsWith("clang_analyzer_explain",
100 &ExprInspectionChecker::analyzerExplain
)
101 .Case("clang_analyzer_dumpExtent",
102 &ExprInspectionChecker::analyzerDumpExtent
)
103 .Case("clang_analyzer_dumpElementCount",
104 &ExprInspectionChecker::analyzerDumpElementCount
)
105 .Case("clang_analyzer_value", &ExprInspectionChecker::analyzerValue
)
106 .StartsWith("clang_analyzer_dumpSvalType",
107 &ExprInspectionChecker::analyzerDumpSValType
)
108 .StartsWith("clang_analyzer_dump",
109 &ExprInspectionChecker::analyzerDump
)
110 .Case("clang_analyzer_getExtent",
111 &ExprInspectionChecker::analyzerGetExtent
)
112 .Case("clang_analyzer_printState",
113 &ExprInspectionChecker::analyzerPrintState
)
114 .Case("clang_analyzer_numTimesReached",
115 &ExprInspectionChecker::analyzerNumTimesReached
)
116 .Case("clang_analyzer_hashDump",
117 &ExprInspectionChecker::analyzerHashDump
)
118 .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote
)
119 .Case("clang_analyzer_express", // This also marks the argument as
121 &ExprInspectionChecker::analyzerExpress
)
122 .StartsWith("clang_analyzer_isTainted",
123 &ExprInspectionChecker::analyzerIsTainted
)
129 (this->*Handler
)(CE
, C
);
133 static const char *getArgumentValueString(const CallExpr
*CE
,
135 if (CE
->getNumArgs() == 0)
136 return "Missing assertion argument";
138 ExplodedNode
*N
= C
.getPredecessor();
139 const LocationContext
*LC
= N
->getLocationContext();
140 ProgramStateRef State
= N
->getState();
142 const Expr
*Assertion
= CE
->getArg(0);
143 SVal AssertionVal
= State
->getSVal(Assertion
, LC
);
145 if (AssertionVal
.isUndef())
148 ProgramStateRef StTrue
, StFalse
;
149 std::tie(StTrue
, StFalse
) =
150 State
->assume(AssertionVal
.castAs
<DefinedOrUnknownSVal
>());
161 llvm_unreachable("Invalid constraint; neither true or false.");
166 ExprInspectionChecker::reportBug(llvm::StringRef Msg
, CheckerContext
&C
,
167 std::optional
<SVal
> ExprVal
) const {
168 ExplodedNode
*N
= C
.generateNonFatalErrorNode();
169 reportBug(Msg
, C
.getBugReporter(), N
, ExprVal
);
174 ExprInspectionChecker::reportBug(llvm::StringRef Msg
, BugReporter
&BR
,
176 std::optional
<SVal
> ExprVal
) const {
181 BT
.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
183 auto R
= std::make_unique
<PathSensitiveBugReport
>(*BT
, Msg
, N
);
185 R
->markInteresting(*ExprVal
);
187 BR
.emitReport(std::move(R
));
191 const Expr
*ExprInspectionChecker::getArgExpr(const CallExpr
*CE
,
192 CheckerContext
&C
) const {
193 if (CE
->getNumArgs() == 0) {
194 reportBug("Missing argument", C
);
197 return CE
->getArg(0);
200 const MemRegion
*ExprInspectionChecker::getArgRegion(const CallExpr
*CE
,
201 CheckerContext
&C
) const {
202 const Expr
*Arg
= getArgExpr(CE
, C
);
206 const MemRegion
*MR
= C
.getSVal(Arg
).getAsRegion();
208 reportBug("Cannot obtain the region", C
);
215 void ExprInspectionChecker::analyzerEval(const CallExpr
*CE
,
216 CheckerContext
&C
) const {
217 const LocationContext
*LC
= C
.getPredecessor()->getLocationContext();
219 // A specific instantiation of an inlined function may have more constrained
220 // values than can generally be assumed. Skip the check.
221 if (LC
->getStackFrame()->getParent() != nullptr)
224 reportBug(getArgumentValueString(CE
, C
), C
);
227 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr
*CE
,
228 CheckerContext
&C
) const {
229 reportBug("REACHABLE", C
);
232 void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr
*CE
,
233 CheckerContext
&C
) const {
234 ++ReachedStats
[CE
].NumTimesReached
;
235 if (!ReachedStats
[CE
].ExampleNode
) {
236 // Later, in checkEndAnalysis, we'd throw a report against it.
237 ReachedStats
[CE
].ExampleNode
= C
.generateNonFatalErrorNode();
241 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr
*CE
,
242 CheckerContext
&C
) const {
243 const LocationContext
*LC
= C
.getPredecessor()->getLocationContext();
245 // An inlined function could conceivably also be analyzed as a top-level
246 // function. We ignore this case and only emit a message (TRUE or FALSE)
247 // when we are analyzing it as an inlined function. This means that
248 // clang_analyzer_checkInlined(true) should always print TRUE, but
249 // clang_analyzer_checkInlined(false) should never actually print anything.
250 if (LC
->getStackFrame()->getParent() == nullptr)
253 reportBug(getArgumentValueString(CE
, C
), C
);
256 void ExprInspectionChecker::analyzerExplain(const CallExpr
*CE
,
257 CheckerContext
&C
) const {
258 const Expr
*Arg
= getArgExpr(CE
, C
);
262 SVal V
= C
.getSVal(Arg
);
263 SValExplainer
Ex(C
.getASTContext());
264 reportBug(Ex
.Visit(V
), C
);
267 static void printHelper(llvm::raw_svector_ostream
&Out
, CheckerContext
&C
,
268 const llvm::APSInt
&I
) {
269 Out
<< I
.getBitWidth() << (I
.isUnsigned() ? "u:" : "s:");
273 static void printHelper(llvm::raw_svector_ostream
&Out
, CheckerContext
&C
,
275 C
.getConstraintManager().printValue(Out
, C
.getState(), Sym
);
278 static void printHelper(llvm::raw_svector_ostream
&Out
, CheckerContext
&C
,
283 template <typename T
>
284 void ExprInspectionChecker::printAndReport(CheckerContext
&C
, T What
) const {
285 llvm::SmallString
<64> Str
;
286 llvm::raw_svector_ostream
OS(Str
);
287 printHelper(OS
, C
, What
);
288 reportBug(OS
.str(), C
);
291 void ExprInspectionChecker::analyzerValue(const CallExpr
*CE
,
292 CheckerContext
&C
) const {
293 const Expr
*Arg
= getArgExpr(CE
, C
);
297 SVal V
= C
.getSVal(Arg
);
298 if (const SymbolRef Sym
= V
.getAsSymbol())
299 printAndReport(C
, Sym
);
300 else if (const llvm::APSInt
*I
= V
.getAsInteger())
301 printAndReport(C
, *I
);
306 void ExprInspectionChecker::analyzerDumpSValType(const CallExpr
*CE
,
307 CheckerContext
&C
) const {
308 const Expr
*Arg
= getArgExpr(CE
, C
);
312 QualType Ty
= C
.getSVal(Arg
).getType(C
.getASTContext());
313 reportBug(Ty
.getAsString(), C
);
316 void ExprInspectionChecker::analyzerDump(const CallExpr
*CE
,
317 CheckerContext
&C
) const {
318 const Expr
*Arg
= getArgExpr(CE
, C
);
322 SVal V
= C
.getSVal(Arg
);
323 printAndReport(C
, V
);
326 void ExprInspectionChecker::analyzerGetExtent(const CallExpr
*CE
,
327 CheckerContext
&C
) const {
328 const Expr
*Arg
= getArgExpr(CE
, C
);
332 ProgramStateRef State
= C
.getState();
333 SVal Size
= getDynamicExtentWithOffset(State
, C
.getSVal(Arg
));
335 State
= State
->BindExpr(CE
, C
.getLocationContext(), Size
);
336 C
.addTransition(State
);
339 void ExprInspectionChecker::analyzerDumpExtent(const CallExpr
*CE
,
340 CheckerContext
&C
) const {
341 const Expr
*Arg
= getArgExpr(CE
, C
);
345 ProgramStateRef State
= C
.getState();
346 SVal Size
= getDynamicExtentWithOffset(State
, C
.getSVal(Arg
));
347 printAndReport(C
, Size
);
350 void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr
*CE
,
351 CheckerContext
&C
) const {
352 const MemRegion
*MR
= getArgRegion(CE
, C
);
357 if (const auto *TVR
= MR
->getAs
<TypedValueRegion
>()) {
358 ElementTy
= TVR
->getValueType();
360 ElementTy
= MR
->castAs
<SymbolicRegion
>()->getPointeeStaticType();
363 assert(!ElementTy
->isPointerType());
365 DefinedOrUnknownSVal ElementCount
= getDynamicElementCountWithOffset(
366 C
.getState(), C
.getSVal(getArgExpr(CE
, C
)), ElementTy
);
367 printAndReport(C
, ElementCount
);
370 void ExprInspectionChecker::analyzerPrintState(const CallExpr
*CE
,
371 CheckerContext
&C
) const {
372 C
.getState()->dump();
375 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr
*CE
,
376 CheckerContext
&C
) const {
377 const Expr
*Arg
= getArgExpr(CE
, C
);
381 SVal Val
= C
.getSVal(Arg
);
382 SymbolRef Sym
= Val
.getAsSymbol();
386 ProgramStateRef State
= C
.getState();
387 State
= State
->add
<MarkedSymbols
>(Sym
);
388 C
.addTransition(State
);
391 void ExprInspectionChecker::checkDeadSymbols(SymbolReaper
&SymReaper
,
392 CheckerContext
&C
) const {
393 ProgramStateRef State
= C
.getState();
394 const MarkedSymbolsTy
&Syms
= State
->get
<MarkedSymbols
>();
395 ExplodedNode
*N
= C
.getPredecessor();
396 for (SymbolRef Sym
: Syms
) {
397 if (!SymReaper
.isDead(Sym
))
400 // The non-fatal error node should be the same for all reports.
401 if (ExplodedNode
*BugNode
= reportBug("SYMBOL DEAD", C
))
403 State
= State
->remove
<MarkedSymbols
>(Sym
);
406 for (auto I
: State
->get
<DenotedSymbols
>()) {
407 SymbolRef Sym
= I
.first
;
408 if (!SymReaper
.isLive(Sym
))
409 State
= State
->remove
<DenotedSymbols
>(Sym
);
412 C
.addTransition(State
, N
);
415 void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph
&G
, BugReporter
&BR
,
416 ExprEngine
&Eng
) const {
417 for (auto Item
: ReachedStats
) {
418 unsigned NumTimesReached
= Item
.second
.NumTimesReached
;
419 ExplodedNode
*N
= Item
.second
.ExampleNode
;
421 reportBug(llvm::to_string(NumTimesReached
), BR
, N
);
423 ReachedStats
.clear();
426 void ExprInspectionChecker::analyzerCrash(const CallExpr
*CE
,
427 CheckerContext
&C
) const {
431 void ExprInspectionChecker::analyzerHashDump(const CallExpr
*CE
,
432 CheckerContext
&C
) const {
433 const LangOptions
&Opts
= C
.getLangOpts();
434 const SourceManager
&SM
= C
.getSourceManager();
435 FullSourceLoc
FL(CE
->getArg(0)->getBeginLoc(), SM
);
436 std::string HashContent
=
437 getIssueString(FL
, getCheckerName().getName(), "Category",
438 C
.getLocationContext()->getDecl(), Opts
);
440 reportBug(HashContent
, C
);
443 void ExprInspectionChecker::analyzerDenote(const CallExpr
*CE
,
444 CheckerContext
&C
) const {
445 if (CE
->getNumArgs() < 2) {
446 reportBug("clang_analyzer_denote() requires a symbol and a string literal",
451 SymbolRef Sym
= C
.getSVal(CE
->getArg(0)).getAsSymbol();
453 reportBug("Not a symbol", C
);
457 const auto *E
= dyn_cast
<StringLiteral
>(CE
->getArg(1)->IgnoreParenCasts());
459 reportBug("Not a string literal", C
);
463 ProgramStateRef State
= C
.getState();
465 C
.addTransition(C
.getState()->set
<DenotedSymbols
>(Sym
, E
));
469 class SymbolExpressor
470 : public SymExprVisitor
<SymbolExpressor
, std::optional
<std::string
>> {
471 ProgramStateRef State
;
474 SymbolExpressor(ProgramStateRef State
) : State(State
) {}
476 std::optional
<std::string
> lookup(const SymExpr
*S
) {
477 if (const StringLiteral
*const *SLPtr
= State
->get
<DenotedSymbols
>(S
)) {
478 const StringLiteral
*SL
= *SLPtr
;
479 return std::string(SL
->getBytes());
484 std::optional
<std::string
> VisitSymExpr(const SymExpr
*S
) {
488 std::optional
<std::string
> VisitSymIntExpr(const SymIntExpr
*S
) {
489 if (std::optional
<std::string
> Str
= lookup(S
))
491 if (std::optional
<std::string
> Str
= Visit(S
->getLHS()))
492 return (*Str
+ " " + BinaryOperator::getOpcodeStr(S
->getOpcode()) + " " +
493 std::to_string(S
->getRHS().getLimitedValue()) +
494 (S
->getRHS().isUnsigned() ? "U" : ""))
499 std::optional
<std::string
> VisitSymSymExpr(const SymSymExpr
*S
) {
500 if (std::optional
<std::string
> Str
= lookup(S
))
502 if (std::optional
<std::string
> Str1
= Visit(S
->getLHS()))
503 if (std::optional
<std::string
> Str2
= Visit(S
->getRHS()))
504 return (*Str1
+ " " + BinaryOperator::getOpcodeStr(S
->getOpcode()) +
510 std::optional
<std::string
> VisitUnarySymExpr(const UnarySymExpr
*S
) {
511 if (std::optional
<std::string
> Str
= lookup(S
))
513 if (std::optional
<std::string
> Str
= Visit(S
->getOperand()))
514 return (UnaryOperator::getOpcodeStr(S
->getOpcode()) + *Str
).str();
518 std::optional
<std::string
> VisitSymbolCast(const SymbolCast
*S
) {
519 if (std::optional
<std::string
> Str
= lookup(S
))
521 if (std::optional
<std::string
> Str
= Visit(S
->getOperand()))
522 return (Twine("(") + S
->getType().getAsString() + ")" + *Str
).str();
528 void ExprInspectionChecker::analyzerExpress(const CallExpr
*CE
,
529 CheckerContext
&C
) const {
530 const Expr
*Arg
= getArgExpr(CE
, C
);
534 SVal ArgVal
= C
.getSVal(CE
->getArg(0));
535 SymbolRef Sym
= ArgVal
.getAsSymbol();
537 reportBug("Not a symbol", C
, ArgVal
);
541 SymbolExpressor
V(C
.getState());
542 auto Str
= V
.Visit(Sym
);
544 reportBug("Unable to express", C
, ArgVal
);
548 reportBug(*Str
, C
, ArgVal
);
551 void ExprInspectionChecker::analyzerIsTainted(const CallExpr
*CE
,
552 CheckerContext
&C
) const {
553 if (CE
->getNumArgs() != 1) {
554 reportBug("clang_analyzer_isTainted() requires exactly one argument", C
);
557 const bool IsTainted
=
558 taint::isTainted(C
.getState(), CE
->getArg(0), C
.getLocationContext());
559 reportBug(IsTainted
? "YES" : "NO", C
);
562 void ento::registerExprInspectionChecker(CheckerManager
&Mgr
) {
563 Mgr
.registerChecker
<ExprInspectionChecker
>();
566 bool ento::shouldRegisterExprInspectionChecker(const CheckerManager
&mgr
) {