clang  3.7.0
DereferenceChecker.cpp
Go to the documentation of this file.
1 //== NullDerefChecker.cpp - Null dereference checker ------------*- 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 // This defines NullDerefChecker, a builtin check in ExprEngine that performs
11 // checks for null pointers at loads and stores.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "ClangSACheckers.h"
16 #include "clang/AST/ExprObjC.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/Support/raw_ostream.h"
23 
24 using namespace clang;
25 using namespace ento;
26 
27 namespace {
28 class DereferenceChecker
29  : public Checker< check::Location,
30  check::Bind,
31  EventDispatcher<ImplicitNullDerefEvent> > {
32  mutable std::unique_ptr<BuiltinBug> BT_null;
33  mutable std::unique_ptr<BuiltinBug> BT_undef;
34 
35  void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
36  bool IsBind = false) const;
37 
38 public:
39  void checkLocation(SVal location, bool isLoad, const Stmt* S,
40  CheckerContext &C) const;
41  void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
42 
43  static void AddDerefSource(raw_ostream &os,
45  const Expr *Ex, const ProgramState *state,
46  const LocationContext *LCtx,
47  bool loadedFrom = false);
48 };
49 } // end anonymous namespace
50 
51 void
52 DereferenceChecker::AddDerefSource(raw_ostream &os,
54  const Expr *Ex,
55  const ProgramState *state,
56  const LocationContext *LCtx,
57  bool loadedFrom) {
58  Ex = Ex->IgnoreParenLValueCasts();
59  switch (Ex->getStmtClass()) {
60  default:
61  break;
62  case Stmt::DeclRefExprClass: {
63  const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
64  if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
65  os << " (" << (loadedFrom ? "loaded from" : "from")
66  << " variable '" << VD->getName() << "')";
67  Ranges.push_back(DR->getSourceRange());
68  }
69  break;
70  }
71  case Stmt::MemberExprClass: {
72  const MemberExpr *ME = cast<MemberExpr>(Ex);
73  os << " (" << (loadedFrom ? "loaded from" : "via")
74  << " field '" << ME->getMemberNameInfo() << "')";
75  SourceLocation L = ME->getMemberLoc();
76  Ranges.push_back(SourceRange(L, L));
77  break;
78  }
79  case Stmt::ObjCIvarRefExprClass: {
80  const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
81  os << " (" << (loadedFrom ? "loaded from" : "via")
82  << " ivar '" << IV->getDecl()->getName() << "')";
83  SourceLocation L = IV->getLocation();
84  Ranges.push_back(SourceRange(L, L));
85  break;
86  }
87  }
88 }
89 
90 void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
91  CheckerContext &C, bool IsBind) const {
92  // Generate an error node.
93  ExplodedNode *N = C.generateSink(State);
94  if (!N)
95  return;
96 
97  // We know that 'location' cannot be non-null. This is what
98  // we call an "explicit" null dereference.
99  if (!BT_null)
100  BT_null.reset(new BuiltinBug(this, "Dereference of null pointer"));
101 
102  SmallString<100> buf;
103  llvm::raw_svector_ostream os(buf);
104 
106 
107  // Walk through lvalue casts to get the original expression
108  // that syntactically caused the load.
109  if (const Expr *expr = dyn_cast<Expr>(S))
110  S = expr->IgnoreParenLValueCasts();
111 
112  if (IsBind) {
113  if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
114  if (BO->isAssignmentOp())
115  S = BO->getRHS();
116  } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
117  assert(DS->isSingleDecl() && "We process decls one by one");
118  if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
119  if (const Expr *Init = VD->getAnyInitializer())
120  S = Init;
121  }
122  }
123 
124  switch (S->getStmtClass()) {
125  case Stmt::ArraySubscriptExprClass: {
126  os << "Array access";
127  const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
128  AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
129  State.get(), N->getLocationContext());
130  os << " results in a null pointer dereference";
131  break;
132  }
133  case Stmt::UnaryOperatorClass: {
134  os << "Dereference of null pointer";
135  const UnaryOperator *U = cast<UnaryOperator>(S);
136  AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
137  State.get(), N->getLocationContext(), true);
138  break;
139  }
140  case Stmt::MemberExprClass: {
141  const MemberExpr *M = cast<MemberExpr>(S);
143  os << "Access to field '" << M->getMemberNameInfo()
144  << "' results in a dereference of a null pointer";
145  AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
146  State.get(), N->getLocationContext(), true);
147  }
148  break;
149  }
150  case Stmt::ObjCIvarRefExprClass: {
151  const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
152  os << "Access to instance variable '" << *IV->getDecl()
153  << "' results in a dereference of a null pointer";
154  AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
155  State.get(), N->getLocationContext(), true);
156  break;
157  }
158  default:
159  break;
160  }
161 
162  os.flush();
163  auto report = llvm::make_unique<BugReport>(
164  *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N);
165 
167 
169  I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
170  report->addRange(*I);
171 
172  C.emitReport(std::move(report));
173 }
174 
175 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
176  CheckerContext &C) const {
177  // Check for dereference of an undefined value.
178  if (l.isUndef()) {
179  if (ExplodedNode *N = C.generateSink()) {
180  if (!BT_undef)
181  BT_undef.reset(
182  new BuiltinBug(this, "Dereference of undefined pointer value"));
183 
184  auto report =
185  llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N);
187  *report);
188  C.emitReport(std::move(report));
189  }
190  return;
191  }
192 
194 
195  // Check for null dereferences.
196  if (!location.getAs<Loc>())
197  return;
198 
199  ProgramStateRef state = C.getState();
200 
201  ProgramStateRef notNullState, nullState;
202  std::tie(notNullState, nullState) = state->assume(location);
203 
204  // The explicit NULL case.
205  if (nullState) {
206  if (!notNullState) {
207  reportBug(nullState, S, C);
208  return;
209  }
210 
211  // Otherwise, we have the case where the location could either be
212  // null or not-null. Record the error node as an "implicit" null
213  // dereference.
214  if (ExplodedNode *N = C.generateSink(nullState)) {
215  ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
216  dispatchEvent(event);
217  }
218  }
219 
220  // From this point forward, we know that the location is not null.
221  C.addTransition(notNullState);
222 }
223 
224 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
225  CheckerContext &C) const {
226  // If we're binding to a reference, check if the value is known to be null.
227  if (V.isUndef())
228  return;
229 
230  const MemRegion *MR = L.getAsRegion();
231  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
232  if (!TVR)
233  return;
234 
235  if (!TVR->getValueType()->isReferenceType())
236  return;
237 
238  ProgramStateRef State = C.getState();
239 
240  ProgramStateRef StNonNull, StNull;
241  std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
242 
243  if (StNull) {
244  if (!StNonNull) {
245  reportBug(StNull, S, C, /*isBind=*/true);
246  return;
247  }
248 
249  // At this point the value could be either null or non-null.
250  // Record this as an "implicit" null dereference.
251  if (ExplodedNode *N = C.generateSink(StNull)) {
252  ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
253  &C.getBugReporter() };
254  dispatchEvent(event);
255  }
256  }
257 
258  // Unlike a regular null dereference, initializing a reference with a
259  // dereferenced null pointer does not actually cause a runtime exception in
260  // Clang's implementation of references.
261  //
262  // int &r = *p; // safe??
263  // if (p != NULL) return; // uh-oh
264  // r = 5; // trap here
265  //
266  // The standard says this is invalid as soon as we try to create a "null
267  // reference" (there is no such thing), but turning this into an assumption
268  // that 'p' is never null will not match our actual runtime behavior.
269  // So we do not record this assumption, allowing us to warn on the last line
270  // of this example.
271  //
272  // We do need to add a transition because we may have generated a sink for
273  // the "implicit" null dereference.
274  C.addTransition(State, this);
275 }
276 
277 void ento::registerDereferenceChecker(CheckerManager &mgr) {
278  mgr.registerChecker<DereferenceChecker>();
279 }
const Expr * getBase() const
Definition: ExprObjC.h:504
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
Definition: ASTMatchers.h:1110
TypedValueRegion - An abstract class representing regions having a typed value.
Definition: MemRegion.h:498
const Expr * getDerefExpr(const Stmt *S)
StringRef getName() const
Definition: Decl.h:168
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:77
SourceLocation getLocation() const
Definition: ExprObjC.h:513
DeclarationNameInfo getMemberNameInfo() const
Retrieve the member declaration name info.
Definition: Expr.h:2541
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph). Uses the default CheckerContex...
virtual QualType getValueType() const =0
LineState State
bool isReferenceType() const
Definition: Type.h:5241
BugReporter & getBugReporter()
SmallVector< CharSourceRange, 8 > Ranges
Definition: Format.cpp:1554
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:2918
Expr * IgnoreParenCasts() LLVM_READONLY
Definition: Expr.cpp:2439
We dereferenced a location that may be null.
Definition: Checker.h:512
ObjCIvarDecl * getDecl()
Definition: ExprObjC.h:500
const LocationContext * getLocationContext() const
ExplodedNode * generateSink(ProgramStateRef State=nullptr, ExplodedNode *Pred=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a sink node. Generating a sink stops exploration of the given path.
const ProgramStateRef & getState() const
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
Definition: SVals.h:86
Expr * getSubExpr() const
Definition: Expr.h:1699
ValueDecl * getDecl()
Definition: Expr.h:994
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
CHECKER * registerChecker()
Used to register checkers.
Encodes a location in the source. The SourceManager can decode this to get at the full include stack...
bool isUndef() const
Definition: SVals.h:121
bool isArrow() const
Definition: Expr.h:2548
bool isDeclRefExprToReference(const Expr *E)
const MemRegion * getAsRegion() const
Definition: SVals.cpp:135
SourceLocation getMemberLoc() const
Definition: Expr.h:2553
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition: Expr.h:2066
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Definition: Expr.cpp:2486
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:474
Expr * getBase() const
Definition: Expr.h:2405
bool trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, BugReport &R, bool IsArg=false, bool EnableNullFPSuppression=true)
A reference to a declared variable, function, enum, etc. [C99 6.5.1p2].
Definition: Expr.h:899
A trivial tuple used to represent a source range.
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:75
Expr * IgnoreParens() LLVM_READONLY
Definition: Expr.cpp:2408