DereferenceChecker.cpp revision f5d6176554eda96ac4097a0a1e0852250cadfb9a
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/StaticAnalyzer/Core/Checker.h"
17#include "clang/StaticAnalyzer/Core/CheckerManager.h"
18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20
21using namespace clang;
22using namespace ento;
23
24namespace {
25class DereferenceChecker
26    : public Checker< check::Location,
27                        EventDispatcher<ImplicitNullDerefEvent> > {
28  mutable llvm::OwningPtr<BuiltinBug> BT_null;
29  mutable llvm::OwningPtr<BuiltinBug> BT_undef;
30
31public:
32  void checkLocation(SVal location, bool isLoad, const Stmt* S,
33                     CheckerContext &C) const;
34
35  static void AddDerefSource(raw_ostream &os,
36                             SmallVectorImpl<SourceRange> &Ranges,
37                             const Expr *Ex, bool loadedFrom = false);
38};
39} // end anonymous namespace
40
41void DereferenceChecker::AddDerefSource(raw_ostream &os,
42                                        SmallVectorImpl<SourceRange> &Ranges,
43                                        const Expr *Ex,
44                                        bool loadedFrom) {
45  Ex = Ex->IgnoreParenLValueCasts();
46  switch (Ex->getStmtClass()) {
47    default:
48      return;
49    case Stmt::DeclRefExprClass: {
50      const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
51      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
52        os << " (" << (loadedFrom ? "loaded from" : "from")
53           << " variable '" <<  VD->getName() << "')";
54        Ranges.push_back(DR->getSourceRange());
55      }
56      return;
57    }
58    case Stmt::MemberExprClass: {
59      const MemberExpr *ME = cast<MemberExpr>(Ex);
60      os << " (" << (loadedFrom ? "loaded from" : "via")
61         << " field '" << ME->getMemberNameInfo() << "')";
62      SourceLocation L = ME->getMemberLoc();
63      Ranges.push_back(SourceRange(L, L));
64      break;
65    }
66  }
67}
68
69void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
70                                       CheckerContext &C) const {
71  // Check for dereference of an undefined value.
72  if (l.isUndef()) {
73    if (ExplodedNode *N = C.generateSink()) {
74      if (!BT_undef)
75        BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value"));
76
77      BugReport *report =
78        new BugReport(*BT_undef, BT_undef->getDescription(), N);
79      report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
80                                        bugreporter::GetDerefExpr(N)));
81      C.EmitReport(report);
82    }
83    return;
84  }
85
86  DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l);
87
88  // Check for null dereferences.
89  if (!isa<Loc>(location))
90    return;
91
92  const ProgramState *state = C.getState();
93  const ProgramState *notNullState, *nullState;
94  llvm::tie(notNullState, nullState) = state->assume(location);
95
96  // The explicit NULL case.
97  if (nullState) {
98    if (!notNullState) {
99      // Generate an error node.
100      ExplodedNode *N = C.generateSink(nullState);
101      if (!N)
102        return;
103
104      // We know that 'location' cannot be non-null.  This is what
105      // we call an "explicit" null dereference.
106      if (!BT_null)
107        BT_null.reset(new BuiltinBug("Dereference of null pointer"));
108
109      llvm::SmallString<100> buf;
110      SmallVector<SourceRange, 2> Ranges;
111
112      // Walk through lvalue casts to get the original expression
113      // that syntactically caused the load.
114      if (const Expr *expr = dyn_cast<Expr>(S))
115        S = expr->IgnoreParenLValueCasts();
116
117      switch (S->getStmtClass()) {
118        case Stmt::ArraySubscriptExprClass: {
119          llvm::raw_svector_ostream os(buf);
120          os << "Array access";
121          const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
122          AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts());
123          os << " results in a null pointer dereference";
124          break;
125        }
126        case Stmt::UnaryOperatorClass: {
127          llvm::raw_svector_ostream os(buf);
128          os << "Dereference of null pointer";
129          const UnaryOperator *U = cast<UnaryOperator>(S);
130          AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), true);
131          break;
132        }
133        case Stmt::MemberExprClass: {
134          const MemberExpr *M = cast<MemberExpr>(S);
135          if (M->isArrow()) {
136            llvm::raw_svector_ostream os(buf);
137            os << "Access to field '" << M->getMemberNameInfo()
138               << "' results in a dereference of a null pointer";
139            AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), true);
140          }
141          break;
142        }
143        case Stmt::ObjCIvarRefExprClass: {
144          const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
145          if (const DeclRefExpr *DR =
146              dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) {
147            if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
148              llvm::raw_svector_ostream os(buf);
149              os << "Instance variable access (via '" << VD->getName()
150                 << "') results in a null pointer dereference";
151            }
152          }
153          Ranges.push_back(IV->getSourceRange());
154          break;
155        }
156        default:
157          break;
158      }
159
160      BugReport *report =
161        new BugReport(*BT_null,
162                              buf.empty() ? BT_null->getDescription():buf.str(),
163                              N);
164
165      report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
166                                        bugreporter::GetDerefExpr(N)));
167
168      for (SmallVectorImpl<SourceRange>::iterator
169            I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
170        report->addRange(*I);
171
172      C.EmitReport(report);
173      return;
174    }
175    else {
176      // Otherwise, we have the case where the location could either be
177      // null or not-null.  Record the error node as an "implicit" null
178      // dereference.
179      if (ExplodedNode *N = C.generateSink(nullState)) {
180        ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
181        dispatchEvent(event);
182      }
183    }
184  }
185
186  // From this point forward, we know that the location is not null.
187  C.generateNode(notNullState);
188}
189
190void ento::registerDereferenceChecker(CheckerManager &mgr) {
191  mgr.registerChecker<DereferenceChecker>();
192}
193