Skip to content

JIT iterator crash #20166

@rlerdorf

Description

@rlerdorf

Description

There is a tricky Heisenberg JIT iterator bug.

It is reproducable with:

git clone https://github.com/phan/phan
cd phan
git checkout 07659073cc4fc8ef3ffa3ee67a49dde66b74d496
./test

Make sure you have Nikita's php-ast extension installed.

Run ./test a few times with JIT enabled and you should see the crash eventually. Opcache settings are:

opcache.enable=On
opcache.enable_cli=On
opcache.jit=tracing           # or any JIT mode
opcache.jit_buffer_size=128M
opcache.file_cache=/tmp/opcache

The crash happens after these tests complete:

  • Phan\Tests\ForkPoolTest::testBasicForkJoin
  • Phan\Tests\ForkPoolTest::testStartupFunction
  • Phan\Tests\PhanTest0 tests (0001_hello_world through 0011_duplicate_function)

It likely crashes during/after the 0012_closures.php test but I can't reproduce it by running an individual test. It somehow accumulates during multiple test runs.

Crash occurs in zend_iterator_unwrap when dereferencing corrupted object pointer:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000055a1541314aa in zend_iterator_unwrap (array_ptr=array_ptr@entry=0x7fc847414bf1) at /home/rasmus/php-src/Zend/zend_iterators.c:102
102             if (Z_OBJ_HT_P(array_ptr) == &iterator_object_handlers) {
(gdb) bt
#0  0x000055a1541314aa in zend_iterator_unwrap (array_ptr=array_ptr@entry=0x7fc847414bf1) at /home/rasmus/php-src/Zend/zend_iterators.c:102
#1  0x000055a1540facec in zend_fe_fetch_object_helper_SPEC () at /home/rasmus/php-src/Zend/zend_vm_execute.h:3048
#2  0x000055a1541097a8 in ZEND_FE_FETCH_R_SPEC_VAR_HANDLER () at /home/rasmus/php-src/Zend/zend_vm_execute.h:23492
#3  execute_ex (ex=0x7fc847414bf1) at /home/rasmus/php-src/Zend/zend_vm_execute.h:118765
#4  0x000055a15410ccf0 in zend_execute (op_array=op_array@entry=0x7fc847498000, return_value=return_value@entry=0x0)
    at /home/rasmus/php-src/Zend/zend_vm_execute.h:121436
#5  0x000055a15416bce6 in zend_execute_script (type=type@entry=8, retval=retval@entry=0x0, file_handle=file_handle@entry=0x7ffd9f935890)
    at /home/rasmus/php-src/Zend/zend.c:1977
#6  0x000055a15400896f in php_execute_script_ex (primary_file=<optimized out>, retval=retval@entry=0x0) at /home/rasmus/php-src/main/main.c:2640
#7  0x000055a154008c87 in php_execute_script (primary_file=<optimized out>) at /home/rasmus/php-src/main/main.c:2680
#8  0x000055a15416d996 in do_cli (argc=argc@entry=2, argv=argv@entry=0x55a160de8dd0) at /home/rasmus/php-src/sapi/cli/php_cli.c:951
#9  0x000055a153a71433 in main (argc=2, argv=0x55a160de8dd0) at /home/rasmus/php-src/sapi/cli/php_cli.c:1362

array_ptr points to invalid address 0x7fc847414bf1

(gdb) zbacktrace
[0x7fc8474149f0] Phan\Language\Element\Func->addParamsToScopeOfFunctionOrMethod(object[0x7fc847414a40], object[0x7fc847414a50], object[0x7fc847414a60],
object[0x7fc847414a70]) /home/rasmus/work/phan/src/Phan/Language/Element/FunctionTrait.php:792
[0x7fc847414960] Phan\Language\Element\Func->ensureScopeInitialized(object[0x7fc8474149b0])
/home/rasmus/work/phan/src/Phan/Language/Element/FunctionTrait.php:1274
[0x7fc8474148a0] Phan\Analysis->{closure:Phan\Analysis::analyzeFunctions():257}(object[0x7fc8474148f0]) /home/rasmus/work/phan/src/Phan/Analysis.php:267
[0x7fc847414770] Phan\Analysis->analyzeFunctions(object[0x7fc8474147c0], NULL) /home/rasmus/work/phan/src/Phan/Analysis.php:328
[0x7fc847414540] Phan\Phan->finishAnalyzingRemainingStatements(object[0x7fc847414590], NULL, array(1)[0x7fc8474145b0], array(0)[0x7fc8474145c0], NULL)
/home/rasmus/work/phan/src/Phan/Phan.php:575
[0x7fc847414360] Phan\Phan->analyzeFileList(object[0x7fc8474143b0], object[0x7fc8474143c0]) /home/rasmus/work/phan/src/Phan/Phan.php:468
[0x7fc847414190] Phan\Tests\AbstractPhanFileTest->testFiles(array(1)[0x7fc8474141e0], "./tests/files/expected/0012_closures.php.expected")
/home/rasmus/work/phan/tests/Phan/AbstractPhanFileTest.php:173
[0x7fc8474140d0] PHPUnit\Framework\TestCase->runTest() /home/rasmus/work/phan/vendor/phpunit/phpunit/src/Framework/TestCase.php:1617
[0x7fc847413ff0] PHPUnit\Framework\TestCase->runBare() /home/rasmus/work/phan/vendor/phpunit/phpunit/src/Framework/TestCase.php:1223
[0x7fc847413d50] PHPUnit\Framework\TestResult->run(object[0x7fc847413da0]) /home/rasmus/work/phan/vendor/phpunit/phpunit/src/Framework/TestResult.php:729
[0x7fc847413b00] PHPUnit\Framework\TestCase->run(object[0x7fc847413b50]) /home/rasmus/work/phan/vendor/phpunit/phpunit/src/Framework/TestCase.php:973
[0x7fc8474139b0] PHPUnit\Framework\TestSuite->run(object[0x7fc847413a00]) /home/rasmus/work/phan/vendor/phpunit/phpunit/src/Framework/TestSuite.php:685
[0x7fc847413860] PHPUnit\Framework\TestSuite->run(object[0x7fc8474138b0]) /home/rasmus/work/phan/vendor/phpunit/phpunit/src/Framework/TestSuite.php:685
[0x7fc847413710] PHPUnit\Framework\TestSuite->run(object[0x7fc847413760]) /home/rasmus/work/phan/vendor/phpunit/phpunit/src/Framework/TestSuite.php:685
[0x7fc8474135c0] PHPUnit\Framework\TestSuite->run(object[0x7fc847413610]) /home/rasmus/work/phan/vendor/phpunit/phpunit/src/Framework/TestSuite.php:685
[0x7fc847413320] PHPUnit\TextUI\TestRunner->run(object[0x7fc847413370], reference, reference, true)
/home/rasmus/work/phan/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:651
[0x7fc847413240] PHPUnit\TextUI\Command->run(array(1)[0x7fc847413290], true) /home/rasmus/work/phan/vendor/phpunit/phpunit/src/TextUI/Command.php:146
[0x7fc8474131a0] PHPUnit\TextUI\Command->main() /home/rasmus/work/phan/vendor/phpunit/phpunit/src/TextUI/Command.php:99
[0x7fc8474130a0] (main) /home/rasmus/work/phan/vendor/phpunit/phpunit/phpunit:107
[0x7fc847413020] (main) /home/rasmus/work/phan/vendor/bin/phpunit:122

So it happens in src/Phan/Language/Element/FunctionTrait.php:790

$function_parameter_list = $function->getParameterList();
// ...
foreach ($function_parameter_list as $parameter) {  // <- CRASH HERE (line 790)
    $real_parameter_name_map[$parameter->getName()] = $parameter;
    self::addParamToScopeOfFunctionOrMethod(...);
}

It looks like the JIT corrupts the parameter list array/iterator during foreach iteration, causing zend_iterator_unwrap to dereference invalid memory. This is likely while Phan is analyzing closure parameters in test file tests/files/src/0012_closures.php

PHP Version

PHP 8.5.0-dev (cli) (built: Oct 14 2025 07:57:13) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.5.0-dev, Copyright (c) Zend Technologies
    with Zend OPcache v8.5.0-dev, Copyright (c), by Zend Technologies

with php-ast installed

Operating System

Debian 13.1

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions