[php-src] PHP-8.4: Fix GH-23991: JIT generates invalid IR for multiple recursive calls

From: Date: Fri, 02 Oct 2026 20:53:14 +0000
Subject: [php-src] PHP-8.4: Fix GH-23991: JIT generates invalid IR for multiple recursive calls
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-141898@lists.php.net to get a copy of this message
Author: ndossche (ndossche) Date: 2026-10-02T22:52:01+02:00 Commit: https://github.com/php/php-src/commit/6abb600217a20d1aa313000b08eed3d6eb9d8675 Raw diff: https://github.com/php/php-src/commit/6abb600217a20d1aa313000b08eed3d6eb9d8675.diff Fix GH-23991: JIT generates invalid IR for multiple recursive calls A direct recursive call that jumps to a RECV entry is converted into a loop back edge. The continuation ENTRY of such a call uses the call's LOOP_END as its fake control edge. Once the entry already was a LOOP_BEGIN with 3 inputs, a further recursive call demoted the previous LOOP_END to an END and put it into a new MERGE. That END then had both a real successor and the fake ENTRY edge, which violates IR assumptions. That causes assert failures in basic block handling code (merge & fix_bb_order). This happened in WordPress's rest_sanitize_value_from_schema(). I don't believe this optimization was ever fully correct, doing this optimization requires more complex handling and is certainly not worth the risk on stable branches. The hangs that OP reports are likely because the worker crashes while holding the shm lock, or something alike. Closes GH-24001. Changed paths: A ext/opcache/tests/jit/gh23991.phpt M NEWS M ext/opcache/jit/zend_jit_ir.c Diff: diff --git a/NEWS b/NEWS index 0052e326ce82..38a3f909e855 100644 --- a/NEWS +++ b/NEWS @@ -114,6 +114,8 @@ PHP NEWS single -0.0 constant). (lazerg) . Fixed crash when a file cache is reused with a different set of extensions declaring frameless functions. (Ilia Alshanetsky) + . Fixed bug GH-23991 (JIT generates invalid IR for multiple recursive calls). + (ndossche) - OpenSSL: . Fixed stream_socket_enable_crypto() leaving the socket non-blocking diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 4c20c115b848..50851d75abb5 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -10388,14 +10388,6 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen insn->inputs_count = 3; insn->op3 = end; break; - } else if (insn->op == IR_LOOP_BEGIN && insn->inputs_count == 3) { - ZEND_ASSERT(jit->ctx.ir_base[insn->op3].op == IR_LOOP_END); - jit->ctx.ir_base[insn->op3].op = IR_END; - ir_MERGE_2(insn->op3, ir_END()); - end = ir_LOOP_END(); - insn = &jit->ctx.ir_base[begin]; - insn->op3 = end; - break; } } /* fallback to indirect JMP or RETURN */ diff --git a/ext/opcache/tests/jit/gh23991.phpt b/ext/opcache/tests/jit/gh23991.phpt new file mode 100644 index 000000000000..21c072793621 --- /dev/null +++ b/ext/opcache/tests/jit/gh23991.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-23991 (JIT generates invalid IR for multiple recursive calls converted into loop) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit_buffer_size=64M +opcache.jit=1235 +opcache.jit_hot_func=1 +--EXTENSIONS-- +opcache +--FILE-- +<?php +function f($value, $recurse, $param = '') { + if (is_array($value)) { + if ($recurse) { + foreach ($value as $k => $v) { + $value[$k] = f($v, $recurse, $param); + } + } + foreach ($value as $k => $v) { + if ($undef) { + $value[$k] = f($v, $recurse, $param); + } + } + } + return $value; +} +var_dump(f([[]], true)); +?> +--EXPECTF-- +Warning: Undefined variable $undef in %s on line %d +array(1) { + [0]=> + array(0) { + } +}

« previous php.cvs (#141898) next »