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

            Bug ID: 118854
           Summary: ASAN heap-after-use in shared_ptr copy constructor
                    when copying a shared_ptr constructed with make_shared
           Product: gcc
           Version: 14.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: nicholas at nicholaswilliams dot net
  Target Milestone: ---

Created attachment 60480
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=60480&action=edit
shared_ptr_segfault.cpp

Profuse apologies if I've actually found a bug in AddressSanitizer, but I'm
99.5% sure the bug is in shared_ptr or, at least, in some atomic resource it is
using.

In an (admittedly extreme) edge case, I can reliably get shared_ptr's copy
constructor to trigger a heap-after-use error when copying a static shared_ptr
as it is destructed by libstdc++ internals at application shutdown.
Interestingly, without address sanitizer, I have not been able to get this to
trigger a segfault. I think that's a timing issue and it would, eventually,
segfault, but I can't prove that. My simple replication program (attached) does
trigger the ASAN error every time.

I'm reporting this against GCC 14.2.1 because that's the latest version
currently installed in our architecture, but I've also duplicated with the
exact same code in GCC 13, GCC 11, and even Apple Clang 16 (which uses some
variation/fork of libstdc++ but is failing in the exact same way with a
slightly different stack within the shared_ptr copy constructor).

This was compiled with:

$ g++ -v -std=c++20 -g -fsanitize=address -fsanitize-address-use-after-scope
-Wall -Wextra -Werror -D_GLIBCXX_ASSERTIONS -D_GLIBCXX_DEBUG -o
shared_ptr_segfault shared_ptr_segfault.cpp

If I reverse the order of these two statements:

  BackgroundThread gBackgroundThread;

  std::shared_ptr< Resource >
  Resource::
  _pInstance = std::make_shared< Resource >();

Or use __attribute__((init_priority)) to control their init order, the problem
goes away. I can do something like that in my real-world use case, but IMO the
shared_ptr copy constructor should not exhibit this problem in any case. It
should yield a shared_ptr managing nothing if the original shared_ptr is
destructing or a complete copy of the shared_ptr otherwise.

I even had an initial instinct, "Oh, I should be protecting `_pInstance` with a
mutex," but of course that achieves nothing, because it's libstdc++ internals
that are destructing the static shared_ptr, not any code over which I have
control, and so the destructing code does not know or care about the mutex.

.cpp file, gcc -v output, and runtime output attached.

Reply via email to