34 #include "llvm/ADT/StringMap.h"
35 #include "llvm/Support/ErrorHandling.h"
38 using namespace clang;
43 class SmartPtrModeling
44 :
public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
47 bool isBoolConversionMethod(
const CallEvent &Call)
const;
51 bool ModelSmartPtrDereference =
false;
52 bool evalCall(
const CallEvent &Call, CheckerContext &C)
const;
53 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C)
const;
61 const char *Sep)
const override;
65 void handleReset(
const CallEvent &Call, CheckerContext &C)
const;
66 void handleRelease(
const CallEvent &Call, CheckerContext &C)
const;
67 void handleSwapMethod(
const CallEvent &Call, CheckerContext &C)
const;
68 void handleGet(
const CallEvent &Call, CheckerContext &C)
const;
69 bool handleAssignOp(
const CallEvent &Call, CheckerContext &C)
const;
70 bool handleMoveCtr(
const CallEvent &Call, CheckerContext &C,
71 const MemRegion *ThisRegion)
const;
72 bool updateMovedSmartPointers(CheckerContext &C,
const MemRegion *ThisRegion,
73 const MemRegion *OtherSmartPtrRegion,
74 const CallEvent &Call)
const;
75 void handleBoolConversion(
const CallEvent &Call, CheckerContext &C)
const;
76 bool handleComparisionOp(
const CallEvent &Call, CheckerContext &C)
const;
77 bool handleOstreamOperator(
const CallEvent &Call, CheckerContext &C)
const;
79 CheckerContext &C)
const;
80 std::pair<SVal, ProgramStateRef>
82 const MemRegion *ThisRegion,
const Expr *E,
85 using SmartPtrMethodHandlerFn =
86 void (SmartPtrModeling::*)(
const CallEvent &
Call, CheckerContext &)
const;
87 CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
88 {{
"reset"}, &SmartPtrModeling::handleReset},
89 {{
"release"}, &SmartPtrModeling::handleRelease},
90 {{
"swap", 1}, &SmartPtrModeling::handleSwapMethod},
91 {{
"get"}, &SmartPtrModeling::handleGet}};
92 const CallDescription StdSwapCall{{
"std",
"swap"}, 2};
93 const CallDescription StdMakeUniqueCall{{
"std",
"make_unique"}};
94 const CallDescription StdMakeUniqueForOverwriteCall{
95 {
"std",
"make_unique_for_overwrite"}};
104 if (!RD || !RD->getDeclContext()->isStdNamespace())
106 if (RD->getDeclName().isIdentifier())
107 return llvm::is_contained(Names, RD->getName());
111 constexpr llvm::StringLiteral
STD_PTR_NAMES[] = {
"shared_ptr",
"unique_ptr",
127 const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
128 if (!MethodDecl || !MethodDecl->getParent())
138 StringRef Name = RD->
getName();
139 return Name ==
"shared_ptr" || Name ==
"unique_ptr" || Name ==
"weak_ptr";
149 const auto *InnerPointVal =
State->get<TrackedRegionMap>(ThisRegion);
150 return InnerPointVal &&
158 static TrackedRegionMapTy
160 TrackedRegionMapTy::Factory &RegionMapFactory,
161 const MemRegion *Region) {
164 for (
const auto &E : RegionMap) {
165 if (E.first->isSubRegionOf(Region))
166 RegionMap = RegionMapFactory.remove(RegionMap, E.first);
172 const MemRegion *Region,
173 const SVal *RegionInnerPointerVal) {
174 if (RegionInnerPointerVal) {
175 State =
State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
177 State =
State->remove<TrackedRegionMap>(Region);
186 const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
190 auto TemplateArgs = TSD->getTemplateArgs().asArray();
191 if (TemplateArgs.empty())
193 auto InnerValueType = TemplateArgs[0].getAsType();
194 return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
202 const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
203 if (!FD || !FD->isFunctionTemplateSpecialization())
205 const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray();
206 if (TemplateArgs.size() == 0)
208 auto ValueType = TemplateArgs[0].getAsType();
209 return C.getASTContext().getPointerType(ValueType.getCanonicalType());
215 const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
216 if (!MethodDecl || !MethodDecl->getParent())
225 const MemRegion *Region) {
226 if (Region->canPrintPretty()) {
228 Region->printPretty(OS);
232 bool SmartPtrModeling::isBoolConversionMethod(
const CallEvent &Call)
const {
237 const auto *CD = dyn_cast_or_null<CXXConversionDecl>(
Call.getDecl());
238 return CD && CD->getConversionType()->isBooleanType();
249 return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace();
255 const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
262 if (OOK != clang::OO_LessLess)
275 bool SmartPtrModeling::evalCall(
const CallEvent &Call,
276 CheckerContext &C)
const {
283 if (handleComparisionOp(Call, C))
287 return handleOstreamOperator(Call, C);
289 if (StdSwapCall.matches(Call)) {
291 assert(
Call.getNumArgs() == 2 &&
"std::swap should have two arguments");
292 const Expr *FirstArg =
Call.getArgExpr(0);
295 return handleSwap(
State,
Call.getArgSVal(0),
Call.getArgSVal(1), C);
298 if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) {
299 if (!ModelSmartPtrDereference)
306 const auto PtrVal =
C.getSValBuilder().getConjuredHeapSymbolVal(
307 Call.getOriginExpr(),
C.getLocationContext(),
310 const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion();
311 State =
State->set<TrackedRegionMap>(ThisRegion, PtrVal);
330 auto &Engine =
State->getStateManager().getOwningEngine();
331 State = Engine.updateObjectsUnderConstruction(
332 *ThisRegionOpt,
nullptr,
State,
C.getLocationContext(),
333 Call.getConstructionContext(), {});
344 if (isBoolConversionMethod(Call)) {
345 const MemRegion *ThisR =
346 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
348 if (ModelSmartPtrDereference) {
353 handleBoolConversion(Call, C);
363 C.addTransition(
State->BindExpr(
364 Call.getOriginExpr(),
C.getLocationContext(),
365 C.getSValBuilder().makeZeroVal(
Call.getResultType())));
371 if (!ModelSmartPtrDereference)
374 if (
const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
375 if (CC->getDecl()->isCopyConstructor())
378 const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
382 QualType ThisType = cast<CXXMethodDecl>(
Call.getDecl())->getThisType();
384 if (CC->getDecl()->isMoveConstructor())
385 return handleMoveCtr(Call, C, ThisRegion);
387 if (
Call.getNumArgs() == 0) {
388 auto NullVal =
C.getSValBuilder().makeNullWithType(ThisType);
389 State =
State->set<TrackedRegionMap>(ThisRegion, NullVal);
392 State,
C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
393 llvm::raw_ostream &OS) {
394 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
395 !BR.isInteresting(ThisRegion))
397 OS <<
"Default constructed smart pointer";
398 checkAndPrettyPrintRegion(OS, ThisRegion);
402 const auto *TrackingExpr =
Call.getArgExpr(0);
403 assert(TrackingExpr->getType()->isPointerType() &&
404 "Adding a non pointer value to TrackedRegionMap");
405 auto ArgVal =
Call.getArgSVal(0);
406 State =
State->set<TrackedRegionMap>(ThisRegion, ArgVal);
408 C.addTransition(
State,
C.getNoteTag([ThisRegion, TrackingExpr,
409 ArgVal](PathSensitiveBugReport &BR,
410 llvm::raw_ostream &OS) {
411 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
412 !BR.isInteresting(ThisRegion))
414 bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
415 OS <<
"Smart pointer";
416 checkAndPrettyPrintRegion(OS, ThisRegion);
417 if (ArgVal.isZeroConstant())
418 OS <<
" is constructed using a null value";
420 OS <<
" is constructed";
426 if (handleAssignOp(Call, C))
429 const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
432 (this->**Handler)(Call, C);
434 return C.isDifferent();
437 std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal(
440 const auto *Ptr =
State->get<TrackedRegionMap>(ThisRegion);
442 return {*Ptr,
State};
443 auto Val =
C.getSValBuilder().conjureSymbolVal(E,
C.getLocationContext(),
444 Type,
C.blockCount());
445 State =
State->set<TrackedRegionMap>(ThisRegion, Val);
449 bool SmartPtrModeling::handleComparisionOp(
const CallEvent &Call,
450 CheckerContext &C)
const {
451 const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
458 if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less ||
459 OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual ||
460 OOK == OO_Spaceship))
470 SVal S) -> std::pair<SVal, ProgramStateRef> {
471 if (S.isZeroConstant()) {
474 const MemRegion *Reg = S.getAsRegion();
476 "this pointer of std::unique_ptr should be obtainable as MemRegion");
478 return retrieveOrConjureInnerPtrVal(
State, Reg, E,
Type, C);
482 SVal Second =
Call.getArgSVal(1);
483 const auto *FirstExpr =
Call.getArgExpr(0);
484 const auto *SecondExpr =
Call.getArgExpr(1);
486 const auto *ResultExpr =
Call.getOriginExpr();
487 const auto *LCtx =
C.getLocationContext();
488 auto &Bldr =
C.getSValBuilder();
491 SVal FirstPtrVal, SecondPtrVal;
492 std::tie(FirstPtrVal,
State) = makeSValFor(
State, FirstExpr, First);
493 std::tie(SecondPtrVal,
State) = makeSValFor(
State, SecondExpr, Second);
496 auto RetVal = Bldr.evalBinOp(
State, BOK, FirstPtrVal, SecondPtrVal,
497 Call.getResultType());
499 if (OOK != OO_Spaceship) {
501 std::tie(TrueState, FalseState) =
502 State->assume(*RetVal.getAs<DefinedOrUnknownSVal>());
505 TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(
true)));
508 FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(
false)));
510 C.addTransition(
State->BindExpr(ResultExpr, LCtx, RetVal));
515 bool SmartPtrModeling::handleOstreamOperator(
const CallEvent &Call,
516 CheckerContext &C)
const {
526 const auto StreamVal =
Call.getArgSVal(0);
527 const MemRegion *StreamThisRegion = StreamVal.getAsRegion();
528 if (!StreamThisRegion)
531 State->invalidateRegions({StreamThisRegion},
Call.getOriginExpr(),
532 C.blockCount(),
C.getLocationContext(),
false);
534 State->BindExpr(
Call.getOriginExpr(),
C.getLocationContext(), StreamVal);
539 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
540 CheckerContext &C)
const {
543 TrackedRegionMapTy TrackedRegions =
State->get<TrackedRegionMap>();
544 for (
auto E : TrackedRegions) {
545 const MemRegion *Region = E.first;
546 bool IsRegDead = !SymReaper.isLiveRegion(Region);
549 State =
State->remove<TrackedRegionMap>(Region);
555 const char *NL,
const char *Sep)
const {
556 TrackedRegionMapTy RS =
State->get<TrackedRegionMap>();
559 Out << Sep <<
"Smart ptr regions :" << NL;
561 I.first->dumpToStream(Out);
575 const CallEvent *Call)
const {
576 TrackedRegionMapTy RegionMap =
State->get<TrackedRegionMap>();
577 TrackedRegionMapTy::Factory &RegionMapFactory =
578 State->get_context<TrackedRegionMap>();
579 for (
const auto *Region : Regions)
581 Region->getBaseRegion());
582 return State->set<TrackedRegionMap>(RegionMap);
586 SymbolReaper &SR)
const {
588 TrackedRegionMapTy TrackedRegions =
State->get<TrackedRegionMap>();
589 for (
auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
590 SVal Val = I->second;
591 for (
auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
597 void SmartPtrModeling::handleReset(
const CallEvent &Call,
598 CheckerContext &C)
const {
600 const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
604 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
608 assert(
Call.getArgExpr(0)->getType()->isPointerType() &&
609 "Adding a non pointer value to TrackedRegionMap");
610 State =
State->set<TrackedRegionMap>(ThisRegion,
Call.getArgSVal(0));
611 const auto *TrackingExpr =
Call.getArgExpr(0);
613 State,
C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
614 llvm::raw_ostream &OS) {
615 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
616 !BR.isInteresting(ThisRegion))
618 bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
619 OS <<
"Smart pointer";
620 checkAndPrettyPrintRegion(OS, ThisRegion);
621 OS <<
" reset using a null value";
627 void SmartPtrModeling::handleRelease(
const CallEvent &Call,
628 CheckerContext &C)
const {
630 const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
634 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
638 const auto *InnerPointVal =
State->get<TrackedRegionMap>(ThisRegion);
645 QualType ThisType = cast<CXXMethodDecl>(
Call.getDecl())->getThisType();
646 auto ValueToUpdate =
C.getSValBuilder().makeNullWithType(ThisType);
647 State =
State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
649 C.addTransition(
State,
C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
650 llvm::raw_ostream &OS) {
651 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
652 !BR.isInteresting(ThisRegion))
655 OS <<
"Smart pointer";
656 checkAndPrettyPrintRegion(OS, ThisRegion);
657 OS <<
" is released and set to null";
663 void SmartPtrModeling::handleSwapMethod(
const CallEvent &Call,
664 CheckerContext &C)
const {
666 const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
670 auto State =
C.getState();
671 handleSwap(
State, IC->getCXXThisVal(),
Call.getArgSVal(0), C);
675 SVal Second, CheckerContext &C)
const {
676 const MemRegion *FirstThisRegion =
First.getAsRegion();
677 if (!FirstThisRegion)
679 const MemRegion *SecondThisRegion = Second.getAsRegion();
680 if (!SecondThisRegion)
683 const auto *FirstInnerPtrVal =
State->get<TrackedRegionMap>(FirstThisRegion);
684 const auto *SecondInnerPtrVal =
685 State->get<TrackedRegionMap>(SecondThisRegion);
690 C.addTransition(
State,
C.getNoteTag([FirstThisRegion, SecondThisRegion](
691 PathSensitiveBugReport &BR,
692 llvm::raw_ostream &OS) {
693 if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
695 if (BR.isInteresting(FirstThisRegion) &&
696 !BR.isInteresting(SecondThisRegion)) {
697 BR.markInteresting(SecondThisRegion);
698 BR.markNotInteresting(FirstThisRegion);
700 if (BR.isInteresting(SecondThisRegion) &&
701 !BR.isInteresting(FirstThisRegion)) {
702 BR.markInteresting(FirstThisRegion);
703 BR.markNotInteresting(SecondThisRegion);
711 void SmartPtrModeling::handleGet(
const CallEvent &Call,
712 CheckerContext &C)
const {
714 const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
718 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
722 SVal InnerPointerVal;
723 std::tie(InnerPointerVal,
State) = retrieveOrConjureInnerPtrVal(
724 State, ThisRegion,
Call.getOriginExpr(),
Call.getResultType(), C);
731 bool SmartPtrModeling::handleAssignOp(
const CallEvent &Call,
732 CheckerContext &C)
const {
734 const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
740 const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
744 QualType ThisType = cast<CXXMethodDecl>(
Call.getDecl())->getThisType();
746 const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
748 if (!OtherSmartPtrRegion) {
749 bool AssignedNull =
Call.getArgSVal(0).isZeroConstant();
752 auto NullVal =
C.getSValBuilder().makeNullWithType(ThisType);
753 State =
State->set<TrackedRegionMap>(ThisRegion, NullVal);
754 C.addTransition(
State,
C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
755 llvm::raw_ostream &OS) {
756 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
757 !BR.isInteresting(ThisRegion))
759 OS <<
"Smart pointer";
760 checkAndPrettyPrintRegion(OS, ThisRegion);
761 OS <<
" is assigned to null";
766 return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
769 bool SmartPtrModeling::handleMoveCtr(
const CallEvent &Call, CheckerContext &C,
770 const MemRegion *ThisRegion)
const {
771 const auto *OtherSmartPtrRegion =
Call.getArgSVal(0).getAsRegion();
772 if (!OtherSmartPtrRegion)
775 return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
778 bool SmartPtrModeling::updateMovedSmartPointers(
779 CheckerContext &C,
const MemRegion *ThisRegion,
780 const MemRegion *OtherSmartPtrRegion,
const CallEvent &Call)
const {
782 QualType ThisType = cast<CXXMethodDecl>(
Call.getDecl())->getThisType();
783 const auto *OtherInnerPtr =
State->get<TrackedRegionMap>(OtherSmartPtrRegion);
785 State =
State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
787 auto NullVal =
C.getSValBuilder().makeNullWithType(ThisType);
788 State =
State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
789 bool IsArgValNull = OtherInnerPtr->isZeroConstant();
793 C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
794 PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
797 if (BR.isInteresting(OtherSmartPtrRegion)) {
798 OS <<
"Smart pointer";
800 OS <<
" is null after being moved to";
803 if (BR.isInteresting(ThisRegion) && IsArgValNull) {
804 OS <<
"A null pointer value is moved to";
806 BR.markInteresting(OtherSmartPtrRegion);
814 auto NullVal =
C.getSValBuilder().makeNullWithType(ThisType);
815 State =
State->remove<TrackedRegionMap>(ThisRegion);
816 State =
State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
817 C.addTransition(
State,
C.getNoteTag([OtherSmartPtrRegion,
818 ThisRegion](PathSensitiveBugReport &BR,
819 llvm::raw_ostream &OS) {
821 !BR.isInteresting(OtherSmartPtrRegion))
823 OS <<
"Smart pointer";
825 OS <<
" is null after; previous value moved to";
833 void SmartPtrModeling::handleBoolConversion(
const CallEvent &Call,
834 CheckerContext &C)
const {
838 const MemRegion *ThisRegion =
839 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
841 QualType ThisType = cast<CXXMethodDecl>(
Call.getDecl())->getThisType();
843 SVal InnerPointerVal;
844 if (
const auto *InnerValPtr =
State->get<TrackedRegionMap>(ThisRegion)) {
845 InnerPointerVal = *InnerValPtr;
850 if (InnerPointerType.isNull())
854 InnerPointerVal =
C.getSValBuilder().conjureSymbolVal(
855 CallExpr, LC, InnerPointerType,
C.blockCount());
856 State =
State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
859 if (
State->isNull(InnerPointerVal).isConstrainedTrue()) {
861 C.getSValBuilder().makeTruthVal(
false));
865 }
else if (
State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
867 C.getSValBuilder().makeTruthVal(
true));
874 C.getSValBuilder().makeZeroVal(
Call.getResultType())));
878 std::tie(NotNullState, NullState) =
879 State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
881 auto NullVal =
C.getSValBuilder().makeNullWithType(ThisType);
883 NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
885 NullState = NullState->BindExpr(
CallExpr,
C.getLocationContext(),
886 C.getSValBuilder().makeTruthVal(
false));
887 C.addTransition(NullState,
C.getNoteTag(
888 [ThisRegion](PathSensitiveBugReport &BR,
889 llvm::raw_ostream &OS) {
890 OS <<
"Assuming smart pointer";
891 checkAndPrettyPrintRegion(OS, ThisRegion);
896 NotNullState->BindExpr(
CallExpr,
C.getLocationContext(),
897 C.getSValBuilder().makeTruthVal(
true));
901 [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
902 OS <<
"Assuming smart pointer";
903 checkAndPrettyPrintRegion(OS, ThisRegion);
904 OS <<
" is non-null";
911 void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
912 auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
913 Checker->ModelSmartPtrDereference =
914 Mgr.getAnalyzerOptions().getCheckerBooleanOption(
915 Checker,
"ModelSmartPtrDereference");
918 bool ento::shouldRegisterSmartPtrModeling(
const CheckerManager &mgr) {