[Analyzer] Add checkRegionChanges for SmartPtrModeling
authorNithin Vadukkumchery Rajendrakumar <vrnithinkumar@gmail.com>
Wed, 15 Jul 2020 00:54:44 +0000 (02:54 +0200)
committerNithin Vadukkumchery Rajendrakumar <vrnithinkumar@gmail.com>
Mon, 20 Jul 2020 23:13:40 +0000 (01:13 +0200)
    Summary:
    Implemented checkRegionChanges for SmartPtrModeling

    Reviewers: NoQ, Szelethus, vsavchenko, xazax.hun

    Reviewed By: NoQ, vsavchenko, xazax.hun

    Subscribers: martong, cfe-commits
    Tags: #clang

    Differential Revision: https://reviews.llvm.org/D83836

clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
clang/test/Analysis/Inputs/system-header-simulator-cxx.h
clang/test/Analysis/smart-ptr.cpp

index bcc7d41..1b21745 100644 (file)
@@ -30,7 +30,8 @@ using namespace clang;
 using namespace ento;
 
 namespace {
-class SmartPtrModeling : public Checker<eval::Call, check::DeadSymbols> {
+class SmartPtrModeling
+    : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges> {
 
   bool isNullAfterMoveMethod(const CallEvent &Call) const;
 
@@ -40,6 +41,12 @@ public:
   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<const MemRegion *> ExplicitRegions,
+                     ArrayRef<const MemRegion *> Regions,
+                     const LocationContext *LCtx, const CallEvent *Call) const;
 
 private:
   ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
@@ -87,6 +94,20 @@ bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
 } // 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 @@ void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
   C.addTransition(State);
 }
 
+ProgramStateRef SmartPtrModeling::checkRegionChanges(
+    ProgramStateRef State, const InvalidatedSymbols *Invalidated,
+    ArrayRef<const MemRegion *> ExplicitRegions,
+    ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
+    const CallEvent *Call) const {
+  TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
+  TrackedRegionMapTy::Factory &RegionMapFactory =
+      State->get_context<TrackedRegionMap>();
+  for (const auto *Region : Regions)
+    RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
+                                        Region->getBaseRegion());
+  return State->set<TrackedRegionMap>(RegionMap);
+}
+
 void SmartPtrModeling::handleReset(const CallEvent &Call,
                                    CheckerContext &C) const {
   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
index 1dee329..d5e7c4c 100644 (file)
@@ -953,24 +953,25 @@ next(ForwardIterator it,
 
 #if __cplusplus >= 201103L
 namespace std {
-  template <typename T> // 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<T> &p) const;
-
-    typename std::add_lvalue_reference<T>::type operator*() const;
-    T *operator->() const;
-    operator bool() const;
-  };
-}
+template <typename T> // 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<T> &p) noexcept;
+
+  typename std::add_lvalue_reference<T>::type operator*() const;
+  T *operator->() const noexcept;
+  operator bool() const noexcept;
+  unique_ptr<T> &operator=(unique_ptr<T> &&p) noexcept;
+};
+} // namespace std
 #endif
 
 #ifdef TEST_INLINABLE_ALLOCATORS
index 4ab7e2b..5645afc 100644 (file)
@@ -67,14 +67,14 @@ void derefAfterRelease() {
   std::unique_ptr<A> 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<A> 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 @@ void derefOnReleasedNullRawPtr() {
   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> &a);
+void pass_smart_ptr_by_const_ref(const std::unique_ptr<A> &a);
+void pass_smart_ptr_by_rvalue_ref(std::unique_ptr<A> &&a);
+void pass_smart_ptr_by_const_rvalue_ref(const std::unique_ptr<A> &&a);
+void pass_smart_ptr_by_ptr(std::unique_ptr<A> *a);
+void pass_smart_ptr_by_const_ptr(const std::unique_ptr<A> *a);
+
+void regioninvalidationTest() {
+  {
+    std::unique_ptr<A> P;
+    pass_smart_ptr_by_ref(P);
+    P->foo(); // no-warning
+  }
+  {
+    std::unique_ptr<A> P;
+    pass_smart_ptr_by_const_ref(P);
+    P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}}
+  }
+  {
+    std::unique_ptr<A> P;
+    pass_smart_ptr_by_rvalue_ref(std::move(P));
+    P->foo(); // no-warning
+  }
+  {
+    std::unique_ptr<A> 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<A> P;
+    pass_smart_ptr_by_ptr(&P);
+    P->foo();
+  }
+  {
+    std::unique_ptr<A> 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<A> 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<A> P(new A());
+    std::unique_ptr<A> Q;
+    Q = std::move(P);
+    Q->foo(); // no-warning
+  }
+  {
+    std::unique_ptr<A> P;
+    std::unique_ptr<A> Q;
+    Q = std::move(P);
+    // TODO: Fix test with expecting warning after '=' operator overloading modeling.
+    Q->foo(); // no-warning
+  }
+}