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