clang  15.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 
14 #include "clang/AST/ExprObjC.h"
15 #include "clang/AST/ExprOpenMP.h"
16 #include "clang/Basic/TargetInfo.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 };
35 
36  BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};
37  BugType BT_Undef{this, "Dereference of undefined pointer value",
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 
45 public:
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,
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 
60 void
61 DereferenceChecker::AddDerefSource(raw_ostream &os,
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 
99 static 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 
117 bool 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 
148 static 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 
154 void 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 
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 
230 
232  I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
233  report->addRange(*I);
234 
235  C.emitReport(std::move(report));
236 }
237 
238 void 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 (!location.getAs<Loc>())
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 
284 void 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 
341 void ento::registerDereferenceChecker(CheckerManager &mgr) {
342  auto *Chk = mgr.registerChecker<DereferenceChecker>();
343  Chk->SuppressAddressSpaces = mgr.getAnalyzerOptions().getCheckerBooleanOption(
344  mgr.getCurrentCheckerName(), "SuppressAddressSpaces");
345 }
346 
347 bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
348  return true;
349 }
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:210
llvm::SmallVector
Definition: LLVM.h:38
clang::SourceLocation
Encodes a location in the source.
Definition: SourceLocation.h:86
TargetInfo.h
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
clang::QualType
A (possibly-)qualified type.
Definition: Type.h:675
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:55
clang::UnaryOperator
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition: Expr.h:2163
ExprOpenMP.h
clang::MemberExpr::getMemberNameInfo
DeclarationNameInfo getMemberNameInfo() const
Retrieve the member declaration name info.
Definition: Expr.h:3341
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:3176
clang::ast_matchers::expr
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
Definition: ASTMatchersInternal.cpp:890
BuiltinCheckerRegistration.h
clang::QualType::getAddressSpace
LangAS getAddressSpace() const
Return the address space of this type.
Definition: Type.h:6621
clang::toTargetAddressSpace
unsigned toTargetAddressSpace(LangAS AS)
Definition: AddressSpaces.h:75
clang::MemberExpr::isArrow
bool isArrow() const
Definition: Expr.h:3348
CheckerManager.h
clang::DeclRefExpr::getDecl
ValueDecl * getDecl()
Definition: Expr.h:1295
clang::QualType::hasAddressSpace
bool hasAddressSpace() const
Check if this type has any address space qualifier.
Definition: Type.h:6616
llvm::SmallString
Definition: LLVM.h:37
clang::VarDecl
Represents a variable declaration or definition.
Definition: Decl.h:874
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:3353
clang::ObjCIvarRefExpr::getLocation
SourceLocation getLocation() const
Definition: ExprObjC.h:589
getDereferenceExpr
static const Expr * getDereferenceExpr(const Stmt *S, bool IsBind=false)
Definition: DereferenceChecker.cpp:99
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:2964
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:2952
clang::MemberExpr::getBase
Expr * getBase() const
Definition: Expr.h:3241
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:2645
State
LineState State
Definition: UnwrappedLineFormatter.cpp:1089
isDeclRefExprToReference
static bool isDeclRefExprToReference(const Expr *E)
Definition: DereferenceChecker.cpp:148
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:2682
clang::MemberExpr
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:3164
clang::ObjCIvarRefExpr::getBase
const Expr * getBase() const
Definition: ExprObjC.h:580
llvm::SmallVectorImpl
Definition: Randstruct.h:18
clang::Expr
This represents one expression.
Definition: Expr.h:109
clang::DeclRefExpr
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1223
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:274