https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118854

--- Comment #8 from Nicholas Williams <nicholas at nicholaswilliams dot net> ---
(In reply to Andrew Pinski from comment #4)
> fsanitize=address just changes timing slightly to give a race condition
> between the thread and the constructor to show up.

That's what I had assumed.

(In reply to Andrew Pinski from comment #5)
> One way of fixing this is to move _pInstance to be a static variable
> inside instance function which makes the initialization dynamic and
> correctly ordered.

This is not correct. I still get a heap-use-after free in the copy constructor
with this code:

  instance()
  {
    static std::shared_ptr< Resource > pInstance = std::make_shared< Resource
>();

    std::shared_ptr< Resource > ptr( pInstance );
    if ( !ptr )
    {
      throw std::runtime_error( "Resource has already destructed" );
    }
    return ptr;
  }

(In reply to Andrew Pinski from comment #3)
> The bug is in your code. 
> 
> > If I reverse the order of these two statements
> 
> That is correct. You have an order of initialization issue.

I disagree with the contention that the bug is in my code. I do agree that
there's a disparity in initialization order, but it's considerably more
difficult to properly control initialization order when you have a
multi-hundred-CPP-file application.

Notably, the copy constructor is declared noexcept. While I know that's
syntatically about *exceptions*, the *implication* is that it should be safe to
call that construct and not experience errors. A segfault/heap-after-use is an
error.

More importantly, though, the C++ documentation for shared_ptr mentions thread
safety in a few ways:

> To satisfy thread safety requirements, the reference counters are typically
> incremented using an equivalent of std::atomic::fetch_add with
> std::memory_order_relaxed.

And:

> If multiple threads of execution access the same shared_ptr object without
> synchronization and any of those accesses uses a non-const member function
> of shared_ptr then a data race will occur

But the copy constructor argument is const, which means only const member
functions / data members of that argument can be used, so it should be thread
safe.

There is a strong implication throughout the totality of the documentation/spec
for shared_ptr and its copy constructor that copying a shared_ptr in a
multi-threaded environment should be safe.

Reply via email to