xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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