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 @@ -30,7 +30,8 @@ using namespace ento; namespace { -class SmartPtrModeling : public Checker { +class SmartPtrModeling + : public Checker { bool isNullAfterMoveMethod(const CallEvent &Call) const; @@ -40,6 +41,12 @@ bool evalCall(const CallEvent &Call, CheckerContext &C) const; void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + ProgramStateRef + checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef ExplicitRegions, + ArrayRef Regions, + const LocationContext *LCtx, const CallEvent *Call) const; private: ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C, @@ -87,6 +94,20 @@ } // namespace ento } // namespace clang +// If a region is removed all of the subregions need to be removed too. +static TrackedRegionMapTy +removeTrackedSubregions(TrackedRegionMapTy RegionMap, + TrackedRegionMapTy::Factory &RegionMapFactory, + const MemRegion *Region) { + if (!Region) + return RegionMap; + for (const auto &E : RegionMap) { + if (E.first->isSubRegionOf(Region)) + RegionMap = RegionMapFactory.remove(RegionMap, E.first); + } + return RegionMap; +} + bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { // TODO: Update CallDescription to support anonymous calls? // TODO: Handle other methods, such as .get() or .release(). @@ -158,6 +179,20 @@ C.addTransition(State); } +ProgramStateRef SmartPtrModeling::checkRegionChanges( + ProgramStateRef State, const InvalidatedSymbols *Invalidated, + ArrayRef ExplicitRegions, + ArrayRef Regions, const LocationContext *LCtx, + const CallEvent *Call) const { + TrackedRegionMapTy RegionMap = State->get(); + TrackedRegionMapTy::Factory &RegionMapFactory = + State->get_context(); + for (const auto *Region : Regions) + RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory, + Region->getBaseRegion()); + return State->set(RegionMap); +} + void SmartPtrModeling::handleReset(const CallEvent &Call, CheckerContext &C) const { const auto *IC = dyn_cast(&Call); 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 @@ -943,24 +943,25 @@ #if __cplusplus >= 201103L namespace std { - template // TODO: Implement the stub for deleter. - class unique_ptr { - public: - unique_ptr() {} - unique_ptr(T *) {} - unique_ptr(const unique_ptr &) = delete; - unique_ptr(unique_ptr &&); - - T *get() const; - T *release() const; - void reset(T *p = nullptr) const; - void swap(unique_ptr &p) const; - - typename std::add_lvalue_reference::type operator*() const; - T *operator->() const; - operator bool() const; - }; -} +template // TODO: Implement the stub for deleter. +class unique_ptr { +public: + unique_ptr() noexcept {} + unique_ptr(T *) noexcept {} + unique_ptr(const unique_ptr &) noexcept = delete; + unique_ptr(unique_ptr &&) noexcept; + + T *get() const noexcept; + T *release() noexcept; + void reset(T *p = nullptr) noexcept; + void swap(unique_ptr &p) noexcept; + + typename std::add_lvalue_reference::type operator*() const; + T *operator->() const noexcept; + operator bool() const noexcept; + unique_ptr &operator=(unique_ptr &&p) noexcept; +}; +} // namespace std #endif #ifdef TEST_INLINABLE_ALLOCATORS diff --git a/clang/test/Analysis/smart-ptr.cpp b/clang/test/Analysis/smart-ptr.cpp --- a/clang/test/Analysis/smart-ptr.cpp +++ b/clang/test/Analysis/smart-ptr.cpp @@ -67,14 +67,14 @@ std::unique_ptr P(new A()); P.release(); clang_analyzer_numTimesReached(); // expected-warning {{1}} - P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} } void derefAfterReset() { std::unique_ptr P(new A()); P.reset(); clang_analyzer_numTimesReached(); // expected-warning {{1}} - P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} } void derefAfterResetWithNull() { @@ -101,3 +101,103 @@ A *AP = P.release(); AP->foo(); // expected-warning {{Called C++ object pointer is null [core.CallAndMessage]}} } + +void pass_smart_ptr_by_ref(std::unique_ptr &a); +void pass_smart_ptr_by_const_ref(const std::unique_ptr &a); +void pass_smart_ptr_by_rvalue_ref(std::unique_ptr &&a); +void pass_smart_ptr_by_const_rvalue_ref(const std::unique_ptr &&a); +void pass_smart_ptr_by_ptr(std::unique_ptr *a); +void pass_smart_ptr_by_const_ptr(const std::unique_ptr *a); + +void regioninvalidationTest() { + { + std::unique_ptr P; + pass_smart_ptr_by_ref(P); + P->foo(); // no-warning + } + { + std::unique_ptr P; + pass_smart_ptr_by_const_ref(P); + P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + } + { + std::unique_ptr P; + pass_smart_ptr_by_rvalue_ref(std::move(P)); + P->foo(); // no-warning + } + { + std::unique_ptr P; + pass_smart_ptr_by_const_rvalue_ref(std::move(P)); + P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + } + { + std::unique_ptr P; + pass_smart_ptr_by_ptr(&P); + P->foo(); + } + { + std::unique_ptr P; + pass_smart_ptr_by_const_ptr(&P); + P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + } +} + +struct StructWithSmartPtr { + std::unique_ptr P; +}; + +void pass_struct_with_smart_ptr_by_ref(StructWithSmartPtr &a); +void pass_struct_with_smart_ptr_by_const_ref(const StructWithSmartPtr &a); +void pass_struct_with_smart_ptr_by_rvalue_ref(StructWithSmartPtr &&a); +void pass_struct_with_smart_ptr_by_const_rvalue_ref(const StructWithSmartPtr &&a); +void pass_struct_with_smart_ptr_by_ptr(StructWithSmartPtr *a); +void pass_struct_with_smart_ptr_by_const_ptr(const StructWithSmartPtr *a); + +void regioninvalidationTestWithinStruct() { + { + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_ref(S); + S.P->foo(); // no-warning + } + { + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_const_ref(S); + S.P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + } + { + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_rvalue_ref(std::move(S)); + S.P->foo(); // no-warning + } + { + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_const_rvalue_ref(std::move(S)); + S.P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + } + { + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_ptr(&S); + S.P->foo(); + } + { + StructWithSmartPtr S; + pass_struct_with_smart_ptr_by_const_ptr(&S); + S.P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + } +} + +void derefAfterAssignment() { + { + std::unique_ptr P(new A()); + std::unique_ptr Q; + Q = std::move(P); + Q->foo(); // no-warning + } + { + std::unique_ptr P; + std::unique_ptr Q; + Q = std::move(P); + // TODO: Fix test with expecting warning after '=' operator overloading modeling. + Q->foo(); // no-warning + } +}