1 //===-- DereferenceChecker.cpp - Null dereference checker -----------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This defines NullDerefChecker, a builtin check in ExprEngine that performs
10 // checks for null pointers at loads and stores.
12 //===----------------------------------------------------------------------===//
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"
26 using namespace clang
;
30 class DereferenceChecker
31 : public Checker
< check::Location
,
33 EventDispatcher
<ImplicitNullDerefEvent
> > {
34 enum DerefKind
{ NullPointer
, UndefinedPointerValue
};
36 BugType BT_Null
{this, "Dereference of null pointer", categories::LogicError
};
37 BugType BT_Undef
{this, "Dereference of undefined pointer value",
38 categories::LogicError
};
40 void reportBug(DerefKind K
, ProgramStateRef State
, const Stmt
*S
,
41 CheckerContext
&C
) const;
43 bool suppressReport(CheckerContext
&C
, const Expr
*E
) const;
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;
50 static void AddDerefSource(raw_ostream
&os
,
51 SmallVectorImpl
<SourceRange
> &Ranges
,
52 const Expr
*Ex
, const ProgramState
*state
,
53 const LocationContext
*LCtx
,
54 bool loadedFrom
= false);
56 bool SuppressAddressSpaces
= false;
58 } // end anonymous namespace
61 DereferenceChecker::AddDerefSource(raw_ostream
&os
,
62 SmallVectorImpl
<SourceRange
> &Ranges
,
64 const ProgramState
*state
,
65 const LocationContext
*LCtx
,
67 Ex
= Ex
->IgnoreParenLValueCasts();
68 switch (Ex
->getStmtClass()) {
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());
80 case Stmt::MemberExprClass
: {
81 const MemberExpr
*ME
= cast
<MemberExpr
>(Ex
);
82 os
<< " (" << (loadedFrom
? "loaded from" : "via")
83 << " field '" << ME
->getMemberNameInfo() << "')";
84 SourceLocation L
= ME
->getMemberLoc();
85 Ranges
.push_back(SourceRange(L
, L
));
88 case Stmt::ObjCIvarRefExprClass
: {
89 const ObjCIvarRefExpr
*IV
= cast
<ObjCIvarRefExpr
>(Ex
);
90 os
<< " (" << (loadedFrom
? "loaded from" : "via")
91 << " ivar '" << IV
->getDecl()->getName() << "')";
92 SourceLocation L
= IV
->getLocation();
93 Ranges
.push_back(SourceRange(L
, L
));
99 static const Expr
*getDereferenceExpr(const Stmt
*S
, bool IsBind
=false){
100 const Expr
*E
= nullptr;
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();
110 std::tie(VD
, Init
) = parseAssignment(S
);
117 bool 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"
128 QualType Ty
= E
->getType();
129 if (!Ty
.hasAddressSpace())
131 if (SuppressAddressSpaces
)
134 const llvm::Triple::ArchType Arch
=
135 C
.getASTContext().getTargetInfo().getTriple().getArch();
137 if ((Arch
== llvm::Triple::x86
) || (Arch
== llvm::Triple::x86_64
)) {
138 switch (toTargetAddressSpace(E
->getType().getAddressSpace())) {
148 static bool isDeclRefExprToReference(const Expr
*E
) {
149 if (const auto *DRE
= dyn_cast
<DeclRefExpr
>(E
))
150 return DRE
->getDecl()->getType()->isReferenceType();
154 void 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
;
160 case DerefKind::NullPointer
:
162 DerefStr1
= " results in a null pointer dereference";
163 DerefStr2
= " results in a dereference of a null pointer";
165 case DerefKind::UndefinedPointerValue
:
167 DerefStr1
= " results in an undefined pointer dereference";
168 DerefStr2
= " results in a dereference of an undefined pointer value";
172 // Generate an error node.
173 ExplodedNode
*N
= C
.generateErrorNode(State
);
177 SmallString
<100> buf
;
178 llvm::raw_svector_ostream
os(buf
);
180 SmallVector
<SourceRange
, 2> Ranges
;
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());
191 case Stmt::OMPArraySectionExprClass
: {
192 os
<< "Array access";
193 const OMPArraySectionExpr
*AE
= cast
<OMPArraySectionExpr
>(S
);
194 AddDerefSource(os
, Ranges
, AE
->getBase()->IgnoreParenCasts(),
195 State
.get(), N
->getLocationContext());
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);
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);
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);
226 auto report
= std::make_unique
<PathSensitiveBugReport
>(
227 *BT
, buf
.empty() ? BT
->getDescription() : buf
.str(), N
);
229 bugreporter::trackExpressionValue(N
, bugreporter::getDerefExpr(S
), *report
);
231 for (SmallVectorImpl
<SourceRange
>::iterator
232 I
= Ranges
.begin(), E
= Ranges
.end(); I
!=E
; ++I
)
233 report
->addRange(*I
);
235 C
.emitReport(std::move(report
));
238 void DereferenceChecker::checkLocation(SVal l
, bool isLoad
, const Stmt
* S
,
239 CheckerContext
&C
) const {
240 // Check for dereference of an undefined value.
242 const Expr
*DerefExpr
= getDereferenceExpr(S
);
243 if (!suppressReport(C
, DerefExpr
))
244 reportBug(DerefKind::UndefinedPointerValue
, C
.getState(), DerefExpr
, C
);
248 DefinedOrUnknownSVal location
= l
.castAs
<DefinedOrUnknownSVal
>();
250 // Check for null dereferences.
251 if (!isa
<Loc
>(location
))
254 ProgramStateRef state
= C
.getState();
256 ProgramStateRef notNullState
, nullState
;
257 std::tie(notNullState
, nullState
) = state
->assume(location
);
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
);
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
273 if (ExplodedNode
*N
= C
.generateSink(nullState
, C
.getPredecessor())) {
274 ImplicitNullDerefEvent event
= {l
, isLoad
, N
, &C
.getBugReporter(),
275 /*IsDirectDereference=*/true};
276 dispatchEvent(event
);
280 // From this point forward, we know that the location is not null.
281 C
.addTransition(notNullState
);
284 void 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.
290 const MemRegion
*MR
= L
.getAsRegion();
291 const TypedValueRegion
*TVR
= dyn_cast_or_null
<TypedValueRegion
>(MR
);
295 if (!TVR
->getValueType()->isReferenceType())
298 ProgramStateRef State
= C
.getState();
300 ProgramStateRef StNonNull
, StNull
;
301 std::tie(StNonNull
, StNull
) = State
->assume(V
.castAs
<DefinedOrUnknownSVal
>());
305 const Expr
*expr
= getDereferenceExpr(S
, /*IsBind=*/true);
306 if (!suppressReport(C
, expr
)) {
307 reportBug(DerefKind::NullPointer
, StNull
, expr
, C
);
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
,
317 /*IsDirectDereference=*/true};
318 dispatchEvent(event
);
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.
326 // int &r = *p; // safe??
327 // if (p != NULL) return; // uh-oh
328 // r = 5; // trap here
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
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);
341 void ento::registerDereferenceChecker(CheckerManager
&mgr
) {
342 auto *Chk
= mgr
.registerChecker
<DereferenceChecker
>();
343 Chk
->SuppressAddressSpaces
= mgr
.getAnalyzerOptions().getCheckerBooleanOption(
344 mgr
.getCurrentCheckerName(), "SuppressAddressSpaces");
347 bool ento::shouldRegisterDereferenceChecker(const CheckerManager
&mgr
) {