Coverage Report

Created: 2020-09-22 08:39

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
Line
Count
Source (jump to first uncovered line)
1
// SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
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 file defines a checker that models various aspects of
10
// C++ smart pointer behavior.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "Move.h"
15
#include "SmartPtr.h"
16
17
#include "clang/AST/DeclCXX.h"
18
#include "clang/AST/DeclarationName.h"
19
#include "clang/AST/ExprCXX.h"
20
#include "clang/AST/Type.h"
21
#include "clang/Basic/LLVM.h"
22
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24
#include "clang/StaticAnalyzer/Core/Checker.h"
25
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
26
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
27
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
28
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
29
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
30
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
31
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
32
#include <string>
33
34
using namespace clang;
35
using namespace ento;
36
37
namespace {
38
class SmartPtrModeling
39
    : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
40
                     check::LiveSymbols> {
41
42
  bool isBoolConversionMethod(const CallEvent &Call) const;
43
44
public:
45
  // Whether the checker should model for null dereferences of smart pointers.
46
  DefaultBool ModelSmartPtrDereference;
47
  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
48
  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
49
  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
50
  ProgramStateRef
51
  checkRegionChanges(ProgramStateRef State,
52
                     const InvalidatedSymbols *Invalidated,
53
                     ArrayRef<const MemRegion *> ExplicitRegions,
54
                     ArrayRef<const MemRegion *> Regions,
55
                     const LocationContext *LCtx, const CallEvent *Call) const;
56
  void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
57
                  const char *Sep) const override;
58
  void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
59
60
private:
61
  void handleReset(const CallEvent &Call, CheckerContext &C) const;
62
  void handleRelease(const CallEvent &Call, CheckerContext &C) const;
63
  void handleSwap(const CallEvent &Call, CheckerContext &C) const;
64
  void handleGet(const CallEvent &Call, CheckerContext &C) const;
65
  bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const;
66
  bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
67
                     const MemRegion *ThisRegion) const;
68
  bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
69
                                const MemRegion *OtherSmartPtrRegion) const;
70
  void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
71
72
  using SmartPtrMethodHandlerFn =
73
      void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
74
  CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
75
      {{"reset"}, &SmartPtrModeling::handleReset},
76
      {{"release"}, &SmartPtrModeling::handleRelease},
77
      {{"swap", 1}, &SmartPtrModeling::handleSwap},
78
      {{"get"}, &SmartPtrModeling::handleGet}};
79
};
80
} // end of anonymous namespace
81
82
REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
83
84
// Define the inter-checker API.
85
namespace clang {
86
namespace ento {
87
namespace smartptr {
88
41.0k
bool isStdSmartPtrCall(const CallEvent &Call) {
89
41.0k
  const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
90
41.0k
  if (!MethodDecl || 
!MethodDecl->getParent()15.1k
)
91
25.9k
    return false;
92
93
15.1k
  const auto *RecordDecl = MethodDecl->getParent();
94
15.1k
  if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace())
95
11.3k
    return false;
96
97
3.79k
  if (RecordDecl->getDeclName().isIdentifier()) {
98
3.79k
    StringRef Name = RecordDecl->getName();
99
3.79k
    return Name == "shared_ptr" || Name == "unique_ptr" || 
Name == "weak_ptr"3.21k
;
100
3.79k
  }
101
0
  return false;
102
0
}
103
104
84
bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
105
84
  const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
106
84
  return InnerPointVal && 
InnerPointVal->isZeroConstant()73
;
107
84
}
108
} // namespace smartptr
109
} // namespace ento
110
} // namespace clang
111
112
// If a region is removed all of the subregions need to be removed too.
113
static TrackedRegionMapTy
114
removeTrackedSubregions(TrackedRegionMapTy RegionMap,
115
                        TrackedRegionMapTy::Factory &RegionMapFactory,
116
84.6k
                        const MemRegion *Region) {
117
84.6k
  if (!Region)
118
0
    return RegionMap;
119
84.6k
  for (const auto &E : RegionMap) {
120
106
    if (E.first->isSubRegionOf(Region))
121
6
      RegionMap = RegionMapFactory.remove(RegionMap, E.first);
122
106
  }
123
84.6k
  return RegionMap;
124
84.6k
}
125
126
static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
127
                                           const MemRegion *Region,
