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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits