xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp (revision 854cbbf4a8e7e98b7461eae2c2a37cfa767f791c)
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   void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,
37                  CheckerContext &C) const;
38 
39   bool suppressReport(CheckerContext &C, const Expr *E) const;
40 
41 public:
42   void checkLocation(SVal location, bool isLoad, const Stmt* S,
43                      CheckerContext &C) const;
44   void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
45 
46   static void AddDerefSource(raw_ostream &os,
47                              SmallVectorImpl<SourceRange> &Ranges,
48                              const Expr *Ex, const ProgramState *state,
49                              const LocationContext *LCtx,
50                              bool loadedFrom = false);
51 
52   bool SuppressAddressSpaces = false;
53 
54   bool CheckNullDereference = false;
55 
56   std::unique_ptr<BugType> BT_Null;
57   std::unique_ptr<BugType> BT_Undef;
58   std::unique_ptr<BugType> BT_Label;
59 };
60 } // end anonymous namespace
61 
62 void
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 
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 
119 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 
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 
156 void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
157                                    const Stmt *S, CheckerContext &C) const {
158   if (!CheckNullDereference) {
159     C.addSink();
160     return;
161   }
162 
163   const BugType *BT = nullptr;
164   llvm::StringRef DerefStr1;
165   llvm::StringRef DerefStr2;
166   switch (K) {
167   case DerefKind::NullPointer:
168     BT = BT_Null.get();
169     DerefStr1 = " results in a null pointer dereference";
170     DerefStr2 = " results in a dereference of a null pointer";
171     break;
172   case DerefKind::UndefinedPointerValue:
173     BT = BT_Undef.get();
174     DerefStr1 = " results in an undefined pointer dereference";
175     DerefStr2 = " results in a dereference of an undefined pointer value";
176     break;
177   case DerefKind::AddressOfLabel:
178     BT = BT_Label.get();
179     DerefStr1 = " results in an undefined pointer dereference";
180     DerefStr2 = " results in a dereference of an address of a label";
181     break;
182   };
183 
184   // Generate an error node.
185   ExplodedNode *N = C.generateErrorNode(State);
186   if (!N)
187     return;
188 
189   SmallString<100> buf;
190   llvm::raw_svector_ostream os(buf);
191 
192   SmallVector<SourceRange, 2> Ranges;
193 
194   switch (S->getStmtClass()) {
195   case Stmt::ArraySubscriptExprClass: {
196     os << "Array access";
197     const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
198     AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
199                    State.get(), N->getLocationContext());
200     os << DerefStr1;
201     break;
202   }
203   case Stmt::ArraySectionExprClass: {
204     os << "Array access";
205     const ArraySectionExpr *AE = cast<ArraySectionExpr>(S);
206     AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
207                    State.get(), N->getLocationContext());
208     os << DerefStr1;
209     break;
210   }
211   case Stmt::UnaryOperatorClass: {
212     os << BT->getDescription();
213     const UnaryOperator *U = cast<UnaryOperator>(S);
214     AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
215                    State.get(), N->getLocationContext(), true);
216     break;
217   }
218   case Stmt::MemberExprClass: {
219     const MemberExpr *M = cast<MemberExpr>(S);
220     if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
221       os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;
222       AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
223                      State.get(), N->getLocationContext(), true);
224     }
225     break;
226   }
227   case Stmt::ObjCIvarRefExprClass: {
228     const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
229     os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;
230     AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
231                    State.get(), N->getLocationContext(), true);
232     break;
233   }
234   default:
235     break;
236   }
237 
238   auto report = std::make_unique<PathSensitiveBugReport>(
239       *BT, buf.empty() ? BT->getDescription() : buf.str(), N);
240 
241   bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
242 
243   for (SmallVectorImpl<SourceRange>::iterator
244        I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
245     report->addRange(*I);
246 
247   C.emitReport(std::move(report));
248 }
249 
250 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
251                                        CheckerContext &C) const {
252   // Check for dereference of an undefined value.
253   if (l.isUndef()) {
254     const Expr *DerefExpr = getDereferenceExpr(S);
255     if (!suppressReport(C, DerefExpr))
256       reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
257     return;
258   }
259 
260   DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
261 
262   // Check for null dereferences.
263   if (!isa<Loc>(location))
264     return;
265 
266   ProgramStateRef state = C.getState();
267 
268   ProgramStateRef notNullState, nullState;
269   std::tie(notNullState, nullState) = state->assume(location);
270 
271   if (nullState) {
272     if (!notNullState) {
273       // We know that 'location' can only be null.  This is what
274       // we call an "explicit" null dereference.
275       const Expr *expr = getDereferenceExpr(S);
276       if (!suppressReport(C, expr)) {
277         reportBug(DerefKind::NullPointer, nullState, expr, C);
278         return;
279       }
280     }
281 
282     // Otherwise, we have the case where the location could either be
283     // null or not-null.  Record the error node as an "implicit" null
284     // dereference.
285     if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
286       ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
287                                       /*IsDirectDereference=*/true};
288       dispatchEvent(event);
289     }
290   }
291 
292   // From this point forward, we know that the location is not null.
293   C.addTransition(notNullState);
294 }
295 
296 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
297                                    CheckerContext &C) const {
298   // If we're binding to a reference, check if the value is known to be null.
299   if (V.isUndef())
300     return;
301 
302   // One should never write to label addresses.
303   if (auto Label = L.getAs<loc::GotoLabel>()) {
304     reportBug(DerefKind::AddressOfLabel, C.getState(), S, C);
305     return;
306   }
307 
308   const MemRegion *MR = L.getAsRegion();
309   const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
310   if (!TVR)
311     return;
312 
313   if (!TVR->getValueType()->isReferenceType())
314     return;
315 
316   ProgramStateRef State = C.getState();
317 
318   ProgramStateRef StNonNull, StNull;
319   std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
320 
321   if (StNull) {
322     if (!StNonNull) {
323       const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
324       if (!suppressReport(C, expr)) {
325         reportBug(DerefKind::NullPointer, StNull, expr, C);
326         return;
327       }
328     }
329 
330     // At this point the value could be either null or non-null.
331     // Record this as an "implicit" null dereference.
332     if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
333       ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
334                                       &C.getBugReporter(),
335                                       /*IsDirectDereference=*/true};
336       dispatchEvent(event);
337     }
338   }
339 
340   // Unlike a regular null dereference, initializing a reference with a
341   // dereferenced null pointer does not actually cause a runtime exception in
342   // Clang's implementation of references.
343   //
344   //   int &r = *p; // safe??
345   //   if (p != NULL) return; // uh-oh
346   //   r = 5; // trap here
347   //
348   // The standard says this is invalid as soon as we try to create a "null
349   // reference" (there is no such thing), but turning this into an assumption
350   // that 'p' is never null will not match our actual runtime behavior.
351   // So we do not record this assumption, allowing us to warn on the last line
352   // of this example.
353   //
354   // We do need to add a transition because we may have generated a sink for
355   // the "implicit" null dereference.
356   C.addTransition(State, this);
357 }
358 
359 void ento::registerDereferenceModeling(CheckerManager &Mgr) {
360   Mgr.registerChecker<DereferenceChecker>();
361 }
362 
363 bool ento::shouldRegisterDereferenceModeling(const CheckerManager &) {
364   return true;
365 }
366 
367 void ento::registerNullDereferenceChecker(CheckerManager &Mgr) {
368   auto *Chk = Mgr.getChecker<DereferenceChecker>();
369   Chk->CheckNullDereference = true;
370   Chk->SuppressAddressSpaces = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
371       Mgr.getCurrentCheckerName(), "SuppressAddressSpaces");
372   Chk->BT_Null.reset(new BugType(Mgr.getCurrentCheckerName(),
373                                  "Dereference of null pointer",
374                                  categories::LogicError));
375   Chk->BT_Undef.reset(new BugType(Mgr.getCurrentCheckerName(),
376                                   "Dereference of undefined pointer value",
377                                   categories::LogicError));
378   Chk->BT_Label.reset(new BugType(Mgr.getCurrentCheckerName(),
379                                   "Dereference of the address of a label",
380                                   categories::LogicError));
381 }
382 
383 bool ento::shouldRegisterNullDereferenceChecker(const CheckerManager &) {
384   return true;
385 }
386