128
18
                                           const SVal *RegionInnerPointerVal) {
129
18
  if (RegionInnerPointerVal) {
130
16
    State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
131
2
  } else {
132
2
    State = State->remove<TrackedRegionMap>(Region);
133
2
  }
134
18
  return State;
135
18
}
136
137
// Helper method to get the inner pointer type of specialized smart pointer
138
// Returns empty type if not found valid inner pointer type.
139
7
static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
140
7
  const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
141
7
  if (!MethodDecl || !MethodDecl->getParent())
142
0
    return {};
143
144
7
  const auto *RecordDecl = MethodDecl->getParent();
145
7
  if (!RecordDecl || !RecordDecl->isInStdNamespace())
146
0
    return {};
147
148
7
  const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl);
149
7
  if (!TSD)
150
0
    return {};
151
152
7
  auto TemplateArgs = TSD->getTemplateArgs().asArray();
153
7
  if (TemplateArgs.size() == 0)
154
0
    return {};
155
7
  auto InnerValueType = TemplateArgs[0].getAsType();
156
7
  return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
157
7
}
158
159
// Helper method to pretty print region and avoid extra spacing.
160
static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
161
114
                                      const MemRegion *Region) {
162
114
  if (Region->canPrintPretty()) {
163
114
    OS << " ";
164
114
    Region->printPretty(OS);
165
114
  }
166
114
}
167
168
291
bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
169
  // TODO: Update CallDescription to support anonymous calls?
170
  // TODO: Handle other methods, such as .get() or .release().
171
  // But once we do, we'd need a visitor to explain null dereferences
172
  // that are found via such modeling.
173
291
  const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
174
291
  return CD && 
CD->getConversionType()->isBooleanType()30
;
175
291
}
176
177
bool SmartPtrModeling::evalCall(const CallEvent &Call,
178
40.6k
                                CheckerContext &C) const {
179
40.6k
  ProgramStateRef State = C.getState();
180
40.6k
  if (!smartptr::isStdSmartPtrCall(Call))
181
40.3k
    return false;
182
183
291
  if (isBoolConversionMethod(Call)) {
184
30
    const MemRegion *ThisR =
185
30
        cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
186
187
30
    if (ModelSmartPtrDereference) {
188
      // The check for the region is moved is duplicated in handleBoolOperation
189
      // method.
190
      // FIXME: Once we model std::move for smart pointers clean up this and use
191
      // that modeling.
192
22
      handleBoolConversion(Call, C);
193
22
      return true;
194
8
    } else {
195
8
      if (!move::isMovedFrom(State, ThisR)) {
196
        // TODO: Model this case as well. At least, avoid invalidation of
197
        // globals.
198
0
        return false;
199
0
      }
200
201
      // TODO: Add a note to bug reports describing this decision.
202
8
      C.addTransition(State->BindExpr(
203
8
          Call.getOriginExpr(), C.getLocationContext(),
204
8
          C.getSValBuilder().makeZeroVal(Call.getResultType())));
205
206
8
      return true;
207
8
    }
208
30
  }
209
210
261
  if (!ModelSmartPtrDereference)
211
56
    return false;
212
213
205
  if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
214
111
    if (CC->getDecl()->isCopyConstructor())
215
0
      return false;
216
217
111
    const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
218
111
    if (!ThisRegion)
219
0
      return false;
220
221
111
    if (CC->getDecl()->isMoveConstructor())
222
12
      return handleMoveCtr(Call, C, ThisRegion);
223
224
99
    if (Call.getNumArgs() == 0) {
225
60
      auto NullVal = C.getSValBuilder().makeNull();
226
60
      State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
227
228
60
      C.addTransition(
229
60
          State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
230
50
                                           llvm::raw_ostream &OS) {
231
50
            if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
232
44
                !BR.isInteresting(ThisRegion))
233
14
              return;
234
36
            OS << "Default constructed smart pointer";
235
36
            checkAndPrettyPrintRegion(OS, ThisRegion);
236
36
            OS << " is null";
237
36
          }));
