Skip to content

SIGSEGV in swoole::PHPCoroutine::create_context() — NULL pointer from executor_globals dereferenced at +0x18, recurring at identical IP across hosts, likely when a coroutine is created during worker shutdown (Swoole 6.0.2) #6105

Description

@80boys

1. What did you do

We run a Swoole\WebSocket\Server in production (Docker) on multiple hosts:

  • Swoole\WebSocket\Server, dispatch_mode => 2, 16 worker processes, max_request => 0
  • Runtime::enableCoroutine(SWOOLE_HOOK_ALL)
  • Long-lived per-connection consumer coroutines that block on
    Swoole\Coroutine\Channel::pop(1.0) in a loop (producer/consumer queue)
  • Swoole\Timer::tick() / Timer::after() timers per connection
  • A Channel-based connection pool (phpredis + runtime hooks)
    No specific user action triggers the crash — it happens spontaneously in
    production, roughly once every few weeks per host.

2. What did you expect

Worker processes should shut down cleanly, even when Channels that still have
blocked consumers are destroyed during shutdown.

3. What actually happened

Worker processes crash with SIGSEGV. Kernel logs from two independent hosts
over ~2 months show four crashes, all at the identical offset 0x3376f inside
the executable segment of swoole.so, faulting address 0x18
:

[Wed Apr 29 19:26:00 2026] host-04: php[1650455]: segfault at 18 ip 00007927e784876f sp 00005bd9aae397f0 error 4 in swoole.so[7927e7815000+170000]
[Sun May 10 20:24:02 2026] host-04: php[1650447]: segfault at 18 ip 00007927e784876f sp 00005bd92f887c10 error 4 in swoole.so[7927e7815000+170000]
[Thu Jul  2 12:50:21 2026] host-01: php[1505854]: segfault at 18 ip 000071bd1999576f sp 00005e73345605f0 error 4 in swoole.so[71bd19962000+170000]
[Thu Jul  2 16:40:27 2026] host-04: php[1008159]: segfault at 18 ip 00007ef0b7d5276f sp 00005a582946e750 error 4 in swoole.so[7ef0b7d1f000+170000]

Symbol resolution

The executable LOAD segment of our swoole.so starts at vaddr 0x75000
(readelf -lW), so the faulting vaddr is 0x75000 + 0x3376f = 0xa876f:

$ gdb -batch -ex 'info symbol 0xa876f' swoole.so
swoole::PHPCoroutine::create_context(swoole::PHPCoroutine::Args*) + 239 in section .text

Disassembly around the faulting instruction

a8712: call   <_emalloc_large@plt>            ; allocate new vm stack (0x2000)
...
a8739: mov    0x209e38(%rip),%rdx             ; rdx = executor_globals
a8748: mov    0x1e8(%rdx),%rdi                ; rdi = EG field at offset 0x1e8  == NULL
a874f: mov    0x1a0(%rdx),%r8
a8756: mov    %rax,0x1d8(%rdx)                ; install new vm stack into EG
a875d: mov    %rsi,0x1d0(%rdx)
a8764: movq   $0x2000,0x1e0(%rdx)
a876f: mov    0x18(%rdi),%rdi                 ; <<< SIGSEGV: NULL->0x18
a8773: movups %xmm0,0x80(%rax)
a877a: mov    %rdi,0x88(%rax)                 ; would be saved into the new context

So create_context() reads a pointer field from executor_globals
(offset 0x1e8) that is NULL, and dereferences its member at offset 0x18
matching the kernel's segfault at 18 exactly, in all four crashes.

Correlation with the server log

The host-01 crash correlates to the second with the Swoole server log: the worker
was destroying coroutine Channels that still had blocked consumers, then
segfaulted:

[2026-07-02 12:50:21 *209.14] WARNING Channel::~Channel() (ERRNO 10003): channel is destroyed, 1 consumers will be discarded
[2026-07-02 12:50:21 *209.14] WARNING Channel::~Channel() (ERRNO 10003): channel is destroyed, 1 consumers will be discarded
[2026-07-02 12:50:22 $17.0]  WARNING Worker::report_error(): worker(pid=209, id=14) abnormal exit, status=0, signal=11
A bug occurred in Swoole-v6.0.2, please report it.

(The pid differs from the kernel log because the server runs inside a container
with its own PID namespace; timestamps match exactly.)
Our hypothesis: during worker shutdown, after the PHP executor state has been
partially torn down (the EG field at 0x1e8 already cleared), something still
triggers the creation of a new coroutine (e.g. destructors of objects holding
Channels wake up / re-schedule work, with SWOOLE_HOOK_ALL enabled), and
create_context() dereferences the NULL EG field without a check. Since all four
crashes across two hosts hit the same instruction with the same faulting address,
this is a deterministic NULL dereference on that code path, not random memory
corruption.
We do not have a minimal reproducer yet. Core dumps are now enabled on the
affected hosts; we will attach a full gdb backtrace as soon as the next crash
occurs.

4. Environment

Swoole version : 6.0.2
PHP version    : 8.2.20 (NTS)
OS             : Linux 6.8.0-40-generic #40-Ubuntu SMP PREEMPT_DYNAMIC x86_64 (Ubuntu, inside Docker)
GCC            : 12.2.0
OpenSSL        : OpenSSL 3.0.15 3 Sep 2024

Output of php --ri swoole:


swoole

Swoole => enabled
Author => Swoole Team <team@swoole.com>
Version => 6.0.2
Built => May 15 2025 10:01:06
coroutine => enabled with boost asm context
epoll => enabled
eventfd => enabled
signalfd => enabled
cpu_affinity => enabled
spinlock => enabled
rwlock => enabled
sockets => enabled
openssl => OpenSSL 3.0.15 3 Sep 2024
dtls => enabled
http2 => enabled
json => enabled
curl-native => enabled
curl-version => 7.88.1
zlib => 1.2.13
brotli => E16777225/D16777225
mutex_timedlock => enabled
pthread_barrier => enabled
futex => enabled
mysqlnd => enabled

Directive => Local Value => Master Value
swoole.enable_library => On => On
swoole.enable_fiber_mock => Off => Off
swoole.enable_preemptive_scheduler => Off => Off
swoole.display_errors => On => On
swoole.use_shortname => On => On
swoole.unixsock_buffer_size => 8388608 => 8388608

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions