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