DereferenceChecker.cpp revision ec8605f1d7ec846dbf51047bfd5c56d32d1ff91c
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, CheckerContext &C) const;
33
34  static void AddDerefSource(llvm::raw_ostream &os,
35                             llvm::SmallVectorImpl<SourceRange> &Ranges,
36                             const Expr *Ex, bool loadedFrom = false);
37};
38} // end anonymous namespace
39
40void DereferenceChecker::AddDerefSource(llvm::raw_ostream &os,
41                                     llvm::SmallVectorImpl<SourceRange> &Ranges,
42                                        const Expr *Ex,
43                                        bool loadedFrom) {
44  Ex = Ex->IgnoreParenLValueCasts();
45  switch (Ex->getStmtClass()) {
46    default:
47      return;
48    case Stmt::DeclRefExprClass: {
49      const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
50      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
51        os << " (" << (loadedFrom ? "loaded from" : "from")
52           << " variable '" <<  VD->getName() << "')";
53        Ranges.push_back(DR->getSourceRange());
54      }
55      return;
56    }
57    case Stmt::MemberExprClass: {
58      const MemberExpr *ME = cast<MemberExpr>(Ex);
59      os << " (" << (loadedFrom ? "loaded from" : "via")
60         << " field '" << ME->getMemberNameInfo() << "')";
61      SourceLocation L = ME->getMemberLoc();
62      Ranges.push_back(SourceRange(L, L));
63      break;
64    }
65  }
66}
67
68void DereferenceChecker::checkLocation(SVal l, bool isLoad,
69                                       CheckerContext &C) const {
70  // Check for dereference of an undefined value.
71  if (l.isUndef()) {
72    if (ExplodedNode *N = C.generateSink()) {
73      if (!BT_undef)
74        BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value"));
75
76      EnhancedBugReport *report =
77        new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N);
78      report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
79                                bugreporter::GetDerefExpr(N));
80      C.EmitReport(report);
81    }
82    return;
83  }
84
85  DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l);
86
87  // Check for null dereferences.
88  if (!isa<Loc>(location))
89    return;
90
91  const Stmt *S = C.getStmt();
92  const GRState *state = C.getState();
93  const GRState *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      llvm::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      EnhancedBugReport *report =
161        new EnhancedBugReport(*BT_null,
162                              buf.empty() ? BT_null->getDescription():buf.str(),
163                              N);
164
165      report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
166                                bugreporter::GetDerefExpr(N));
167
168      for (llvm::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.addTransition(notNullState);
188}
189
190void ento::registerDereferenceChecker(CheckerManager &mgr) {
191  mgr.registerChecker<DereferenceChecker>();
192}
193