238
39
    } else {
239
39
      const auto *TrackingExpr = Call.getArgExpr(0);
240
39
      assert(TrackingExpr->getType()->isPointerType() &&
241
39
             "Adding a non pointer value to TrackedRegionMap");
242
39
      auto ArgVal = Call.getArgSVal(0);
243
39
      State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
244
245
39
      C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
246
39
                                           ArgVal](PathSensitiveBugReport &BR,
247
31
                                                   llvm::raw_ostream &OS) {
248
31
        if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
249
25
            !BR.isInteresting(ThisRegion))
250
10
          return;
251
21
        bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
252
21
        OS << "Smart pointer";
253
21
        checkAndPrettyPrintRegion(OS, ThisRegion);
254
21
        if (ArgVal.isZeroConstant())
255
4
          OS << " is constructed using a null value";
256
17
        else
257
17
          OS << " is constructed";
258
21
      }));
259
39
    }
260
99
    return true;
261
99
  }
262
263
94
  if (handleAssignOp(Call, C))
264
17
    return true;
265
266
77
  const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
267
77
  if (!Handler)
268
28
    return false;
269
49
  (this->**Handler)(Call, C);
270
271
49
  return C.isDifferent();
272
49
}
273
274
void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
275
166k
                                        CheckerContext &C) const {
276
166k
  ProgramStateRef State = C.getState();
277
  // Clean up dead regions from the region map.
278
166k
  TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
279
646
  for (auto E : TrackedRegions) {
280
646
    const MemRegion *Region = E.first;
281
646
    bool IsRegDead = !SymReaper.isLiveRegion(Region);
282
283
646
    if (IsRegDead)
284
69
      State = State->remove<TrackedRegionMap>(Region);
285
646
  }
286
166k
  C.addTransition(State);
287
166k
}
288
289
void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
290
40
                                  const char *NL, const char *Sep) const {
291
40
  TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
292
293
40
  if (!RS.isEmpty()) {
294
0
    Out << Sep << "Smart ptr regions :" << NL;
295
0
    for (auto I : RS) {
296
0
      I.first->dumpToStream(Out);
297
0
      if (smartptr::isNullSmartPtr(State, I.first))
298
0
        Out << ": Null";
299
0
      else
300
0
        Out << ": Non Null";
301
0
      Out << NL;
302
0
    }
303
0
  }
304
40
}
305
306
ProgramStateRef SmartPtrModeling::checkRegionChanges(
307
    ProgramStateRef State, const InvalidatedSymbols *Invalidated,
308
    ArrayRef<const MemRegion *> ExplicitRegions,
309
    ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
310
46.8k
    const CallEvent *Call) const {
311
46.8k
  TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
312
46.8k
  TrackedRegionMapTy::Factory &RegionMapFactory =
313
46.8k
      State->get_context<TrackedRegionMap>();
314
46.8k
  for (const auto *Region : Regions)
315
84.6k
    RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
316
84.6k
                                        Region->getBaseRegion());
317
46.8k
  return State->set<TrackedRegionMap>(RegionMap);
318
46.8k
}
319
320
void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
321
166k
                                        SymbolReaper &SR) const {
322
  // Marking tracked symbols alive
323
166k
  TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
324
166k
  for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; 
++I646
) {
325
646
    SVal Val = I->second;
326
895
    for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; 
++si249
) {
327
249
      SR.markLive(*si);
328
249
    }
