Skip to content

gcc 15+ miscompiles structured bindings in coroutine loops (tracking upstream gcc PR 124584) #3431

@travisdowns

Description

@travisdowns

Tracking issue for upstream gcc bug PR 124584 - [C++20 Coroutines] Destructor of tuple-protocol structured binding from co_await skipped at -O1+ (regression in 15) - as it affects seastar.

The bug, in one line: gcc 15+ miscompiles tuple-protocol structured bindings in coroutine loops. When auto [...] = co_await ...; sits in a while / for body inside a coroutine, gcc's lifetime tracking of the hidden tuple that backs the binding is broken across loop iterations, so the destructor either gets skipped (leaks) or runs on memory that was never tuple-constructed (UBSan / ASan aborts).

Credit: Avi root-caused this class of failure to the gcc bug while investigating the pollable_fd_state leak in the cross-shard accept path (see #3394). Several other seastar failures are downstream of the same compiler bug.

Symptoms seen in seastar

  1. Destructor skipped between loop iterations - the structured-binding-captured object (a pollable_fd wrapper, an intrusive_ptr, etc.) is never destroyed at end-of-iteration, surfacing as a memory leak. Seen on the accept loops.
  2. Destructor runs on uninitialized memory - at end-of-iteration the hidden tuple's destructor fires on stack that was never tuple-constructed, hitting ASan/UBSan. In sanitize mode this aborts both rpc client and server loops at startup:
    /usr/include/c++/15/optional:337:12: runtime error:
      load of value 190, which is not a valid value for type 'bool'
    

Reproduced with gcc-15.2 on ubuntu 26.04, sanitize+C++23, detect_stack_use_after_return=1. Clang appears unaffected.

Affected sites in seastar

All four are structured bindings decomposed from a co_await inside a coroutine loop, all introduced ~Aug 2025 when these loops were coroutinized:

Replacing either rpc structured binding with a named local variable + std::get<> makes the UBSan abort disappear, confirming the structured-binding hidden-tuple lifetime is the cause.

Related work already in flight in this repo

Workaround in this repo until gcc fixes 124584

.github/workflows/gen-matrix.py excludes g++ + debug and g++ + sanitize from the CI matrix. To be reverted once a fixed gcc is in the CI containers:

  • Drop the g++ + debug and g++ + sanitize rows from EXCLUDED_PAIRS in .github/workflows/gen-matrix.py.

(Add further items here if other workarounds accumulate.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions