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