vrnithinkumar created this revision.
Herald added subscribers: cfe-commits, martong.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D83836

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

Index: clang/test/Analysis/smart-ptr.cpp
===================================================================
--- clang/test/Analysis/smart-ptr.cpp
+++ clang/test/Analysis/smart-ptr.cpp
@@ -67,14 +67,14 @@
   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,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> &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]}}
+  }
+}
+
+/*
+// TODO: Enable this test after '=' operator overloading modeling.
+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);
+    Q->foo(); // warning
+  }
+}
+*/
Index: clang/test/Analysis/Inputs/system-header-simulator-cxx.h
===================================================================
--- clang/test/Analysis/Inputs/system-header-simulator-cxx.h
+++ clang/test/Analysis/Inputs/system-header-simulator-cxx.h
@@ -943,24 +943,25 @@
 
 #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() {}
+  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;
+  unique_ptr<T> &operator=(unique_ptr<T> &&p);
+};
+} // namespace std
 #endif
 
 #ifdef TEST_INLINABLE_ALLOCATORS
Index: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
+++ clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
@@ -30,7 +30,8 @@
 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 @@
   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,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<TrackedRegionMap>()) {
+    if (E.first->isSubRegionOf(Region))
+      State = State->remove<TrackedRegionMap>(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,40 @@
   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 {
+  if (Call) {
+    // Relax invalidation upon function calls: only invalidate parameters
+    // that are passed directly via non-const pointers or non-const references
+    // or rvalue references.
+    // In case of an InstanceCall don't invalidate the this-region since
+    // it is fully handled in checkPreCall and checkPostCall.
+    const MemRegion *ThisRegion = nullptr;
+    if (const auto *IC = dyn_cast<CXXInstanceCall>(Call))
+      ThisRegion = IC->getCXXThisVal().getAsRegion();
+
+    // Requested ("explicit") regions are the regions passed into the call
+    // directly, but not all of them end up being invalidated.
+    // But when they do, they appear in the InvalidatedRegions array as well.
+    for (const auto *Region : ExplicitRegions) {
+      if (ThisRegion == Region)
+        continue;
+      if (llvm::find(Regions, Region) != std::end(Regions))
+        State = removeTrackedRegions(State, Region);
+    }
+  } else {
+    // For invalidations that aren't caused by calls, assume nothing. In
+    // particular, direct write into an object's field invalidates the status.
+    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<CXXInstanceCall>(&Call);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to