Coverage Report

Created: 2020-10-24 06:27

/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/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
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",
37
                   categories::LogicError};
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,
48
                             SmallVectorImpl<SourceRange> &Ranges,
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,
57
                                   SmallVectorImpl<SourceRange> &Ranges,
58
                                   const Expr *Ex,
59
                                   const ProgramState *state,
60
                                   const LocationContext *LCtx,
61
847
                                   bool loadedFrom) {
62
847
  Ex = Ex->IgnoreParenLValueCasts();
63
847
  switch (Ex->getStmtClass()) {
64
109
    default:
65
109
      break;
66
687
    case Stmt::DeclRefExprClass: {
67
687
      const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
68
687
      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
69
674
        os << " (" << (loadedFrom ? "loaded from" : 
"from"13
)
70
687
           << " variable '" <<  VD->getName() << "')";
71
687
        Ranges.push_back(DR->getSourceRange());
72
687
      }
73
687
      break;
74
0
    }
75
46
    case Stmt::MemberExprClass: {
76
46
      const MemberExpr *ME = cast<MemberExpr>(Ex);
77
27
      os << " (" << (loadedFrom ? 
"loaded from"19
: "via")
78
46
         << " field '" << ME->getMemberNameInfo() << "')";
79
46
      SourceLocation L = ME->getMemberLoc();
80
46
      Ranges.push_back(SourceRange(L, L));
81
46
      break;
82
0
    }
83
5
    case Stmt::ObjCIvarRefExprClass: {
84
5
      const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
85
3
      os << " (" << (loadedFrom ? "loaded from" : 
"via"2
)
86
5
         << " ivar '" << IV->getDecl()->getName() << "')";
87
5
      SourceLocation L = IV->getLocation();
88
5
      Ranges.push_back(SourceRange(L, L));
89
5
      break;
90
0
    }
91
847
  }
92
847
}
93
94
869
static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
95
869
  const Expr *E = nullptr;
96
97
  // Walk through lvalue casts to get the original expression
98
  // that syntactically caused the load.
99
869
  if (const Expr *expr = dyn_cast<Expr>(S))
100
864
    E = expr->IgnoreParenLValueCasts();
101
102
869
  if (IsBind) {
103
9
    const VarDecl *VD;
104
9
    const Expr *Init;
105
9
    std::tie(VD, Init) = parseAssignment(S);
106
9
    if (VD && 
Init5
)
107
5
      E = Init;
108
9
  }
109
869
  return E;
110
869
}
111
112
869
static bool suppressReport(const Expr *E) {
113
  // Do not report dereferences on memory in non-default address spaces.
114
869
  return E->getType().hasAddressSpace();
115
869
}
116
117
10
static bool isDeclRefExprToReference(const Expr *E) {
118
10
  if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
119
1
    return DRE->getDecl()->getType()->isReferenceType();
120
9
  return false;
121
9
}
122
123
void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
124
859
                                   const Stmt *S, CheckerContext &C) const {
125
859
  const BugType *BT = nullptr;
126
859
  llvm::StringRef DerefStr1;
127
859
  llvm::StringRef DerefStr2;
128
859
  switch (K) {
129
847
  case DerefKind::NullPointer:
130
847
    BT = &BT_Null;
131
847
    DerefStr1 = " results in a null pointer dereference";
132
847
    DerefStr2 = " results in a dereference of a null pointer";
133
847
    break;
134
12
  case DerefKind::UndefinedPointerValue:
135
12
    BT = &BT_Undef;
136
12
    DerefStr1 = " results in an undefined pointer dereference";
137
12
    DerefStr2 = " results in a dereference of an undefined pointer value";
138
12
    break;
139
859
  };
140
141
  // Generate an error node.
142
859
  ExplodedNode *N = C.generateErrorNode(State);
143
859
  if (!N)
144
0
    return;
145
146
859
  SmallString<100> buf;
147
859
  llvm::raw_svector_ostream os(buf);
148
149
859
  SmallVector<SourceRange, 2> Ranges;
150
151
859
  switch (S->getStmtClass()) {
152
43
  case Stmt::ArraySubscriptExprClass: {
153
43
    os << "Array access";
154
43
    const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
155
43
    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
156
43
                   State.get(), N->getLocationContext());
157
43
    os << DerefStr1;
158
43
    break;
159
0
  }
160
0
  case Stmt::OMPArraySectionExprClass: {
161
0
    os << "Array access";
162
0
    const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S);
163
0
    AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
164
0
                   State.get(), N->getLocationContext());