329
646
  }
330
166k
}
331
332
void SmartPtrModeling::handleReset(const CallEvent &Call,
333
14
                                   CheckerContext &C) const {
334
14
  ProgramStateRef State = C.getState();
335
14
  const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
336
14
  if (!IC)
337
0
    return;
338
339
14
  const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
340
14
  if (!ThisRegion)
341
0
    return;
342
343
14
  assert(Call.getArgExpr(0)->getType()->isPointerType() &&
344
14
         "Adding a non pointer value to TrackedRegionMap");
345
14
  State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
346
14
  const auto *TrackingExpr = Call.getArgExpr(0);
347
14
  C.addTransition(
348
14
      State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
349
10
                                                     llvm::raw_ostream &OS) {
350
10
        if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
351
5
            !BR.isInteresting(ThisRegion))
352
6
          return;
353
4
        bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
354
4
        OS << "Smart pointer";
355
4
        checkAndPrettyPrintRegion(OS, ThisRegion);
356
4
        OS << " reset using a null value";
357
4
      }));
358
  // TODO: Make sure to ivalidate the region in the Store if we don't have
359
  // time to model all methods.
360
14
}
361
362
void SmartPtrModeling::handleRelease(const CallEvent &Call,
363
8
                                     CheckerContext &C) const {
364
8
  ProgramStateRef State = C.getState();
365
8
  const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
366
8
  if (!IC)
367
0
    return;
368
369
8
  const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
370
8
  if (!ThisRegion)
371
0
    return;
372
373
8
  const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
374
375
8
  if (InnerPointVal) {
376
8
    State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
377
8
                            *InnerPointVal);
378
8
  }
379
380
8
  auto ValueToUpdate = C.getSValBuilder().makeNull();
381
8
  State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
382
383
8
  C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
384
7
                                                   llvm::raw_ostream &OS) {
385
7
    if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
386
3
        !BR.isInteresting(ThisRegion))
387
5
      return;
388
389
2
    OS << "Smart pointer";
390
2
    checkAndPrettyPrintRegion(OS, ThisRegion);
391
2
    OS << " is released and set to null";
392
2
  }));
393
  // TODO: Add support to enable MallocChecker to start tracking the raw
394
  // pointer.
395
8
}
396
397
void SmartPtrModeling::handleSwap(const CallEvent &Call,
398
9
                                  CheckerContext &C) const {
399
  // To model unique_ptr::swap() method.
400
9
  const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
401
9
  if (!IC)
402
0
    return;
403
404
9
  const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
405
9
  if (!ThisRegion)
406
0
    return;
407
408
9
  const auto *ArgRegion = Call.getArgSVal(0).getAsRegion();
409
9
  if (!ArgRegion)
410
0
    return;
411
412
9
  auto State = C.getState();
413
9
  const auto *ThisRegionInnerPointerVal =
414
9
      State->get<TrackedRegionMap>(ThisRegion);
415
9
  const auto *ArgRegionInnerPointerVal =
416
9
      State->get<TrackedRegionMap>(ArgRegion);
417
418
  // Swap the tracked region values.
419
9
  State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal);
420
9
  State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal);
421
422
9
  C.addTransition(
423
9
      State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR,
424
7
                                                  llvm::raw_ostream &OS) {
425
7
        if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
426
6
            !BR.isInteresting(ThisRegion))
427
3
          return;
428
4
        BR.markInteresting(ArgRegion);
429
4
        OS << "Swapped null smart pointer";
430
4
        checkAndPrettyPrintRegion(OS, ArgRegion);
431
4
        OS << " with smart pointer";
432
4
        checkAndPrettyPrintRegion(OS, ThisRegion);
433
4
      }));
