Coverage Report

Created: 2023-09-21 18:56

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
Line
Count
Source (jump to first uncovered line)
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
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",
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
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,
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
60
void
61
DereferenceChecker::AddDerefSource(raw_ostream &os,
62
                                   SmallVectorImpl<SourceRange> &Ranges,
63
                                   const Expr *Ex,
64
                                   const ProgramState *state,
65
                                   const LocationContext *LCtx,
66
896
                                   bool loadedFrom) {
67
896
  Ex = Ex->IgnoreParenLValueCasts();
68
896
  switch (Ex->getStmtClass()) {
69
115
    default:
70
115
      break;
71
712
    case Stmt::DeclRefExprClass: {
72
712
      const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
73
712
      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
74
711
        os << " (" << (loadedFrom ? 
"loaded from"697
:
"from"14
)
75
711
           << " variable '" <<  VD->getName() << "')";
76
711
        Ranges.push_back(DR->getSourceRange());
77
711
      }
78
712
      break;
79
0
    }
80
64
    case Stmt::MemberExprClass: {
81
64
      const MemberExpr *ME = cast<MemberExpr>(Ex);
82
64
      os << " (" << (loadedFrom ? 
"loaded from"37
:
"via"27
)
83
64
         << " field '" << ME->getMemberNameInfo() << "')";
84
64
      SourceLocation L = ME->getMemberLoc();
85
64
      Ranges.push_back(SourceRange(L, L));
86
64
      break;
87
0
    }
88
5
    case Stmt::ObjCIvarRefExprClass: {
89
5
      const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
90
5
      os << " (" << (loadedFrom ? 
"loaded from"3
:
"via"2
)
91
5
         << " ivar '" << IV->getDecl()->getName() << "')";
92
5
      SourceLocation L = IV->getLocation();
93
5
      Ranges.push_back(SourceRange(L, L));
94
5
      break;
95
0
    }
96
896
  }
97
896
}
98
99
931
static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
100
931
  const Expr *E = nullptr;
101
102
  // Walk through lvalue casts to get the original expression
103
  // that syntactically caused the load.
104
931
  if (const Expr *expr = dyn_cast<Expr>(S))
105
913
    E = expr->IgnoreParenLValueCasts();
106
107
931
  if (IsBind) {
108
22
    const VarDecl *VD;
109
22
    const Expr *Init;
110
22
    std::tie(VD, Init) = parseAssignment(S);
111
22
    if (VD && 
Init18
)
112
18
      E = Init;
113
22
  }
114
931
  return E;
115
931
}
116
117
bool DereferenceChecker::suppressReport(CheckerContext &C,
118
931
                                        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
931
  QualType Ty = E->getType();
129
931
  if (!Ty.hasAddressSpace())
130
915
    return false;
131
16
  if (SuppressAddressSpaces)
132
14
    return true;
133
134
2
  const llvm::Triple::ArchType Arch =
135
2
      C.getASTContext().getTargetInfo().getTriple().getArch();
136
137
2
  if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) {
138
1
    switch (toTargetAddressSpace(E->getType().getAddressSpace())) {
139
0
    case 256:
140
0
    case 257:
141
0
    case 258:
142
0
      return true;
143
1
    }
144
1
  }
145
2
  return false;
146
2
}
147
148
10
static bool isDeclRefExprToReference(const Expr *E) {
149
10
  if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
150
1
    return DRE->getDecl()->getType()->isReferenceType();
151
9
  return false;
152
10
}
153
154
void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
155
917
                                   const Stmt *S, CheckerContext &C) const {
156
917
  const BugType *BT = nullptr;
157
917
  llvm::StringRef DerefStr1;
158
917
  llvm::StringRef DerefStr2;
159
917
  switch (K) {
160
905
  case DerefKind::NullPointer:
161
905
    BT = &BT_Null;
162
905
    DerefStr1 = " results in a null pointer dereference";
163
905
    DerefStr2 = " results in a dereference of a null pointer";
164
905
    break;
165
12
  case DerefKind::UndefinedPointerValue:
166
12
    BT = &BT_Undef;
167
12
    DerefStr1 = " results in an undefined pointer dereference";
168
12
    DerefStr2 = " results in a dereference of an undefined pointer value";
169
12
    break;
170
917
  };
171
172
  // Generate an error node.
173
917
  ExplodedNode *N = C.generateErrorNode(State);
174
917
  if (!N)
175
0
    return;
176
177
917
  SmallString<100> buf;
178
917
  llvm::raw_svector_ostream os(buf);
179
180
917
  SmallVector<SourceRange, 2> Ranges;
181
182
917
  switch (S->getStmtClass()) {
183
44
  case Stmt::ArraySubscriptExprClass: {
184
44
    os << "Array access";
185
44
    const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
186
44
    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
187
44
                   State.get(), N->getLocationContext());
188
44
    os << DerefStr1;
189
44
    break;
190
0
  }
191
0
  case Stmt::OMPArraySectionExprClass: {
192
0
    os << "Array access";
193
0
    const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S);
194
0
    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
195
0
                   State.get(), N->getLocationContext());
