1f4a2713aSLionel Sambuc //== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==//
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc // The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc //
10f4a2713aSLionel Sambuc // This defines NullDerefChecker, a builtin check in ExprEngine that performs
11f4a2713aSLionel Sambuc // checks for null pointers at loads and stores.
12f4a2713aSLionel Sambuc //
13f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
14f4a2713aSLionel Sambuc
15f4a2713aSLionel Sambuc #include "ClangSACheckers.h"
16f4a2713aSLionel Sambuc #include "clang/AST/ExprObjC.h"
17f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/Checker.h"
19f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21f4a2713aSLionel Sambuc #include "llvm/ADT/SmallString.h"
22f4a2713aSLionel Sambuc #include "llvm/Support/raw_ostream.h"
23f4a2713aSLionel Sambuc
24f4a2713aSLionel Sambuc using namespace clang;
25f4a2713aSLionel Sambuc using namespace ento;
26f4a2713aSLionel Sambuc
27f4a2713aSLionel Sambuc namespace {
28f4a2713aSLionel Sambuc class DereferenceChecker
29f4a2713aSLionel Sambuc : public Checker< check::Location,
30f4a2713aSLionel Sambuc check::Bind,
31f4a2713aSLionel Sambuc EventDispatcher<ImplicitNullDerefEvent> > {
32*0a6a1f1dSLionel Sambuc mutable std::unique_ptr<BuiltinBug> BT_null;
33*0a6a1f1dSLionel Sambuc mutable std::unique_ptr<BuiltinBug> BT_undef;
34f4a2713aSLionel Sambuc
35f4a2713aSLionel Sambuc void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
36f4a2713aSLionel Sambuc bool IsBind = false) const;
37f4a2713aSLionel Sambuc
38f4a2713aSLionel Sambuc public:
39f4a2713aSLionel Sambuc void checkLocation(SVal location, bool isLoad, const Stmt* S,
40f4a2713aSLionel Sambuc CheckerContext &C) const;
41f4a2713aSLionel Sambuc void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
42f4a2713aSLionel Sambuc
43f4a2713aSLionel Sambuc static void AddDerefSource(raw_ostream &os,
44f4a2713aSLionel Sambuc SmallVectorImpl<SourceRange> &Ranges,
45f4a2713aSLionel Sambuc const Expr *Ex, const ProgramState *state,
46f4a2713aSLionel Sambuc const LocationContext *LCtx,
47f4a2713aSLionel Sambuc bool loadedFrom = false);
48f4a2713aSLionel Sambuc };
49f4a2713aSLionel Sambuc } // end anonymous namespace
50f4a2713aSLionel Sambuc
51f4a2713aSLionel Sambuc void
AddDerefSource(raw_ostream & os,SmallVectorImpl<SourceRange> & Ranges,const Expr * Ex,const ProgramState * state,const LocationContext * LCtx,bool loadedFrom)52f4a2713aSLionel Sambuc DereferenceChecker::AddDerefSource(raw_ostream &os,
53f4a2713aSLionel Sambuc SmallVectorImpl<SourceRange> &Ranges,
54f4a2713aSLionel Sambuc const Expr *Ex,
55f4a2713aSLionel Sambuc const ProgramState *state,
56f4a2713aSLionel Sambuc const LocationContext *LCtx,
57f4a2713aSLionel Sambuc bool loadedFrom) {
58f4a2713aSLionel Sambuc Ex = Ex->IgnoreParenLValueCasts();
59f4a2713aSLionel Sambuc switch (Ex->getStmtClass()) {
60f4a2713aSLionel Sambuc default:
61f4a2713aSLionel Sambuc break;
62f4a2713aSLionel Sambuc case Stmt::DeclRefExprClass: {
63f4a2713aSLionel Sambuc const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
64f4a2713aSLionel Sambuc if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
65f4a2713aSLionel Sambuc os << " (" << (loadedFrom ? "loaded from" : "from")
66f4a2713aSLionel Sambuc << " variable '" << VD->getName() << "')";
67f4a2713aSLionel Sambuc Ranges.push_back(DR->getSourceRange());
68f4a2713aSLionel Sambuc }
69f4a2713aSLionel Sambuc break;
70f4a2713aSLionel Sambuc }
71f4a2713aSLionel Sambuc case Stmt::MemberExprClass: {
72f4a2713aSLionel Sambuc const MemberExpr *ME = cast<MemberExpr>(Ex);
73f4a2713aSLionel Sambuc os << " (" << (loadedFrom ? "loaded from" : "via")
74f4a2713aSLionel Sambuc << " field '" << ME->getMemberNameInfo() << "')";
75f4a2713aSLionel Sambuc SourceLocation L = ME->getMemberLoc();
76f4a2713aSLionel Sambuc Ranges.push_back(SourceRange(L, L));
77f4a2713aSLionel Sambuc break;
78f4a2713aSLionel Sambuc }
79f4a2713aSLionel Sambuc case Stmt::ObjCIvarRefExprClass: {
80f4a2713aSLionel Sambuc const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
81f4a2713aSLionel Sambuc os << " (" << (loadedFrom ? "loaded from" : "via")
82f4a2713aSLionel Sambuc << " ivar '" << IV->getDecl()->getName() << "')";
83f4a2713aSLionel Sambuc SourceLocation L = IV->getLocation();
84f4a2713aSLionel Sambuc Ranges.push_back(SourceRange(L, L));
85f4a2713aSLionel Sambuc break;
86f4a2713aSLionel Sambuc }
87f4a2713aSLionel Sambuc }
88f4a2713aSLionel Sambuc }
89f4a2713aSLionel Sambuc
reportBug(ProgramStateRef State,const Stmt * S,CheckerContext & C,bool IsBind) const90f4a2713aSLionel Sambuc void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
91f4a2713aSLionel Sambuc CheckerContext &C, bool IsBind) const {
92f4a2713aSLionel Sambuc // Generate an error node.
93f4a2713aSLionel Sambuc ExplodedNode *N = C.generateSink(State);
94f4a2713aSLionel Sambuc if (!N)
95f4a2713aSLionel Sambuc return;
96f4a2713aSLionel Sambuc
97f4a2713aSLionel Sambuc // We know that 'location' cannot be non-null. This is what
98f4a2713aSLionel Sambuc // we call an "explicit" null dereference.
99f4a2713aSLionel Sambuc if (!BT_null)
100*0a6a1f1dSLionel Sambuc BT_null.reset(new BuiltinBug(this, "Dereference of null pointer"));
101f4a2713aSLionel Sambuc
102f4a2713aSLionel Sambuc SmallString<100> buf;
103f4a2713aSLionel Sambuc llvm::raw_svector_ostream os(buf);
104f4a2713aSLionel Sambuc
105f4a2713aSLionel Sambuc SmallVector<SourceRange, 2> Ranges;
106f4a2713aSLionel Sambuc
107f4a2713aSLionel Sambuc // Walk through lvalue casts to get the original expression
108f4a2713aSLionel Sambuc // that syntactically caused the load.
109f4a2713aSLionel Sambuc if (const Expr *expr = dyn_cast<Expr>(S))
110f4a2713aSLionel Sambuc S = expr->IgnoreParenLValueCasts();
111f4a2713aSLionel Sambuc
112f4a2713aSLionel Sambuc if (IsBind) {
113f4a2713aSLionel Sambuc if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
114f4a2713aSLionel Sambuc if (BO->isAssignmentOp())
115f4a2713aSLionel Sambuc S = BO->getRHS();
116f4a2713aSLionel Sambuc } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
117f4a2713aSLionel Sambuc assert(DS->isSingleDecl() && "We process decls one by one");
118f4a2713aSLionel Sambuc if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
119f4a2713aSLionel Sambuc if (const Expr *Init = VD->getAnyInitializer())
120f4a2713aSLionel Sambuc S = Init;
121f4a2713aSLionel Sambuc }
122f4a2713aSLionel Sambuc }
123f4a2713aSLionel Sambuc
124f4a2713aSLionel Sambuc switch (S->getStmtClass()) {
125f4a2713aSLionel Sambuc case Stmt::ArraySubscriptExprClass: {
126f4a2713aSLionel Sambuc os << "Array access";
127f4a2713aSLionel Sambuc const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
128f4a2713aSLionel Sambuc AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
129*0a6a1f1dSLionel Sambuc State.get(), N->getLocationContext());
130f4a2713aSLionel Sambuc os << " results in a null pointer dereference";
131f4a2713aSLionel Sambuc break;
132f4a2713aSLionel Sambuc }
133f4a2713aSLionel Sambuc case Stmt::UnaryOperatorClass: {
134f4a2713aSLionel Sambuc os << "Dereference of null pointer";
135f4a2713aSLionel Sambuc const UnaryOperator *U = cast<UnaryOperator>(S);
136f4a2713aSLionel Sambuc AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
137*0a6a1f1dSLionel Sambuc State.get(), N->getLocationContext(), true);
138f4a2713aSLionel Sambuc break;
139f4a2713aSLionel Sambuc }
140f4a2713aSLionel Sambuc case Stmt::MemberExprClass: {
141f4a2713aSLionel Sambuc const MemberExpr *M = cast<MemberExpr>(S);
142f4a2713aSLionel Sambuc if (M->isArrow() || bugreporter::isDeclRefExprToReference(M->getBase())) {
143f4a2713aSLionel Sambuc os << "Access to field '" << M->getMemberNameInfo()
144f4a2713aSLionel Sambuc << "' results in a dereference of a null pointer";
145f4a2713aSLionel Sambuc AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
146*0a6a1f1dSLionel Sambuc State.get(), N->getLocationContext(), true);
147f4a2713aSLionel Sambuc }
148f4a2713aSLionel Sambuc break;
149f4a2713aSLionel Sambuc }
150f4a2713aSLionel Sambuc case Stmt::ObjCIvarRefExprClass: {
151f4a2713aSLionel Sambuc const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
152f4a2713aSLionel Sambuc os << "Access to instance variable '" << *IV->getDecl()
153f4a2713aSLionel Sambuc << "' results in a dereference of a null pointer";
154f4a2713aSLionel Sambuc AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
155*0a6a1f1dSLionel Sambuc State.get(), N->getLocationContext(), true);
156f4a2713aSLionel Sambuc break;
157f4a2713aSLionel Sambuc }
158f4a2713aSLionel Sambuc default:
159f4a2713aSLionel Sambuc break;
160f4a2713aSLionel Sambuc }
161f4a2713aSLionel Sambuc
162f4a2713aSLionel Sambuc os.flush();
163f4a2713aSLionel Sambuc BugReport *report =
164f4a2713aSLionel Sambuc new BugReport(*BT_null,
165f4a2713aSLionel Sambuc buf.empty() ? BT_null->getDescription() : buf.str(),
166f4a2713aSLionel Sambuc N);
167f4a2713aSLionel Sambuc
168f4a2713aSLionel Sambuc bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report);
169f4a2713aSLionel Sambuc
170f4a2713aSLionel Sambuc for (SmallVectorImpl<SourceRange>::iterator
171f4a2713aSLionel Sambuc I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
172f4a2713aSLionel Sambuc report->addRange(*I);
173f4a2713aSLionel Sambuc
174f4a2713aSLionel Sambuc C.emitReport(report);
175f4a2713aSLionel Sambuc }
176f4a2713aSLionel Sambuc
checkLocation(SVal l,bool isLoad,const Stmt * S,CheckerContext & C) const177f4a2713aSLionel Sambuc void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
178f4a2713aSLionel Sambuc CheckerContext &C) const {
179f4a2713aSLionel Sambuc // Check for dereference of an undefined value.
180f4a2713aSLionel Sambuc if (l.isUndef()) {
181f4a2713aSLionel Sambuc if (ExplodedNode *N = C.generateSink()) {
182f4a2713aSLionel Sambuc if (!BT_undef)
183*0a6a1f1dSLionel Sambuc BT_undef.reset(
184*0a6a1f1dSLionel Sambuc new BuiltinBug(this, "Dereference of undefined pointer value"));
185f4a2713aSLionel Sambuc
186f4a2713aSLionel Sambuc BugReport *report =
187f4a2713aSLionel Sambuc new BugReport(*BT_undef, BT_undef->getDescription(), N);
188f4a2713aSLionel Sambuc bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S),
189f4a2713aSLionel Sambuc *report);
190f4a2713aSLionel Sambuc C.emitReport(report);
191f4a2713aSLionel Sambuc }
192f4a2713aSLionel Sambuc return;
193f4a2713aSLionel Sambuc }
194f4a2713aSLionel Sambuc
195f4a2713aSLionel Sambuc DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
196f4a2713aSLionel Sambuc
197f4a2713aSLionel Sambuc // Check for null dereferences.
198f4a2713aSLionel Sambuc if (!location.getAs<Loc>())
199f4a2713aSLionel Sambuc return;
200f4a2713aSLionel Sambuc
201f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
202f4a2713aSLionel Sambuc
203f4a2713aSLionel Sambuc ProgramStateRef notNullState, nullState;
204*0a6a1f1dSLionel Sambuc std::tie(notNullState, nullState) = state->assume(location);
205f4a2713aSLionel Sambuc
206f4a2713aSLionel Sambuc // The explicit NULL case.
207f4a2713aSLionel Sambuc if (nullState) {
208f4a2713aSLionel Sambuc if (!notNullState) {
209f4a2713aSLionel Sambuc reportBug(nullState, S, C);
210f4a2713aSLionel Sambuc return;
211f4a2713aSLionel Sambuc }
212f4a2713aSLionel Sambuc
213f4a2713aSLionel Sambuc // Otherwise, we have the case where the location could either be
214f4a2713aSLionel Sambuc // null or not-null. Record the error node as an "implicit" null
215f4a2713aSLionel Sambuc // dereference.
216f4a2713aSLionel Sambuc if (ExplodedNode *N = C.generateSink(nullState)) {
217f4a2713aSLionel Sambuc ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
218f4a2713aSLionel Sambuc dispatchEvent(event);
219f4a2713aSLionel Sambuc }
220f4a2713aSLionel Sambuc }
221f4a2713aSLionel Sambuc
222f4a2713aSLionel Sambuc // From this point forward, we know that the location is not null.
223f4a2713aSLionel Sambuc C.addTransition(notNullState);
224f4a2713aSLionel Sambuc }
225f4a2713aSLionel Sambuc
checkBind(SVal L,SVal V,const Stmt * S,CheckerContext & C) const226f4a2713aSLionel Sambuc void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
227f4a2713aSLionel Sambuc CheckerContext &C) const {
228f4a2713aSLionel Sambuc // If we're binding to a reference, check if the value is known to be null.
229f4a2713aSLionel Sambuc if (V.isUndef())
230f4a2713aSLionel Sambuc return;
231f4a2713aSLionel Sambuc
232f4a2713aSLionel Sambuc const MemRegion *MR = L.getAsRegion();
233f4a2713aSLionel Sambuc const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
234f4a2713aSLionel Sambuc if (!TVR)
235f4a2713aSLionel Sambuc return;
236f4a2713aSLionel Sambuc
237f4a2713aSLionel Sambuc if (!TVR->getValueType()->isReferenceType())
238f4a2713aSLionel Sambuc return;
239f4a2713aSLionel Sambuc
240f4a2713aSLionel Sambuc ProgramStateRef State = C.getState();
241f4a2713aSLionel Sambuc
242f4a2713aSLionel Sambuc ProgramStateRef StNonNull, StNull;
243*0a6a1f1dSLionel Sambuc std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
244f4a2713aSLionel Sambuc
245f4a2713aSLionel Sambuc if (StNull) {
246f4a2713aSLionel Sambuc if (!StNonNull) {
247f4a2713aSLionel Sambuc reportBug(StNull, S, C, /*isBind=*/true);
248f4a2713aSLionel Sambuc return;
249f4a2713aSLionel Sambuc }
250f4a2713aSLionel Sambuc
251f4a2713aSLionel Sambuc // At this point the value could be either null or non-null.
252f4a2713aSLionel Sambuc // Record this as an "implicit" null dereference.
253f4a2713aSLionel Sambuc if (ExplodedNode *N = C.generateSink(StNull)) {
254f4a2713aSLionel Sambuc ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
255f4a2713aSLionel Sambuc &C.getBugReporter() };
256f4a2713aSLionel Sambuc dispatchEvent(event);
257f4a2713aSLionel Sambuc }
258f4a2713aSLionel Sambuc }
259f4a2713aSLionel Sambuc
260f4a2713aSLionel Sambuc // Unlike a regular null dereference, initializing a reference with a
261f4a2713aSLionel Sambuc // dereferenced null pointer does not actually cause a runtime exception in
262f4a2713aSLionel Sambuc // Clang's implementation of references.
263f4a2713aSLionel Sambuc //
264f4a2713aSLionel Sambuc // int &r = *p; // safe??
265f4a2713aSLionel Sambuc // if (p != NULL) return; // uh-oh
266f4a2713aSLionel Sambuc // r = 5; // trap here
267f4a2713aSLionel Sambuc //
268f4a2713aSLionel Sambuc // The standard says this is invalid as soon as we try to create a "null
269f4a2713aSLionel Sambuc // reference" (there is no such thing), but turning this into an assumption
270f4a2713aSLionel Sambuc // that 'p' is never null will not match our actual runtime behavior.
271f4a2713aSLionel Sambuc // So we do not record this assumption, allowing us to warn on the last line
272f4a2713aSLionel Sambuc // of this example.
273f4a2713aSLionel Sambuc //
274f4a2713aSLionel Sambuc // We do need to add a transition because we may have generated a sink for
275f4a2713aSLionel Sambuc // the "implicit" null dereference.
276f4a2713aSLionel Sambuc C.addTransition(State, this);
277f4a2713aSLionel Sambuc }
278f4a2713aSLionel Sambuc
registerDereferenceChecker(CheckerManager & mgr)279f4a2713aSLionel Sambuc void ento::registerDereferenceChecker(CheckerManager &mgr) {
280f4a2713aSLionel Sambuc mgr.registerChecker<DereferenceChecker>();
281f4a2713aSLionel Sambuc }
282