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,6 +35,7 @@ using namespace ento; namespace { + class SmartPtrModeling : public Checker { @@ -76,6 +77,9 @@ {{"release"}, &SmartPtrModeling::handleRelease}, {{"swap", 1}, &SmartPtrModeling::handleSwap}, {{"get"}, &SmartPtrModeling::handleGet}}; + const CallDescription StdMakeUniqueCall{{"std", "make_unique"}}; + const CallDescription StdMakeUniqueForOverwriteCall{ + {"std", "make_unique_for_overwrite"}}; }; } // end of anonymous namespace @@ -137,12 +141,8 @@ // Helper method to get the inner pointer type of specialized smart pointer // Returns empty type if not found valid inner pointer type. -static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { - const auto *MethodDecl = dyn_cast_or_null(Call.getDecl()); - if (!MethodDecl || !MethodDecl->getParent()) - return {}; - - const auto *RecordDecl = MethodDecl->getParent(); +static QualType getInnerPointerType(const CXXRecordDecl *RecordDecl, + CheckerContext &C) { if (!RecordDecl || !RecordDecl->isInStdNamespace()) return {}; @@ -157,6 +157,17 @@ return C.getASTContext().getPointerType(InnerValueType.getCanonicalType()); } +// Helper method to get the inner pointer type of specialized smart pointer +// Returns empty type if not found valid inner pointer type. +static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { + const auto *MethodDecl = dyn_cast_or_null(Call.getDecl()); + if (!MethodDecl || !MethodDecl->getParent()) + return {}; + + const auto *RecordDecl = MethodDecl->getParent(); + return getInnerPointerType(RecordDecl, C); +} + // Helper method to pretty print region and avoid extra spacing. static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, const MemRegion *Region) { @@ -177,7 +188,63 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + if (Call.isCalled(StdMakeUniqueCall)) { + const Optional ThisRegionOpt = Call.getReturnValueUnderConstruction(); + if (!ThisRegionOpt) + return false; + + const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion(); + const TypedValueRegion *TVR = llvm::dyn_cast(ThisRegion); + assert(TVR && "expected std::make_unique to return a std::unique_ptr " + "object (which is typed)"); + const QualType InnerPtrType = + getInnerPointerType(TVR->getValueType()->getAsCXXRecordDecl(), C); + assert(InnerPtrType.getTypePtr() && + "expected to retrieve type of inner pointer of std::make_unique"); + const auto PtrVal = C.getSValBuilder().conjureSymbolVal( + Call.getOriginExpr(), C.getLocationContext(), InnerPtrType, + C.blockCount()); + + State = State->set(ThisRegion, PtrVal); + State = State->assume(PtrVal, true); + + auto &Engine = State->getStateManager().getOwningEngine(); + State = Engine.updateObjectsUnderConstruction( + *ThisRegionOpt, nullptr, State, C.getLocationContext(), + Call.getConstructionContext(), {}); + + C.addTransition(State); + return true; + } + + if (Call.isCalled(StdMakeUniqueForOverwriteCall)) { + const Optional ThisRegionOpt = Call.getReturnValueUnderConstruction(); + if (!ThisRegionOpt) + return false; + const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion(); + const auto NullVal = C.getSValBuilder().makeNull(); + State = State->set(ThisRegion, NullVal); + + auto &Engine = State->getStateManager().getOwningEngine(); + State = Engine.updateObjectsUnderConstruction( + *ThisRegionOpt, nullptr, State, C.getLocationContext(), + Call.getConstructionContext(), {}); + + C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + OS << "std::unique_ptr"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " constructed by std::make_unique_for_overwrite is null"; + })); + return true; + } + if (!smartptr::isStdSmartPtrCall(Call)) return false; diff --git a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h --- a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h +++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h @@ -978,6 +978,17 @@ void swap(unique_ptr &x, unique_ptr &y) noexcept { x.swap(y); } + +template +unique_ptr make_unique(Args &&...args); + +#if __cplusplus >= 202003L + +template +unique_ptr make_unique_for_overwrite(); + +#endif + } // namespace std #endif diff --git a/clang/test/Analysis/smart-ptr-text-output.cpp b/clang/test/Analysis/smart-ptr-text-output.cpp --- a/clang/test/Analysis/smart-ptr-text-output.cpp +++ b/clang/test/Analysis/smart-ptr-text-output.cpp @@ -313,3 +313,27 @@ // expected-note@-1{{Dereference of null smart pointer 'P'}} } } + +void makeUniqueReturnsNonNullUniquePtr() { + auto P = std::make_unique(); + if (!P) { // expected-note {{Taking false branch}} + P->foo(); // should have no warning here, path is impossible + } + P.reset(); // expected-note {{Smart pointer 'P' reset using a null value}} + // Now P is null + if (!P) { + // expected-note@-1 {{Taking true branch}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P'}} + } +} + +#if __cplusplus >= 202003L + +void makeUniqueForOverwriteReturnsNullUniquePtr() { + auto P = std::make_unique_for_overwrite(); // expected-note {{std::unique_ptr 'uptr' constructed by std::make_unique_for_overwrite is null}} + *P; // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P'}} +} + +#endif