Coverage Report

Created: 2021-01-19 06:58

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