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