clang  6.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 
47  typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
48  CheckerContext &C) const;
49 
50  ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C) const;
51  ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR,
52  ExplodedNode *N) const;
53 
54 public:
55  bool evalCall(const CallExpr *CE, CheckerContext &C) const;
56  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
57  void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
58  ExprEngine &Eng) const;
59 };
60 }
61 
63 
64 bool ExprInspectionChecker::evalCall(const CallExpr *CE,
65  CheckerContext &C) const {
66  // These checks should have no effect on the surrounding environment
67  // (globals should not be invalidated, etc), hence the use of evalCall.
68  FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
69  .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
70  .Case("clang_analyzer_checkInlined",
71  &ExprInspectionChecker::analyzerCheckInlined)
72  .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
73  .Case("clang_analyzer_warnIfReached",
74  &ExprInspectionChecker::analyzerWarnIfReached)
75  .Case("clang_analyzer_warnOnDeadSymbol",
76  &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
77  .StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain)
78  .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump)
79  .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent)
80  .Case("clang_analyzer_printState",
81  &ExprInspectionChecker::analyzerPrintState)
82  .Case("clang_analyzer_numTimesReached",
83  &ExprInspectionChecker::analyzerNumTimesReached)
84  .Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump)
85  .Default(nullptr);
86 
87  if (!Handler)
88  return false;
89 
90  (this->*Handler)(CE, C);
91  return true;
92 }
93 
94 static const char *getArgumentValueString(const CallExpr *CE,
95  CheckerContext &C) {
96  if (CE->getNumArgs() == 0)
97  return "Missing assertion argument";
98 
100  const LocationContext *LC = N->getLocationContext();
102 
103  const Expr *Assertion = CE->getArg(0);
104  SVal AssertionVal = State->getSVal(Assertion, LC);
105 
106  if (AssertionVal.isUndef())
107  return "UNDEFINED";
108 
109  ProgramStateRef StTrue, StFalse;
110  std::tie(StTrue, StFalse) =
111  State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
112 
113  if (StTrue) {
114  if (StFalse)
115  return "UNKNOWN";
116  else
117  return "TRUE";
118  } else {
119  if (StFalse)
120  return "FALSE";
121  else
122  llvm_unreachable("Invalid constraint; neither true or false.");
123  }
124 }
125 
126 ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
127  CheckerContext &C) const {
129  reportBug(Msg, C.getBugReporter(), N);
130  return N;
131 }
132 
133 ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
134  BugReporter &BR,
135  ExplodedNode *N) const {
136  if (!N)
137  return nullptr;
138 
139  if (!BT)
140  BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
141 
142  BR.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N));
143  return N;
144 }
145 
146 void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
147  CheckerContext &C) const {
149 
150  // A specific instantiation of an inlined function may have more constrained
151  // values than can generally be assumed. Skip the check.
152  if (LC->getCurrentStackFrame()->getParent() != nullptr)
153  return;
154 
155  reportBug(getArgumentValueString(CE, C), C);
156 }
157 
158 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
159  CheckerContext &C) const {
160  reportBug("REACHABLE", C);
161 }
162 
163 void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE,
164  CheckerContext &C) const {
165  ++ReachedStats[CE].NumTimesReached;
166  if (!ReachedStats[CE].ExampleNode) {
167  // Later, in checkEndAnalysis, we'd throw a report against it.
168  ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode();
169  }
170 }
171 
172 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
173  CheckerContext &C) const {
175 
176  // An inlined function could conceivably also be analyzed as a top-level
177  // function. We ignore this case and only emit a message (TRUE or FALSE)
178  // when we are analyzing it as an inlined function. This means that
179  // clang_analyzer_checkInlined(true) should always print TRUE, but
180  // clang_analyzer_checkInlined(false) should never actually print anything.
181  if (LC->getCurrentStackFrame()->getParent() == nullptr)
182  return;
183 
184  reportBug(getArgumentValueString(CE, C), C);
185 }
186 
187 void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
188  CheckerContext &C) const {
189  if (CE->getNumArgs() == 0) {
190  reportBug("Missing argument for explaining", C);
191  return;
192  }
193 
194  SVal V = C.getSVal(CE->getArg(0));
196  reportBug(Ex.Visit(V), C);
197 }
198 
199 void ExprInspectionChecker::analyzerDump(const CallExpr *CE,
200  CheckerContext &C) const {
201  if (CE->getNumArgs() == 0) {
202  reportBug("Missing argument for dumping", C);
203  return;
204  }
205 
206  SVal V = C.getSVal(CE->getArg(0));
207 
209  llvm::raw_svector_ostream OS(Str);
210  V.dumpToStream(OS);
211  reportBug(OS.str(), C);
212 }
213 
214 void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
215  CheckerContext &C) const {
216  if (CE->getNumArgs() == 0) {
217  reportBug("Missing region for obtaining extent", C);
218  return;
219  }
220 
221  auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion());
222  if (!MR) {
223  reportBug("Obtaining extent of a non-region", C);
224  return;
225  }
226 
228  State = State->BindExpr(CE, C.getLocationContext(),
229  MR->getExtent(C.getSValBuilder()));
230  C.addTransition(State);
231 }
232 
233 void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE,
234  CheckerContext &C) const {
235  C.getState()->dump();
236 }
237 
238 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
239  CheckerContext &C) const {
240  if (CE->getNumArgs() == 0)
241  return;
242  SVal Val = C.getSVal(CE->getArg(0));
243  SymbolRef Sym = Val.getAsSymbol();
244  if (!Sym)
245  return;
246 
248  State = State->add<MarkedSymbols>(Sym);
249  C.addTransition(State);
250 }
251 
252 void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
253  CheckerContext &C) const {
255  const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
256  ExplodedNode *N = C.getPredecessor();
257  for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
258  SymbolRef Sym = *I;
259  if (!SymReaper.isDead(Sym))
260  continue;
261 
262  // The non-fatal error node should be the same for all reports.
263  if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C))
264  N = BugNode;
265  State = State->remove<MarkedSymbols>(Sym);
266  }
267  C.addTransition(State, N);
268 }
269 
270 void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
271  ExprEngine &Eng) const {
272  for (auto Item: ReachedStats) {
273  unsigned NumTimesReached = Item.second.NumTimesReached;
274  ExplodedNode *N = Item.second.ExampleNode;
275 
276  reportBug(llvm::to_string(NumTimesReached), BR, N);
277  }
278  ReachedStats.clear();
279 }
280 
281 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
282  CheckerContext &C) const {
283  LLVM_BUILTIN_TRAP;
284 }
285 
286 void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE,
287  CheckerContext &C) const {
288  const LangOptions &Opts = C.getLangOpts();
289  const SourceManager &SM = C.getSourceManager();
290  FullSourceLoc FL(CE->getArg(0)->getLocStart(), SM);
291  std::string HashContent =
292  GetIssueString(SM, FL, getCheckName().getName(), "Category",
293  C.getLocationContext()->getDecl(), Opts);
294 
295  reportBug(HashContent, C);
296 }
297 
298 void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
299  Mgr.registerChecker<ExprInspectionChecker>();
300 }
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2339
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2327
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
const ProgramStateRef & getState() const
bool isDead(SymbolRef sym) const
Returns whether or not a symbol has been confirmed dead.
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
SVal getSVal(const Stmt *S) const
Get the value of arbitrary expressions at this point in the path.
#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set of type NameTy, suitable for placement into the ProgramState.
Symbolic value.
Definition: SymExpr.h:29
StringRef getCalleeName(const FunctionDecl *FunDecl) const
Get the name of the called function (path-sensitive).
void dumpToStream(raw_ostream &OS) const
Definition: SVals.cpp:274
LineState State
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:48
BugReporter & getBugReporter()
const StackFrameContext * getCurrentStackFrame() const
const LocationContext * getLocationContext() const
const LocationContext * getParent() const
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:116
Expr - This represents one expression.
Definition: Expr.h:119
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
const SourceManager & SM
Definition: Format.cpp:1327
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:403
CHECKER * registerChecker()
Used to register checkers.
void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Definition: SVals.h:63
A class responsible for cleaning up unused symbols.
Dataflow Directional Tag Classes.
static const char * getArgumentValueString(const CallExpr *CE, CheckerContext &C)
const Decl * getDecl() const
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:92
const ProgramStateRef & getState() const
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.
SourceManager & getSourceManager()
SValBuilder & getSValBuilder()
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2270
A SourceLocation and its associated SourceManager.
bool isUndef() const
Definition: SVals.h:129
const LocationContext * getLocationContext() const
SourceLocation getLocStart() const LLVM_READONLY
Definition: Stmt.cpp:277
This class handles loading and caching of source files into memory.
const LangOptions & getLangOpts() const