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