diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -35,11 +35,19 @@ using namespace ento; namespace { + +enum class MakeUniqueKind { + Regular, // ie, std::make_unique + ForOverwrite, // ie, std::make_unique_for_overwrite + None // ie, is neither of the above two +}; + class SmartPtrModeling : public Checker { + check::LiveSymbols, check::Bind> { bool isBoolConversionMethod(const CallEvent &Call) const; + MakeUniqueKind isStdMakeUniqueCall(const CallEvent &Call) const; public: // Whether the checker should model for null dereferences of smart pointers. @@ -56,6 +64,7 @@ void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const override; void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; + void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; private: void handleReset(const CallEvent &Call, CheckerContext &C) const; @@ -68,6 +77,7 @@ bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, const MemRegion *OtherSmartPtrRegion) const; void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; + void handleStdMakeUnique(const CallEvent &Call, CheckerContext &C, MakeUniqueKind Kind) const; using SmartPtrMethodHandlerFn = void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; @@ -79,7 +89,21 @@ }; } // end of anonymous namespace +class MakeUniqueKindWrapper { + const MakeUniqueKind Kind; + +public: + MakeUniqueKindWrapper(MakeUniqueKind Kind) : Kind(Kind) {} + MakeUniqueKind get() const { return Kind; } + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(static_cast(Kind)); + } + bool operator==(const MakeUniqueKind &RHS) const { return Kind == RHS; } + bool operator!=(const MakeUniqueKind &RHS) const { return Kind != RHS; } +}; + REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) +REGISTER_LIST_WITH_PROGRAMSTATE(MakeUniqueKindList, MakeUniqueKindWrapper) // Define the inter-checker API. namespace clang { @@ -175,8 +199,34 @@ return CD && CD->getConversionType()->isBooleanType(); } +MakeUniqueKind SmartPtrModeling::isStdMakeUniqueCall(const CallEvent &Call) const { + if (Call.getKind() != CallEventKind::CE_Function) + return MakeUniqueKind::None; + const auto *D = Call.getDecl(); + if (!D) + return MakeUniqueKind::None; + const auto *FTD = llvm::dyn_cast(D); + if (!FTD) + return MakeUniqueKind::None; + if (FTD->getDeclName().isIdentifier()) { + StringRef Name = FTD->getName(); + if (Name == "make_unique") + return MakeUniqueKind::Regular; + else if (Name == "make_unique_for_overwrite") + return MakeUniqueKind::ForOverwrite; + } + return MakeUniqueKind::None; +} + bool SmartPtrModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { + + MakeUniqueKind Kind = isStdMakeUniqueCall(Call); + if (Kind != MakeUniqueKind::None) { + handleStdMakeUnique(Call, C, Kind); + return true; + } + ProgramStateRef State = C.getState(); if (!smartptr::isStdSmartPtrCall(Call)) return false; @@ -272,6 +322,54 @@ return C.isDifferent(); } +void SmartPtrModeling::handleStdMakeUnique(const CallEvent &Call, + CheckerContext &C, MakeUniqueKind Kind) const { + assert(Kind != MakeUniqueKind::None && "Call is not to make_unique or make_unique_for_overwrite"); + ProgramStateRef State = C.getState(); + State = State->add(Kind); + C.addTransition(State); +} + +bool isUniquePtrType(QualType QT) { + const auto *T = QT.getTypePtr(); + if (!T) + return false; + const auto *Decl = T->getAsCXXRecordDecl(); + if (!Decl || !Decl->getDeclContext()->isStdNamespace()) + return false; + const IdentifierInfo *ID = Decl->getIdentifier(); + if (!ID) + return false; + const StringRef Name = ID->getName(); + return Name == "unique_ptr"; +} + +void SmartPtrModeling::checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const { + const auto *TVR = dyn_cast_or_null(L.getAsRegion()); + if (!TVR) + return; + if (!isUniquePtrType(TVR->getValueType())) + return; + const auto *ThisRegion = dyn_cast(TVR); + if (!ThisRegion) + return; + + ProgramStateRef State = C.getState(); + auto KindList = State->get(); + assert(!KindList.isEmpty() && "Expected list to contain the kind of the last make_unique"); + auto Kind = KindList.getHead(); + assert(Kind != MakeUniqueKind::None && "Bind is not to make_unique or make_unique_for_overwrite"); + if (Kind == MakeUniqueKind::ForOverwrite) { + auto RHSVal = C.getSValBuilder().makeNull(); + State = State->set(ThisRegion, RHSVal); + } else { + // TODO: Encode information that the inner pointer for + // unique_ptr made by std::make_unique is *not* null + } + State = State->set(KindList.getTail()); + C.addTransition(State); +} + void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { ProgramStateRef State = C.getState();