clang  3.9.0
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"
15 #include "llvm/ADT/StringSwitch.h"
16 
17 using namespace clang;
18 using namespace ento;
19 
20 namespace {
21 class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols> {
22  mutable std::unique_ptr<BugType> BT;
23 
24  void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
25  void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
26  void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
27  void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
28  void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
29  void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;
30  void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const;
31 
32  typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
33  CheckerContext &C) const;
34 
35  void reportBug(llvm::StringRef Msg, CheckerContext &C) const;
36 
37 public:
38  bool evalCall(const CallExpr *CE, CheckerContext &C) const;
39  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
40 };
41 }
42 
44 
45 bool ExprInspectionChecker::evalCall(const CallExpr *CE,
46  CheckerContext &C) const {
47  // These checks should have no effect on the surrounding environment
48  // (globals should not be invalidated, etc), hence the use of evalCall.
49  FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
50  .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
51  .Case("clang_analyzer_checkInlined",
52  &ExprInspectionChecker::analyzerCheckInlined)
53  .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
54  .Case("clang_analyzer_warnIfReached",
55  &ExprInspectionChecker::analyzerWarnIfReached)
56  .Case("clang_analyzer_warnOnDeadSymbol",
57  &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
58  .Case("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain)
59  .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent)
60  .Default(nullptr);
61 
62  if (!Handler)
63  return false;
64 
65  (this->*Handler)(CE, C);
66  return true;
67 }
68 
69 static const char *getArgumentValueString(const CallExpr *CE,
70  CheckerContext &C) {
71  if (CE->getNumArgs() == 0)
72  return "Missing assertion argument";
73 
75  const LocationContext *LC = N->getLocationContext();
77 
78  const Expr *Assertion = CE->getArg(0);
79  SVal AssertionVal = State->getSVal(Assertion, LC);
80 
81  if (AssertionVal.isUndef())
82  return "UNDEFINED";
83 
84  ProgramStateRef StTrue, StFalse;
85  std::tie(StTrue, StFalse) =
86  State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
87 
88  if (StTrue) {
89  if (StFalse)
90  return "UNKNOWN";
91  else
92  return "TRUE";
93  } else {
94  if (StFalse)
95  return "FALSE";
96  else
97  llvm_unreachable("Invalid constraint; neither true or false.");
98  }
99 }
100 
101 void ExprInspectionChecker::reportBug(llvm::StringRef Msg,
102  CheckerContext &C) const {
103  if (!BT)
104  BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
105 
107  if (!N)
108  return;
109 
110  C.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N));
111 }
112 
113 void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
114  CheckerContext &C) const {
116 
117  // A specific instantiation of an inlined function may have more constrained
118  // values than can generally be assumed. Skip the check.
119  if (LC->getCurrentStackFrame()->getParent() != nullptr)
120  return;
121 
122  reportBug(getArgumentValueString(CE, C), C);
123 }
124 
125 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
126  CheckerContext &C) const {
127  reportBug("REACHABLE", C);
128 }
129 
130 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
131  CheckerContext &C) const {
133 
134  // An inlined function could conceivably also be analyzed as a top-level
135  // function. We ignore this case and only emit a message (TRUE or FALSE)
136  // when we are analyzing it as an inlined function. This means that
137  // clang_analyzer_checkInlined(true) should always print TRUE, but
138  // clang_analyzer_checkInlined(false) should never actually print anything.
139  if (LC->getCurrentStackFrame()->getParent() == nullptr)
140  return;
141 
142  reportBug(getArgumentValueString(CE, C), C);
143 }
144 
145 void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
146  CheckerContext &C) const {
147  if (CE->getNumArgs() == 0)
148  reportBug("Missing argument for explaining", C);
149 
150  SVal V = C.getSVal(CE->getArg(0));
152  reportBug(Ex.Visit(V), C);
153 }
154 
155 void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
156  CheckerContext &C) const {
157  if (CE->getNumArgs() == 0)
158  reportBug("Missing region for obtaining extent", C);
159 
160  auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion());
161  if (!MR)
162  reportBug("Obtaining extent of a non-region", C);
163 
165  State = State->BindExpr(CE, C.getLocationContext(),
166  MR->getExtent(C.getSValBuilder()));
167  C.addTransition(State);
168 }
169 
170 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
171  CheckerContext &C) const {
172  if (CE->getNumArgs() == 0)
173  return;
174  SVal Val = C.getSVal(CE->getArg(0));
175  SymbolRef Sym = Val.getAsSymbol();
176  if (!Sym)
177  return;
178 
179  ProgramStateRef State = C.getState();
180  State = State->add<MarkedSymbols>(Sym);
181  C.addTransition(State);
182 }
183 
184 void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
185  CheckerContext &C) const {
186  ProgramStateRef State = C.getState();
187  const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
188  for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
189  SymbolRef Sym = *I;
190  if (!SymReaper.isDead(Sym))
191  continue;
192 
193  reportBug("SYMBOL DEAD", C);
194  State = State->remove<MarkedSymbols>(Sym);
195  }
196  C.addTransition(State);
197 }
198 
199 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
200  CheckerContext &C) const {
201  LLVM_BUILTIN_TRAP;
202 }
203 
204 void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
205  Mgr.registerChecker<ExprInspectionChecker>();
206 }
207 
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2217
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
#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
LineState State
detail::InMemoryDirectory::const_iterator I
const LocationContext * getLocationContext() const
bool isDead(SymbolRef sym) const
Returns whether or not a symbol has been confirmed dead.
Expr - This represents one expression.
Definition: Expr.h:105
const ProgramStateRef & getState() const
const ProgramStateRef & getState() const
#define bool
Definition: stdbool.h:31
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
CHECKER * registerChecker()
Used to register checkers.
const StackFrameContext * getCurrentStackFrame() const
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Definition: SVals.h:46
A class responsible for cleaning up unused symbols.
bool isUndef() const
Definition: SVals.h:121
const LocationContext * getParent() const
static const char * getArgumentValueString(const CallExpr *CE, CheckerContext &C)
detail::InMemoryDirectory::const_iterator E
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2205
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:111
SValBuilder & getSValBuilder()
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2148
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:75
const LocationContext * getLocationContext() const
SVal getSVal(const Stmt *S) const
Get the value of arbitrary expressions at this point in the path.