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/AST/ExprOpenMP.h"
18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19 #include "clang/StaticAnalyzer/Core/Checker.h"
20 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
23 #include "llvm/ADT/SmallString.h"
24 #include "llvm/Support/raw_ostream.h"
25 
26 using namespace clang;
27 using namespace ento;
28 
29 namespace {
30 class DereferenceChecker
31     : public Checker< check::Location,
32                       check::Bind,
33                       EventDispatcher<ImplicitNullDerefEvent> > {
34   mutable std::unique_ptr<BuiltinBug> BT_null;
35   mutable std::unique_ptr<BuiltinBug> BT_undef;
36 
37   void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
38                  bool IsBind = false) const;
39 
40 public:
41   void checkLocation(SVal location, bool isLoad, const Stmt* S,
42                      CheckerContext &C) const;
43   void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
44 
45   static void AddDerefSource(raw_ostream &os,
46                              SmallVectorImpl<SourceRange> &Ranges,
47                              const Expr *Ex, const ProgramState *state,
48                              const LocationContext *LCtx,
49                              bool loadedFrom = false);
50 };
51 } // end anonymous namespace
52 
53 void
54 DereferenceChecker::AddDerefSource(raw_ostream &os,
55                                    SmallVectorImpl<SourceRange> &Ranges,
56                                    const Expr *Ex,
57                                    const ProgramState *state,
58                                    const LocationContext *LCtx,
59                                    bool loadedFrom) {
60   Ex = Ex->IgnoreParenLValueCasts();
61   switch (Ex->getStmtClass()) {
62     default:
63       break;
64     case Stmt::DeclRefExprClass: {
65       const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
66       if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
67         os << " (" << (loadedFrom ? "loaded from" : "from")
68            << " variable '" <<  VD->getName() << "')";
69         Ranges.push_back(DR->getSourceRange());
70       }
71       break;
72     }
73     case Stmt::MemberExprClass: {
74       const MemberExpr *ME = cast<MemberExpr>(Ex);
75       os << " (" << (loadedFrom ? "loaded from" : "via")
76          << " field '" << ME->getMemberNameInfo() << "')";
77       SourceLocation L = ME->getMemberLoc();
78       Ranges.push_back(SourceRange(L, L));
79       break;
80     }
81     case Stmt::ObjCIvarRefExprClass: {
82       const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
83       os << " (" << (loadedFrom ? "loaded from" : "via")
84          << " ivar '" << IV->getDecl()->getName() << "')";
85       SourceLocation L = IV->getLocation();
86       Ranges.push_back(SourceRange(L, L));
87       break;
88     }
89   }
90 }
91 
92 void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
93                                    CheckerContext &C, bool IsBind) const {
94   // Generate an error node.
95   ExplodedNode *N = C.generateErrorNode(State);
96   if (!N)
97     return;
98 
99   // We know that 'location' cannot be non-null.  This is what
100   // we call an "explicit" null dereference.
101   if (!BT_null)
102     BT_null.reset(new BuiltinBug(this, "Dereference of null pointer"));
103 
104   SmallString<100> buf;
105   llvm::raw_svector_ostream os(buf);
106 
107   SmallVector<SourceRange, 2> Ranges;
108 
109   // Walk through lvalue casts to get the original expression
110   // that syntactically caused the load.
111   if (const Expr *expr = dyn_cast<Expr>(S))
112     S = expr->IgnoreParenLValueCasts();
113 
114   if (IsBind) {
115     const VarDecl *VD;
116     const Expr *Init;
117     std::tie(VD, Init) = parseAssignment(S);
118     if (VD && Init)
119       S = Init;
120   }
121 
122   switch (S->getStmtClass()) {
123   case Stmt::ArraySubscriptExprClass: {
124     os << "Array access";
125     const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
126     AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
127                    State.get(), N->getLocationContext());
128     os << " results in a null pointer dereference";
129     break;
130   }
131   case Stmt::OMPArraySectionExprClass: {
132     os << "Array access";
133     const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S);
134     AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
135                    State.get(), N->getLocationContext());
136     os << " results in a null pointer dereference";
137     break;
138   }
139   case Stmt::UnaryOperatorClass: {
140     os << "Dereference of null pointer";
141     const UnaryOperator *U = cast<UnaryOperator>(S);
142     AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
143                    State.get(), N->getLocationContext(), true);
144     break;
145   }
146   case Stmt::MemberExprClass: {
147     const MemberExpr *M = cast<MemberExpr>(S);
148     if (M->isArrow() || bugreporter::isDeclRefExprToReference(M->getBase())) {
149       os << "Access to field '" << M->getMemberNameInfo()
150          << "' results in a dereference of a null pointer";
151       AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
152                      State.get(), N->getLocationContext(), true);
153     }
154     break;
155   }
156   case Stmt::ObjCIvarRefExprClass: {
157     const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
158     os << "Access to instance variable '" << *IV->getDecl()
159        << "' results in a dereference of a null pointer";
160     AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
161                    State.get(), N->getLocationContext(), true);
162     break;
163   }
164   default:
165     break;
166   }
167 
168   auto report = llvm::make_unique<BugReport>(
169       *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N);
170 
171   bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report);
172 
173   for (SmallVectorImpl<SourceRange>::iterator
174        I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
175     report->addRange(*I);
176 
177   C.emitReport(std::move(report));
178 }
179 
180 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
181                                        CheckerContext &C) const {
182   // Check for dereference of an undefined value.
183   if (l.isUndef()) {
184     if (ExplodedNode *N = C.generateErrorNode()) {
185       if (!BT_undef)
186         BT_undef.reset(
187             new BuiltinBug(this, "Dereference of undefined pointer value"));
188 
189       auto report =
190           llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N);
191       bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S),
192                                          *report);
193       C.emitReport(std::move(report));
194     }
195     return;
196   }
197 
198   DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
199 
200   // Check for null dereferences.
201   if (!location.getAs<Loc>())
202     return;
203 
204   ProgramStateRef state = C.getState();
205 
206   ProgramStateRef notNullState, nullState;
207   std::tie(notNullState, nullState) = state->assume(location);
208 
209   // The explicit NULL case.
210   if (nullState) {
211     if (!notNullState) {
212       reportBug(nullState, S, C);
213       return;
214     }
215 
216     // Otherwise, we have the case where the location could either be
217     // null or not-null.  Record the error node as an "implicit" null
218     // dereference.
219     if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
220       ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
221                                       /*IsDirectDereference=*/false};
222       dispatchEvent(event);
223     }
224   }
225 
226   // From this point forward, we know that the location is not null.
227   C.addTransition(notNullState);
228 }
229 
230 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
231                                    CheckerContext &C) const {
232   // If we're binding to a reference, check if the value is known to be null.
233   if (V.isUndef())
234     return;
235 
236   const MemRegion *MR = L.getAsRegion();
237   const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
238   if (!TVR)
239     return;
240 
241   if (!TVR->getValueType()->isReferenceType())
242     return;
243 
244   ProgramStateRef State = C.getState();
245 
246   ProgramStateRef StNonNull, StNull;
247   std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
248 
249   if (StNull) {
250     if (!StNonNull) {
251       reportBug(StNull, S, C, /*isBind=*/true);
252       return;
253     }
254 
255     // At this point the value could be either null or non-null.
256     // Record this as an "implicit" null dereference.
257     if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
258       ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
259                                       &C.getBugReporter(),
260                                       /*IsDirectDereference=*/false};
261       dispatchEvent(event);
262     }
263   }
264 
265   // Unlike a regular null dereference, initializing a reference with a
266   // dereferenced null pointer does not actually cause a runtime exception in
267   // Clang's implementation of references.
268   //
269   //   int &r = *p; // safe??
270   //   if (p != NULL) return; // uh-oh
271   //   r = 5; // trap here
272   //
273   // The standard says this is invalid as soon as we try to create a "null
274   // reference" (there is no such thing), but turning this into an assumption
275   // that 'p' is never null will not match our actual runtime behavior.
276   // So we do not record this assumption, allowing us to warn on the last line
277   // of this example.
278   //
279   // We do need to add a transition because we may have generated a sink for
280   // the "implicit" null dereference.
281   C.addTransition(State, this);
282 }
283 
284 void ento::registerDereferenceChecker(CheckerManager &mgr) {
285   mgr.registerChecker<DereferenceChecker>();
286 }
287