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