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