165
0
    os << DerefStr1;
166
0
    break;
167
0
  }
168
775
  case Stmt::UnaryOperatorClass: {
169
775
    os << BT->getDescription();
170
775
    const UnaryOperator *U = cast<UnaryOperator>(S);
171
775
    AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
172
775
                   State.get(), N->getLocationContext(), true);
173
775
    break;
174
0
  }
175
25
  case Stmt::MemberExprClass: {
176
25
    const MemberExpr *M = cast<MemberExpr>(S);
177
25
    if (M->isArrow() || 
isDeclRefExprToReference(M->getBase())10
) {
178
16
      os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;
179
16
      AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
180
16
                     State.get(), N->getLocationContext(), true);
181
16
    }
182
25
    break;
183
0
  }
184
13
  case Stmt::ObjCIvarRefExprClass: {
185
13
    const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
186
13
    os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;
187
13
    AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
188
13
                   State.get(), N->getLocationContext(), true);
189
13
    break;
190
0
  }
191
3
  default:
192
3
    break;
193
859
  }
194
195
859
  auto report = std::make_unique<PathSensitiveBugReport>(
196
847
      *BT, buf.empty() ? 
BT->getDescription()12
: StringRef(buf), N);
197
198
859
  bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
199
200
859
  for (SmallVectorImpl<SourceRange>::iterator
201
1.59k
       I = Ranges.begin(), E = Ranges.end(); I!=E; 
++I738
)
202
738
    report->addRange(*I);
203
204
859
  C.emitReport(std::move(report));
205
859
}
206
207
void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
208
172k
                                       CheckerContext &C) const {
209
  // Check for dereference of an undefined value.
210
172k
  if (l.isUndef()) {
211
12
    const Expr *DerefExpr = getDereferenceExpr(S);
212
12
    if (!suppressReport(DerefExpr))
213
12
      reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
214
12
    return;
215
12
  }
216
217
172k
  DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
218
219
  // Check for null dereferences.
220
172k
  if (!location.getAs<Loc>())
221
0
    return;
222
223
172k
  ProgramStateRef state = C.getState();
224
225
172k
  ProgramStateRef notNullState, nullState;
226
172k
  std::tie(notNullState, nullState) = state->assume(location);
227
228
172k
  if (nullState) {
229
3.91k
    if (!notNullState) {
230
      // We know that 'location' can only be null.  This is what
231
      // we call an "explicit" null dereference.
232
848
      const Expr *expr = getDereferenceExpr(S);
233
848
      if (!suppressReport(expr)) {
234
839
        reportBug(DerefKind::NullPointer, nullState, expr, C);
235
839
        return;
236
839
      }
237
3.07k
    }
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
3.07k
    if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
243
3.06k
      ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
244
3.06k
                                      /*IsDirectDereference=*/true};
245
3.06k
      dispatchEvent(event);
246
3.06k
    }
247
3.07k
  }
248
249
  // From this point forward, we know that the location is not null.
250
171k
  C.addTransition(notNullState);
251
171k
}
252
253
void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
254
67.7k
                                   CheckerContext &C) const {
255
  // If we're binding to a reference, check if the value is known to be null.
256
67.7k
  if (V.isUndef())
257
316
    return;
258
259
67.4k
  const MemRegion *MR = L.getAsRegion();
260
67.4k
  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
261
67.4k
  if (!TVR)
262
607
    return;
263
264
66.8k
  if (!TVR->getValueType()->isReferenceType())
265
66.1k
    return;
266
267
682
  ProgramStateRef State = C.getState();
268
269
682
  ProgramStateRef StNonNull, StNull;
270
682
  std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
271
272
682
  if (StNull) {
273
25
    if (!StNonNull) {
274
9
      const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
275
9
      if (!suppressReport(expr)) {
276
8
        reportBug(DerefKind::NullPointer, StNull, expr, C);
277
8
        return;
278
8
      }
279
17
    }
280
281
    // At this point the value could be either null or non-null.
282
    // Record this as an "implicit" null dereference.
283
17
    if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
284
17
      ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
285
17
                                      &C.getBugReporter(),
286
17
                                      /*IsDirectDereference=*/true};
287
17
      dispatchEvent(event);
288
17
    }
289
17
  }
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
674
  C.addTransition(State, this);
308
674
}
309
310
1.08k
void ento::registerDereferenceChecker(CheckerManager &mgr) {
311
1.08k
  mgr.registerChecker<DereferenceChecker>();
312
1.08k
}
313
314
2.16k
bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
315
2.16k
  return true;
316
2.16k
}