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,18 @@ } // namespace ento } // namespace clang +// If a region is removed all of the subregions needs to be removed too. +static ProgramStateRef removeTrackedRegions(ProgramStateRef State, + const MemRegion *Region) { + if (!Region) + return State; + for (const auto &E : State->get()) { + if (E.first->isSubRegionOf(Region)) + State = State->remove(E.first); + } + return State; +} + 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 +177,16 @@ C.addTransition(State); } +ProgramStateRef SmartPtrModeling::checkRegionChanges( + ProgramStateRef State, const InvalidatedSymbols *Invalidated, + ArrayRef ExplicitRegions, + ArrayRef Regions, const LocationContext *LCtx, + const CallEvent *Call) const { + for (const auto *Region : Regions) + State = removeTrackedRegions(State, Region->getBaseRegion()); + return State; +} + 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() {} + 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; + unique_ptr &operator=(unique_ptr &&p); +}; +} // 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,105 @@ 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]}} + } +} + +/* +// TODO: Enable this test after '=' operator overloading modeling. +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); + Q->foo(); // warning + } +} +*/