DereferenceChecker.cpp revision a93d0f280693b8418bc88cf7a8c93325f7fcf4c6
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"
17#include "clang/StaticAnalyzer/Core/Checker.h"
18#include "clang/StaticAnalyzer/Core/CheckerManager.h"
19#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21#include "llvm/ADT/SmallString.h"
22#include "llvm/Support/raw_ostream.h"
23
24using namespace clang;
25using namespace ento;
26
27namespace {
28class DereferenceChecker
29    : public Checker< check::Location,
30                      check::Bind,
31                      EventDispatcher<ImplicitNullDerefEvent> > {
32  mutable OwningPtr<BuiltinBug> BT_null;
33  mutable OwningPtr<BuiltinBug> BT_undef;
34
35  void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
36                 bool IsBind = false) const;
37
38public:
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,
44                             SmallVectorImpl<SourceRange> &Ranges,
45                             const Expr *Ex, const ProgramState *state,
46                             const LocationContext *LCtx,
47                             bool loadedFrom = false);
48};
49} // end anonymous namespace
50
51void
52DereferenceChecker::AddDerefSource(raw_ostream &os,
53                                   SmallVectorImpl<SourceRange> &Ranges,
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  }
80}
81
82void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
83                                   CheckerContext &C, bool IsBind) const {
84  // Generate an error node.
85  ExplodedNode *N = C.generateSink(State);
86  if (!N)
87    return;
88
89  // We know that 'location' cannot be non-null.  This is what
90  // we call an "explicit" null dereference.
91  if (!BT_null)
92    BT_null.reset(new BuiltinBug("Dereference of null pointer"));
93
94  SmallString<100> buf;
95  llvm::raw_svector_ostream os(buf);
96
97  SmallVector<SourceRange, 2> Ranges;
98
99  // Walk through lvalue casts to get the original expression
100  // that syntactically caused the load.
101  if (const Expr *expr = dyn_cast<Expr>(S))
102    S = expr->IgnoreParenLValueCasts();
103
104  if (IsBind) {
105    if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
106      if (BO->isAssignmentOp())
107        S = BO->getRHS();
108    } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
109      assert(DS->isSingleDecl() && "We process decls one by one");
110      if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
111        if (const Expr *Init = VD->getAnyInitializer())
112          S = Init;
113    }
114  }
115
116  switch (S->getStmtClass()) {
117  case Stmt::ArraySubscriptExprClass: {
118    os << "Array access";
119    const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
120    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
121                   State.getPtr(), N->getLocationContext());
122    os << " results in a null pointer dereference";
123    break;
124  }
125  case Stmt::UnaryOperatorClass: {
126    os << "Dereference of null pointer";
127    const UnaryOperator *U = cast<UnaryOperator>(S);
128    AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
129                   State.getPtr(), N->getLocationContext(), true);
130    break;
131  }
132  case Stmt::MemberExprClass: {
133    const MemberExpr *M = cast<MemberExpr>(S);
134    if (M->isArrow() || bugreporter::isDeclRefExprToReference(M->getBase())) {
135      os << "Access to field '" << M->getMemberNameInfo()
136         << "' results in a dereference of a null pointer";
137      AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
138                     State.getPtr(), N->getLocationContext(), true);
139    }
140    break;
141  }
142  case Stmt::ObjCIvarRefExprClass: {
143    const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
144    os << "Access to instance variable '" << *IV->getDecl()
145       << "' results in a dereference of a null pointer";
146    AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
147                   State.getPtr(), N->getLocationContext(), true);
148    break;
149  }
150  default:
151    break;
152  }
153
154  os.flush();
155  BugReport *report =
156    new BugReport(*BT_null,
157                  buf.empty() ? BT_null->getDescription() : buf.str(),
158                  N);
159
160  bugreporter::trackNullOrUndefValue(N, bugreporter::GetDerefExpr(N), *report);
161
162  for (SmallVectorImpl<SourceRange>::iterator
163       I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
164    report->addRange(*I);
165
166  C.emitReport(report);
167}
168
169void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
170                                       CheckerContext &C) const {
171  // Check for dereference of an undefined value.
172  if (l.isUndef()) {
173    if (ExplodedNode *N = C.generateSink()) {
174      if (!BT_undef)
175        BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value"));
176
177      BugReport *report =
178        new BugReport(*BT_undef, BT_undef->getDescription(), N);
179      bugreporter::trackNullOrUndefValue(N, bugreporter::GetDerefExpr(N),
180                                         *report);
181      C.emitReport(report);
182    }
183    return;
184  }
185
186  DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l);
187
188  // Check for null dereferences.
189  if (!isa<Loc>(location))
190    return;
191
192  ProgramStateRef state = C.getState();
193
194  ProgramStateRef notNullState, nullState;
195  llvm::tie(notNullState, nullState) = state->assume(location);
196
197  // The explicit NULL case.
198  if (nullState) {
199    if (!notNullState) {
200      reportBug(nullState, S, C);
201      return;
202    }
203
204    // Otherwise, we have the case where the location could either be
205    // null or not-null.  Record the error node as an "implicit" null
206    // dereference.
207    if (ExplodedNode *N = C.generateSink(nullState)) {
208      ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
209      dispatchEvent(event);
210    }
211  }
212
213  // From this point forward, we know that the location is not null.
214  C.addTransition(notNullState);
215}
216
217void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
218                                   CheckerContext &C) const {
219  // If we're binding to a reference, check if the value is known to be null.
220  if (V.isUndef())
221    return;
222
223  const MemRegion *MR = L.getAsRegion();
224  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
225  if (!TVR)
226    return;
227
228  if (!TVR->getValueType()->isReferenceType())
229    return;
230
231  ProgramStateRef State = C.getState();
232
233  ProgramStateRef StNonNull, StNull;
234  llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V));
235
236  if (StNull) {
237    if (!StNonNull) {
238      reportBug(StNull, S, C, /*isBind=*/true);
239      return;
240    }
241
242    // At this point the value could be either null or non-null.
243    // Record this as an "implicit" null dereference.
244    if (ExplodedNode *N = C.generateSink(StNull)) {
245      ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
246                                       &C.getBugReporter() };
247      dispatchEvent(event);
248    }
249  }
250
251  // Unlike a regular null dereference, initializing a reference with a
252  // dereferenced null pointer does not actually cause a runtime exception in
253  // Clang's implementation of references.
254  //
255  //   int &r = *p; // safe??
256  //   if (p != NULL) return; // uh-oh
257  //   r = 5; // trap here
258  //
259  // The standard says this is invalid as soon as we try to create a "null
260  // reference" (there is no such thing), but turning this into an assumption
261  // that 'p' is never null will not match our actual runtime behavior.
262  // So we do not record this assumption, allowing us to warn on the last line
263  // of this example.
264  //
265  // We do need to add a transition because we may have generated a sink for
266  // the "implicit" null dereference.
267  C.addTransition(State, this);
268}
269
270void ento::registerDereferenceChecker(CheckerManager &mgr) {
271  mgr.registerChecker<DereferenceChecker>();
272}
273