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/raw_ostream.h"
23
24using namespace clang;
25using namespace ento;
26
27namespace {
28
29class DerefBugType : public BugType {
30 StringRef ArrayMsg, FieldMsg;
31
32public:
33 DerefBugType(CheckerFrontend *FE, StringRef Desc, const char *AMsg,
34 const char *FMsg = nullptr)
35 : BugType(FE, Desc), ArrayMsg(AMsg), FieldMsg(FMsg ? FMsg : AMsg) {}
36 StringRef getArrayMsg() const { return ArrayMsg; }
37 StringRef getFieldMsg() const { return FieldMsg; }
38};
39
40class DereferenceChecker
41 : public CheckerFamily<check::Location, check::Bind,
42 EventDispatcher<ImplicitNullDerefEvent>> {
43 void reportBug(const DerefBugType &BT, ProgramStateRef State, const Stmt *S,
44 CheckerContext &C) const;
45
46 bool suppressReport(CheckerContext &C, const Expr *E) const;
47
48public:
49 void checkLocation(SVal location, bool isLoad, const Stmt* S,
50 CheckerContext &C) const;
51 void checkBind(SVal L, SVal V, const Stmt *S, bool AtDeclInit,
52 CheckerContext &C) const;
53
54 static void AddDerefSource(raw_ostream &os,
55 SmallVectorImpl<SourceRange> &Ranges,
56 const Expr *Ex, const ProgramState *state,
57 const LocationContext *LCtx,
58 bool loadedFrom = false);
59
60 CheckerFrontend NullDerefChecker, FixedDerefChecker;
61 const DerefBugType NullBug{&NullDerefChecker, "Dereference of null pointer",
62 "a null pointer dereference",
63 "a dereference of a null pointer"};
64 const DerefBugType UndefBug{&NullDerefChecker,
65 "Dereference of undefined pointer value",
66 "an undefined pointer dereference",
67 "a dereference of an undefined pointer value"};
68 const DerefBugType LabelBug{&NullDerefChecker,
69 "Dereference of the address of a label",
70 "an undefined pointer dereference",
71 "a dereference of an address of a label"};
72 const DerefBugType FixedAddressBug{&FixedDerefChecker,
73 "Dereference of a fixed address",
74 "a dereference of a fixed address"};
75
76 StringRef getDebugTag() const override { return "DereferenceChecker"; }
77};
78} // end anonymous namespace
79
80void
81DereferenceChecker::AddDerefSource(raw_ostream &os,
83 const Expr *Ex,
84 const ProgramState *state,
85 const LocationContext *LCtx,
86 bool loadedFrom) {
87 Ex = Ex->IgnoreParenLValueCasts();
88 switch (Ex->getStmtClass()) {
89 default:
90 break;
91 case Stmt::DeclRefExprClass: {
92 const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
93 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
94 os << " (" << (loadedFrom ? "loaded from" : "from")
95 << " variable '" << VD->getName() << "')";
96 Ranges.push_back(DR->getSourceRange());
97 }
98 break;
99 }
100 case Stmt::MemberExprClass: {
101 const MemberExpr *ME = cast<MemberExpr>(Ex);
102 os << " (" << (loadedFrom ? "loaded from" : "via")
103 << " field '" << ME->getMemberNameInfo() << "')";
104 SourceLocation L = ME->getMemberLoc();
105 Ranges.push_back(SourceRange(L, L));
106 break;
107 }
108 case Stmt::ObjCIvarRefExprClass: {
109 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
110 os << " (" << (loadedFrom ? "loaded from" : "via")
111 << " ivar '" << IV->getDecl()->getName() << "')";
112 SourceLocation L = IV->getLocation();
113 Ranges.push_back(SourceRange(L, L));
114 break;
115 }
116 }
117}
118
119static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
120 const Expr *E = nullptr;
121
122 // Walk through lvalue casts to get the original expression
123 // that syntactically caused the load.
124 if (const Expr *expr = dyn_cast<Expr>(S))
125 E = expr->IgnoreParenLValueCasts();
126
127 if (IsBind) {
128 const VarDecl *VD;
129 const Expr *Init;
130 std::tie(VD, Init) = parseAssignment(S);
131 if (VD && Init)
132 E = Init;
133 }
134 return E;
135}
136
137bool DereferenceChecker::suppressReport(CheckerContext &C,
138 const Expr *E) const {
139 // Do not report dereferences on memory that use address space #256, #257,
140 // and #258. Those address spaces are used when dereferencing address spaces
141 // relative to the GS, FS, and SS segments on x86/x86-64 targets.
142 // Dereferencing a null pointer in these address spaces is not defined
143 // as an error. All other null dereferences in other address spaces
144 // are defined as an error unless explicitly defined.
145 // See https://clang.llvm.org/docs/LanguageExtensions.html, the section
146 // "X86/X86-64 Language Extensions"
147
148 QualType Ty = E->getType();
149 if (!Ty.hasAddressSpace())
150 return false;
151 if (C.getAnalysisManager()
152 .getAnalyzerOptions()
153 .ShouldSuppressAddressSpaceDereferences)
154 return true;
155
156 const llvm::Triple::ArchType Arch =
157 C.getASTContext().getTargetInfo().getTriple().getArch();
158
159 if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) {
161 case 256:
162 case 257:
163 case 258:
164 return true;
165 }
166 }
167 return false;
168}
169
170static bool isDeclRefExprToReference(const Expr *E) {
171 if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
172 return DRE->getDecl()->getType()->isReferenceType();
173 return false;
174}
175
176void DereferenceChecker::reportBug(const DerefBugType &BT,
177 ProgramStateRef State, const Stmt *S,
178 CheckerContext &C) const {
179 if (&BT == &FixedAddressBug) {
180 if (!FixedDerefChecker.isEnabled())
181 // Deliberately don't add a sink node if check is disabled.
182 // This situation may be valid in special cases.
183 return;
184 } else {
185 if (!NullDerefChecker.isEnabled()) {
186 C.addSink();
187 return;
188 }
189 }
190
191 // Generate an error node.
192 ExplodedNode *N = C.generateErrorNode(State);
193 if (!N)
194 return;
195
196 SmallString<100> Buf;
197 llvm::raw_svector_ostream Out(Buf);
198
199 SmallVector<SourceRange, 2> Ranges;
200
201 switch (S->getStmtClass()) {
202 case Stmt::ArraySubscriptExprClass: {
203 Out << "Array access";
204 const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
205 AddDerefSource(Out, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(),
206 N->getLocationContext());
207 Out << " results in " << BT.getArrayMsg();
208 break;
209 }
210 case Stmt::ArraySectionExprClass: {
211 Out << "Array access";
212 const ArraySectionExpr *AE = cast<ArraySectionExpr>(S);
213 AddDerefSource(Out, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(),
214 N->getLocationContext());
215 Out << " results in " << BT.getArrayMsg();
216 break;
217 }
218 case Stmt::UnaryOperatorClass: {
219 Out << BT.getDescription();
220 const UnaryOperator *U = cast<UnaryOperator>(S);
221 AddDerefSource(Out, Ranges, U->getSubExpr()->IgnoreParens(), State.get(),
222 N->getLocationContext(), true);
223 break;
224 }
225 case Stmt::MemberExprClass: {
226 const MemberExpr *M = cast<MemberExpr>(S);
227 if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
228 Out << "Access to field '" << M->getMemberNameInfo() << "' results in "
229 << BT.getFieldMsg();
230 AddDerefSource(Out, Ranges, M->getBase()->IgnoreParenCasts(), State.get(),
231 N->getLocationContext(), true);
232 }
233 break;
234 }
235 case Stmt::ObjCIvarRefExprClass: {
236 const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
237 Out << "Access to instance variable '" << *IV->getDecl() << "' results in "
238 << BT.getFieldMsg();
239 AddDerefSource(Out, Ranges, IV->getBase()->IgnoreParenCasts(), State.get(),
240 N->getLocationContext(), true);
241 break;
242 }
243 default:
244 break;
245 }
246
247 auto BR = std::make_unique<PathSensitiveBugReport>(
248 BT, Buf.empty() ? BT.getDescription() : Buf.str(), N);
249
251
252 for (SmallVectorImpl<SourceRange>::iterator
253 I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
254 BR->addRange(*I);
255
256 C.emitReport(std::move(BR));
257}
258
259void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
260 CheckerContext &C) const {
261 // Check for dereference of an undefined value.
262 if (l.isUndef()) {
263 const Expr *DerefExpr = getDereferenceExpr(S);
264 if (!suppressReport(C, DerefExpr))
265 reportBug(UndefBug, C.getState(), DerefExpr, C);
266 return;
267 }
268
269 DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
270
271 // Check for null dereferences.
272 if (!isa<Loc>(location))
273 return;
274
275 ProgramStateRef state = C.getState();
276
277 ProgramStateRef notNullState, nullState;
278 std::tie(notNullState, nullState) = state->assume(location);
279
280 if (nullState) {
281 if (!notNullState) {
282 // We know that 'location' can only be null. This is what
283 // we call an "explicit" null dereference.
284 const Expr *expr = getDereferenceExpr(S);
285 if (!suppressReport(C, expr)) {
286 reportBug(NullBug, nullState, expr, C);
287 return;
288 }
289 }
290
291 // Otherwise, we have the case where the location could either be
292 // null or not-null. Record the error node as an "implicit" null
293 // dereference.
294 if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
295 ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
296 /*IsDirectDereference=*/true};
297 dispatchEvent(event);
298 }
299 }
300
301 if (location.isConstant()) {
302 const Expr *DerefExpr = getDereferenceExpr(S, isLoad);
303 if (!suppressReport(C, DerefExpr))
304 reportBug(FixedAddressBug, notNullState, DerefExpr, C);
305 return;
306 }
307
308 // From this point forward, we know that the location is not null.
309 C.addTransition(notNullState);
310}
311
312void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
313 bool AtDeclInit, CheckerContext &C) const {
314 // If we're binding to a reference, check if the value is known to be null.
315 if (V.isUndef())
316 return;
317
318 // One should never write to label addresses.
319 if (auto Label = L.getAs<loc::GotoLabel>()) {
320 reportBug(LabelBug, C.getState(), S, C);
321 return;
322 }
323
324 const MemRegion *MR = L.getAsRegion();
325 const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
326 if (!TVR)
327 return;
328
329 if (!TVR->getValueType()->isReferenceType())
330 return;
331
332 ProgramStateRef State = C.getState();
333
334 ProgramStateRef StNonNull, StNull;
335 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
336
337 if (StNull) {
338 if (!StNonNull) {
339 const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
340 if (!suppressReport(C, expr)) {
341 reportBug(NullBug, StNull, expr, C);
342 return;
343 }
344 }
345
346 // At this point the value could be either null or non-null.
347 // Record this as an "implicit" null dereference.
348 if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
349 ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
350 &C.getBugReporter(),
351 /*IsDirectDereference=*/true};
352 dispatchEvent(event);
353 }
354 }
355
356 if (V.isConstant()) {
357 const Expr *DerefExpr = getDereferenceExpr(S, true);
358 if (!suppressReport(C, DerefExpr))
359 reportBug(FixedAddressBug, State, DerefExpr, C);
360 return;
361 }
362
363 // Unlike a regular null dereference, initializing a reference with a
364 // dereferenced null pointer does not actually cause a runtime exception in
365 // Clang's implementation of references.
366 //
367 // int &r = *p; // safe??
368 // if (p != NULL) return; // uh-oh
369 // r = 5; // trap here
370 //
371 // The standard says this is invalid as soon as we try to create a "null
372 // reference" (there is no such thing), but turning this into an assumption
373 // that 'p' is never null will not match our actual runtime behavior.
374 // So we do not record this assumption, allowing us to warn on the last line
375 // of this example.
376 //
377 // We do need to add a transition because we may have generated a sink for
378 // the "implicit" null dereference.
379 C.addTransition(State, this);
380}
381
382void ento::registerNullDereferenceChecker(CheckerManager &Mgr) {
383 Mgr.getChecker<DereferenceChecker>()->NullDerefChecker.enable(Mgr);
384}
385
386bool ento::shouldRegisterNullDereferenceChecker(const CheckerManager &) {
387 return true;
388}
389
390void ento::registerFixedAddressDereferenceChecker(CheckerManager &Mgr) {
391 Mgr.getChecker<DereferenceChecker>()->FixedDerefChecker.enable(Mgr);
392}
393
394bool ento::shouldRegisterFixedAddressDereferenceChecker(
395 const CheckerManager &) {
396 return true;
397}
#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:7171
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:3078
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Skip past any parentheses and lvalue casts which might surround this expression until reaching a fixe...
Definition Expr.cpp:3090
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3069
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:300
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:8411
bool hasAddressSpace() const
Check if this type has any address space qualifier.
Definition TypeBase.h:8406
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:334
bool isReferenceType() const
Definition TypeBase.h:8546
Expr * getSubExpr() const
Definition Expr.h:2285
Represents a variable declaration or definition.
Definition Decl.h:925
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 ...
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