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