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