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