clang  9.0.0svn
ExprInspectionChecker.cpp
Go to the documentation of this file.
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 
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 
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 
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>> {
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 }
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2586
const SymExpr * SymbolRef
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2573
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
std::string getName(ArrayRef< StringRef > Parts) const
Get the platform-specific name separator.
LineState State
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Stmt.cpp:263
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
StringRef getOpcodeStr() const
Definition: Expr.h:3369
const LocationContext * getParent() const
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:2724
return Out str()
This represents one expression.
Definition: Expr.h:108
const SourceManager & SM
Definition: Format.cpp:1489
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set of type NameTy, suitable for placement into the ProgramState.
Dataflow Directional Tag Classes.
static const char * getArgumentValueString(const CallExpr *CE, CheckerContext &C)
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
const StackFrameContext * getStackFrame() const
StringRef getBytes() const
Allow access to clients that need the byte representation, such as ASTWriterStmt::VisitStringLiteral(...
Definition: Expr.h:1690
std::string GetIssueString(const SourceManager &SM, FullSourceLoc &IssueLoc, llvm::StringRef CheckerName, llvm::StringRef BugType, const Decl *D, const LangOptions &LangOpts)
Get the string representation of issue hash.
StringLiteral - This represents a string literal expression, e.g.
Definition: Expr.h:1599
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2429
A SourceLocation and its associated SourceManager.
This class handles loading and caching of source files into memory.