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