clang 19.0.0git
DereferenceChecker.cpp
Go to the documentation of this file.
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/AST/ExprObjC.h"
23#include "llvm/ADT/SmallString.h"
24#include "llvm/Support/raw_ostream.h"
25
26using namespace clang;
27using namespace ento;
28
29namespace {
30class DereferenceChecker
31 : public Checker< check::Location,
32 check::Bind,
33 EventDispatcher<ImplicitNullDerefEvent> > {
34 enum DerefKind { NullPointer, UndefinedPointerValue };
35
36 BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};
37 BugType BT_Undef{this, "Dereference of undefined pointer value",
39
40 void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,
41 CheckerContext &C) const;
42
43 bool suppressReport(CheckerContext &C, const Expr *E) const;
44
45public:
46 void checkLocation(SVal location, bool isLoad, const Stmt* S,
47 CheckerContext &C) const;
48 void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
49
50 static void AddDerefSource(raw_ostream &os,
52 const Expr *Ex, const ProgramState *state,
53 const LocationContext *LCtx,
54 bool loadedFrom = false);
55
56 bool SuppressAddressSpaces = false;
57};
58} // end anonymous namespace
59
60void
61DereferenceChecker::AddDerefSource(raw_ostream &os,
63 const Expr *Ex,
64 const ProgramState *state,
65 const LocationContext *LCtx,
66 bool loadedFrom) {
67 Ex = Ex->IgnoreParenLValueCasts();
68 switch (Ex->getStmtClass()) {
69 default:
70 break;
71 case Stmt::DeclRefExprClass: {
72 const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
73 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
74 os << " (" << (loadedFrom ? "loaded from" : "from")
75 << " variable '" << VD->getName() << "')";
76 Ranges.push_back(DR->getSourceRange());
77 }
78 break;
79 }
80 case Stmt::MemberExprClass: {
81 const MemberExpr *ME = cast<MemberExpr>(Ex);
82 os << " (" << (loadedFrom ? "loaded from" : "via")
83 << " field '" << ME->getMemberNameInfo() << "')";
85 Ranges.push_back(SourceRange(L, L));
86 break;
87 }
88 case Stmt::ObjCIvarRefExprClass: {
89 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
90 os << " (" << (loadedFrom ? "loaded from" : "via")
91 << " ivar '" << IV->getDecl()->getName() << "')";
93 Ranges.push_back(SourceRange(L, L));
94 break;
95 }
96 }
97}
98
99static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
100 const Expr *E = nullptr;
101
102 // Walk through lvalue casts to get the original expression
103 // that syntactically caused the load.
104 if (const Expr *expr = dyn_cast<Expr>(S))
105 E = expr->IgnoreParenLValueCasts();
106
107 if (IsBind) {
108 const VarDecl *VD;
109 const Expr *Init;
110 std::tie(VD, Init) = parseAssignment(S);
111 if (VD && Init)
112 E = Init;
113 }
114 return E;
115}
116
117bool DereferenceChecker::suppressReport(CheckerContext &C,
118 const Expr *E) const {
119 // Do not report dereferences on memory that use address space #256, #257,
120 // and #258. Those address spaces are used when dereferencing address spaces
121 // relative to the GS, FS, and SS segments on x86/x86-64 targets.
122 // Dereferencing a null pointer in these address spaces is not defined
123 // as an error. All other null dereferences in other address spaces
124 // are defined as an error unless explicitly defined.
125 // See https://clang.llvm.org/docs/LanguageExtensions.html, the section
126 // "X86/X86-64 Language Extensions"
127
128 QualType Ty = E->getType();
129 if (!Ty.hasAddressSpace())
130 return false;
131 if (SuppressAddressSpaces)
132 return true;
133
134 const llvm::Triple::ArchType Arch =
135 C.getASTContext().getTargetInfo().getTriple().getArch();
136
137 if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) {
139 case 256:
140 case 257:
141 case 258:
142 return true;
143 }
144 }
145 return false;
146}
147
148static bool isDeclRefExprToReference(const Expr *E) {
149 if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
150 return DRE->getDecl()->getType()->isReferenceType();
151 return false;
152}
153
154void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
155 const Stmt *S, CheckerContext &C) const {
156 const BugType *BT = nullptr;
157 llvm::StringRef DerefStr1;
158 llvm::StringRef DerefStr2;
159 switch (K) {
160 case DerefKind::NullPointer:
161 BT = &BT_Null;
162 DerefStr1 = " results in a null pointer dereference";
163 DerefStr2 = " results in a dereference of a null pointer";
164 break;
165 case DerefKind::UndefinedPointerValue:
166 BT = &BT_Undef;
167 DerefStr1 = " results in an undefined pointer dereference";
168 DerefStr2 = " results in a dereference of an undefined pointer value";
169 break;
170 };
171
172 // Generate an error node.
173 ExplodedNode *N = C.generateErrorNode(State);
174 if (!N)
175 return;
176
178 llvm::raw_svector_ostream os(buf);
179
181
182 switch (S->getStmtClass()) {
183 case Stmt::ArraySubscriptExprClass: {
184 os << "Array access";
185 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
186 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
187 State.get(), N->getLocationContext());
188 os << DerefStr1;
189 break;
190 }
191 case Stmt::ArraySectionExprClass: {
192 os << "Array access";
193 const ArraySectionExpr *AE = cast<ArraySectionExpr>(S);
194 AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
195 State.get(), N->getLocationContext());
196 os << DerefStr1;
197 break;
198 }
199 case Stmt::UnaryOperatorClass: {
200 os << BT->getDescription();
201 const UnaryOperator *U = cast<UnaryOperator>(S);
202 AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
203 State.get(), N->getLocationContext(), true);
204 break;
205 }
206 case Stmt::MemberExprClass: {
207 const MemberExpr *M = cast<MemberExpr>(S);
208 if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
209 os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;
210 AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
211 State.get(), N->getLocationContext(), true);
212 }
213 break;
214 }
215 case Stmt::ObjCIvarRefExprClass: {
216 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
217 os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;
218 AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
219 State.get(), N->getLocationContext(), true);
220 break;
221 }
222 default:
223 break;
224 }
225
226 auto report = std::make_unique<PathSensitiveBugReport>(
227 *BT, buf.empty() ? BT->getDescription() : buf.str(), N);
228
230
232 I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
233 report->addRange(*I);
234
235 C.emitReport(std::move(report));
236}
237
238void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
239 CheckerContext &C) const {
240 // Check for dereference of an undefined value.
241 if (l.isUndef()) {
242 const Expr *DerefExpr = getDereferenceExpr(S);
243 if (!suppressReport(C, DerefExpr))
244 reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
245 return;
246 }
247
249
250 // Check for null dereferences.
251 if (!isa<Loc>(location))
252 return;
253
254 ProgramStateRef state = C.getState();
255
256 ProgramStateRef notNullState, nullState;
257 std::tie(notNullState, nullState) = state->assume(location);
258
259 if (nullState) {
260 if (!notNullState) {
261 // We know that 'location' can only be null. This is what
262 // we call an "explicit" null dereference.
263 const Expr *expr = getDereferenceExpr(S);
264 if (!suppressReport(C, expr)) {
265 reportBug(DerefKind::NullPointer, nullState, expr, C);
266 return;
267 }
268 }
269
270 // Otherwise, we have the case where the location could either be
271 // null or not-null. Record the error node as an "implicit" null
272 // dereference.
273 if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
274 ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
275 /*IsDirectDereference=*/true};
276 dispatchEvent(event);
277 }
278 }
279
280 // From this point forward, we know that the location is not null.
281 C.addTransition(notNullState);
282}
283
284void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
285 CheckerContext &C) const {
286 // If we're binding to a reference, check if the value is known to be null.
287 if (V.isUndef())
288 return;
289
290 const MemRegion *MR = L.getAsRegion();
291 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
292 if (!TVR)
293 return;
294
295 if (!TVR->getValueType()->isReferenceType())
296 return;
297
298 ProgramStateRef State = C.getState();
299
300 ProgramStateRef StNonNull, StNull;
301 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
302
303 if (StNull) {
304 if (!StNonNull) {
305 const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
306 if (!suppressReport(C, expr)) {
307 reportBug(DerefKind::NullPointer, StNull, expr, C);
308 return;
309 }
310 }
311
312 // At this point the value could be either null or non-null.
313 // Record this as an "implicit" null dereference.
314 if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
315 ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
316 &C.getBugReporter(),
317 /*IsDirectDereference=*/true};
318 dispatchEvent(event);
319 }
320 }
321
322 // Unlike a regular null dereference, initializing a reference with a
323 // dereferenced null pointer does not actually cause a runtime exception in
324 // Clang's implementation of references.
325 //
326 // int &r = *p; // safe??
327 // if (p != NULL) return; // uh-oh
328 // r = 5; // trap here
329 //
330 // The standard says this is invalid as soon as we try to create a "null
331 // reference" (there is no such thing), but turning this into an assumption
332 // that 'p' is never null will not match our actual runtime behavior.
333 // So we do not record this assumption, allowing us to warn on the last line
334 // of this example.
335 //
336 // We do need to add a transition because we may have generated a sink for
337 // the "implicit" null dereference.
338 C.addTransition(State, this);
339}
340
341void ento::registerDereferenceChecker(CheckerManager &mgr) {
342 auto *Chk = mgr.registerChecker<DereferenceChecker>();
343 Chk->SuppressAddressSpaces = mgr.getAnalyzerOptions().getCheckerBooleanOption(
344 mgr.getCurrentCheckerName(), "SuppressAddressSpaces");
345}
346
347bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
348 return true;
349}
#define V(N, I)
Definition: ASTContext.h:3284
static const Expr * getDereferenceExpr(const Stmt *S, bool IsBind=false)
static bool isDeclRefExprToReference(const Expr *E)
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
This class represents BOTH the OpenMP Array Section and OpenACC 'subarray', with a boolean differenti...
Definition: Expr.h:6734
Expr * getBase()
Get base of the array section.
Definition: Expr.h:6800
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition: Expr.h:2664
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1260
ValueDecl * getDecl()
Definition: Expr.h:1328
This represents one expression.
Definition: Expr.h:110
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:3064
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Skip past any parentheses and lvalue casts which might surround this expression until reaching a fixe...
Definition: Expr.cpp:3076
QualType getType() const
Definition: Expr.h:142
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:3172
SourceLocation getMemberLoc() const
getMemberLoc - Return the location of the "member", in X->F, it is the location of 'F'.
Definition: Expr.h:3361
Expr * getBase() const
Definition: Expr.h:3249
DeclarationNameInfo getMemberNameInfo() const
Retrieve the member declaration name info.
Definition: Expr.h:3349
bool isArrow() const
Definition: Expr.h:3356
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:276
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:549
SourceLocation getLocation() const
Definition: ExprObjC.h:592
ObjCIvarDecl * getDecl()
Definition: ExprObjC.h:579
const Expr * getBase() const
Definition: ExprObjC.h:583
A (possibly-)qualified type.
Definition: Type.h:940
LangAS getAddressSpace() const
Return the address space of this type.
Definition: Type.h:7485
bool hasAddressSpace() const
Check if this type has any address space qualifier.
Definition: Type.h:7480
Encodes a location in the source.
A trivial tuple used to represent a source range.
Stmt - This represents one statement.
Definition: Stmt.h:84
StmtClass getStmtClass() const
Definition: Stmt.h:1358
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:326
bool isReferenceType() const
Definition: Type.h:7624
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition: Expr.h:2183
Represents a variable declaration or definition.
Definition: Decl.h:918
StringRef getDescription() const
Definition: BugType.h:48
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
CheckerNameRef getCurrentCheckerName() const
const LocationContext * getLocationContext() const
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:96
ProgramState - This class encapsulates:
Definition: ProgramState.h:71
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
bool isUndef() const
Definition: SVals.h:104
const MemRegion * getAsRegion() const
Definition: SVals.cpp:120
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:82
TypedValueRegion - An abstract class representing regions having a typed value.
Definition: MemRegion.h:530
virtual QualType getValueType() const =0
Defines the clang::TargetInfo interface.
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
const Expr * getDerefExpr(const Stmt *S)
Given that expression S represents a pointer that would be dereferenced, try to find a sub-expression...
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
std::pair< const clang::VarDecl *, const clang::Expr * > parseAssignment(const Stmt *S)
The JSON file list parser is used to communicate input to InstallAPI.
unsigned toTargetAddressSpace(LangAS AS)
Definition: AddressSpaces.h:81
We dereferenced a location that may be null.
Definition: Checker.h:548