434
9
}
435
436
void SmartPtrModeling::handleGet(const CallEvent &Call,
437
18
                                 CheckerContext &C) const {
438
18
  ProgramStateRef State = C.getState();
439
18
  const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
440
18
  if (!IC)
441
0
    return;
442
443
18
  const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
444
18
  if (!ThisRegion)
445
0
    return;
446
447
18
  SVal InnerPointerVal;
448
18
  if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
449
14
    InnerPointerVal = *InnerValPtr;
450
4
  } else {
451
4
    const auto *CallExpr = Call.getOriginExpr();
452
4
    InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
453
4
        CallExpr, C.getLocationContext(), Call.getResultType(), C.blockCount());
454
4
    State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
455
4
  }
456
457
18
  State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
458
18
                          InnerPointerVal);
459
  // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
460
18
  C.addTransition(State);
461
18
}
462
463
bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
464
94
                                      CheckerContext &C) const {
465
94
  ProgramStateRef State = C.getState();
466
94
  const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
467
94
  if (!OC)
468
49
    return false;
469
45
  OverloadedOperatorKind OOK = OC->getOverloadedOperator();
470
45
  if (OOK != OO_Equal)
471
28
    return false;
472
17
  const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
473
17
  if (!ThisRegion)
474
0
    return false;
475
476
17
  const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
477
  // In case of 'nullptr' or '0' assigned
478
17
  if (!OtherSmartPtrRegion) {
479
5
    bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
480
5
    if (!AssignedNull)
481
0
      return false;
482
5
    auto NullVal = C.getSValBuilder().makeNull();
483
5
    State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
484
5
    C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
485
5
                                                     llvm::raw_ostream &OS) {
486
5
      if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
487
5
          !BR.isInteresting(ThisRegion))
488
0
        return;
489
5
      OS << "Smart pointer";
490
5
      checkAndPrettyPrintRegion(OS, ThisRegion);
491
5
      OS << " is assigned to null";
492
5
    }));
493
5
    return true;
494
5
  }
495
496
12
  return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
497
12
}
498
499
bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
500
12
                                     const MemRegion *ThisRegion) const {
501
12
  const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
502
12
  if (!OtherSmartPtrRegion)
503
0
    return false;
504
505
12
  return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
506
12
}
507
508
bool SmartPtrModeling::updateMovedSmartPointers(
509
    CheckerContext &C, const MemRegion *ThisRegion,
510
24
    const MemRegion *OtherSmartPtrRegion) const {
511
24
  ProgramStateRef State = C.getState();
512
24
  const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
513
24
  if (OtherInnerPtr) {
514
15
    State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
515
15
    auto NullVal = C.getSValBuilder().makeNull();
516
15
    State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
517
15
    bool IsArgValNull = OtherInnerPtr->isZeroConstant();
518
519
15
    C.addTransition(
520
15
        State,
521
15
        C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
522
12
                         PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
523
12
          if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
524
0
            return;
525
12
          if (BR.isInteresting(OtherSmartPtrRegion)) {
526
6
            OS << "Smart pointer";
527
6
            checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
528
6
            OS << " is null after being moved to";
529
6
            checkAndPrettyPrintRegion(OS, ThisRegion);
530
6
          }
531
12
          if (BR.isInteresting(ThisRegion) && 
IsArgValNull5
) {
532
5
            OS << "A null pointer value is moved to";
533
5
            checkAndPrettyPrintRegion(OS, ThisRegion);
534
5
            BR.markInteresting(OtherSmartPtrRegion);
535
5
          }
536
12
        }));
537
15
    return true;
538
9
  } else {
539
    // In case we dont know anything about value we are moving from
540
    // remove the entry from map for which smart pointer got moved to.
541
9
    auto NullVal = C.getSValBuilder().makeNull();
542
9
    State = State->remove<TrackedRegionMap>(ThisRegion);
543
9
    State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
544
9
    C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
545
9
                                         ThisRegion](PathSensitiveBugReport &BR,
546
8
                                                     llvm::raw_ostream &OS) {
547
8
      if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
548
4
          !BR.isInteresting(OtherSmartPtrRegion))
