clang 22.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"
22#include "llvm/Support/FormatVariadic.h"
23#include "llvm/Support/raw_ostream.h"
24
25using namespace clang;
26using namespace ento;
27
28namespace {
29
30class DerefBugType : public BugType {
31 StringRef ArrayMsg, FieldMsg;
32
33public:
34 DerefBugType(CheckerFrontend *FE, StringRef Desc, const char *AMsg,
35 const char *FMsg = nullptr)
36 : BugType(FE, Desc), ArrayMsg(AMsg), FieldMsg(FMsg ? FMsg : AMsg) {}
37 StringRef getArrayMsg() const { return ArrayMsg; }
38 StringRef getFieldMsg() const { return FieldMsg; }
39};
40
41class DereferenceChecker
42 : public CheckerFamily<check::Location, check::Bind,
43 check::PreStmt<BinaryOperator>,
44 EventDispatcher<ImplicitNullDerefEvent>> {
45 void reportDerefBug(const DerefBugType &BT, ProgramStateRef State,
46 const Stmt *S, CheckerContext &C) const;
47
48 bool suppressReport(CheckerContext &C, const Expr *E) const;
49
50public:
51 void checkLocation(SVal location, bool isLoad, const Stmt* S,
52 CheckerContext &C) const;
53 void checkBind(SVal L, SVal V, const Stmt *S, bool AtDeclInit,
54 CheckerContext &C) const;
55 void checkPreStmt(const BinaryOperator *Op, CheckerContext &C) const;
56
57 static void AddDerefSource(raw_ostream &os,
58 SmallVectorImpl<SourceRange> &Ranges,
59 const Expr *Ex, const ProgramState *state,
60 const LocationContext *LCtx,
61 bool loadedFrom = false);
62
63 CheckerFrontend NullDerefChecker, FixedDerefChecker, NullPointerArithmChecker;
64 const DerefBugType NullBug{&NullDerefChecker, "Dereference of null pointer",
65 "a null pointer dereference",
66 "a dereference of a null pointer"};
67 const DerefBugType UndefBug{&NullDerefChecker,
68 "Dereference of undefined pointer value",
69 "an undefined pointer dereference",
70 "a dereference of an undefined pointer value"};
71 const DerefBugType LabelBug{&NullDerefChecker,
72 "Dereference of the address of a label",
73 "an undefined pointer dereference",
74 "a dereference of an address of a label"};
75 const DerefBugType FixedAddressBug{&FixedDerefChecker,
76 "Dereference of a fixed address",
77 "a dereference of a fixed address"};
78 const BugType NullPointerArithmBug{
79 &NullPointerArithmChecker,
80 "Possibly undefined arithmetic operation involving a null pointer"};
81
82 StringRef getDebugTag() const override { return "DereferenceChecker"; }
83};
84
85struct ValueDescStr {
86 SmallVectorImpl<SourceRange> &Ranges;
87 const Expr *Ex;
88 const ProgramState *State;
89 const LocationContext *LCtx;
90 bool IsPointer;
91 ConditionTruthVal IsNull;
92};
93
94} // end anonymous namespace
95
96void
97DereferenceChecker::AddDerefSource(raw_ostream &os,
99 const Expr *Ex,
100 const ProgramState *state,
101 const LocationContext *LCtx,
102 bool loadedFrom) {
103 Ex = Ex->IgnoreParenLValueCasts();
104 switch (Ex->getStmtClass()) {
105 default:
106 break;
107 case Stmt::DeclRefExprClass: {
108 const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
109 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
110 os << " (" << (loadedFrom ? "loaded from" : "from")
111 << " variable '" << VD->getName() << "')";
112 Ranges.push_back(DR->getSourceRange());
113 }
114 break;
115 }
116 case Stmt::MemberExprClass: {
117 const MemberExpr *ME = cast<MemberExpr>(Ex);
118 os << " (" << (loadedFrom ? "loaded from" : "via")
119 << " field '" << ME->getMemberNameInfo() << "')";
120 SourceLocation L = ME->getMemberLoc();
121 Ranges.push_back(SourceRange(L, L));
122 break;
123 }
124 case Stmt::ObjCIvarRefExprClass: {
125 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
126 os << " (" << (loadedFrom ? "loaded from" : "via")
127 << " ivar '" << IV->getDecl()->getName() << "')";
128 SourceLocation L = IV->getLocation();
129 Ranges.push_back(SourceRange(L, L));
130 break;
131 }
132 }
133}
134
135static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
136 const Expr *E = nullptr;
137
138 // Walk through lvalue casts to get the original expression
139 // that syntactically caused the load.
140 if (const Expr *expr = dyn_cast<Expr>(S))
141 E = expr->IgnoreParenLValueCasts();
142
143 if (IsBind) {
144 const VarDecl *VD;
145 const Expr *Init;
146 std::tie(VD, Init) = parseAssignment(S);
147 if (VD && Init)
148 E = Init;
149 }
150 return E;
151}
152
153bool DereferenceChecker::suppressReport(CheckerContext &C,
154 const Expr *E) const {
155 // Do not report dereferences on memory that use address space #256, #257,
156 // and #258. Those address spaces are used when dereferencing address spaces
157 // relative to the GS, FS, and SS segments on x86/x86-64 targets.
158 // Dereferencing a null pointer in these address spaces is not defined
159 // as an error. All other null dereferences in other address spaces
160 // are defined as an error unless explicitly defined.
161 // See https://clang.llvm.org/docs/LanguageExtensions.html, the section
162 // "X86/X86-64 Language Extensions"
163
164 QualType Ty = E->getType();
165 if (!Ty.hasAddressSpace())
166 return false;
167 if (C.getAnalysisManager()
168 .getAnalyzerOptions()
169 .ShouldSuppressAddressSpaceDereferences)
170 return true;
171
172 const llvm::Triple::ArchType Arch =
173 C.getASTContext().getTargetInfo().getTriple().getArch();
174
175 if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) {
177 case 256:
178 case 257:
179 case 258:
180 return true;
181 }
182 }
183 return false;
184}
185
186static bool isDeclRefExprToReference(const Expr *E) {
187 if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
188 return DRE->getDecl()->getType()->isReferenceType();
189 return false;
190}
191
192void DereferenceChecker::reportDerefBug(const DerefBugType &BT,
193 ProgramStateRef State, const Stmt *S,
194 CheckerContext &C) const {
195 if (&BT == &FixedAddressBug) {
196 if (!FixedDerefChecker.isEnabled())
197 // Deliberately don't add a sink node if check is disabled.
198 // This situation may be valid in special cases.
199 return;
200 } else {
201 if (!NullDerefChecker.isEnabled()) {
202 C.addSink();
203 return;
204 }
205 }
206
207 // Generate an error node.
208 ExplodedNode *N = C.generateErrorNode(State);
209 if (!N)
210 return;
211
212 SmallString<100> Buf;
213 llvm::raw_svector_ostream Out(Buf);
214
215 SmallVector<SourceRange, 2> Ranges;
216
217 switch (S->getStmtClass()) {
218 case Stmt::ArraySubscriptExprClass: {
219 Out << "Array access";
220 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
221 AddDerefSource(Out, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(),
222 N->getLocationContext());
223 Out << " results in " << BT.getArrayMsg();
224 break;
225 }
226 case Stmt::ArraySectionExprClass: {
227 Out << "Array access";
228 const ArraySectionExpr *AE = cast<ArraySectionExpr>(S);
229 AddDerefSource(Out, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(),
230 N->getLocationContext());
231 Out << " results in " << BT.getArrayMsg();
232 break;
233 }
234 case Stmt::UnaryOperatorClass: {
235 Out << BT.getDescription();
236 const UnaryOperator *U = cast<UnaryOperator>(S);
237 AddDerefSource(Out, Ranges, U->getSubExpr()->IgnoreParens(), State.get(),
238 N->getLocationContext(), true);
239 break;
240 }
241 case Stmt::MemberExprClass: {
242 const MemberExpr *M = cast<MemberExpr>(S);
243 if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
244 Out << "Access to field '" << M->getMemberNameInfo() << "' results in "
245 << BT.getFieldMsg();
246 AddDerefSource(Out, Ranges, M->getBase()->IgnoreParenCasts(), State.get(),
247 N->getLocationContext(), true);
248 }
249 break;
250 }
251 case Stmt::ObjCIvarRefExprClass: {
252 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
253 Out << "Access to instance variable '" << *IV->getDecl() << "' results in "
254 << BT.getFieldMsg();
255 AddDerefSource(Out, Ranges, IV->getBase()->IgnoreParenCasts(), State.get(),
256 N->getLocationContext(), true);
257 break;
258 }
259 default:
260 break;
261 }
262
263 auto BR = std::make_unique<PathSensitiveBugReport>(
264 BT, Buf.empty() ? BT.getDescription() : Buf.str(), N);
265
267
268 for (const auto &R : Ranges)
269 BR->addRange(R);
270
271 C.emitReport(std::move(BR));
272}
273
274void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
275 CheckerContext &C) const {
276 // Check for dereference of an undefined value.
277 if (l.isUndef()) {
278 const Expr *DerefExpr = getDereferenceExpr(S);
279 if (!suppressReport(C, DerefExpr))
280 reportDerefBug(UndefBug, C.getState(), DerefExpr, C);
281 return;
282 }
283
284 DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
285
286 // Check for null dereferences.
287 if (!isa<Loc>(location))
288 return;
289
290 ProgramStateRef state = C.getState();
291
292 ProgramStateRef notNullState, nullState;
293 std::tie(notNullState, nullState) = state->assume(location);
294
295 if (nullState) {
296 if (!notNullState) {
297 // We know that 'location' can only be null. This is what
298 // we call an "explicit" null dereference.
299 const Expr *expr = getDereferenceExpr(S);
300 if (!suppressReport(C, expr)) {
301 reportDerefBug(NullBug, nullState, expr, C);
302 return;
303 }
304 }
305
306 // Otherwise, we have the case where the location could either be
307 // null or not-null. Record the error node as an "implicit" null
308 // dereference.
309 if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
310 ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
311 /*IsDirectDereference=*/true};
312 dispatchEvent(event);
313 }
314 }
315
316 if (location.isConstant()) {
317 const Expr *DerefExpr = getDereferenceExpr(S, isLoad);
318 if (!suppressReport(C, DerefExpr))
319 reportDerefBug(FixedAddressBug, notNullState, DerefExpr, C);
320 return;
321 }
322
323 // From this point forward, we know that the location is not null.
324 C.addTransition(notNullState);
325}
326
327void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
328 bool AtDeclInit, CheckerContext &C) const {
329 // If we're binding to a reference, check if the value is known to be null.
330 if (V.isUndef())
331 return;
332
333 // One should never write to label addresses.
334 if (auto Label = L.getAs<loc::GotoLabel>()) {
335 reportDerefBug(LabelBug, C.getState(), S, C);
336 return;
337 }
338
339 const MemRegion *MR = L.getAsRegion();
340 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
341 if (!TVR)
342 return;
343
344 if (!TVR->getValueType()->isReferenceType())
345 return;
346
347 ProgramStateRef State = C.getState();
348
349 ProgramStateRef StNonNull, StNull;
350 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
351
352 if (StNull) {
353 if (!StNonNull) {
354 const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
355 if (!suppressReport(C, expr)) {
356 reportDerefBug(NullBug, StNull, expr, C);
357 return;
358 }
359 }
360
361 // At this point the value could be either null or non-null.
362 // Record this as an "implicit" null dereference.
363 if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
364 ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
365 &C.getBugReporter(),
366 /*IsDirectDereference=*/true};
367 dispatchEvent(event);
368 }
369 }
370
371 if (V.isConstant()) {
372 const Expr *DerefExpr = getDereferenceExpr(S, true);
373 if (!suppressReport(C, DerefExpr))
374 reportDerefBug(FixedAddressBug, State, DerefExpr, C);
375 return;
376 }
377
378 // Unlike a regular null dereference, initializing a reference with a
379 // dereferenced null pointer does not actually cause a runtime exception in
380 // Clang's implementation of references.
381 //
382 // int &r = *p; // safe??
383 // if (p != NULL) return; // uh-oh
384 // r = 5; // trap here
385 //
386 // The standard says this is invalid as soon as we try to create a "null
387 // reference" (there is no such thing), but turning this into an assumption
388 // that 'p' is never null will not match our actual runtime behavior.
389 // So we do not record this assumption, allowing us to warn on the last line
390 // of this example.
391 //
392 // We do need to add a transition because we may have generated a sink for
393 // the "implicit" null dereference.
394 C.addTransition(State, this);
395}
396
397namespace llvm {
398template <> struct format_provider<ValueDescStr> {
399 static void format(const ValueDescStr &V, raw_ostream &Stream,
400 StringRef Style) {
401 static const char *ValueStr[2][3] = {
402 {"zero", "nonzero integer value", "probably nonzero integer value"},
403 {"null pointer", "non-null pointer", "probably non-null pointer"},
404 };
405 Stream
406 << ValueStr[V.IsPointer][V.IsNull.isConstrainedTrue()
407 ? 0
408 : (V.IsNull.isConstrainedFalse() ? 1 : 2)];
409 DereferenceChecker::AddDerefSource(Stream, V.Ranges, V.Ex, V.State, V.LCtx,
410 false);
411 }
412};
413} // namespace llvm
414
415void DereferenceChecker::checkPreStmt(const BinaryOperator *Op,
416 CheckerContext &C) const {
417 if (!Op->isAdditiveOp() || !NullPointerArithmChecker.isEnabled())
418 return;
419 const Expr *E1 = Op->getLHS();
420 const Expr *E2 = Op->getRHS();
421 QualType T1 = E1->getType().getCanonicalType();
422 QualType T2 = E2->getType().getCanonicalType();
423 bool T1IsPointer = T1->isPointerType();
424 bool T2IsPointer = T2->isPointerType();
425 if (T1->isIntegerType() && T2->isIntegerType())
426 return;
427 if (!T1IsPointer && !T1->isIntegerType() && !T2IsPointer &&
428 !T2->isIntegerType())
429 return;
430
431 ProgramStateRef State = C.getState();
432 ConditionTruthVal V1IsNull = State->isNull(C.getSVal(E1));
433 ConditionTruthVal V2IsNull = State->isNull(C.getSVal(E2));
434 bool IsConstrained = true;
435
436 // Check cases 'NULL + x' and 'NULL - x'
437 if (T1IsPointer && !T2IsPointer) {
438 if (!V1IsNull.isConstrainedTrue() || V2IsNull.isConstrainedTrue())
439 return;
440 IsConstrained = V2IsNull.isConstrainedFalse();
441 }
442
443 // Check case 'x + NULL'
444 if (!T1IsPointer && T2IsPointer) {
445 if (V1IsNull.isConstrainedTrue() || !V2IsNull.isConstrainedTrue())
446 return;
447 IsConstrained = V1IsNull.isConstrainedFalse();
448 }
449
450 // Check case 'NULL - p' or 'p - NULL'
451 if (T1IsPointer && T2IsPointer) {
452 if (!V1IsNull.isConstrainedTrue() && !V2IsNull.isConstrainedTrue())
453 return;
454 if (V1IsNull.isConstrainedTrue() && V2IsNull.isConstrainedTrue())
455 return;
456 IsConstrained =
457 V1IsNull.isConstrainedFalse() || V2IsNull.isConstrainedFalse();
458 }
459
460 SmallVector<SourceRange, 2> Ranges;
461 const char *OpcodeStr =
462 Op->getOpcode() == BO_Add ? "Addition" : "Subtraction";
463 const char *ResultStr = IsConstrained ? "results" : "may result";
464 ValueDescStr DerefArg1{
465 Ranges, E1, State.get(), C.getLocationContext(), T1IsPointer, V1IsNull};
466 ValueDescStr DerefArg2{
467 Ranges, E2, State.get(), C.getLocationContext(), T2IsPointer, V2IsNull};
468 std::string Msg =
469 llvm::formatv("{0} of a {1} and a {2} {3} in undefined behavior",
470 OpcodeStr, DerefArg1, DerefArg2, ResultStr);
471
472 ExplodedNode *N = C.generateErrorNode(State);
473 if (!N)
474 return;
475 auto BR =
476 std::make_unique<PathSensitiveBugReport>(NullPointerArithmBug, Msg, N);
477 if (V1IsNull.isConstrainedTrue())
479 if (V2IsNull.isConstrainedTrue())
481 for (const auto &R : Ranges)
482 BR->addRange(R);
483
484 C.emitReport(std::move(BR));
485}
486
487void ento::registerNullDereferenceChecker(CheckerManager &Mgr) {
488 Mgr.getChecker<DereferenceChecker>()->NullDerefChecker.enable(Mgr);
489}
490
491bool ento::shouldRegisterNullDereferenceChecker(const CheckerManager &) {
492 return true;
493}
494
495void ento::registerFixedAddressDereferenceChecker(CheckerManager &Mgr) {
496 Mgr.getChecker<DereferenceChecker>()->FixedDerefChecker.enable(Mgr);
497}
498
499bool ento::shouldRegisterFixedAddressDereferenceChecker(
500 const CheckerManager &) {
501 return true;
502}
503
504void ento::registerNullPointerArithmChecker(CheckerManager &Mgr) {
505 Mgr.getChecker<DereferenceChecker>()->NullPointerArithmChecker.enable(Mgr);
506}
507
508bool ento::shouldRegisterNullPointerArithmChecker(const CheckerManager &) {
509 return true;
510}
#define V(N, I)
static const Expr * getDereferenceExpr(const Stmt *S, bool IsBind=false)
static bool isDeclRefExprToReference(const Expr *E)
Expr * getBase()
Get base of the array section.
Definition Expr.h:7183
Expr * getLHS() const
Definition Expr.h:4022
Expr * getRHS() const
Definition Expr.h:4024
static bool isAdditiveOp(Opcode Opc)
Definition Expr.h:4058
Opcode getOpcode() const
Definition Expr.h:4017
ValueDecl * getDecl()
Definition Expr.h:1338
This represents one expression.
Definition Expr.h:112
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition Expr.cpp:3090
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Skip past any parentheses and lvalue casts which might surround this expression until reaching a fixe...
Definition Expr.cpp:3102
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3081
QualType getType() const
Definition Expr.h:144
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
SourceLocation getMemberLoc() const
getMemberLoc - Return the location of the "member", in X->F, it is the location of 'F'.
Definition Expr.h:3487
Expr * getBase() const
Definition Expr.h:3375
DeclarationNameInfo getMemberNameInfo() const
Retrieve the member declaration name info.
Definition Expr.h:3475
bool isArrow() const
Definition Expr.h:3482
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:301
SourceLocation getLocation() const
Definition ExprObjC.h:591
ObjCIvarDecl * getDecl()
Definition ExprObjC.h:578
const Expr * getBase() const
Definition ExprObjC.h:582
LangAS getAddressSpace() const
Return the address space of this type.
Definition TypeBase.h:8404
QualType getCanonicalType() const
Definition TypeBase.h:8330
bool hasAddressSpace() const
Check if this type has any address space qualifier.
Definition TypeBase.h:8399
Stmt - This represents one statement.
Definition Stmt.h:85
StmtClass getStmtClass() const
Definition Stmt.h:1472
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:338
bool isPointerType() const
Definition TypeBase.h:8515
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition TypeBase.h:8915
bool isReferenceType() const
Definition TypeBase.h:8539
Expr * getSubExpr() const
Definition Expr.h:2285
Represents a variable declaration or definition.
Definition Decl.h:926
Checker families (where a single backend class implements multiple related frontends) should derive f...
Definition Checker.h:584
CHECKER * getChecker(AT &&...Args)
If the the singleton instance of a checker class is not yet constructed, then construct it (with the ...
bool isConstrainedFalse() const
Return true if the constraint is perfectly constrained to 'false'.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
const LocationContext * getLocationContext() const
ProgramState - This class encapsulates:
bool isUndef() const
Definition SVals.h:107
bool isConstant() const
Definition SVals.cpp:245
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition SVals.h:87
const MemRegion * getAsRegion() const
Definition SVals.cpp:119
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition SVals.h:83
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.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
std::pair< const clang::VarDecl *, const clang::Expr * > parseAssignment(const Stmt *S)
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
unsigned toTargetAddressSpace(LangAS AS)
U cast(CodeGen::Address addr)
Definition Address.h:327
static void format(const ValueDescStr &V, raw_ostream &Stream, StringRef Style)