Skip to content

Commit 7c26c21

Browse files
committed
Add VM interrupt tailcall helper reproducer
1 parent c325e9b commit 7c26c21

4 files changed

Lines changed: 99 additions & 1 deletion

File tree

ext/zend_test/object_handlers.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,44 @@ ZEND_METHOD(NumericCastableNoOperations, __construct)
232232
ZVAL_COPY(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), n);
233233
}
234234

235+
static zend_class_entry *vm_interrupt_comparable_ce;
236+
static zend_object_handlers vm_interrupt_comparable_object_handlers;
237+
238+
static zend_object* vm_interrupt_comparable_object_create_ex(zend_class_entry* ce, zend_long l) {
239+
zend_object *obj = zend_objects_new(ce);
240+
object_properties_init(obj, ce);
241+
obj->handlers = &vm_interrupt_comparable_object_handlers;
242+
ZVAL_LONG(OBJ_PROP_NUM(obj, 0), l);
243+
return obj;
244+
}
245+
246+
static zend_object *vm_interrupt_comparable_object_create(zend_class_entry *ce)
247+
{
248+
return vm_interrupt_comparable_object_create_ex(ce, 0);
249+
}
250+
251+
static int vm_interrupt_comparable_compare(zval *op1, zval *op2)
252+
{
253+
ZEND_COMPARE_OBJECTS_FALLBACK(op1, op2);
254+
255+
zend_atomic_bool_store_ex(&EG(vm_interrupt), true);
256+
257+
return ZEND_THREEWAY_COMPARE(
258+
Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op1), 0)),
259+
Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op2), 0)));
260+
}
261+
262+
ZEND_METHOD(VmInterruptComparable, __construct)
263+
{
264+
zend_long l;
265+
266+
ZEND_PARSE_PARAMETERS_START(1, 1)
267+
Z_PARAM_LONG(l)
268+
ZEND_PARSE_PARAMETERS_END();
269+
270+
ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), l);
271+
}
272+
235273
static zend_class_entry *dimension_handlers_no_ArrayAccess_ce;
236274
static zend_object_handlers dimension_handlers_no_ArrayAccess_object_handlers;
237275

@@ -302,6 +340,11 @@ void zend_test_object_handlers_init(void)
302340
memcpy(&numeric_castable_no_operation_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
303341
numeric_castable_no_operation_object_handlers.cast_object = numeric_castable_no_operation_cast_object;
304342

343+
vm_interrupt_comparable_ce = register_class_VmInterruptComparable();
344+
vm_interrupt_comparable_ce->create_object = vm_interrupt_comparable_object_create;
345+
memcpy(&vm_interrupt_comparable_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
346+
vm_interrupt_comparable_object_handlers.compare = vm_interrupt_comparable_compare;
347+
305348
dimension_handlers_no_ArrayAccess_ce = register_class_DimensionHandlersNoArrayAccess();
306349
dimension_handlers_no_ArrayAccess_ce->create_object = dimension_handlers_no_ArrayAccess_object_create;
307350
memcpy(&dimension_handlers_no_ArrayAccess_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));

ext/zend_test/object_handlers.stub.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ final class NumericCastableNoOperations {
2323
public function __construct(int|float $val) {}
2424
}
2525

26+
final class VmInterruptComparable {
27+
private int $val;
28+
public function __construct(int $val) {}
29+
}
30+
2631
class DimensionHandlersNoArrayAccess {
2732
public bool $read = false;
2833
public bool $write = false;

ext/zend_test/object_handlers_arginfo.h

Lines changed: 25 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Observer: VM interrupt during tailcall helper dispatch
3+
--DESCRIPTION--
4+
This exercises a VM interrupt raised while an opcode handler dispatches to an
5+
extra-argument helper. On the tailcall VM, the helper may return an opline
6+
tagged with ZEND_VM_ENTER_BIT; treating that tagged value as a zend_op * before
7+
tailcalling the next handler can crash.
8+
--EXTENSIONS--
9+
zend_test
10+
--INI--
11+
opcache.jit=0
12+
zend_test.observer.set_vm_interrupt_on_begin=1
13+
--FILE--
14+
<?php
15+
function trigger(VmInterruptComparable $left, VmInterruptComparable $right): object
16+
{
17+
if ($left < $right) {
18+
return new Exception();
19+
}
20+
return new stdClass();
21+
}
22+
23+
echo get_class(trigger(new VmInterruptComparable(2), new VmInterruptComparable(1))), "\n";
24+
?>
25+
--EXPECT--
26+
stdClass

0 commit comments

Comments
 (0)