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

            Bug ID: 126307
           Summary: [TSan] Fiber destruction crashes with stale
                    Trace::local_head
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: sanitizer
          Assignee: unassigned at gcc dot gnu.org
          Reporter: alexey.klimkin at intel dot com
                CC: dodji at gcc dot gnu.org, dvyukov at gcc dot gnu.org,
                    jakub at gcc dot gnu.org, kcc at gcc dot gnu.org
  Target Milestone: ---
              Host: x86_64-pc-linux-gnu
            Target: x86_64-pc-linux-gnu
             Build: x86_64-pc-linux-gnu

> *This was generated by AI during triage.*

Repeated `__tsan_create_fiber`/`__tsan_destroy_fiber` calls crash GCC's
ThreadSanitizer runtime because `Trace::local_head` is not initialized.

Tested with GCC master commit
`58ce322cb0ca9369d5a9aced23731e25b150ce00` on
`x86_64-pc-linux-gnu`.

Minimal reproducer:

```c
extern void *__tsan_create_fiber(unsigned flags);
extern void __tsan_destroy_fiber(void *fiber);

int main(void) {
  for (unsigned i = 0; i < 10000; ++i) {
    void *fiber = __tsan_create_fiber(0);
    __tsan_destroy_fiber(fiber);
  }
}
```

Build and run:

```sh
gcc -O2 -g -fsanitize=thread -fno-pie -no-pie fiber-destroy.c -o fiber-destroy
./fiber-destroy
```

On this host, 7,943 iterations pass; 7,944 iterations reliably crash:

```text
ThreadSanitizer:DEADLYSIGNAL
ERROR: ThreadSanitizer: SEGV on unknown address 0x001d00001c14
ThreadSanitizer: nested bug in the same thread, aborting.
```

GDB resolves the failure to:

```text
__tsan::ThreadContext::OnFinished
  at libsanitizer/tsan/tsan_rtl_thread.cpp:283
__sanitizer::ThreadContextBase::SetFinished
__sanitizer::ThreadRegistry::FinishThread
__tsan::ThreadFinish
__tsan::FiberDestroy
```

The failing `ThreadContext` has `trace.parts.Size() == 0` but a stale,
non-null `trace.local_head`. `Trace::local_head` lacks an initializer, so
allocator reuse eventually exposes arbitrary contents.

LLVM fixed this upstream by initializing `local_head` to `nullptr`:

- LLVM PR: https://github.com/llvm/llvm-project/pull/171766
- LLVM commit:
https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca

GCC master currently records compiler-rt merge revision
`8723fe5606de6dfb344afacd667c20f55bb2f5e0`, which predates this fix.

Applying the upstream one-line initialization locally gives:

- GCC C/C++ TSan suites: 495 expected passes, zero unexpected failures.
- Standalone fiber churn: 100,000 iterations pass.
- Original downstream workload: 10,000 repetitions pass.

Suggested resolution: import or backport LLVM commit `cdfdb06c9155`, with a
C/C++ TSan regression that churns enough fibers to force allocator reuse.

Reply via email to