Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions ext/opcache/jit/zend_jit_ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -14482,17 +14482,17 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,
prop_ref = ir_ADD_OFFSET(obj_ref, prop_info->offset);
prop_addr = ZEND_ADDR_REF_ZVAL(prop_ref);
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
if (opline->opcode == ZEND_FETCH_OBJ_W || !(res_info & MAY_BE_GUARD) || !JIT_G(current_frame)) {
/* perform IS_UNDEF check only after result type guard (during deoptimization) */
int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
/* Always check for IS_UNDEF to prevent infinite loop when
* opline->handler has been replaced with JIT code and the
* result type guard deoptimization dispatches back to it. */
int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);

if (!exit_addr) {
return 0;
}
prop_type_ref = jit_Z_TYPE_INFO(jit, prop_addr);
ir_GUARD(prop_type_ref, ir_CONST_ADDR(exit_addr));
if (!exit_addr) {
return 0;
}
prop_type_ref = jit_Z_TYPE_INFO(jit, prop_addr);
ir_GUARD(prop_type_ref, ir_CONST_ADDR(exit_addr));
} else {
prop_type_ref = jit_Z_TYPE_INFO(jit, prop_addr);
ir_ref if_def = ir_IF(prop_type_ref);
Expand Down
55 changes: 55 additions & 0 deletions ext/opcache/tests/jit/gh21267.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
--TEST--
GH-21267 (JIT infinite loop on FETCH_OBJ_R with IS_UNDEF property in polymorphic context)
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.jit=tracing
opcache.jit_buffer_size=64M
opcache.jit_hot_loop=61
opcache.jit_hot_func=127
opcache.jit_hot_return=8
opcache.jit_hot_side_exit=8
--FILE--
<?php
class Base
{
public $incrementing = true;
public function __get($name) { return null; }
public function getIncrementing() { return $this->incrementing; }
public function getCasts(): array
{
if ($this->getIncrementing()) {
return ['id' => 'int'];
}
return [];
}
}

class ChildA extends Base {}
class ChildB extends Base {}
class ChildC extends Base { public $incrementing = false; }

$classes = [ChildA::class, ChildB::class, ChildC::class];

// Warmup
for ($round = 0; $round < 5; $round++) {
for ($i = 0; $i < 1000; $i++) {
$m = new $classes[$i % 3]();
$m->getIncrementing();
$m->getCasts();
}
}

// Trigger
for ($i = 0; $i < 2000; $i++) {
$m = new $classes[$i % 3]();
if ($i % 7 === 0) {
unset($m->incrementing);
}
$m->getIncrementing();
$m->getCasts();
}

echo "OK\n";
--EXPECT--
OK
Loading