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