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