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