1 //==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "Taint.h"
10 #include "clang/Analysis/IssueHash.h"
11 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
12 #include "clang/StaticAnalyzer/Checkers/SValExplainer.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"
20
21 using namespace clang;
22 using namespace ento;
23
24 namespace {
25 class ExprInspectionChecker
26 : public Checker<eval::Call, check::DeadSymbols, check::EndAnalysis> {
27 mutable std::unique_ptr<BugType> BT;
28
29 // These stats are per-analysis, not per-branch, hence they shouldn't
30 // stay inside the program state.
31 struct ReachedStat {
32 ExplodedNode *ExampleNode;
33 unsigned NumTimesReached;
34 };
35 mutable llvm::DenseMap<const CallExpr *, ReachedStat> ReachedStats;
36
37 void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
38 void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
39 void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
40 void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const;
41 void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
42 void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
43 void analyzerDump(const CallExpr *CE, CheckerContext &C) const;
44 void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;
45 void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const;
46 void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const;
47 void analyzerDumpExtent(const CallExpr *CE, CheckerContext &C) const;
48 void analyzerDumpElementCount(const CallExpr *CE, CheckerContext &C) const;
49 void analyzerHashDump(const CallExpr *CE, CheckerContext &C) const;
50 void analyzerDenote(const CallExpr *CE, CheckerContext &C) const;
51 void analyzerExpress(const CallExpr *CE, CheckerContext &C) const;
52 void analyzerIsTainted(const CallExpr *CE, CheckerContext &C) const;
53
54 typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
55 CheckerContext &C) const;
56
57 // Optional parameter `ExprVal` for expression value to be marked interesting.
58 ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C,
59 Optional<SVal> ExprVal = None) const;
60 ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N,
61 Optional<SVal> ExprVal = None) const;
62
63 const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const;
64 const MemRegion *getArgRegion(const CallExpr *CE, CheckerContext &C) const;
65
66 public:
67 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
68 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
69 void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
70 ExprEngine &Eng) const;
71 };
72 } // namespace
73
REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols,SymbolRef)74 REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef)
75 REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *)
76
77 bool ExprInspectionChecker::evalCall(const CallEvent &Call,
78 CheckerContext &C) const {
79 const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
80 if (!CE)
81 return false;
82
83 // These checks should have no effect on the surrounding environment
84 // (globals should not be invalidated, etc), hence the use of evalCall.
85 FnCheck Handler =
86 llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
87 .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
88 .Case("clang_analyzer_checkInlined",
89 &ExprInspectionChecker::analyzerCheckInlined)
90 .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
91 .Case("clang_analyzer_warnIfReached",
92 &ExprInspectionChecker::analyzerWarnIfReached)
93 .Case("clang_analyzer_warnOnDeadSymbol",
94 &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
95 .StartsWith("clang_analyzer_explain",
96 &ExprInspectionChecker::analyzerExplain)
97 .Case("clang_analyzer_dumpExtent",
98 &ExprInspectionChecker::analyzerDumpExtent)
99 .Case("clang_analyzer_dumpElementCount",
100 &ExprInspectionChecker::analyzerDumpElementCount)
101 .StartsWith("clang_analyzer_dump",
102 &ExprInspectionChecker::analyzerDump)
103 .Case("clang_analyzer_getExtent",
104 &ExprInspectionChecker::analyzerGetExtent)
105 .Case("clang_analyzer_printState",
106 &ExprInspectionChecker::analyzerPrintState)
107 .Case("clang_analyzer_numTimesReached",
108 &ExprInspectionChecker::analyzerNumTimesReached)
109 .Case("clang_analyzer_hashDump",
110 &ExprInspectionChecker::analyzerHashDump)
111 .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote)
112 .Case("clang_analyzer_express",
113 &ExprInspectionChecker::analyzerExpress)
114 .StartsWith("clang_analyzer_isTainted",
115 &ExprInspectionChecker::analyzerIsTainted)
116 .Default(nullptr);
117
118 if (!Handler)
119 return false;
120
121 (this->*Handler)(CE, C);
122 return true;
123 }
124
getArgumentValueString(const CallExpr * CE,CheckerContext & C)125 static const char *getArgumentValueString(const CallExpr *CE,
126 CheckerContext &C) {
127 if (CE->getNumArgs() == 0)
128 return "Missing assertion argument";
129
130 ExplodedNode *N = C.getPredecessor();
131 const LocationContext *LC = N->getLocationContext();
132 ProgramStateRef State = N->getState();
133
134 const Expr *Assertion = CE->getArg(0);
135 SVal AssertionVal = State->getSVal(Assertion, LC);
136
137 if (AssertionVal.isUndef())
138 return "UNDEFINED";
139
140 ProgramStateRef StTrue, StFalse;
141 std::tie(StTrue, StFalse) =
142 State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
143
144 if (StTrue) {
145 if (StFalse)
146 return "UNKNOWN";
147 else
148 return "TRUE";
149 } else {
150 if (StFalse)
151 return "FALSE";
152 else
153 llvm_unreachable("Invalid constraint; neither true or false.");
154 }
155 }
156
reportBug(llvm::StringRef Msg,CheckerContext & C,Optional<SVal> ExprVal) const157 ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
158 CheckerContext &C,
159 Optional<SVal> ExprVal) const {
160 ExplodedNode *N = C.generateNonFatalErrorNode();
161 reportBug(Msg, C.getBugReporter(), N, ExprVal);
162 return N;
163 }
164
reportBug(llvm::StringRef Msg,BugReporter & BR,ExplodedNode * N,Optional<SVal> ExprVal) const165 ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
166 BugReporter &BR, ExplodedNode *N,
167 Optional<SVal> ExprVal) const {
168 if (!N)
169 return nullptr;
170
171 if (!BT)
172 BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
173
174 auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
175 if (ExprVal) {
176 R->markInteresting(*ExprVal);
177 }
178 BR.emitReport(std::move(R));
179 return N;
180 }
181
getArgExpr(const CallExpr * CE,CheckerContext & C) const182 const Expr *ExprInspectionChecker::getArgExpr(const CallExpr *CE,
183 CheckerContext &C) const {
184 if (CE->getNumArgs() == 0) {
185 reportBug("Missing argument", C);
186 return nullptr;
187 }
188 return CE->getArg(0);
189 }
190
getArgRegion(const CallExpr * CE,CheckerContext & C) const191 const MemRegion *ExprInspectionChecker::getArgRegion(const CallExpr *CE,
192 CheckerContext &C) const {
193 const Expr *Arg = getArgExpr(CE, C);
194 if (!Arg)
195 return nullptr;
196
197 const MemRegion *MR = C.getSVal(Arg).getAsRegion();
198 if (!MR) {
199 reportBug("Cannot obtain the region", C);
200 return nullptr;
201 }
202
203 return MR;
204 }
205
analyzerEval(const CallExpr * CE,CheckerContext & C) const206 void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
207 CheckerContext &C) const {
208 const LocationContext *LC = C.getPredecessor()->getLocationContext();
209
210 // A specific instantiation of an inlined function may have more constrained
211 // values than can generally be assumed. Skip the check.
212 if (LC->getStackFrame()->getParent() != nullptr)
213 return;
214
215 reportBug(getArgumentValueString(CE, C), C);
216 }
217
analyzerWarnIfReached(const CallExpr * CE,CheckerContext & C) const218 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
219 CheckerContext &C) const {
220 reportBug("REACHABLE", C);
221 }
222
analyzerNumTimesReached(const CallExpr * CE,CheckerContext & C) const223 void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE,
224 CheckerContext &C) const {
225 ++ReachedStats[CE].NumTimesReached;
226 if (!ReachedStats[CE].ExampleNode) {
227 // Later, in checkEndAnalysis, we'd throw a report against it.
228 ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode();
229 }
230 }
231
analyzerCheckInlined(const CallExpr * CE,CheckerContext & C) const232 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
233 CheckerContext &C) const {
234 const LocationContext *LC = C.getPredecessor()->getLocationContext();
235
236 // An inlined function could conceivably also be analyzed as a top-level
237 // function. We ignore this case and only emit a message (TRUE or FALSE)
238 // when we are analyzing it as an inlined function. This means that
239 // clang_analyzer_checkInlined(true) should always print TRUE, but
240 // clang_analyzer_checkInlined(false) should never actually print anything.
241 if (LC->getStackFrame()->getParent() == nullptr)
242 return;
243
244 reportBug(getArgumentValueString(CE, C), C);
245 }
246
analyzerExplain(const CallExpr * CE,CheckerContext & C) const247 void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
248 CheckerContext &C) const {
249 const Expr *Arg = getArgExpr(CE, C);
250 if (!Arg)
251 return;
252
253 SVal V = C.getSVal(Arg);
254 SValExplainer Ex(C.getASTContext());
255 reportBug(Ex.Visit(V), C);
256 }
257
analyzerDump(const CallExpr * CE,CheckerContext & C) const258 void ExprInspectionChecker::analyzerDump(const CallExpr *CE,
259 CheckerContext &C) const {
260 const Expr *Arg = getArgExpr(CE, C);
261 if (!Arg)
262 return;
263
264 SVal V = C.getSVal(Arg);
265
266 llvm::SmallString<32> Str;
267 llvm::raw_svector_ostream OS(Str);
268 V.dumpToStream(OS);
269 reportBug(OS.str(), C);
270 }
271
analyzerGetExtent(const CallExpr * CE,CheckerContext & C) const272 void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
273 CheckerContext &C) const {
274 const MemRegion *MR = getArgRegion(CE, C);
275 if (!MR)
276 return;
277
278 ProgramStateRef State = C.getState();
279 DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, C.getSValBuilder());
280
281 State = State->BindExpr(CE, C.getLocationContext(), Size);
282 C.addTransition(State);
283 }
284
analyzerDumpExtent(const CallExpr * CE,CheckerContext & C) const285 void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE,
286 CheckerContext &C) const {
287 const MemRegion *MR = getArgRegion(CE, C);
288 if (!MR)
289 return;
290
291 DefinedOrUnknownSVal Size =
292 getDynamicExtent(C.getState(), MR, C.getSValBuilder());
293
294 SmallString<64> Msg;
295 llvm::raw_svector_ostream Out(Msg);
296 Out << Size;
297 reportBug(Out.str(), C);
298 }
299
analyzerDumpElementCount(const CallExpr * CE,CheckerContext & C) const300 void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE,
301 CheckerContext &C) const {
302 const MemRegion *MR = getArgRegion(CE, C);
303 if (!MR)
304 return;
305
306 QualType ElementTy;
307 if (const auto *TVR = MR->getAs<TypedValueRegion>()) {
308 ElementTy = TVR->getValueType();
309 } else {
310 ElementTy =
311 MR->castAs<SymbolicRegion>()->getSymbol()->getType()->getPointeeType();
312 }
313
314 assert(!ElementTy->isPointerType());
315
316 DefinedOrUnknownSVal ElementCount =
317 getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy);
318
319 SmallString<128> Msg;
320 llvm::raw_svector_ostream Out(Msg);
321 Out << ElementCount;
322 reportBug(Out.str(), C);
323 }
324
analyzerPrintState(const CallExpr * CE,CheckerContext & C) const325 void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE,
326 CheckerContext &C) const {
327 C.getState()->dump();
328 }
329
analyzerWarnOnDeadSymbol(const CallExpr * CE,CheckerContext & C) const330 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
331 CheckerContext &C) const {
332 const Expr *Arg = getArgExpr(CE, C);
333 if (!Arg)
334 return;
335
336 SVal Val = C.getSVal(Arg);
337 SymbolRef Sym = Val.getAsSymbol();
338 if (!Sym)
339 return;
340
341 ProgramStateRef State = C.getState();
342 State = State->add<MarkedSymbols>(Sym);
343 C.addTransition(State);
344 }
345
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const346 void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
347 CheckerContext &C) const {
348 ProgramStateRef State = C.getState();
349 const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
350 ExplodedNode *N = C.getPredecessor();
351 for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
352 SymbolRef Sym = *I;
353 if (!SymReaper.isDead(Sym))
354 continue;
355
356 // The non-fatal error node should be the same for all reports.
357 if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C))
358 N = BugNode;
359 State = State->remove<MarkedSymbols>(Sym);
360 }
361
362 for (auto I : State->get<DenotedSymbols>()) {
363 SymbolRef Sym = I.first;
364 if (!SymReaper.isLive(Sym))
365 State = State->remove<DenotedSymbols>(Sym);
366 }
367
368 C.addTransition(State, N);
369 }
370
checkEndAnalysis(ExplodedGraph & G,BugReporter & BR,ExprEngine & Eng) const371 void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
372 ExprEngine &Eng) const {
373 for (auto Item : ReachedStats) {
374 unsigned NumTimesReached = Item.second.NumTimesReached;
375 ExplodedNode *N = Item.second.ExampleNode;
376
377 reportBug(llvm::to_string(NumTimesReached), BR, N);
378 }
379 ReachedStats.clear();
380 }
381
analyzerCrash(const CallExpr * CE,CheckerContext & C) const382 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
383 CheckerContext &C) const {
384 LLVM_BUILTIN_TRAP;
385 }
386
analyzerHashDump(const CallExpr * CE,CheckerContext & C) const387 void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE,
388 CheckerContext &C) const {
389 const LangOptions &Opts = C.getLangOpts();
390 const SourceManager &SM = C.getSourceManager();
391 FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM);
392 std::string HashContent =
393 getIssueString(FL, getCheckerName().getName(), "Category",
394 C.getLocationContext()->getDecl(), Opts);
395
396 reportBug(HashContent, C);
397 }
398
analyzerDenote(const CallExpr * CE,CheckerContext & C) const399 void ExprInspectionChecker::analyzerDenote(const CallExpr *CE,
400 CheckerContext &C) const {
401 if (CE->getNumArgs() < 2) {
402 reportBug("clang_analyzer_denote() requires a symbol and a string literal",
403 C);
404 return;
405 }
406
407 SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
408 if (!Sym) {
409 reportBug("Not a symbol", C);
410 return;
411 }
412
413 const auto *E = dyn_cast<StringLiteral>(CE->getArg(1)->IgnoreParenCasts());
414 if (!E) {
415 reportBug("Not a string literal", C);
416 return;
417 }
418
419 ProgramStateRef State = C.getState();
420
421 C.addTransition(C.getState()->set<DenotedSymbols>(Sym, E));
422 }
423
424 namespace {
425 class SymbolExpressor
426 : public SymExprVisitor<SymbolExpressor, Optional<std::string>> {
427 ProgramStateRef State;
428
429 public:
SymbolExpressor(ProgramStateRef State)430 SymbolExpressor(ProgramStateRef State) : State(State) {}
431
lookup(const SymExpr * S)432 Optional<std::string> lookup(const SymExpr *S) {
433 if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) {
434 const StringLiteral *SL = *SLPtr;
435 return std::string(SL->getBytes());
436 }
437 return None;
438 }
439
VisitSymExpr(const SymExpr * S)440 Optional<std::string> VisitSymExpr(const SymExpr *S) { return lookup(S); }
441
VisitSymIntExpr(const SymIntExpr * S)442 Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) {
443 if (Optional<std::string> Str = lookup(S))
444 return Str;
445 if (Optional<std::string> Str = Visit(S->getLHS()))
446 return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " +
447 std::to_string(S->getRHS().getLimitedValue()) +
448 (S->getRHS().isUnsigned() ? "U" : ""))
449 .str();
450 return None;
451 }
452
VisitSymSymExpr(const SymSymExpr * S)453 Optional<std::string> VisitSymSymExpr(const SymSymExpr *S) {
454 if (Optional<std::string> Str = lookup(S))
455 return Str;
456 if (Optional<std::string> Str1 = Visit(S->getLHS()))
457 if (Optional<std::string> Str2 = Visit(S->getRHS()))
458 return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) +
459 " " + *Str2)
460 .str();
461 return None;
462 }
463
VisitSymbolCast(const SymbolCast * S)464 Optional<std::string> VisitSymbolCast(const SymbolCast *S) {
465 if (Optional<std::string> Str = lookup(S))
466 return Str;
467 if (Optional<std::string> Str = Visit(S->getOperand()))
468 return (Twine("(") + S->getType().getAsString() + ")" + *Str).str();
469 return None;
470 }
471 };
472 } // namespace
473
analyzerExpress(const CallExpr * CE,CheckerContext & C) const474 void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
475 CheckerContext &C) const {
476 const Expr *Arg = getArgExpr(CE, C);
477 if (!Arg)
478 return;
479
480 SVal ArgVal = C.getSVal(CE->getArg(0));
481 SymbolRef Sym = ArgVal.getAsSymbol();
482 if (!Sym) {
483 reportBug("Not a symbol", C);
484 return;
485 }
486
487 SymbolExpressor V(C.getState());
488 auto Str = V.Visit(Sym);
489 if (!Str) {
490 reportBug("Unable to express", C);
491 return;
492 }
493
494 reportBug(*Str, C, ArgVal);
495 }
496
analyzerIsTainted(const CallExpr * CE,CheckerContext & C) const497 void ExprInspectionChecker::analyzerIsTainted(const CallExpr *CE,
498 CheckerContext &C) const {
499 if (CE->getNumArgs() != 1) {
500 reportBug("clang_analyzer_isTainted() requires exactly one argument", C);
501 return;
502 }
503 const bool IsTainted =
504 taint::isTainted(C.getState(), CE->getArg(0), C.getLocationContext());
505 reportBug(IsTainted ? "YES" : "NO", C);
506 }
507
registerExprInspectionChecker(CheckerManager & Mgr)508 void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
509 Mgr.registerChecker<ExprInspectionChecker>();
510 }
511
shouldRegisterExprInspectionChecker(const CheckerManager & mgr)512 bool ento::shouldRegisterExprInspectionChecker(const CheckerManager &mgr) {
513 return true;
514 }
515