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