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

--- Comment #13 from Jarrod Smith <jarrod.smith at ardentblue dot com> ---
Additional data points and a standard-library-only testcase for this one
(attached above, sb_coawait.cpp; returns 0 when correct). It covers two
scenarios:
A destroys the frame via coroutine_handle::destroy() while suspended at the
binding's initializer (the tuple was never constructed, so nothing may be
destroyed); B runs the coroutine to completion and checks every constructed
object is destroyed exactly once. A second suspend point follows the binding
in the same scope.

  gcc 15.3.0  -O0   A ok               B: ctor=2 dtor=1  (destructor skipped at
-O0 too, not only -O1+)
  gcc 15.3.0  -O2   A ok               B: ctor=2 dtor=1
  gcc 15.2.1  -O0   A: ctor=0 dtor=1   B ok              (destructor runs on
the never-constructed binding)
  gcc 15.2.1  -O2   A ok               B: ctor=2 dtor=1
  gcc 14.2.1, gcc 16.2.0, clang 20.1.8: all correct at -O0 and -O2

So on the 15 branch the lowering is wrong in both directions depending on
version and level: 15.2.1 at -O0 destroys the binding when the frame is
destroyed before the binding was constructed (on a real type this is a
destructor call on uninitialized storage), while 15.3.0, and 15.x at -O1+,
never destroy it.

Two discriminators: -DNO_SECOND_SUSPEND (remove the later suspend point) makes
15.3.0 correct, and -DPLAIN_VARIABLE (auto r = co_await ...; auto& [n, t] = r;)
is correct everywhere. At -O2, 15.x also warns

  sb_coawait.cpp:55:3: warning: '<anonymous>' may be used uninitialized
[-Wmaybe-uninitialized]

at the closing brace of the enclosing loop, which looks like the slot's
destroy guard being read before it is written.

Real-world shape where the -O0 variant bites: a boost::asio accept loop,

  auto [e, client] = co_await acceptor.async_accept(strand,
as_tuple(use_awaitable));

with a backoff co_await in the error branch. Destroying the io_context with the
accept pending runs ~basic_stream_socket on the never-constructed slot and
segfaults on every SIGTERM; a loop whose binding holds a shared_ptr destroys
the previous iteration's stale tuple a second time (ASan: heap-use-after-free).
Boost-based repros available if useful.

Since 16.2.0 is correct, is a backport of the fix to 15.4 planned? Happy to
test a 15-branch patch against both the attached case and the asio cases.

15.2.1 above is Red Hat 15.2.1-7 (Fedora 42); 15.3.0 is the FSF release,
x86_64-linux-gnu, plain "g++ -std=c++20 -O0|-O2 sb_coawait.cpp".
  • [Bug c++/124584] [15 Regre... jarrod.smith at ardentblue dot com via Gcc-bugs
    • [Bug c++/124584] [15 ... jarrod.smith at ardentblue dot com via Gcc-bugs

Reply via email to