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