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
26using namespace clang;
27using namespace ento;
28
29namespace {
30class 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
45public:
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
60void
61DereferenceChecker::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
99static 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
117bool 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
148static 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
154void 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
238void 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
284void 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
341void ento::registerDereferenceChecker(CheckerManager &mgr) {
342  auto *Chk = mgr.registerChecker<DereferenceChecker>();
343  Chk->SuppressAddressSpaces = mgr.getAnalyzerOptions().getCheckerBooleanOption(
344      mgr.getCurrentCheckerName(), "SuppressAddressSpaces");
345}
346
347bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
348  return true;
349}
350