clang  9.0.0svn
DereferenceChecker.cpp
Go to the documentation of this file.
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 
15 #include "clang/AST/ExprObjC.h"
16 #include "clang/AST/ExprOpenMP.h"
22 #include "llvm/ADT/SmallString.h"
23 #include "llvm/Support/raw_ostream.h"
24 
25 using namespace clang;
26 using namespace ento;
27 
28 namespace {
29 class DereferenceChecker
30  : public Checker< check::Location,
31  check::Bind,
32  EventDispatcher<ImplicitNullDerefEvent> > {
33  mutable std::unique_ptr<BuiltinBug> BT_null;
34  mutable std::unique_ptr<BuiltinBug> BT_undef;
35 
36  void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C) const;
37 
38 public:
39  void checkLocation(SVal location, bool isLoad, const Stmt* S,
40  CheckerContext &C) const;
41  void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
42 
43  static void AddDerefSource(raw_ostream &os,
45  const Expr *Ex, const ProgramState *state,
46  const LocationContext *LCtx,
47  bool loadedFrom = false);
48 };
49 } // end anonymous namespace
50 
51 void
52 DereferenceChecker::AddDerefSource(raw_ostream &os,
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 
90 static const Expr *getDereferenceExpr(const Stmt *S, bool 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(VD, Init) = parseAssignment(S);
102  if (VD && Init)
103  E = Init;
104  }
105  return E;
106 }
107 
108 static 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 
113 static 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 
119 void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
120  CheckerContext &C) const {
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<100> buf;
132  llvm::raw_svector_ostream os(buf);
133 
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 
188  I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
189  report->addRange(*I);
190 
191  C.emitReport(std::move(report));
192 }
193 
194 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
195  CheckerContext &C) const {
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 notNullState, nullState;
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 = {l, isLoad, N, &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 
246 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
247  CheckerContext &C) const {
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 StNonNull, StNull;
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=*/true, N,
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 
303 void ento::registerDereferenceChecker(CheckerManager &mgr) {
304  mgr.registerChecker<DereferenceChecker>();
305 }
306 
307 bool ento::shouldRegisterDereferenceChecker(const LangOptions &LO) {
308  return true;
309 }
Stmt - This represents one statement.
Definition: Stmt.h:66
Expr * getBase() const
Definition: Expr.h:2884
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Represents a variable declaration or definition.
Definition: Decl.h:812
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
LineState State
static bool suppressReport(const Expr *E)
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
i32 captured_struct **param SharedsTy A type which contains references the shared variables *param Shareds Context with the list of shared variables from the p *TaskFunction *param Data Additional data for task generation like final * state
bool hasAddressSpace() const
Definition: Type.h:352
bool isArrow() const
Definition: Expr.h:2991
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:2947
ObjCIvarDecl * getDecl()
Definition: ExprObjC.h:576
OpenMP 4.0 [2.4, Array Sections].
Definition: ExprOpenMP.h:44
std::pair< const clang::VarDecl *, const clang::Expr * > parseAssignment(const Stmt *S)
static bool isDeclRefExprToReference(const Expr *E)
This represents one expression.
Definition: Expr.h:108
#define V(N, I)
Definition: ASTContext.h:2907
QualType getType() const
Definition: Expr.h:137
UnaryOperator - This represents the unary-expression&#39;s (except sizeof and alignof), the postinc/postdec operators from postfix-expression, and various extensions.
Definition: Expr.h:2016
SourceLocation getMemberLoc() const
getMemberLoc - Return the location of the "member", in X->F, it is the location of &#39;F&#39;...
Definition: Expr.h:2996
ValueDecl * getDecl()
Definition: Expr.h:1217
Encodes a location in the source.
Expr * getSubExpr() const
Definition: Expr.h:2046
static const Expr * getDereferenceExpr(const Stmt *S, bool IsBind=false)
Dataflow Directional Tag Classes.
DeclarationNameInfo getMemberNameInfo() const
Retrieve the member declaration name info.
Definition: Expr.h:2984
StmtClass getStmtClass() const
Definition: Stmt.h:1087
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition: Expr.h:2432
SourceLocation getLocation() const
Definition: ExprObjC.h:589
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Skip past any parentheses and lvalue casts which might surround this expression until reaching a fixe...
Definition: Expr.cpp:2959
const Expr * getBase() const
Definition: ExprObjC.h:580
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:546
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:2807
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition: Type.h:6169
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:251
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:275
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1141
A trivial tuple used to represent a source range.
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:2938
Expr * getBase()
An array section can be written only as Base[LowerBound:Length].
Definition: ExprOpenMP.h:81