[Analyzer] Add checkRegionChanges for SmartPtrModeling

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
This commit is contained in:
Nithin Vadukkumchery Rajendrakumar 2020-07-15 02:54:44 +02:00
parent a54c42df9a
commit a560910211
3 changed files with 155 additions and 19 deletions

View 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);

View 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 &&);
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;
T *release() const;
void reset(T *p = nullptr) const;
void swap(unique_ptr<T> &p) const;
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;
operator bool() const;
};
}
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

View 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
}
}