You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Disclaimer: Claude Code helped me create the reproducer. The repo contains two Dockerfiles (with JIT on/off) that shows the difference in behaviour and loop.sh script that runs composer install over and over and shows that it hangs in about 3 % of performed runs.
There are some details in the README but consider them with a grain of salt.
To reproduce the problem in the cloned link repository:
# Build both images (PHP 8.5.6 + Composer 2.9.5)
docker build -t composer-jit-hang -f Dockerfile.jit .
docker build -t composer-jit-nojit -f Dockerfile.nojit .
# 1) See the hang with opcache.jit=tracing.
# loop.sh bootstraps vendor/ on first run (one ~30 s install to bring in
# Symfony Flex, which has to be loaded as a plugin for the JIT bug to
# fire), then runs `composer install --dry-run` 200 times. Expect a
# handful of those runs to be killed at the 15 s deadline — last
# measurement: 19 hangs in 500 runs.
docker run --rm \
-v "$PWD":/work \
composer-jit-hang bash /loop.sh 200
# 2) Same project, same Composer, same PHP, JIT off — zero hangs.
docker run --rm \
-v "$PWD":/work \
composer-jit-nojit bash /loop.sh 200
PHP Version
PHP 8.5.6 (cli) (built: May 8 2026 19:23:57) (NTS)
Copyright (c) The PHP Group
Built by https://github.com/docker-library/php
Do you know what it's hung in? If you can still reproduce this, can you attach gdb to the process, source the .gdbinit file for PHP 8.5, and run zbacktrace as well as backtrace?
Thanks for the reproducer. This is a duplicate of GH-22915, which is fixed in PHP 8.5.10.
It's this: RuleSetGenerator::addRulesForPackage(), the tracing JIT makes $this->addedMap assume a packed array.
Once the array is no longer packed, the packed guard fails.
Depending on timing, this exit sometimes gets blacklisted. The code it then runs to hand back to the interpreter increments the array's refcount before it saves the other live registers. That increment can overwrite the register holding $package->id in %rax.
The interpreter then re-runs the isset() with garbage data, and returns false. The same packages are re-enqueued over and over and the loop never ends.
With the official Docker images and your project: 8.5.9 hung in 6 of 500 runs, 8.5.10 in 0 of 500. Please upgrade to 8.5.10 or later.
Activity
morrisonlevi commentedon Jun 23, 2026
Do you know what it's hung in? If you can still reproduce this, can you attach gdb to the process, source the .gdbinit file for PHP 8.5, and run
zbacktraceas well asbacktrace?ndossche commentedon Oct 6, 2026
Thanks for the reproducer. This is a duplicate of GH-22915, which is fixed in PHP 8.5.10.
It's this:
RuleSetGenerator::addRulesForPackage(), the tracing JIT makes$this->addedMapassume a packed array.Once the array is no longer packed, the packed guard fails.
Depending on timing, this exit sometimes gets blacklisted. The code it then runs to hand back to the interpreter increments the array's refcount before it saves the other live registers. That increment can overwrite the register holding
$package->idin%rax.The interpreter then re-runs the isset() with garbage data, and returns false. The same packages are re-enqueued over and over and the loop never ends.
With the official Docker images and your project: 8.5.9 hung in 6 of 500 runs, 8.5.10 in 0 of 500. Please upgrade to 8.5.10 or later.