1e5dd7070Spatrick //===-- DereferenceChecker.cpp - Null dereference checker -----------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This defines NullDerefChecker, a builtin check in ExprEngine that performs
10e5dd7070Spatrick // checks for null pointers at loads and stores.
11e5dd7070Spatrick //
12e5dd7070Spatrick //===----------------------------------------------------------------------===//
13e5dd7070Spatrick
14e5dd7070Spatrick #include "clang/AST/ExprObjC.h"
15e5dd7070Spatrick #include "clang/AST/ExprOpenMP.h"
16*12c85518Srobert #include "clang/Basic/TargetInfo.h"
17*12c85518Srobert #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
20e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h"
21e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
23e5dd7070Spatrick #include "llvm/ADT/SmallString.h"
24e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
25e5dd7070Spatrick
26e5dd7070Spatrick using namespace clang;
27e5dd7070Spatrick using namespace ento;
28e5dd7070Spatrick
29e5dd7070Spatrick namespace {
30e5dd7070Spatrick class DereferenceChecker
31e5dd7070Spatrick : public Checker< check::Location,
32e5dd7070Spatrick check::Bind,
33e5dd7070Spatrick EventDispatcher<ImplicitNullDerefEvent> > {
34a9ac8606Spatrick enum DerefKind { NullPointer, UndefinedPointerValue };
35e5dd7070Spatrick
36a9ac8606Spatrick BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};
37a9ac8606Spatrick BugType BT_Undef{this, "Dereference of undefined pointer value",
38a9ac8606Spatrick categories::LogicError};
39a9ac8606Spatrick
40a9ac8606Spatrick void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,
41a9ac8606Spatrick CheckerContext &C) const;
42e5dd7070Spatrick
43*12c85518Srobert bool suppressReport(CheckerContext &C, const Expr *E) const;
44*12c85518Srobert
45e5dd7070Spatrick public:
46e5dd7070Spatrick void checkLocation(SVal location, bool isLoad, const Stmt* S,
47e5dd7070Spatrick CheckerContext &C) const;
48e5dd7070Spatrick void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
49e5dd7070Spatrick
50e5dd7070Spatrick static void AddDerefSource(raw_ostream &os,
51e5dd7070Spatrick SmallVectorImpl<SourceRange> &Ranges,
52e5dd7070Spatrick const Expr *Ex, const ProgramState *state,
53e5dd7070Spatrick const LocationContext *LCtx,
54e5dd7070Spatrick bool loadedFrom = false);
55*12c85518Srobert
56*12c85518Srobert bool SuppressAddressSpaces = false;
57e5dd7070Spatrick };
58e5dd7070Spatrick } // end anonymous namespace
59e5dd7070Spatrick
60e5dd7070Spatrick void
AddDerefSource(raw_ostream & os,SmallVectorImpl<SourceRange> & Ranges,const Expr * Ex,const ProgramState * state,const LocationContext * LCtx,bool loadedFrom)61e5dd7070Spatrick DereferenceChecker::AddDerefSource(raw_ostream &os,
62e5dd7070Spatrick SmallVectorImpl<SourceRange> &Ranges,
63e5dd7070Spatrick const Expr *Ex,
64e5dd7070Spatrick const ProgramState *state,
65e5dd7070Spatrick const LocationContext *LCtx,
66e5dd7070Spatrick bool loadedFrom) {
67e5dd7070Spatrick Ex = Ex->IgnoreParenLValueCasts();
68e5dd7070Spatrick switch (Ex->getStmtClass()) {
69e5dd7070Spatrick default:
70e5dd7070Spatrick break;
71e5dd7070Spatrick case Stmt::DeclRefExprClass: {
72e5dd7070Spatrick const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
73e5dd7070Spatrick if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
74e5dd7070Spatrick os << " (" << (loadedFrom ? "loaded from" : "from")
75e5dd7070Spatrick << " variable '" << VD->getName() << "')";
76e5dd7070Spatrick Ranges.push_back(DR->getSourceRange());
77e5dd7070Spatrick }
78e5dd7070Spatrick break;
79e5dd7070Spatrick }
80e5dd7070Spatrick case Stmt::MemberExprClass: {
81e5dd7070Spatrick const MemberExpr *ME = cast<MemberExpr>(Ex);
82e5dd7070Spatrick os << " (" << (loadedFrom ? "loaded from" : "via")
83e5dd7070Spatrick << " field '" << ME->getMemberNameInfo() << "')";
84e5dd7070Spatrick SourceLocation L = ME->getMemberLoc();
85e5dd7070Spatrick Ranges.push_back(SourceRange(L, L));
86e5dd7070Spatrick break;
87e5dd7070Spatrick }
88e5dd7070Spatrick case Stmt::ObjCIvarRefExprClass: {
89e5dd7070Spatrick const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
90e5dd7070Spatrick os << " (" << (loadedFrom ? "loaded from" : "via")
91e5dd7070Spatrick << " ivar '" << IV->getDecl()->getName() << "')";
92e5dd7070Spatrick SourceLocation L = IV->getLocation();
93e5dd7070Spatrick Ranges.push_back(SourceRange(L, L));
94e5dd7070Spatrick break;
95e5dd7070Spatrick }
96e5dd7070Spatrick }
97e5dd7070Spatrick }
98e5dd7070Spatrick
getDereferenceExpr(const Stmt * S,bool IsBind=false)99e5dd7070Spatrick static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
100e5dd7070Spatrick const Expr *E = nullptr;
101e5dd7070Spatrick
102e5dd7070Spatrick // Walk through lvalue casts to get the original expression
103e5dd7070Spatrick // that syntactically caused the load.
104e5dd7070Spatrick if (const Expr *expr = dyn_cast<Expr>(S))
105e5dd7070Spatrick E = expr->IgnoreParenLValueCasts();
106e5dd7070Spatrick
107e5dd7070Spatrick if (IsBind) {
108e5dd7070Spatrick const VarDecl *VD;
109e5dd7070Spatrick const Expr *Init;
110e5dd7070Spatrick std::tie(VD, Init) = parseAssignment(S);
111e5dd7070Spatrick if (VD && Init)
112e5dd7070Spatrick E = Init;
113e5dd7070Spatrick }
114e5dd7070Spatrick return E;
115e5dd7070Spatrick }
116e5dd7070Spatrick
suppressReport(CheckerContext & C,const Expr * E) const117*12c85518Srobert bool DereferenceChecker::suppressReport(CheckerContext &C,
118*12c85518Srobert const Expr *E) const {
119*12c85518Srobert // Do not report dereferences on memory that use address space #256, #257,
120*12c85518Srobert // and #258. Those address spaces are used when dereferencing address spaces
121*12c85518Srobert // relative to the GS, FS, and SS segments on x86/x86-64 targets.
122*12c85518Srobert // Dereferencing a null pointer in these address spaces is not defined
123*12c85518Srobert // as an error. All other null dereferences in other address spaces
124*12c85518Srobert // are defined as an error unless explicitly defined.
125*12c85518Srobert // See https://clang.llvm.org/docs/LanguageExtensions.html, the section
126*12c85518Srobert // "X86/X86-64 Language Extensions"
127*12c85518Srobert
128*12c85518Srobert QualType Ty = E->getType();
129*12c85518Srobert if (!Ty.hasAddressSpace())
130*12c85518Srobert return false;
131*12c85518Srobert if (SuppressAddressSpaces)
132*12c85518Srobert return true;
133*12c85518Srobert
134*12c85518Srobert const llvm::Triple::ArchType Arch =
135*12c85518Srobert C.getASTContext().getTargetInfo().getTriple().getArch();
136*12c85518Srobert
137*12c85518Srobert if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) {
138*12c85518Srobert switch (toTargetAddressSpace(E->getType().getAddressSpace())) {
139*12c85518Srobert case 256:
140*12c85518Srobert case 257:
141*12c85518Srobert case 258:
142*12c85518Srobert return true;
143*12c85518Srobert }
144*12c85518Srobert }
145*12c85518Srobert return false;
146e5dd7070Spatrick }
147e5dd7070Spatrick
isDeclRefExprToReference(const Expr * E)148e5dd7070Spatrick static bool isDeclRefExprToReference(const Expr *E) {
149e5dd7070Spatrick if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
150e5dd7070Spatrick return DRE->getDecl()->getType()->isReferenceType();
151e5dd7070Spatrick return false;
152e5dd7070Spatrick }
153e5dd7070Spatrick
reportBug(DerefKind K,ProgramStateRef State,const Stmt * S,CheckerContext & C) const154a9ac8606Spatrick void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
155a9ac8606Spatrick const Stmt *S, CheckerContext &C) const {
156a9ac8606Spatrick const BugType *BT = nullptr;
157a9ac8606Spatrick llvm::StringRef DerefStr1;
158a9ac8606Spatrick llvm::StringRef DerefStr2;
159a9ac8606Spatrick switch (K) {
160a9ac8606Spatrick case DerefKind::NullPointer:
161a9ac8606Spatrick BT = &BT_Null;
162a9ac8606Spatrick DerefStr1 = " results in a null pointer dereference";
163a9ac8606Spatrick DerefStr2 = " results in a dereference of a null pointer";
164a9ac8606Spatrick break;
165a9ac8606Spatrick case DerefKind::UndefinedPointerValue:
166a9ac8606Spatrick BT = &BT_Undef;
167a9ac8606Spatrick DerefStr1 = " results in an undefined pointer dereference";
168a9ac8606Spatrick DerefStr2 = " results in a dereference of an undefined pointer value";
169a9ac8606Spatrick break;
170a9ac8606Spatrick };
171a9ac8606Spatrick
172e5dd7070Spatrick // Generate an error node.
173e5dd7070Spatrick ExplodedNode *N = C.generateErrorNode(State);
174e5dd7070Spatrick if (!N)
175e5dd7070Spatrick return;
176e5dd7070Spatrick
177e5dd7070Spatrick SmallString<100> buf;
178e5dd7070Spatrick llvm::raw_svector_ostream os(buf);
179e5dd7070Spatrick
180e5dd7070Spatrick SmallVector<SourceRange, 2> Ranges;
181e5dd7070Spatrick
182e5dd7070Spatrick switch (S->getStmtClass()) {
183e5dd7070Spatrick case Stmt::ArraySubscriptExprClass: {
184e5dd7070Spatrick os << "Array access";
185e5dd7070Spatrick const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
186e5dd7070Spatrick AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
187e5dd7070Spatrick State.get(), N->getLocationContext());
188a9ac8606Spatrick os << DerefStr1;
189e5dd7070Spatrick break;
190e5dd7070Spatrick }
191e5dd7070Spatrick case Stmt::OMPArraySectionExprClass: {
192e5dd7070Spatrick os << "Array access";
193e5dd7070Spatrick const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S);
194e5dd7070Spatrick AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
195e5dd7070Spatrick State.get(), N->getLocationContext());
196a9ac8606Spatrick os << DerefStr1;
197e5dd7070Spatrick break;
198e5dd7070Spatrick }
199e5dd7070Spatrick case Stmt::UnaryOperatorClass: {
200a9ac8606Spatrick os << BT->getDescription();
201e5dd7070Spatrick const UnaryOperator *U = cast<UnaryOperator>(S);
202e5dd7070Spatrick AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
203e5dd7070Spatrick State.get(), N->getLocationContext(), true);
204e5dd7070Spatrick break;
205e5dd7070Spatrick }
206e5dd7070Spatrick case Stmt::MemberExprClass: {
207e5dd7070Spatrick const MemberExpr *M = cast<MemberExpr>(S);
208e5dd7070Spatrick if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
209a9ac8606Spatrick os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;
210e5dd7070Spatrick AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
211e5dd7070Spatrick State.get(), N->getLocationContext(), true);
212e5dd7070Spatrick }
213e5dd7070Spatrick break;
214e5dd7070Spatrick }
215e5dd7070Spatrick case Stmt::ObjCIvarRefExprClass: {
216e5dd7070Spatrick const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
217a9ac8606Spatrick os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;
218e5dd7070Spatrick AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
219e5dd7070Spatrick State.get(), N->getLocationContext(), true);
220e5dd7070Spatrick break;
221e5dd7070Spatrick }
222e5dd7070Spatrick default:
223e5dd7070Spatrick break;
224e5dd7070Spatrick }
225e5dd7070Spatrick
226e5dd7070Spatrick auto report = std::make_unique<PathSensitiveBugReport>(
227a9ac8606Spatrick *BT, buf.empty() ? BT->getDescription() : buf.str(), N);
228e5dd7070Spatrick
229e5dd7070Spatrick bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
230e5dd7070Spatrick
231e5dd7070Spatrick for (SmallVectorImpl<SourceRange>::iterator
232e5dd7070Spatrick I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
233e5dd7070Spatrick report->addRange(*I);
234e5dd7070Spatrick
235e5dd7070Spatrick C.emitReport(std::move(report));
236e5dd7070Spatrick }
237e5dd7070Spatrick
checkLocation(SVal l,bool isLoad,const Stmt * S,CheckerContext & C) const238e5dd7070Spatrick void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
239e5dd7070Spatrick CheckerContext &C) const {
240e5dd7070Spatrick // Check for dereference of an undefined value.
241e5dd7070Spatrick if (l.isUndef()) {
242a9ac8606Spatrick const Expr *DerefExpr = getDereferenceExpr(S);
243*12c85518Srobert if (!suppressReport(C, DerefExpr))
244a9ac8606Spatrick reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
245e5dd7070Spatrick return;
246e5dd7070Spatrick }
247e5dd7070Spatrick
248e5dd7070Spatrick DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
249e5dd7070Spatrick
250e5dd7070Spatrick // Check for null dereferences.
251*12c85518Srobert if (!isa<Loc>(location))
252e5dd7070Spatrick return;
253e5dd7070Spatrick
254e5dd7070Spatrick ProgramStateRef state = C.getState();
255e5dd7070Spatrick
256e5dd7070Spatrick ProgramStateRef notNullState, nullState;
257e5dd7070Spatrick std::tie(notNullState, nullState) = state->assume(location);
258e5dd7070Spatrick
259e5dd7070Spatrick if (nullState) {
260e5dd7070Spatrick if (!notNullState) {
261a9ac8606Spatrick // We know that 'location' can only be null. This is what
262a9ac8606Spatrick // we call an "explicit" null dereference.
263e5dd7070Spatrick const Expr *expr = getDereferenceExpr(S);
264*12c85518Srobert if (!suppressReport(C, expr)) {
265a9ac8606Spatrick reportBug(DerefKind::NullPointer, nullState, expr, C);
266e5dd7070Spatrick return;
267e5dd7070Spatrick }
268e5dd7070Spatrick }
269e5dd7070Spatrick
270e5dd7070Spatrick // Otherwise, we have the case where the location could either be
271e5dd7070Spatrick // null or not-null. Record the error node as an "implicit" null
272e5dd7070Spatrick // dereference.
273e5dd7070Spatrick if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
274e5dd7070Spatrick ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
275e5dd7070Spatrick /*IsDirectDereference=*/true};
276e5dd7070Spatrick dispatchEvent(event);
277e5dd7070Spatrick }
278e5dd7070Spatrick }
279e5dd7070Spatrick
280e5dd7070Spatrick // From this point forward, we know that the location is not null.
281e5dd7070Spatrick C.addTransition(notNullState);
282e5dd7070Spatrick }
283e5dd7070Spatrick
checkBind(SVal L,SVal V,const Stmt * S,CheckerContext & C) const284e5dd7070Spatrick void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
285e5dd7070Spatrick CheckerContext &C) const {
286e5dd7070Spatrick // If we're binding to a reference, check if the value is known to be null.
287e5dd7070Spatrick if (V.isUndef())
288e5dd7070Spatrick return;
289e5dd7070Spatrick
290e5dd7070Spatrick const MemRegion *MR = L.getAsRegion();
291e5dd7070Spatrick const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
292e5dd7070Spatrick if (!TVR)
293e5dd7070Spatrick return;
294e5dd7070Spatrick
295e5dd7070Spatrick if (!TVR->getValueType()->isReferenceType())
296e5dd7070Spatrick return;
297e5dd7070Spatrick
298e5dd7070Spatrick ProgramStateRef State = C.getState();
299e5dd7070Spatrick
300e5dd7070Spatrick ProgramStateRef StNonNull, StNull;
301e5dd7070Spatrick std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
302e5dd7070Spatrick
303e5dd7070Spatrick if (StNull) {
304e5dd7070Spatrick if (!StNonNull) {
305e5dd7070Spatrick const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
306*12c85518Srobert if (!suppressReport(C, expr)) {
307a9ac8606Spatrick reportBug(DerefKind::NullPointer, StNull, expr, C);
308e5dd7070Spatrick return;
309e5dd7070Spatrick }
310e5dd7070Spatrick }
311e5dd7070Spatrick
312e5dd7070Spatrick // At this point the value could be either null or non-null.
313e5dd7070Spatrick // Record this as an "implicit" null dereference.
314e5dd7070Spatrick if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
315e5dd7070Spatrick ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
316e5dd7070Spatrick &C.getBugReporter(),
317e5dd7070Spatrick /*IsDirectDereference=*/true};
318e5dd7070Spatrick dispatchEvent(event);
319e5dd7070Spatrick }
320e5dd7070Spatrick }
321e5dd7070Spatrick
322e5dd7070Spatrick // Unlike a regular null dereference, initializing a reference with a
323e5dd7070Spatrick // dereferenced null pointer does not actually cause a runtime exception in
324e5dd7070Spatrick // Clang's implementation of references.
325e5dd7070Spatrick //
326e5dd7070Spatrick // int &r = *p; // safe??
327e5dd7070Spatrick // if (p != NULL) return; // uh-oh
328e5dd7070Spatrick // r = 5; // trap here
329e5dd7070Spatrick //
330e5dd7070Spatrick // The standard says this is invalid as soon as we try to create a "null
331e5dd7070Spatrick // reference" (there is no such thing), but turning this into an assumption
332e5dd7070Spatrick // that 'p' is never null will not match our actual runtime behavior.
333e5dd7070Spatrick // So we do not record this assumption, allowing us to warn on the last line
334e5dd7070Spatrick // of this example.
335e5dd7070Spatrick //
336e5dd7070Spatrick // We do need to add a transition because we may have generated a sink for
337e5dd7070Spatrick // the "implicit" null dereference.
338e5dd7070Spatrick C.addTransition(State, this);
339e5dd7070Spatrick }
340e5dd7070Spatrick
registerDereferenceChecker(CheckerManager & mgr)341e5dd7070Spatrick void ento::registerDereferenceChecker(CheckerManager &mgr) {
342*12c85518Srobert auto *Chk = mgr.registerChecker<DereferenceChecker>();
343*12c85518Srobert Chk->SuppressAddressSpaces = mgr.getAnalyzerOptions().getCheckerBooleanOption(
344*12c85518Srobert mgr.getCurrentCheckerName(), "SuppressAddressSpaces");
345e5dd7070Spatrick }
346e5dd7070Spatrick
shouldRegisterDereferenceChecker(const CheckerManager & mgr)347ec727ea7Spatrick bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
348e5dd7070Spatrick return true;
349e5dd7070Spatrick }
350