clang  14.0.0git
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  enum DerefKind { NullPointer, UndefinedPointerValue };
34 
35  BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};
36  BugType BT_Undef{this, "Dereference of undefined pointer value",
38 
39  void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,
40  CheckerContext &C) const;
41 
42 public:
43  void checkLocation(SVal location, bool isLoad, const Stmt* S,
44  CheckerContext &C) const;
45  void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
46 
47  static void AddDerefSource(raw_ostream &os,
49  const Expr *Ex, const ProgramState *state,
50  const LocationContext *LCtx,
51  bool loadedFrom = false);
52 };
53 } // end anonymous namespace
54 
55 void
56 DereferenceChecker::AddDerefSource(raw_ostream &os,
58  const Expr *Ex,
59  const ProgramState *state,
60  const LocationContext *LCtx,
61  bool loadedFrom) {
62  Ex = Ex->IgnoreParenLValueCasts();
63  switch (Ex->getStmtClass()) {
64  default:
65  break;
66  case Stmt::DeclRefExprClass: {
67  const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
68  if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
69  os << " (" << (loadedFrom ? "loaded from" : "from")
70  << " variable '" << VD->getName() << "')";
71  Ranges.push_back(DR->getSourceRange());
72  }
73  break;
74  }
75  case Stmt::MemberExprClass: {
76  const MemberExpr *ME = cast<MemberExpr>(Ex);
77  os << " (" << (loadedFrom ? "loaded from" : "via")
78  << " field '" << ME->getMemberNameInfo() << "')";
79  SourceLocation L = ME->getMemberLoc();
80  Ranges.push_back(SourceRange(L, L));
81  break;
82  }
83  case Stmt::ObjCIvarRefExprClass: {
84  const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
85  os << " (" << (loadedFrom ? "loaded from" : "via")
86  << " ivar '" << IV->getDecl()->getName() << "')";
87  SourceLocation L = IV->getLocation();
88  Ranges.push_back(SourceRange(L, L));
89  break;
90  }
91  }
92 }
93 
94 static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
95  const Expr *E = nullptr;
96 
97  // Walk through lvalue casts to get the original expression
98  // that syntactically caused the load.
99  if (const Expr *expr = dyn_cast<Expr>(S))
100  E = expr->IgnoreParenLValueCasts();
101 
102  if (IsBind) {
103  const VarDecl *VD;
104  const Expr *Init;
105  std::tie(VD, Init) = parseAssignment(S);
106  if (VD && Init)
107  E = Init;
108  }
109  return E;
110 }
111 
112 static bool suppressReport(const Expr *E) {
113  // Do not report dereferences on memory in non-default address spaces.
114  return E->getType().hasAddressSpace();
115 }
116 
117 static bool isDeclRefExprToReference(const Expr *E) {
118  if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
119  return DRE->getDecl()->getType()->isReferenceType();
120  return false;
121 }
122 
123 void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
124  const Stmt *S, CheckerContext &C) const {
125  const BugType *BT = nullptr;
126  llvm::StringRef DerefStr1;
127  llvm::StringRef DerefStr2;
128  switch (K) {
129  case DerefKind::NullPointer:
130  BT = &BT_Null;
131  DerefStr1 = " results in a null pointer dereference";
132  DerefStr2 = " results in a dereference of a null pointer";
133  break;
134  case DerefKind::UndefinedPointerValue:
135  BT = &BT_Undef;
136  DerefStr1 = " results in an undefined pointer dereference";
137  DerefStr2 = " results in a dereference of an undefined pointer value";
138  break;
139  };
140 
141  // Generate an error node.
142  ExplodedNode *N = C.generateErrorNode(State);
143  if (!N)
144  return;
145 
146  SmallString<100> buf;
147  llvm::raw_svector_ostream os(buf);
148 
150 
151  switch (S->getStmtClass()) {
152  case Stmt::ArraySubscriptExprClass: {
153  os << "Array access";
154  const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
155  AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
156  State.get(), N->getLocationContext());
157  os << DerefStr1;
158  break;
159  }
160  case Stmt::OMPArraySectionExprClass: {
161  os << "Array access";
162  const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S);
163  AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
164  State.get(), N->getLocationContext());
165  os << DerefStr1;
166  break;
167  }
168  case Stmt::UnaryOperatorClass: {
169  os << BT->getDescription();
170  const UnaryOperator *U = cast<UnaryOperator>(S);
171  AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
172  State.get(), N->getLocationContext(), true);
173  break;
174  }
175  case Stmt::MemberExprClass: {
176  const MemberExpr *M = cast<MemberExpr>(S);
177  if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
178  os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;
179  AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
180  State.get(), N->getLocationContext(), true);
181  }
182  break;
183  }
184  case Stmt::ObjCIvarRefExprClass: {
185  const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
186  os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;
187  AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
188  State.get(), N->getLocationContext(), true);
189  break;
190  }
191  default:
192  break;
193  }
194 
195  auto report = std::make_unique<PathSensitiveBugReport>(
196  *BT, buf.empty() ? BT->getDescription() : buf.str(), N);
197 
199 
201  I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
202  report->addRange(*I);
203 
204  C.emitReport(std::move(report));
205 }
206 
207 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
208  CheckerContext &C) const {
209  // Check for dereference of an undefined value.
210  if (l.isUndef()) {
211  const Expr *DerefExpr = getDereferenceExpr(S);
212  if (!suppressReport(DerefExpr))
213  reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
214  return;
215  }
216 
217  DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
218 
219  // Check for null dereferences.
220  if (!location.getAs<Loc>())
221  return;
222 
223  ProgramStateRef state = C.getState();
224 
225  ProgramStateRef notNullState, nullState;
226  std::tie(notNullState, nullState) = state->assume(location);
227 
228  if (nullState) {
229  if (!notNullState) {
230  // We know that 'location' can only be null. This is what
231  // we call an "explicit" null dereference.
232  const Expr *expr = getDereferenceExpr(S);
233  if (!suppressReport(expr)) {
234  reportBug(DerefKind::NullPointer, nullState, expr, C);
235  return;
236  }
237  }
238 
239  // Otherwise, we have the case where the location could either be
240  // null or not-null. Record the error node as an "implicit" null
241  // dereference.
242  if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
243  ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
244  /*IsDirectDereference=*/true};
245  dispatchEvent(event);
246  }
247  }
248 
249  // From this point forward, we know that the location is not null.
250  C.addTransition(notNullState);
251 }
252 
253 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
254  CheckerContext &C) const {
255  // If we're binding to a reference, check if the value is known to be null.
256  if (V.isUndef())
257  return;
258 
259  const MemRegion *MR = L.getAsRegion();
260  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
261  if (!TVR)
262  return;
263 
264  if (!TVR->getValueType()->isReferenceType())
265  return;
266 
267  ProgramStateRef State = C.getState();
268 
269  ProgramStateRef StNonNull, StNull;
270  std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
271 
272  if (StNull) {
273  if (!StNonNull) {
274  const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
275  if (!suppressReport(expr)) {
276  reportBug(DerefKind::NullPointer, StNull, expr, C);
277  return;
278  }
279  }
280 
281  // At this point the value could be either null or non-null.
282  // Record this as an "implicit" null dereference.
283  if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
284  ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
285  &C.getBugReporter(),
286  /*IsDirectDereference=*/true};
287  dispatchEvent(event);
288  }
289  }
290 
291  // Unlike a regular null dereference, initializing a reference with a
292  // dereferenced null pointer does not actually cause a runtime exception in
293  // Clang's implementation of references.
294  //
295  // int &r = *p; // safe??
296  // if (p != NULL) return; // uh-oh
297  // r = 5; // trap here
298  //
299  // The standard says this is invalid as soon as we try to create a "null
300  // reference" (there is no such thing), but turning this into an assumption
301  // that 'p' is never null will not match our actual runtime behavior.
302  // So we do not record this assumption, allowing us to warn on the last line
303  // of this example.
304  //
305  // We do need to add a transition because we may have generated a sink for
306  // the "implicit" null dereference.
307  C.addTransition(State, this);
308 }
309 
310 void ento::registerDereferenceChecker(CheckerManager &mgr) {
311  mgr.registerChecker<DereferenceChecker>();
312 }
313 
314 bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
315  return true;
316 }
clang::LocationContext
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
Definition: AnalysisDeclContext.h:215
clang::SourceRange
A trivial tuple used to represent a source range.
Definition: SourceLocation.h:212
llvm::SmallVector
Definition: LLVM.h:38
clang::SourceLocation
Encodes a location in the source.
Definition: SourceLocation.h:88
clang::Stmt::getSourceRange
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:324
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:54
clang::UnaryOperator
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition: Expr.h:2157
ExprOpenMP.h
clang::MemberExpr::getMemberNameInfo
DeclarationNameInfo getMemberNameInfo() const
Retrieve the member declaration name info.
Definition: Expr.h:3339
clang::ObjCIvarRefExpr::getDecl
ObjCIvarDecl * getDecl()
Definition: ExprObjC.h:576
clang::ento::parseAssignment
std::pair< const clang::VarDecl *, const clang::Expr * > parseAssignment(const Stmt *S)
Definition: CheckerHelpers.cpp:81
U
V
#define V(N, I)
Definition: ASTContext.h:3121
clang::ast_matchers::expr
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
Definition: ASTMatchersInternal.cpp:889
BuiltinCheckerRegistration.h
clang::MemberExpr::isArrow
bool isArrow() const
Definition: Expr.h:3346
CheckerManager.h
clang::DeclRefExpr::getDecl
ValueDecl * getDecl()
Definition: Expr.h:1289
clang::QualType::hasAddressSpace
bool hasAddressSpace() const
Check if this type has any address space qualifier.
Definition: Type.h:6540
llvm::SmallString
Definition: LLVM.h:37
clang::VarDecl
Represents a variable declaration or definition.
Definition: Decl.h:876
clang::MemberExpr::getMemberLoc
SourceLocation getMemberLoc() const
getMemberLoc - Return the location of the "member", in X->F, it is the location of 'F'.
Definition: Expr.h:3351
clang::ObjCIvarRefExpr::getLocation
SourceLocation getLocation() const
Definition: ExprObjC.h:589
getDereferenceExpr
static const Expr * getDereferenceExpr(const Stmt *S, bool IsBind=false)
Definition: DereferenceChecker.cpp:94
ExprObjC.h
clang::Expr::IgnoreParenLValueCasts
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Skip past any parentheses and lvalue casts which might surround this expression until reaching a fixe...
Definition: Expr.cpp:2936
state
and static some checkers Checker The latter are built on top of the former via the Checker and CheckerVisitor and attempts to isolate them from much of the gore of the internal analysis the analyzer is basically a source code simulator that traces out possible paths of execution The state of the and the combination of state and program point is a node in an exploded which has the entry program point and initial state
Definition: README.txt:30
clang::ento::bugreporter::trackExpressionValue
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
clang::Expr::IgnoreParenCasts
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:2924
clang::MemberExpr::getBase
Expr * getBase() const
Definition: Expr.h:3239
clang::ento::bugreporter::getDerefExpr
const Expr * getDerefExpr(const Stmt *S)
clang::Stmt::getStmtClass
StmtClass getStmtClass() const
Definition: Stmt.h:1163
BugType.h
clang::OMPArraySectionExpr::getBase
Expr * getBase()
An array section can be written only as Base[LowerBound:Length].
Definition: ExprOpenMP.h:85
ProgramState
and static some checkers Checker The latter are built on top of the former via the Checker and CheckerVisitor and attempts to isolate them from much of the gore of the internal analysis the analyzer is basically a source code simulator that traces out possible paths of execution The state of the and the combination of state and program point is a node in an exploded which has the entry program point and initial and then simulate transitions by analyzing individual expressions The analysis of an expression can cause the state to resulting in a new node in the ExplodedGraph with an updated program point and an updated state A bug is found by hitting a node that satisfies some bug condition(basically a violation of a checking invariant). The analyzer traces out multiple paths by reasoning about branches and then bifurcating the state it can contain cycles as paths loop back onto each other and cache out ProgramState and ExplodedNodes are basically immutable once created Once one creates a ProgramState
Definition: README.txt:34
clang::ArraySubscriptExpr
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition: Expr.h:2639
suppressReport
static bool suppressReport(const Expr *E)
Definition: DereferenceChecker.cpp:112
State
LineState State
Definition: UnwrappedLineFormatter.cpp:986
isDeclRefExprToReference
static bool isDeclRefExprToReference(const Expr *E)
Definition: DereferenceChecker.cpp:117
CheckerContext.h
Checker.h
clang::ento::categories::LogicError
const char *const LogicError
Definition: CommonBugCategories.cpp:17
clang::OMPArraySectionExpr
OpenMP 5.0 [2.1.5, Array Sections].
Definition: ExprOpenMP.h:56
clang
Definition: CalledOnceCheck.h:17
clang::Stmt
Stmt - This represents one statement.
Definition: Stmt.h:69
CheckerHelpers.h
clang::Expr::getType
QualType getType() const
Definition: Expr.h:141
clang::ArraySubscriptExpr::getBase
Expr * getBase()
Definition: Expr.h:2676
clang::MemberExpr
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:3162
clang::ObjCIvarRefExpr::getBase
const Expr * getBase() const
Definition: ExprObjC.h:580
llvm::SmallVectorImpl
Definition: LLVM.h:39
clang::Expr
This represents one expression.
Definition: Expr.h:109
clang::DeclRefExpr
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1217
clang::ObjCIvarRefExpr
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:548
clang::NamedDecl::getName
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:276