Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
35269 views
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"
15
#include "clang/AST/ExprOpenMP.h"
16
#include "clang/Basic/TargetInfo.h"
17
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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
enum DerefKind { NullPointer, UndefinedPointerValue, AddressOfLabel };
35
36
BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};
37
BugType BT_Undef{this, "Dereference of undefined pointer value",
38
categories::LogicError};
39
BugType BT_Label{this, "Dereference of the address of a label",
40
categories::LogicError};
41
42
void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,
43
CheckerContext &C) const;
44
45
bool suppressReport(CheckerContext &C, const Expr *E) const;
46
47
public:
48
void checkLocation(SVal location, bool isLoad, const Stmt* S,
49
CheckerContext &C) const;
50
void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
51
52
static void AddDerefSource(raw_ostream &os,
53
SmallVectorImpl<SourceRange> &Ranges,
54
const Expr *Ex, const ProgramState *state,
55
const LocationContext *LCtx,
56
bool loadedFrom = false);
57
58
bool SuppressAddressSpaces = false;
59
};
60
} // end anonymous namespace
61
62
void
63
DereferenceChecker::AddDerefSource(raw_ostream &os,
64
SmallVectorImpl<SourceRange> &Ranges,
65
const Expr *Ex,
66
const ProgramState *state,
67
const LocationContext *LCtx,
68
bool loadedFrom) {
69
Ex = Ex->IgnoreParenLValueCasts();
70
switch (Ex->getStmtClass()) {
71
default:
72
break;
73
case Stmt::DeclRefExprClass: {
74
const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
75
if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
76
os << " (" << (loadedFrom ? "loaded from" : "from")
77
<< " variable '" << VD->getName() << "')";
78
Ranges.push_back(DR->getSourceRange());
79
}
80
break;
81
}
82
case Stmt::MemberExprClass: {
83
const MemberExpr *ME = cast<MemberExpr>(Ex);
84
os << " (" << (loadedFrom ? "loaded from" : "via")
85
<< " field '" << ME->getMemberNameInfo() << "')";
86
SourceLocation L = ME->getMemberLoc();
87
Ranges.push_back(SourceRange(L, L));
88
break;
89
}
90
case Stmt::ObjCIvarRefExprClass: {
91
const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
92
os << " (" << (loadedFrom ? "loaded from" : "via")
93
<< " ivar '" << IV->getDecl()->getName() << "')";
94
SourceLocation L = IV->getLocation();
95
Ranges.push_back(SourceRange(L, L));
96
break;
97
}
98
}
99
}
100
101
static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
102
const Expr *E = nullptr;
103
104
// Walk through lvalue casts to get the original expression
105
// that syntactically caused the load.
106
if (const Expr *expr = dyn_cast<Expr>(S))
107
E = expr->IgnoreParenLValueCasts();
108
109
if (IsBind) {
110
const VarDecl *VD;
111
const Expr *Init;
112
std::tie(VD, Init) = parseAssignment(S);
113
if (VD && Init)
114
E = Init;
115
}
116
return E;
117
}
118
119
bool DereferenceChecker::suppressReport(CheckerContext &C,
120
const Expr *E) const {
121
// Do not report dereferences on memory that use address space #256, #257,
122
// and #258. Those address spaces are used when dereferencing address spaces
123
// relative to the GS, FS, and SS segments on x86/x86-64 targets.
124
// Dereferencing a null pointer in these address spaces is not defined
125
// as an error. All other null dereferences in other address spaces
126
// are defined as an error unless explicitly defined.
127
// See https://clang.llvm.org/docs/LanguageExtensions.html, the section
128
// "X86/X86-64 Language Extensions"
129
130
QualType Ty = E->getType();
131
if (!Ty.hasAddressSpace())
132
return false;
133
if (SuppressAddressSpaces)
134
return true;
135
136
const llvm::Triple::ArchType Arch =
137
C.getASTContext().getTargetInfo().getTriple().getArch();
138
139
if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) {
140
switch (toTargetAddressSpace(E->getType().getAddressSpace())) {
141
case 256:
142
case 257:
143
case 258:
144
return true;
145
}
146
}
147
return false;
148
}
149
150
static bool isDeclRefExprToReference(const Expr *E) {
151
if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
152
return DRE->getDecl()->getType()->isReferenceType();
153
return false;
154
}
155
156
void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
157
const Stmt *S, CheckerContext &C) const {
158
const BugType *BT = nullptr;
159
llvm::StringRef DerefStr1;
160
llvm::StringRef DerefStr2;
161
switch (K) {
162
case DerefKind::NullPointer:
163
BT = &BT_Null;
164
DerefStr1 = " results in a null pointer dereference";
165
DerefStr2 = " results in a dereference of a null pointer";
166
break;
167
case DerefKind::UndefinedPointerValue:
168
BT = &BT_Undef;
169
DerefStr1 = " results in an undefined pointer dereference";
170
DerefStr2 = " results in a dereference of an undefined pointer value";
171
break;
172
case DerefKind::AddressOfLabel:
173
BT = &BT_Label;
174
DerefStr1 = " results in an undefined pointer dereference";
175
DerefStr2 = " results in a dereference of an address of a label";
176
break;
177
};
178
179
// Generate an error node.
180
ExplodedNode *N = C.generateErrorNode(State);
181
if (!N)
182
return;
183
184
SmallString<100> buf;
185
llvm::raw_svector_ostream os(buf);
186
187
SmallVector<SourceRange, 2> Ranges;
188
189
switch (S->getStmtClass()) {
190
case Stmt::ArraySubscriptExprClass: {
191
os << "Array access";
192
const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
193
AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
194
State.get(), N->getLocationContext());
195
os << DerefStr1;
196
break;
197
}
198
case Stmt::ArraySectionExprClass: {
199
os << "Array access";
200
const ArraySectionExpr *AE = cast<ArraySectionExpr>(S);
201
AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
202
State.get(), N->getLocationContext());
203
os << DerefStr1;
204
break;
205
}
206
case Stmt::UnaryOperatorClass: {
207
os << BT->getDescription();
208
const UnaryOperator *U = cast<UnaryOperator>(S);
209
AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
210
State.get(), N->getLocationContext(), true);
211
break;
212
}
213
case Stmt::MemberExprClass: {
214
const MemberExpr *M = cast<MemberExpr>(S);
215
if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
216
os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;
217
AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
218
State.get(), N->getLocationContext(), true);
219
}
220
break;
221
}
222
case Stmt::ObjCIvarRefExprClass: {
223
const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
224
os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;
225
AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
226
State.get(), N->getLocationContext(), true);
227
break;
228
}
229
default:
230
break;
231
}
232
233
auto report = std::make_unique<PathSensitiveBugReport>(
234
*BT, buf.empty() ? BT->getDescription() : buf.str(), N);
235
236
bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
237
238
for (SmallVectorImpl<SourceRange>::iterator
239
I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
240
report->addRange(*I);
241
242
C.emitReport(std::move(report));
243
}
244
245
void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
246
CheckerContext &C) const {
247
// Check for dereference of an undefined value.
248
if (l.isUndef()) {
249
const Expr *DerefExpr = getDereferenceExpr(S);
250
if (!suppressReport(C, DerefExpr))
251
reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
252
return;
253
}
254
255
DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
256
257
// Check for null dereferences.
258
if (!isa<Loc>(location))
259
return;
260
261
ProgramStateRef state = C.getState();
262
263
ProgramStateRef notNullState, nullState;
264
std::tie(notNullState, nullState) = state->assume(location);
265
266
if (nullState) {
267
if (!notNullState) {
268
// We know that 'location' can only be null. This is what
269
// we call an "explicit" null dereference.
270
const Expr *expr = getDereferenceExpr(S);
271
if (!suppressReport(C, expr)) {
272
reportBug(DerefKind::NullPointer, nullState, expr, C);
273
return;
274
}
275
}
276
277
// Otherwise, we have the case where the location could either be
278
// null or not-null. Record the error node as an "implicit" null
279
// dereference.
280
if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
281
ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
282
/*IsDirectDereference=*/true};
283
dispatchEvent(event);
284
}
285
}
286
287
// From this point forward, we know that the location is not null.
288
C.addTransition(notNullState);
289
}
290
291
void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
292
CheckerContext &C) const {
293
// If we're binding to a reference, check if the value is known to be null.
294
if (V.isUndef())
295
return;
296
297
// One should never write to label addresses.
298
if (auto Label = L.getAs<loc::GotoLabel>()) {
299
reportBug(DerefKind::AddressOfLabel, C.getState(), S, C);
300
return;
301
}
302
303
const MemRegion *MR = L.getAsRegion();
304
const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
305
if (!TVR)
306
return;
307
308
if (!TVR->getValueType()->isReferenceType())
309
return;
310
311
ProgramStateRef State = C.getState();
312
313
ProgramStateRef StNonNull, StNull;
314
std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
315
316
if (StNull) {
317
if (!StNonNull) {
318
const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
319
if (!suppressReport(C, expr)) {
320
reportBug(DerefKind::NullPointer, StNull, expr, C);
321
return;
322
}
323
}
324
325
// At this point the value could be either null or non-null.
326
// Record this as an "implicit" null dereference.
327
if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
328
ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
329
&C.getBugReporter(),
330
/*IsDirectDereference=*/true};
331
dispatchEvent(event);
332
}
333
}
334
335
// Unlike a regular null dereference, initializing a reference with a
336
// dereferenced null pointer does not actually cause a runtime exception in
337
// Clang's implementation of references.
338
//
339
// int &r = *p; // safe??
340
// if (p != NULL) return; // uh-oh
341
// r = 5; // trap here
342
//
343
// The standard says this is invalid as soon as we try to create a "null
344
// reference" (there is no such thing), but turning this into an assumption
345
// that 'p' is never null will not match our actual runtime behavior.
346
// So we do not record this assumption, allowing us to warn on the last line
347
// of this example.
348
//
349
// We do need to add a transition because we may have generated a sink for
350
// the "implicit" null dereference.
351
C.addTransition(State, this);
352
}
353
354
void ento::registerDereferenceChecker(CheckerManager &mgr) {
355
auto *Chk = mgr.registerChecker<DereferenceChecker>();
356
Chk->SuppressAddressSpaces = mgr.getAnalyzerOptions().getCheckerBooleanOption(
357
mgr.getCurrentCheckerName(), "SuppressAddressSpaces");
358
}
359
360
bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
361
return true;
362
}
363
364