549
4
        return;
550
4
      OS << "Smart pointer";
551
4
      checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
552
4
      OS << " is null after; previous value moved to";
553
4
      checkAndPrettyPrintRegion(OS, ThisRegion);
554
4
    }));
555
9
    return true;
556
9
  }
557
0
  return false;
558
0
}
559
560
void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
561
22
                                            CheckerContext &C) const {
562
  // To model unique_ptr::operator bool
563
22
  ProgramStateRef State = C.getState();
564
22
  const Expr *CallExpr = Call.getOriginExpr();
565
22
  const MemRegion *ThisRegion =
566
22
      cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
567
568
22
  SVal InnerPointerVal;
569
22
  if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
570
15
    InnerPointerVal = *InnerValPtr;
571
7
  } else {
572
    // In case of inner pointer SVal is not available we create
573
    // conjureSymbolVal for inner pointer value.
574
7
    auto InnerPointerType = getInnerPointerType(Call, C);
575
7
    if (InnerPointerType.isNull())
576
0
      return;
577
578
7
    const LocationContext *LC = C.getLocationContext();
579
7
    InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
580
7
        CallExpr, LC, InnerPointerType, C.blockCount());
581
7
    State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
582
7
  }
583
584
22
  if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
585
5
    State = State->BindExpr(CallExpr, C.getLocationContext(),
586
5
                            C.getSValBuilder().makeTruthVal(false));
587
588
5
    C.addTransition(State);
589
5
    return;
590
17
  } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
591
8
    State = State->BindExpr(CallExpr, C.getLocationContext(),
592
8
                            C.getSValBuilder().makeTruthVal(true));
593
594
8
    C.addTransition(State);
595
8
    return;
596
9
  } else if (move::isMovedFrom(State, ThisRegion)) {
597
0
    C.addTransition(
598
0
        State->BindExpr(CallExpr, C.getLocationContext(),
599
0
                        C.getSValBuilder().makeZeroVal(Call.getResultType())));
600
0
    return;
601
9
  } else {
602
9
    ProgramStateRef NotNullState, NullState;
603
9
    std::tie(NotNullState, NullState) =
604
9
        State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
605
606
9
    auto NullVal = C.getSValBuilder().makeNull();
607
    // Explicitly tracking the region as null.
608
9
    NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
609
610
9
    NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
611
9
                                    C.getSValBuilder().makeTruthVal(false));
612
9
    C.addTransition(NullState, C.getNoteTag(
613
9
                                   [ThisRegion](PathSensitiveBugReport &BR,
614
6
                                                llvm::raw_ostream &OS) {
615
6
                                     OS << "Assuming smart pointer";
616
6
                                     checkAndPrettyPrintRegion(OS, ThisRegion);
617
6
                                     OS << " is null";
618
6
                                   },
619
9
                                   /*IsPrunable=*/true));
620
9
    NotNullState =
621
9
        NotNullState->BindExpr(CallExpr, C.getLocationContext(),
622
9
                               C.getSValBuilder().makeTruthVal(true));
623
9
    C.addTransition(
624
9
        NotNullState,
625
9
        C.getNoteTag(
626
7
            [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
627
7
              OS << "Assuming smart pointer";
628
7
              checkAndPrettyPrintRegion(OS, ThisRegion);
629
7
              OS << " is non-null";
630
7
            },
631
9
            /*IsPrunable=*/true));
632
9
    return;
633
9
  }
634
22
}
635
636
69
void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
637
69
  auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
638
69
  Checker->ModelSmartPtrDereference =
639
69
      Mgr.getAnalyzerOptions().getCheckerBooleanOption(
640
69
          Checker, "ModelSmartPtrDereference");
641
69
}
642
643
138
bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
644
138
  const LangOptions &LO = mgr.getLangOpts();
645
138
  return LO.CPlusPlus;
646
138
}