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