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 GRExprEngine that performs
11 // checks for null pointers at loads and stores.
13 //===----------------------------------------------------------------------===//
15 #include "GRExprEngineInternalChecks.h"
16 #include "clang/GR/BugReporter/BugType.h"
17 #include "clang/GR/Checkers/DereferenceChecker.h"
18 #include "clang/GR/PathSensitive/Checker.h"
19 #include "clang/GR/PathSensitive/GRExprEngine.h"
21 using namespace clang
;
24 class DereferenceChecker
: public Checker
{
27 llvm::SmallVector
<ExplodedNode
*, 2> ImplicitNullDerefNodes
;
29 DereferenceChecker() : BT_null(0), BT_undef(0) {}
30 static void *getTag() { static int tag
= 0; return &tag
; }
31 void visitLocation(CheckerContext
&C
, const Stmt
*S
, SVal location
);
33 std::pair
<ExplodedNode
* const*, ExplodedNode
* const*>
34 getImplicitNodes() const {
35 return std::make_pair(ImplicitNullDerefNodes
.data(),
36 ImplicitNullDerefNodes
.data() +
37 ImplicitNullDerefNodes
.size());
39 void AddDerefSource(llvm::raw_ostream
&os
,
40 llvm::SmallVectorImpl
<SourceRange
> &Ranges
,
41 const Expr
*Ex
, bool loadedFrom
= false);
43 } // end anonymous namespace
45 void clang::RegisterDereferenceChecker(GRExprEngine
&Eng
) {
46 Eng
.registerCheck(new DereferenceChecker());
49 std::pair
<ExplodedNode
* const *, ExplodedNode
* const *>
50 clang::GetImplicitNullDereferences(GRExprEngine
&Eng
) {
51 DereferenceChecker
*checker
= Eng
.getChecker
<DereferenceChecker
>();
53 return std::make_pair((ExplodedNode
* const *) 0,
54 (ExplodedNode
* const *) 0);
55 return checker
->getImplicitNodes();
58 void DereferenceChecker::AddDerefSource(llvm::raw_ostream
&os
,
59 llvm::SmallVectorImpl
<SourceRange
> &Ranges
,
62 Ex
= Ex
->IgnoreParenLValueCasts();
63 switch (Ex
->getStmtClass()) {
66 case Stmt::DeclRefExprClass
: {
67 const DeclRefExpr
*DR
= cast
<DeclRefExpr
>(Ex
);
68 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(DR
->getDecl())) {
69 os
<< " (" << (loadedFrom
? "loaded from" : "from")
70 << " variable '" << VD
->getName() << "')";
71 Ranges
.push_back(DR
->getSourceRange());
75 case Stmt::MemberExprClass
: {
76 const MemberExpr
*ME
= cast
<MemberExpr
>(Ex
);
77 os
<< " (" << (loadedFrom
? "loaded from" : "via")
78 << " field '" << ME
->getMemberNameInfo() << "')";
79 SourceLocation L
= ME
->getMemberLoc();
80 Ranges
.push_back(SourceRange(L
, L
));
86 void DereferenceChecker::visitLocation(CheckerContext
&C
, const Stmt
*S
,
88 // Check for dereference of an undefined value.
90 if (ExplodedNode
*N
= C
.generateSink()) {
92 BT_undef
= new BuiltinBug("Dereference of undefined pointer value");
94 EnhancedBugReport
*report
=
95 new EnhancedBugReport(*BT_undef
, BT_undef
->getDescription(), N
);
96 report
->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue
,
97 bugreporter::GetDerefExpr(N
));
103 DefinedOrUnknownSVal location
= cast
<DefinedOrUnknownSVal
>(l
);
105 // Check for null dereferences.
106 if (!isa
<Loc
>(location
))
109 const GRState
*state
= C
.getState();
110 const GRState
*notNullState
, *nullState
;
111 llvm::tie(notNullState
, nullState
) = state
->assume(location
);
113 // The explicit NULL case.
116 // Generate an error node.
117 ExplodedNode
*N
= C
.generateSink(nullState
);
121 // We know that 'location' cannot be non-null. This is what
122 // we call an "explicit" null dereference.
124 BT_null
= new BuiltinBug("Dereference of null pointer");
126 llvm::SmallString
<100> buf
;
127 llvm::SmallVector
<SourceRange
, 2> Ranges
;
129 // Walk through lvalue casts to get the original expression
130 // that syntactically caused the load.
131 if (const Expr
*expr
= dyn_cast
<Expr
>(S
))
132 S
= expr
->IgnoreParenLValueCasts();
134 switch (S
->getStmtClass()) {
135 case Stmt::ArraySubscriptExprClass
: {
136 llvm::raw_svector_ostream
os(buf
);
137 os
<< "Array access";
138 const ArraySubscriptExpr
*AE
= cast
<ArraySubscriptExpr
>(S
);
139 AddDerefSource(os
, Ranges
, AE
->getBase()->IgnoreParenCasts());
140 os
<< " results in a null pointer dereference";
143 case Stmt::UnaryOperatorClass
: {
144 llvm::raw_svector_ostream
os(buf
);
145 os
<< "Dereference of null pointer";
146 const UnaryOperator
*U
= cast
<UnaryOperator
>(S
);
147 AddDerefSource(os
, Ranges
, U
->getSubExpr()->IgnoreParens(), true);
150 case Stmt::MemberExprClass
: {
151 const MemberExpr
*M
= cast
<MemberExpr
>(S
);
153 llvm::raw_svector_ostream
os(buf
);
154 os
<< "Access to field '" << M
->getMemberNameInfo()
155 << "' results in a dereference of a null pointer";
156 AddDerefSource(os
, Ranges
, M
->getBase()->IgnoreParenCasts(), true);
160 case Stmt::ObjCIvarRefExprClass
: {
161 const ObjCIvarRefExpr
*IV
= cast
<ObjCIvarRefExpr
>(S
);
162 if (const DeclRefExpr
*DR
=
163 dyn_cast
<DeclRefExpr
>(IV
->getBase()->IgnoreParenCasts())) {
164 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(DR
->getDecl())) {
165 llvm::raw_svector_ostream
os(buf
);
166 os
<< "Instance variable access (via '" << VD
->getName()
167 << "') results in a null pointer dereference";
170 Ranges
.push_back(IV
->getSourceRange());
177 EnhancedBugReport
*report
=
178 new EnhancedBugReport(*BT_null
,
179 buf
.empty() ? BT_null
->getDescription():buf
.str(),
182 report
->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue
,
183 bugreporter::GetDerefExpr(N
));
185 for (llvm::SmallVectorImpl
<SourceRange
>::iterator
186 I
= Ranges
.begin(), E
= Ranges
.end(); I
!=E
; ++I
)
187 report
->addRange(*I
);
189 C
.EmitReport(report
);
193 // Otherwise, we have the case where the location could either be
194 // null or not-null. Record the error node as an "implicit" null
196 if (ExplodedNode
*N
= C
.generateSink(nullState
))
197 ImplicitNullDerefNodes
.push_back(N
);
201 // From this point forward, we know that the location is not null.
202 C
.addTransition(notNullState
);