Boost logo

Boost :

From: Andrey Semashev (andrey.semashev_at_[hidden])
Date: 2020-07-31 13:11:17


On 2020-07-31 15:56, Andrey Semashev wrote:
> Please, don't top-post. Our discussion guidelines are described here:
>
> https://www.boost.org/community/policy.html
>
> On 2020-07-31 13:06, 逸霖 杨 via Boost wrote:
>> Hi Andrey,
>> Thanks for repaid response.
>>
>> It is freeing the memory at last decrement, but not immediately.
>>
>> It frees memory when all related objects’ reference count are
>> decreased to zero.
>
> Yes, it frees memory when the reference count becomes zero. But it
> *destroys* the object (i.e. calls its destructor) earlier, before the
> reference count is zero.
>
>>      void Dispose() {
>>          if (this->_ptr != nullptr) {
>>              PTR* _ptr = this->_ptr;
>>              this->_ptr = nullptr;
>>              _ptr->~PTR();
>
>                ^^^ here ^^^
>
>>              RelRef(_ptr);
>>          }
>> }
>
>> As above,  dispose method calls its pointing object’s destructor, but
>> not free that object with free(ptr) function,
>
> After the destructor call, the object no longer exists, regardless of
> whether the memory it used to be placed in is freed or not.
>
> Case 1:
>
>   struct A
>   {
>     unsigned int x = 0u;
>
>     ~A() { x = 0xBAADF00D; }
>   };
>
>   rcgc_shared_ptr< A > p1;
>   {
>     rcgc_shared_ptr< A > p2(new A());
>     p1 = p2;
>     std::cout << p1->x << std::endl;
>   }
>   std::cout << p1->x << std::endl; // (1)
>
> The above is a bug because (1) accesses the object A after its
> destruction. If A::x was, say a unique_ptr, it would be null or pointing
> to already freed memory.
>
> Case 2 (which follows from Case 1)
>
>   struct B;
>
>   struct A
>   {
>     rcgc_shared_ptr< B > m_b;
>     std::string m_x;
>
>     ~A() { std::cout << m_b->m_x << std::endl; }
>   };
>
>   struct B
>   {
>     rcgc_shared_ptr< A > m_a;
>     std::string m_x;
>
>     ~B() { std::cout << m_a->m_x << std::endl; }
>   };
>
>   rcgc_shared_ptr< A > p(new A());
>   p->m_b.reset(new B());
>   p->m_b->m_a = p;
>
> When p gets destroyed, it will destroy the object A, even though it is
> still referenced by B::m_a. When A::m_b is destroyed, it will call ~B,
> which will access A::m_x, which is already destroyed.

I should note that in the code samples above I'm ignoring the mismatch
between `operator new` that is used to allocate objects and `std::free`
that rcgc_shared_ptr uses to free memory. That is another bug, of course.

>> If you have Visual Studio and run the code which detects memory leak with
>> #ifdef _WIN32
>>      _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
>> #endif
>>
>> You will see no memory leak at all.
>
> This check only shows that no memory is leaked. The problem here is not
> a memory leak but incorrect memory access (potential use-after-destory
> and use-after-free).


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk