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
AddDerefSource(raw_ostream & os,SmallVectorImpl<SourceRange> & Ranges,const Expr * Ex,const ProgramState * state,const LocationContext * LCtx,bool loadedFrom)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
getDereferenceExpr(const Stmt * S,bool IsBind=false)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
suppressReport(const Expr * E)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
isDeclRefExprToReference(const Expr * E)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
reportBug(DerefKind K,ProgramStateRef State,const Stmt * S,CheckerContext & C) const123 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
checkLocation(SVal l,bool isLoad,const Stmt * S,CheckerContext & C) const207 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
checkBind(SVal L,SVal V,const Stmt * S,CheckerContext & C) const253 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
registerDereferenceChecker(CheckerManager & mgr)310 void ento::registerDereferenceChecker(CheckerManager &mgr) {
311 mgr.registerChecker<DereferenceChecker>();
312 }
313
shouldRegisterDereferenceChecker(const CheckerManager & mgr)314 bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
315 return true;
316 }
317