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;
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,
} // 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().
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);
#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
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() {
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
+ }
+}