1 //== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This defines NullDerefChecker, a builtin check in ExprEngine that performs
11 // checks for null pointers at loads and stores.
13 //===----------------------------------------------------------------------===//
15 #include "InternalChecks.h"
16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17 #include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/Checker.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
21 using namespace clang
;
25 class DereferenceChecker
: public Checker
{
28 llvm::SmallVector
<ExplodedNode
*, 2> ImplicitNullDerefNodes
;
30 DereferenceChecker() : BT_null(0), BT_undef(0) {}
31 static void *getTag() { static int tag
= 0; return &tag
; }
32 void visitLocation(CheckerContext
&C
, const Stmt
*S
, SVal location
,
35 std::pair
<ExplodedNode
* const*, ExplodedNode
* const*>
36 getImplicitNodes() const {
37 return std::make_pair(ImplicitNullDerefNodes
.data(),
38 ImplicitNullDerefNodes
.data() +
39 ImplicitNullDerefNodes
.size());
41 void AddDerefSource(llvm::raw_ostream
&os
,
42 llvm::SmallVectorImpl
<SourceRange
> &Ranges
,
43 const Expr
*Ex
, bool loadedFrom
= false);
45 } // end anonymous namespace
47 void ento::RegisterDereferenceChecker(ExprEngine
&Eng
) {
48 Eng
.registerCheck(new DereferenceChecker());
51 std::pair
<ExplodedNode
* const *, ExplodedNode
* const *>
52 ento::GetImplicitNullDereferences(ExprEngine
&Eng
) {
53 DereferenceChecker
*checker
= Eng
.getChecker
<DereferenceChecker
>();
55 return std::make_pair((ExplodedNode
* const *) 0,
56 (ExplodedNode
* const *) 0);
57 return checker
->getImplicitNodes();
60 void DereferenceChecker::AddDerefSource(llvm::raw_ostream
&os
,
61 llvm::SmallVectorImpl
<SourceRange
> &Ranges
,
64 Ex
= Ex
->IgnoreParenLValueCasts();
65 switch (Ex
->getStmtClass()) {
68 case Stmt::DeclRefExprClass
: {
69 const DeclRefExpr
*DR
= cast
<DeclRefExpr
>(Ex
);
70 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(DR
->getDecl())) {
71 os
<< " (" << (loadedFrom
? "loaded from" : "from")
72 << " variable '" << VD
->getName() << "')";
73 Ranges
.push_back(DR
->getSourceRange());
77 case Stmt::MemberExprClass
: {
78 const MemberExpr
*ME
= cast
<MemberExpr
>(Ex
);
79 os
<< " (" << (loadedFrom
? "loaded from" : "via")
80 << " field '" << ME
->getMemberNameInfo() << "')";
81 SourceLocation L
= ME
->getMemberLoc();
82 Ranges
.push_back(SourceRange(L
, L
));
88 void DereferenceChecker::visitLocation(CheckerContext
&C
, const Stmt
*S
,
89 SVal l
, bool isLoad
) {
90 // Check for dereference of an undefined value.
92 if (ExplodedNode
*N
= C
.generateSink()) {
94 BT_undef
= new BuiltinBug("Dereference of undefined pointer value");
96 EnhancedBugReport
*report
=
97 new EnhancedBugReport(*BT_undef
, BT_undef
->getDescription(), N
);
98 report
->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue
,
99 bugreporter::GetDerefExpr(N
));
100 C
.EmitReport(report
);
105 DefinedOrUnknownSVal location
= cast
<DefinedOrUnknownSVal
>(l
);
107 // Check for null dereferences.
108 if (!isa
<Loc
>(location
))
111 const GRState
*state
= C
.getState();
112 const GRState
*notNullState
, *nullState
;
113 llvm::tie(notNullState
, nullState
) = state
->assume(location
);
115 // The explicit NULL case.
118 // Generate an error node.
119 ExplodedNode
*N
= C
.generateSink(nullState
);
123 // We know that 'location' cannot be non-null. This is what
124 // we call an "explicit" null dereference.
126 BT_null
= new BuiltinBug("Dereference of null pointer");
128 llvm::SmallString
<100> buf
;
129 llvm::SmallVector
<SourceRange
, 2> Ranges
;
131 // Walk through lvalue casts to get the original expression
132 // that syntactically caused the load.
133 if (const Expr
*expr
= dyn_cast
<Expr
>(S
))
134 S
= expr
->IgnoreParenLValueCasts();
136 switch (S
->getStmtClass()) {
137 case Stmt::ArraySubscriptExprClass
: {
138 llvm::raw_svector_ostream
os(buf
);
139 os
<< "Array access";
140 const ArraySubscriptExpr
*AE
= cast
<ArraySubscriptExpr
>(S
);
141 AddDerefSource(os
, Ranges
, AE
->getBase()->IgnoreParenCasts());
142 os
<< " results in a null pointer dereference";
145 case Stmt::UnaryOperatorClass
: {
146 llvm::raw_svector_ostream
os(buf
);
147 os
<< "Dereference of null pointer";
148 const UnaryOperator
*U
= cast
<UnaryOperator
>(S
);
149 AddDerefSource(os
, Ranges
, U
->getSubExpr()->IgnoreParens(), true);
152 case Stmt::MemberExprClass
: {
153 const MemberExpr
*M
= cast
<MemberExpr
>(S
);
155 llvm::raw_svector_ostream
os(buf
);
156 os
<< "Access to field '" << M
->getMemberNameInfo()
157 << "' results in a dereference of a null pointer";
158 AddDerefSource(os
, Ranges
, M
->getBase()->IgnoreParenCasts(), true);
162 case Stmt::ObjCIvarRefExprClass
: {
163 const ObjCIvarRefExpr
*IV
= cast
<ObjCIvarRefExpr
>(S
);
164 if (const DeclRefExpr
*DR
=
165 dyn_cast
<DeclRefExpr
>(IV
->getBase()->IgnoreParenCasts())) {
166 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(DR
->getDecl())) {
167 llvm::raw_svector_ostream
os(buf
);
168 os
<< "Instance variable access (via '" << VD
->getName()
169 << "') results in a null pointer dereference";
172 Ranges
.push_back(IV
->getSourceRange());
179 EnhancedBugReport
*report
=
180 new EnhancedBugReport(*BT_null
,
181 buf
.empty() ? BT_null
->getDescription():buf
.str(),
184 report
->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue
,
185 bugreporter::GetDerefExpr(N
));
187 for (llvm::SmallVectorImpl
<SourceRange
>::iterator
188 I
= Ranges
.begin(), E
= Ranges
.end(); I
!=E
; ++I
)
189 report
->addRange(*I
);
191 C
.EmitReport(report
);
195 // Otherwise, we have the case where the location could either be
196 // null or not-null. Record the error node as an "implicit" null
198 if (ExplodedNode
*N
= C
.generateSink(nullState
))
199 ImplicitNullDerefNodes
.push_back(N
);
203 // From this point forward, we know that the location is not null.
204 C
.addTransition(notNullState
);