1218887Sdim//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==//
2218887Sdim//
3218887Sdim//                     The LLVM Compiler Infrastructure
4218887Sdim//
5218887Sdim// This file is distributed under the University of Illinois Open Source
6218887Sdim// License. See LICENSE.TXT for details.
7218887Sdim//
8218887Sdim//===----------------------------------------------------------------------===//
9218887Sdim//
10218887Sdim// This defines NullDerefChecker, a builtin check in ExprEngine that performs
11218887Sdim// checks for null pointers at loads and stores.
12218887Sdim//
13218887Sdim//===----------------------------------------------------------------------===//
14218887Sdim
15221345Sdim#include "ClangSACheckers.h"
16234353Sdim#include "clang/AST/ExprObjC.h"
17249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
19221345Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
20221345Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21234353Sdim#include "llvm/ADT/SmallString.h"
22249423Sdim#include "llvm/Support/raw_ostream.h"
23218887Sdim
24218887Sdimusing namespace clang;
25218887Sdimusing namespace ento;
26218887Sdim
27218887Sdimnamespace {
28221345Sdimclass DereferenceChecker
29221345Sdim    : public Checker< check::Location,
30239462Sdim                      check::Bind,
31239462Sdim                      EventDispatcher<ImplicitNullDerefEvent> > {
32234353Sdim  mutable OwningPtr<BuiltinBug> BT_null;
33234353Sdim  mutable OwningPtr<BuiltinBug> BT_undef;
34221345Sdim
35239462Sdim  void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
36239462Sdim                 bool IsBind = false) const;
37239462Sdim
38218887Sdimpublic:
39226633Sdim  void checkLocation(SVal location, bool isLoad, const Stmt* S,
40226633Sdim                     CheckerContext &C) const;
41239462Sdim  void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
42218887Sdim
43243830Sdim  static void AddDerefSource(raw_ostream &os,
44226633Sdim                             SmallVectorImpl<SourceRange> &Ranges,
45234353Sdim                             const Expr *Ex, const ProgramState *state,
46234353Sdim                             const LocationContext *LCtx,
47234353Sdim                             bool loadedFrom = false);
48218887Sdim};
49218887Sdim} // end anonymous namespace
50218887Sdim
51243830Sdimvoid
52234353SdimDereferenceChecker::AddDerefSource(raw_ostream &os,
53234353Sdim                                   SmallVectorImpl<SourceRange> &Ranges,
54234353Sdim                                   const Expr *Ex,
55234353Sdim                                   const ProgramState *state,
56234353Sdim                                   const LocationContext *LCtx,
57234353Sdim                                   bool loadedFrom) {
58218887Sdim  Ex = Ex->IgnoreParenLValueCasts();
59218887Sdim  switch (Ex->getStmtClass()) {
60218887Sdim    default:
61234353Sdim      break;
62218887Sdim    case Stmt::DeclRefExprClass: {
63218887Sdim      const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
64218887Sdim      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
65218887Sdim        os << " (" << (loadedFrom ? "loaded from" : "from")
66218887Sdim           << " variable '" <<  VD->getName() << "')";
67218887Sdim        Ranges.push_back(DR->getSourceRange());
68218887Sdim      }
69234353Sdim      break;
70218887Sdim    }
71218887Sdim    case Stmt::MemberExprClass: {
72218887Sdim      const MemberExpr *ME = cast<MemberExpr>(Ex);
73218887Sdim      os << " (" << (loadedFrom ? "loaded from" : "via")
74218887Sdim         << " field '" << ME->getMemberNameInfo() << "')";
75218887Sdim      SourceLocation L = ME->getMemberLoc();
76218887Sdim      Ranges.push_back(SourceRange(L, L));
77218887Sdim      break;
78218887Sdim    }
79249423Sdim    case Stmt::ObjCIvarRefExprClass: {
80249423Sdim      const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
81249423Sdim      os << " (" << (loadedFrom ? "loaded from" : "via")
82249423Sdim         << " ivar '" << IV->getDecl()->getName() << "')";
83249423Sdim      SourceLocation L = IV->getLocation();
84249423Sdim      Ranges.push_back(SourceRange(L, L));
85249423Sdim      break;
86249423Sdim    }
87218887Sdim  }
88218887Sdim}
89218887Sdim
90239462Sdimvoid DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
91239462Sdim                                   CheckerContext &C, bool IsBind) const {
92239462Sdim  // Generate an error node.
93239462Sdim  ExplodedNode *N = C.generateSink(State);
94239462Sdim  if (!N)
95239462Sdim    return;
96239462Sdim
97239462Sdim  // We know that 'location' cannot be non-null.  This is what
98239462Sdim  // we call an "explicit" null dereference.
99239462Sdim  if (!BT_null)
100239462Sdim    BT_null.reset(new BuiltinBug("Dereference of null pointer"));
101239462Sdim
102239462Sdim  SmallString<100> buf;
103243830Sdim  llvm::raw_svector_ostream os(buf);
104243830Sdim
105239462Sdim  SmallVector<SourceRange, 2> Ranges;
106239462Sdim
107239462Sdim  // Walk through lvalue casts to get the original expression
108239462Sdim  // that syntactically caused the load.
109239462Sdim  if (const Expr *expr = dyn_cast<Expr>(S))
110239462Sdim    S = expr->IgnoreParenLValueCasts();
111239462Sdim
112239462Sdim  if (IsBind) {
113239462Sdim    if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
114239462Sdim      if (BO->isAssignmentOp())
115239462Sdim        S = BO->getRHS();
116239462Sdim    } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
117239462Sdim      assert(DS->isSingleDecl() && "We process decls one by one");
118239462Sdim      if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
119239462Sdim        if (const Expr *Init = VD->getAnyInitializer())
120239462Sdim          S = Init;
121239462Sdim    }
122239462Sdim  }
123239462Sdim
124239462Sdim  switch (S->getStmtClass()) {
125239462Sdim  case Stmt::ArraySubscriptExprClass: {
126239462Sdim    os << "Array access";
127239462Sdim    const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
128243830Sdim    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
129243830Sdim                   State.getPtr(), N->getLocationContext());
130239462Sdim    os << " results in a null pointer dereference";
131239462Sdim    break;
132239462Sdim  }
133239462Sdim  case Stmt::UnaryOperatorClass: {
134239462Sdim    os << "Dereference of null pointer";
135239462Sdim    const UnaryOperator *U = cast<UnaryOperator>(S);
136243830Sdim    AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
137243830Sdim                   State.getPtr(), N->getLocationContext(), true);
138239462Sdim    break;
139239462Sdim  }
140239462Sdim  case Stmt::MemberExprClass: {
141239462Sdim    const MemberExpr *M = cast<MemberExpr>(S);
142243830Sdim    if (M->isArrow() || bugreporter::isDeclRefExprToReference(M->getBase())) {
143239462Sdim      os << "Access to field '" << M->getMemberNameInfo()
144239462Sdim         << "' results in a dereference of a null pointer";
145243830Sdim      AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
146243830Sdim                     State.getPtr(), N->getLocationContext(), true);
147239462Sdim    }
148239462Sdim    break;
149239462Sdim  }
150239462Sdim  case Stmt::ObjCIvarRefExprClass: {
151239462Sdim    const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
152243830Sdim    os << "Access to instance variable '" << *IV->getDecl()
153243830Sdim       << "' results in a dereference of a null pointer";
154243830Sdim    AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
155243830Sdim                   State.getPtr(), N->getLocationContext(), true);
156239462Sdim    break;
157239462Sdim  }
158239462Sdim  default:
159239462Sdim    break;
160239462Sdim  }
161239462Sdim
162243830Sdim  os.flush();
163239462Sdim  BugReport *report =
164239462Sdim    new BugReport(*BT_null,
165239462Sdim                  buf.empty() ? BT_null->getDescription() : buf.str(),
166239462Sdim                  N);
167239462Sdim
168249423Sdim  bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report);
169239462Sdim
170239462Sdim  for (SmallVectorImpl<SourceRange>::iterator
171239462Sdim       I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
172239462Sdim    report->addRange(*I);
173239462Sdim
174243830Sdim  C.emitReport(report);
175239462Sdim}
176239462Sdim
177226633Sdimvoid DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
178221345Sdim                                       CheckerContext &C) const {
179218887Sdim  // Check for dereference of an undefined value.
180218887Sdim  if (l.isUndef()) {
181218887Sdim    if (ExplodedNode *N = C.generateSink()) {
182218887Sdim      if (!BT_undef)
183221345Sdim        BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value"));
184218887Sdim
185226633Sdim      BugReport *report =
186226633Sdim        new BugReport(*BT_undef, BT_undef->getDescription(), N);
187249423Sdim      bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S),
188243830Sdim                                         *report);
189243830Sdim      C.emitReport(report);
190218887Sdim    }
191218887Sdim    return;
192218887Sdim  }
193218887Sdim
194249423Sdim  DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
195218887Sdim
196218887Sdim  // Check for null dereferences.
197249423Sdim  if (!location.getAs<Loc>())
198218887Sdim    return;
199218887Sdim
200234353Sdim  ProgramStateRef state = C.getState();
201239462Sdim
202234353Sdim  ProgramStateRef notNullState, nullState;
203218887Sdim  llvm::tie(notNullState, nullState) = state->assume(location);
204218887Sdim
205218887Sdim  // The explicit NULL case.
206218887Sdim  if (nullState) {
207218887Sdim    if (!notNullState) {
208239462Sdim      reportBug(nullState, S, C);
209239462Sdim      return;
210239462Sdim    }
211218887Sdim
212239462Sdim    // Otherwise, we have the case where the location could either be
213239462Sdim    // null or not-null.  Record the error node as an "implicit" null
214239462Sdim    // dereference.
215239462Sdim    if (ExplodedNode *N = C.generateSink(nullState)) {
216239462Sdim      ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
217239462Sdim      dispatchEvent(event);
218239462Sdim    }
219239462Sdim  }
220218887Sdim
221239462Sdim  // From this point forward, we know that the location is not null.
222239462Sdim  C.addTransition(notNullState);
223239462Sdim}
224218887Sdim
225239462Sdimvoid DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
226239462Sdim                                   CheckerContext &C) const {
227239462Sdim  // If we're binding to a reference, check if the value is known to be null.
228239462Sdim  if (V.isUndef())
229239462Sdim    return;
230218887Sdim
231239462Sdim  const MemRegion *MR = L.getAsRegion();
232239462Sdim  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
233239462Sdim  if (!TVR)
234239462Sdim    return;
235218887Sdim
236239462Sdim  if (!TVR->getValueType()->isReferenceType())
237239462Sdim    return;
238218887Sdim
239239462Sdim  ProgramStateRef State = C.getState();
240218887Sdim
241239462Sdim  ProgramStateRef StNonNull, StNull;
242249423Sdim  llvm::tie(StNonNull, StNull) =
243249423Sdim      State->assume(V.castAs<DefinedOrUnknownSVal>());
244234353Sdim
245239462Sdim  if (StNull) {
246239462Sdim    if (!StNonNull) {
247239462Sdim      reportBug(StNull, S, C, /*isBind=*/true);
248218887Sdim      return;
249218887Sdim    }
250239462Sdim
251239462Sdim    // At this point the value could be either null or non-null.
252239462Sdim    // Record this as an "implicit" null dereference.
253239462Sdim    if (ExplodedNode *N = C.generateSink(StNull)) {
254239462Sdim      ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
255239462Sdim                                       &C.getBugReporter() };
256239462Sdim      dispatchEvent(event);
257218887Sdim    }
258218887Sdim  }
259218887Sdim
260239462Sdim  // Unlike a regular null dereference, initializing a reference with a
261239462Sdim  // dereferenced null pointer does not actually cause a runtime exception in
262239462Sdim  // Clang's implementation of references.
263239462Sdim  //
264239462Sdim  //   int &r = *p; // safe??
265239462Sdim  //   if (p != NULL) return; // uh-oh
266239462Sdim  //   r = 5; // trap here
267239462Sdim  //
268239462Sdim  // The standard says this is invalid as soon as we try to create a "null
269239462Sdim  // reference" (there is no such thing), but turning this into an assumption
270239462Sdim  // that 'p' is never null will not match our actual runtime behavior.
271239462Sdim  // So we do not record this assumption, allowing us to warn on the last line
272239462Sdim  // of this example.
273239462Sdim  //
274239462Sdim  // We do need to add a transition because we may have generated a sink for
275239462Sdim  // the "implicit" null dereference.
276239462Sdim  C.addTransition(State, this);
277218887Sdim}
278221345Sdim
279221345Sdimvoid ento::registerDereferenceChecker(CheckerManager &mgr) {
280221345Sdim  mgr.registerChecker<DereferenceChecker>();
281221345Sdim}
282