clang  8.0.0svn
ExprInspectionChecker.cpp
Go to the documentation of this file.
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 "ClangSACheckers.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 
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 
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  if (!isa<SymbolData>(Sym)) {
325  reportBug("Not an atomic symbol", C);
326  return;
327  }
328 
329  const auto *E = dyn_cast<StringLiteral>(CE->getArg(1)->IgnoreParenCasts());
330  if (!E) {
331  reportBug("Not a string literal", C);
332  return;
333  }
334 
335  ProgramStateRef State = C.getState();
336 
337  C.addTransition(C.getState()->set<DenotedSymbols>(Sym, E));
338 }
339 
340 namespace {
341 class SymbolExpressor
342  : public SymExprVisitor<SymbolExpressor, Optional<std::string>> {
344 
345 public:
346  SymbolExpressor(ProgramStateRef State) : State(State) {}
347 
348  Optional<std::string> VisitSymExpr(const SymExpr *S) {
349  if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) {
350  const StringLiteral *SL = *SLPtr;
351  return std::string(SL->getBytes());
352  }
353  return None;
354  }
355 
356  Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) {
357  if (auto 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 (auto Str1 = Visit(S->getLHS()))
367  if (auto Str2 = Visit(S->getRHS()))
368  return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) +
369  " " + *Str2).str();
370  return None;
371  }
372 };
373 } // namespace
374 
375 void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
376  CheckerContext &C) const {
377  if (CE->getNumArgs() == 0) {
378  reportBug("clang_analyzer_express() requires a symbol", C);
379  return;
380  }
381 
382  SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
383  if (!Sym) {
384  reportBug("Not a symbol", C);
385  return;
386  }
387 
388  SymbolExpressor V(C.getState());
389  auto Str = V.Visit(Sym);
390  if (!Str) {
391  reportBug("Unable to express", C);
392  return;
393  }
394 
395  reportBug(*Str, C);
396 }
397 
398 void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
399  Mgr.registerChecker<ExprInspectionChecker>();
400 }
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2495
const SymExpr * SymbolRef
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2483
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:288
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:50
StringRef getOpcodeStr() const
Definition: Expr.h:3304
const LocationContext * getParent() const
Expr * IgnoreParenCasts() LLVM_READONLY
IgnoreParenCasts - Ignore parentheses and casts.
Definition: Expr.cpp:2557
This represents one expression.
Definition: Expr.h:106
const SourceManager & SM
Definition: Format.cpp:1490
#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)
const StackFrameContext * getStackFrame() const
StringRef getBytes() const
Allow access to clients that need the byte representation, such as ASTWriterStmt::VisitStringLiteral(...
Definition: Expr.h:1668
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:1577
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2407
A SourceLocation and its associated SourceManager.
This class handles loading and caching of source files into memory.