Clang Project

clang_source_code/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
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/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15#include "clang/AST/ExprObjC.h"
16#include "clang/AST/ExprOpenMP.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/ADT/SmallString.h"
23#include "llvm/Support/raw_ostream.h"
24
25using namespace clang;
26using namespace ento;
27
28namespace {
29class DereferenceChecker
30    : public Checker< check::Location,
31                      check::Bind,
32                      EventDispatcher<ImplicitNullDerefEvent> > {
33  mutable std::unique_ptr<BuiltinBugBT_null;
34  mutable std::unique_ptr<BuiltinBugBT_undef;
35
36  void reportBug(ProgramStateRef Stateconst Stmt *SCheckerContext &Cconst;
37
38public:
39  void checkLocation(SVal locationbool isLoadconst StmtS,
40                     CheckerContext &Cconst;
41  void checkBind(SVal LSVal Vconst Stmt *SCheckerContext &Cconst;
42
43  static void AddDerefSource(raw_ostream &os,
44                             SmallVectorImpl<SourceRange> &Ranges,
45                             const Expr *Exconst ProgramState *state,
46                             const LocationContext *LCtx,
47                             bool loadedFrom = false);
48};
49// end anonymous namespace
50
51void
52DereferenceChecker::AddDerefSource(raw_ostream &os,
53                                   SmallVectorImpl<SourceRange> &Ranges,
54                                   const Expr *Ex,
55                                   const ProgramState *state,
56                                   const LocationContext *LCtx,
57                                   bool loadedFrom) {
58  Ex = Ex->IgnoreParenLValueCasts();
59  switch (Ex->getStmtClass()) {
60    default:
61      break;
62    case Stmt::DeclRefExprClass: {
63      const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
64      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
65        os << " (" << (loadedFrom ? "loaded from" : "from")
66           << " variable '" <<  VD->getName() << "')";
67        Ranges.push_back(DR->getSourceRange());
68      }
69      break;
70    }
71    case Stmt::MemberExprClass: {
72      const MemberExpr *ME = cast<MemberExpr>(Ex);
73      os << " (" << (loadedFrom ? "loaded from" : "via")
74         << " field '" << ME->getMemberNameInfo() << "')";
75      SourceLocation L = ME->getMemberLoc();
76      Ranges.push_back(SourceRange(L, L));
77      break;
78    }
79    case Stmt::ObjCIvarRefExprClass: {
80      const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
81      os << " (" << (loadedFrom ? "loaded from" : "via")
82         << " ivar '" << IV->getDecl()->getName() << "')";
83      SourceLocation L = IV->getLocation();
84      Ranges.push_back(SourceRange(L, L));
85      break;
86    }
87  }
88}
89
90static const Expr *getDereferenceExpr(const Stmt *Sbool IsBind=false){
91  const Expr *E = nullptr;
92
93  // Walk through lvalue casts to get the original expression
94  // that syntactically caused the load.
95  if (const Expr *expr = dyn_cast<Expr>(S))
96    E = expr->IgnoreParenLValueCasts();
97
98  if (IsBind) {
99    const VarDecl *VD;
100    const Expr *Init;
101    std::tie(VDInit) = parseAssignment(S);
102    if (VD && Init)
103      E = Init;
104  }
105  return E;
106}
107
108static bool suppressReport(const Expr *E) {
109  // Do not report dereferences on memory in non-default address spaces.
110  return E->getType().getQualifiers().hasAddressSpace();
111}
112
113static bool isDeclRefExprToReference(const Expr *E) {
114  if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
115    return DRE->getDecl()->getType()->isReferenceType();
116  return false;
117}
118
119void DereferenceChecker::reportBug(ProgramStateRef Stateconst Stmt *S,
120                                   CheckerContext &Cconst {
121  // Generate an error node.
122  ExplodedNode *N = C.generateErrorNode(State);
123  if (!N)
124    return;
125
126  // We know that 'location' cannot be non-null.  This is what
127  // we call an "explicit" null dereference.
128  if (!BT_null)
129    BT_null.reset(new BuiltinBug(this"Dereference of null pointer"));
130
131  SmallString<100buf;
132  llvm::raw_svector_ostream os(buf);
133
134  SmallVector<SourceRange2Ranges;
135
136  switch (S->getStmtClass()) {
137  case Stmt::ArraySubscriptExprClass: {
138    os << "Array access";
139    const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
140    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
141                   State.get(), N->getLocationContext());
142    os << " results in a null pointer dereference";
143    break;
144  }
145  case Stmt::OMPArraySectionExprClass: {
146    os << "Array access";
147    const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S);
148    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
149                   State.get(), N->getLocationContext());
150    os << " results in a null pointer dereference";
151    break;
152  }
153  case Stmt::UnaryOperatorClass: {
154    os << "Dereference of null pointer";
155    const UnaryOperator *U = cast<UnaryOperator>(S);
156    AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
157                   State.get(), N->getLocationContext(), true);
158    break;
159  }
160  case Stmt::MemberExprClass: {
161    const MemberExpr *M = cast<MemberExpr>(S);
162    if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
163      os << "Access to field '" << M->getMemberNameInfo()
164         << "' results in a dereference of a null pointer";
165      AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
166                     State.get(), N->getLocationContext(), true);
167    }
168    break;
169  }
170  case Stmt::ObjCIvarRefExprClass: {
171    const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
172    os << "Access to instance variable '" << *IV->getDecl()
173       << "' results in a dereference of a null pointer";
174    AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
175                   State.get(), N->getLocationContext(), true);
176    break;
177  }
178  default:
179    break;
180  }
181
182  auto report = llvm::make_unique<BugReport>(
183      *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N);
184
185  bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
186
187  for (SmallVectorImpl<SourceRange>::iterator
188       I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
189    report->addRange(*I);
190
191  C.emitReport(std::move(report));
192}
193
194void DereferenceChecker::checkLocation(SVal lbool isLoadconst StmtS,
195                                       CheckerContext &Cconst {
196  // Check for dereference of an undefined value.
197  if (l.isUndef()) {
198    if (ExplodedNode *N = C.generateErrorNode()) {
199      if (!BT_undef)
200        BT_undef.reset(
201            new BuiltinBug(this"Dereference of undefined pointer value"));
202
203      auto report =
204          llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N);
205      bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
206      C.emitReport(std::move(report));
207    }
208    return;
209  }
210
211  DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
212
213  // Check for null dereferences.
214  if (!location.getAs<Loc>())
215    return;
216
217  ProgramStateRef state = C.getState();
218
219  ProgramStateRef notNullStatenullState;
220  std::tie(notNullState, nullState) = state->assume(location);
221
222  // The explicit NULL case.
223  if (nullState) {
224    if (!notNullState) {
225      const Expr *expr = getDereferenceExpr(S);
226      if (!suppressReport(expr)) {
227        reportBug(nullState, expr, C);
228        return;
229      }
230    }
231
232    // Otherwise, we have the case where the location could either be
233    // null or not-null.  Record the error node as an "implicit" null
234    // dereference.
235    if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
236      ImplicitNullDerefEvent event = {lisLoadN, &C.getBugReporter(),
237                                      /*IsDirectDereference=*/true};
238      dispatchEvent(event);
239    }
240  }
241
242  // From this point forward, we know that the location is not null.
243  C.addTransition(notNullState);
244}
245
246void DereferenceChecker::checkBind(SVal LSVal Vconst Stmt *S,
247                                   CheckerContext &Cconst {
248  // If we're binding to a reference, check if the value is known to be null.
249  if (V.isUndef())
250    return;
251
252  const MemRegion *MR = L.getAsRegion();
253  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
254  if (!TVR)
255    return;
256
257  if (!TVR->getValueType()->isReferenceType())
258    return;
259
260  ProgramStateRef State = C.getState();
261
262  ProgramStateRef StNonNullStNull;
263  std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
264
265  if (StNull) {
266    if (!StNonNull) {
267      const Expr *expr = getDereferenceExpr(S/*IsBind=*/true);
268      if (!suppressReport(expr)) {
269        reportBug(StNull, expr, C);
270        return;
271      }
272    }
273
274    // At this point the value could be either null or non-null.
275    // Record this as an "implicit" null dereference.
276    if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
277      ImplicitNullDerefEvent event = {V/*isLoad=*/trueN,
278                                      &C.getBugReporter(),
279                                      /*IsDirectDereference=*/true};
280      dispatchEvent(event);
281    }
282  }
283
284  // Unlike a regular null dereference, initializing a reference with a
285  // dereferenced null pointer does not actually cause a runtime exception in
286  // Clang's implementation of references.
287  //
288  //   int &r = *p; // safe??
289  //   if (p != NULL) return; // uh-oh
290  //   r = 5; // trap here
291  //
292  // The standard says this is invalid as soon as we try to create a "null
293  // reference" (there is no such thing), but turning this into an assumption
294  // that 'p' is never null will not match our actual runtime behavior.
295  // So we do not record this assumption, allowing us to warn on the last line
296  // of this example.
297  //
298  // We do need to add a transition because we may have generated a sink for
299  // the "implicit" null dereference.
300  C.addTransition(State, this);
301}
302
303void ento::registerDereferenceChecker(CheckerManager &mgr) {
304  mgr.registerChecker<DereferenceChecker>();
305}
306
307bool ento::shouldRegisterDereferenceChecker(const LangOptions &LO) {
308  return true;
309}
310