196
0
    os << DerefStr1;
197
0
    break;
198
0
  }
199
822
  case Stmt::UnaryOperatorClass: {
200
822
    os << BT->getDescription();
201
822
    const UnaryOperator *U = cast<UnaryOperator>(S);
202
822
    AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
203
822
                   State.get(), N->getLocationContext(), true);
204
822
    break;
205
0
  }
206
26
  case Stmt::MemberExprClass: {
207
26
    const MemberExpr *M = cast<MemberExpr>(S);
208
26
    if (M->isArrow() || 
isDeclRefExprToReference(M->getBase())10
) {
209
17
      os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;
210
17
      AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
211
17
                     State.get(), N->getLocationContext(), true);
212
17
    }
213
26
    break;
214
0
  }
215
13
  case Stmt::ObjCIvarRefExprClass: {
216
13
    const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
217
13
    os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;
218
13
    AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
219
13
                   State.get(), N->getLocationContext(), true);
220
13
    break;
221
0
  }
222
12
  default:
223
12
    break;
224
917
  }
225
226
917
  auto report = std::make_unique<PathSensitiveBugReport>(
227
917
      *BT, buf.empty() ? 
BT->getDescription()21
:
buf.str()896
, N);
228
229
917
  bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
230
231
917
  for (SmallVectorImpl<SourceRange>::iterator
232
1.69k
       I = Ranges.begin(), E = Ranges.end(); I!=E; 
++I780
)
233
780
    report->addRange(*I);
234
235
917
  C.emitReport(std::move(report));
236
917
}
237
238
void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
239
263k
                                       CheckerContext &C) const {
240
  // Check for dereference of an undefined value.
241
263k
  if (l.isUndef()) {
242
12
    const Expr *DerefExpr = getDereferenceExpr(S);
243
12
    if (!suppressReport(C, DerefExpr))
244
12
      reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
245
12
    return;
246
12
  }
247
248
263k
  DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
249
250
  // Check for null dereferences.
251
263k
  if (!isa<Loc>(location))
252
0
    return;
253
254
263k
  ProgramStateRef state = C.getState();
255
256
263k
  ProgramStateRef notNullState, nullState;
257
263k
  std::tie(notNullState, nullState) = state->assume(location);
258
259
263k
  if (nullState) {
260
4.14k
    if (!notNullState) {
261
      // We know that 'location' can only be null.  This is what
262
      // we call an "explicit" null dereference.
263
897
      const Expr *expr = getDereferenceExpr(S);
264
897
      if (!suppressReport(C, expr)) {
265
888
        reportBug(DerefKind::NullPointer, nullState, expr, C);
266
888
        return;
267
888
      }
268
897
    }
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
3.25k
    if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
274
3.24k
      ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
275
3.24k
                                      /*IsDirectDereference=*/true};
276
3.24k
      dispatchEvent(event);
277
3.24k
    }
278
3.25k
  }
279
280
  // From this point forward, we know that the location is not null.
281
262k
  C.addTransition(notNullState);
282
262k
}
283
284
void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
285
82.2k
                                   CheckerContext &C) const {
286
  // If we're binding to a reference, check if the value is known to be null.
287
82.2k
  if (V.isUndef())
288
361
    return;
289
290
81.9k
  const MemRegion *MR = L.getAsRegion();
291
81.9k
  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
292
81.9k
  if (!TVR)
293
701
    return;
294
295
81.2k
  if (!TVR->getValueType()->isReferenceType())
296
80.3k
    return;
297
298
901
  ProgramStateRef State = C.getState();
299
300
901
  ProgramStateRef StNonNull, StNull;
301
901
  std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
302
303
901
  if (StNull) {
304
38
    if (!StNonNull) {
305
22
      const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
306
22
      if (!suppressReport(C, expr)) {
307
17
        reportBug(DerefKind::NullPointer, StNull, expr, C);
308
17
        return;
309
17
      }
310
22
    }
311
312
    // At this point the value could be either null or non-null.
313
    // Record this as an "implicit" null dereference.
314
21
    if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
315
21
      ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
316
21
                                      &C.getBugReporter(),
317
21
                                      /*IsDirectDereference=*/true};
318
21
      dispatchEvent(event);
319
21
    }
320
21
  }
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
884
  C.addTransition(State, this);
339
884
}
340
341
1.28k
void ento::registerDereferenceChecker(CheckerManager &mgr) {
342
1.28k
  auto *Chk = mgr.registerChecker<DereferenceChecker>();
343
1.28k
  Chk->SuppressAddressSpaces = mgr.getAnalyzerOptions().getCheckerBooleanOption(
344
1.28k
      mgr.getCurrentCheckerName(), "SuppressAddressSpaces");
345
1.28k
}
346
347
2.57k
bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
348
2.57k
  return true;
349
2.57k
}