Skip to content

Segfault in PHP-FPM with tracing JIT (opcache.jit=1205) due to stale base pointer in rbp (Regression in 8.5.5) #22084

@martinvenus

Description

@martinvenus

Description

After upgrading from PHP 8.5.4 to 8.5.5, php-fpm workers started segfaulting deterministically under normal web traffic. Issue persists on 8.5.6. Only tracing JIT (opcache.jit=1205) is affected; function JIT (1235) and disabled JIT both work fine.

Environment

  • PHP 8.5.5 and 8.5.6 (official php:8.5.6-fpm Docker image, Debian Trixie base)
  • x86_64
  • Application: Nette Framework + Dibi 5.1.1 + ext/mysqli, MariaDB 11.8

JIT configuration matrix

opcache.jit Result
1205 (tracing) Workers segfault within seconds of receiving traffic
1235 (function) Stable
off Stable

PHP 8.5.4 with opcache.jit=1205 was stable. Regression introduced in 8.5.5, not resolved in 8.5.6.

opcache config

opcache.enable=1
opcache.memory_consumption=256
opcache.jit=1205
opcache.jit_buffer_size=128M

Crash signature (host dmesg)

Repeated segfaults at a small, stable set of offsets in the php-fpm binary (binary base changes due to ASLR; offsets are consistent):

php-fpm[294862]: segfault at 7e3e1bc12bc0 ip ...4c9466 ... error 4 in php-fpm[4c9466,...+801000]
php-fpm[515992]: segfault at 702b7efcdf8c ip ...4bc8a6 ... error 4 in php-fpm[4bc8a6,...+801000]
php-fpm[521652]: segfault at 7b4425b40ca0 ip ...4bc17b ... error 4 in php-fpm[4bc17b,...+801000]
php-fpm[529400]: segfault at 72f91dc4c1c0 ip ...4c9421 ... error 4 in php-fpm[4c9421,...+801000]

error 4 = user-mode read fault on unmapped page. Fault addresses are high (7e…, 7b…, 72…), consistent with reads through a stale pointer.

Disassembly at crash offsets

Official Docker image is stripped, so all faulting offsets fall under the preceding exported symbol opcache_preloading@@Base. The actual crashing code is unexported, almost certainly JIT-generated VM handler inlines.

Faulting instruction at 4c9466 (inside a tight loop):

4c9460: mov    (%rax),%esi             ; load 32-bit index from source
4c9462: add    $0x4,%rax               ; advance source pointer
4c9466: mov    0x0(%rbp,%rsi,4),%esi   ; <-- FAULT: rbp[rsi*4]

Faulting instruction at 4c9421:

4c940d: movslq -0x2c(%rdx),%rdi
4c9411: cmp    %r8d,0x74(%r15)         ; bounds check vs. value at r15+0x74
4c9415: jb     4c94b7 <...>            ; branch if below
4c941b: mov    0x1c(%rdx),%eax         ; load constant index (op_array slot?)
4c941e: mov    0x10(%rdx),%esi
4c9421: mov    0x0(%rbp,%rax,4),%eax   ; <-- FAULT: rbp[rax*4]

Both faulting instructions are mov [rbp + reg*4], reg32 — 32-bit indexed loads consistent with PHP literal/constant pool lookup (op_array->literals is a zval[] accessed by 32-bit indices from zend_op operands). The rbp register holds what appears to be a stale base pointer pointing into freed memory.

Hypothesis

JIT-compiled tracing code appears to hold a base pointer (possibly op_array->literals) across a point where the underlying memory is freed or moved, then dereferences it via a stable 32-bit index from the opcode stream. Maintainers will know better — this is only a guess based on the instruction pattern.

Possibly related: GH-21395 (UAF in JIT, fixed in 8.5.5). The fix may be incomplete, or it may have introduced an adjacent regression. Tracing-only failure (function JIT works) strongly suggests a tracing-specific bug, e.g. in trace recording / side-exit assumptions about value lifetimes.

Reproduction

Plain web traffic against the application is enough; workers crash within seconds. Happy to provide:

  • The specific application code paths (Nette controller, Dibi queries)
  • Larger dmesg samples
  • strace / ltrace output
  • A core dump (can be arranged if you can guide on getting useful symbols from the official php-fpm image)
  • Test with opcache.jit_buffer_size reduced, or specific opcache.jit_* tuning

Switching to opcache.jit=1235 is a viable workaround for now.

Metadata

Metadata

Assignees

No one assigned

    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