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
3 changes: 3 additions & 0 deletions Zend/zend_vm_execute.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Zend/zend_vm_gen.php
Original file line number Diff line number Diff line change
Expand Up @@ -2136,6 +2136,9 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"# define ZEND_VM_DISPATCH_TO_HELPER(call) \\\n");
out($f," do { \\\n");
out($f," opline = call; \\\n");
out($f," if (UNEXPECTED(((uintptr_t)opline & ZEND_VM_ENTER_BIT))) { \\\n");
out($f," return opline; \\\n");
out($f," } \\\n");
out($f," ZEND_VM_TAIL_CALL(opline->handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); \\\n");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the alternative would be returning instead of tail-calling here. But that might be slower.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts @arnaud-lb ?

out($f," } while (0)\n");
out($f,"# define ZEND_VM_DISPATCH_TO_LEAVE_HELPER(helper) opline = &call_leave_op; SAVE_OPLINE(); ZEND_VM_CONTINUE()\n");
Expand Down
43 changes: 43 additions & 0 deletions ext/zend_test/object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,44 @@ ZEND_METHOD(NumericCastableNoOperations, __construct)
ZVAL_COPY(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), n);
}

static zend_class_entry *vm_interrupt_comparable_ce;
static zend_object_handlers vm_interrupt_comparable_object_handlers;

static zend_object* vm_interrupt_comparable_object_create_ex(zend_class_entry* ce, zend_long l) {
zend_object *obj = zend_objects_new(ce);
object_properties_init(obj, ce);
obj->handlers = &vm_interrupt_comparable_object_handlers;
ZVAL_LONG(OBJ_PROP_NUM(obj, 0), l);
return obj;
}

static zend_object *vm_interrupt_comparable_object_create(zend_class_entry *ce)
{
return vm_interrupt_comparable_object_create_ex(ce, 0);
}

static int vm_interrupt_comparable_compare(zval *op1, zval *op2)
{
ZEND_COMPARE_OBJECTS_FALLBACK(op1, op2);

zend_atomic_bool_store_ex(&EG(vm_interrupt), true);

return ZEND_THREEWAY_COMPARE(
Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op1), 0)),
Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op2), 0)));
}

ZEND_METHOD(VmInterruptComparable, __construct)
{
zend_long l;

ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_LONG(l)
ZEND_PARSE_PARAMETERS_END();

ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), l);
}

static zend_class_entry *dimension_handlers_no_ArrayAccess_ce;
static zend_object_handlers dimension_handlers_no_ArrayAccess_object_handlers;

Expand Down Expand Up @@ -302,6 +340,11 @@ void zend_test_object_handlers_init(void)
memcpy(&numeric_castable_no_operation_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
numeric_castable_no_operation_object_handlers.cast_object = numeric_castable_no_operation_cast_object;

vm_interrupt_comparable_ce = register_class_VmInterruptComparable();
vm_interrupt_comparable_ce->create_object = vm_interrupt_comparable_object_create;
memcpy(&vm_interrupt_comparable_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
vm_interrupt_comparable_object_handlers.compare = vm_interrupt_comparable_compare;

dimension_handlers_no_ArrayAccess_ce = register_class_DimensionHandlersNoArrayAccess();
dimension_handlers_no_ArrayAccess_ce->create_object = dimension_handlers_no_ArrayAccess_object_create;
memcpy(&dimension_handlers_no_ArrayAccess_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
Expand Down
5 changes: 5 additions & 0 deletions ext/zend_test/object_handlers.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ final class NumericCastableNoOperations {
public function __construct(int|float $val) {}
}

final class VmInterruptComparable {
private int $val;
public function __construct(int $val) {}
}

class DimensionHandlersNoArrayAccess {
public bool $read = false;
public bool $write = false;
Expand Down
26 changes: 25 additions & 1 deletion ext/zend_test/object_handlers_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions ext/zend_test/tests/observer_vm_interrupt_tailcall_helper.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Observer: VM interrupt during tailcall helper dispatch
--DESCRIPTION--
This exercises a VM interrupt raised while an opcode handler dispatches to an
extra-argument helper. On the tailcall VM, the helper may return an opline
tagged with ZEND_VM_ENTER_BIT; treating that tagged value as a zend_op * before
tailcalling the next handler can crash.
--EXTENSIONS--
zend_test
--INI--
opcache.jit=0
zend_test.observer.set_vm_interrupt_on_begin=1
--FILE--
<?php
function trigger(VmInterruptComparable $left, VmInterruptComparable $right): object
{
if ($left < $right) {
return new Exception();
}
return new stdClass();
}

echo get_class(trigger(new VmInterruptComparable(2), new VmInterruptComparable(1))), "\n";
?>
--EXPECT--
stdClass
Loading