Skip to content

Commit 4bb7282

Browse files
committed
Fix handling of abstract/deprecated exception
The exception mechanism assumes that exceptions from DO_FCALL are already happening after the function call. This means that we are currently leaking the passed arguments, and I think we can also corrupt the VM stack due to incorrect frame linking in some cases (there are assertion failures if the VM stack page size is reduced). Instead handle the stack frame freeing manually for this special case.
1 parent 1db0bad commit 4bb7282

File tree

4 files changed

+42
-29
lines changed

4 files changed

+42
-29
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Check that arguments are freed when a call to an abstract method throws
3+
--FILE--
4+
<?php
5+
6+
abstract class Test {
7+
abstract static function method();
8+
}
9+
10+
try {
11+
Test::method(new stdClass);
12+
} catch (Error $e) {
13+
echo $e->getMessage(), "\n";
14+
}
15+
16+
?>
17+
--EXPECT--
18+
Cannot call abstract method Test::method()

Zend/zend_execute.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,6 +1537,12 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(c
15371537
ZSTR_VAL(fbc->common.function_name));
15381538
}
15391539

1540+
static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_abstract_method(const zend_function *fbc)
1541+
{
1542+
zend_throw_error(NULL, "Cannot call abstract method %s::%s()",
1543+
ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
1544+
}
1545+
15401546
static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value OPLINE_DC EXECUTE_DATA_DC)
15411547
{
15421548
zend_uchar c;

Zend/zend_vm_def.h

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,16 +1047,6 @@ ZEND_VM_COLD_HELPER(zend_this_not_in_object_context_helper, ANY, ANY)
10471047
HANDLE_EXCEPTION();
10481048
}
10491049

1050-
ZEND_VM_COLD_HELPER(zend_abstract_method_helper, ANY, ANY, zend_function *func)
1051-
{
1052-
USE_OPLINE
1053-
1054-
SAVE_OPLINE();
1055-
zend_throw_error(NULL, "Cannot call abstract method %s::%s()", ZSTR_VAL(func->common.scope->name), ZSTR_VAL(func->common.function_name));
1056-
UNDEF_RESULT();
1057-
HANDLE_EXCEPTION();
1058-
}
1059-
10601050
ZEND_VM_COLD_HELPER(zend_undefined_function_helper, ANY, ANY)
10611051
{
10621052
USE_OPLINE
@@ -4143,12 +4133,15 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL))
41434133
EX(call) = call->prev_execute_data;
41444134
if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) {
41454135
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) {
4146-
ZEND_VM_DISPATCH_TO_HELPER(zend_abstract_method_helper, func, fbc);
4136+
zend_abstract_method(fbc);
4137+
ZEND_VM_C_LABEL(fcall_except):
4138+
UNDEF_RESULT();
4139+
zend_vm_stack_free_args(call);
4140+
ZEND_VM_C_GOTO(fcall_end);
41474141
} else {
41484142
zend_deprecated_function(fbc);
41494143
if (UNEXPECTED(EG(exception) != NULL)) {
4150-
UNDEF_RESULT();
4151-
HANDLE_EXCEPTION();
4144+
ZEND_VM_C_GOTO(fcall_except);
41524145
}
41534146
}
41544147
}

Zend/zend_vm_execute.h

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -781,16 +781,6 @@ static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_th
781781
HANDLE_EXCEPTION();
782782
}
783783

784-
static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_abstract_method_helper_SPEC(zend_function *func ZEND_OPCODE_HANDLER_ARGS_DC)
785-
{
786-
USE_OPLINE
787-
788-
SAVE_OPLINE();
789-
zend_throw_error(NULL, "Cannot call abstract method %s::%s()", ZSTR_VAL(func->common.scope->name), ZSTR_VAL(func->common.function_name));
790-
UNDEF_RESULT();
791-
HANDLE_EXCEPTION();
792-
}
793-
794784
static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_undefined_function_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS)
795785
{
796786
USE_OPLINE
@@ -1557,12 +1547,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
15571547
EX(call) = call->prev_execute_data;
15581548
if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) {
15591549
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) {
1560-
ZEND_VM_TAIL_CALL(zend_abstract_method_helper_SPEC(fbc ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
1550+
zend_abstract_method(fbc);
1551+
fcall_except:
1552+
UNDEF_RESULT();
1553+
zend_vm_stack_free_args(call);
1554+
goto fcall_end;
15611555
} else {
15621556
zend_deprecated_function(fbc);
15631557
if (UNEXPECTED(EG(exception) != NULL)) {
1564-
UNDEF_RESULT();
1565-
HANDLE_EXCEPTION();
1558+
goto fcall_except;
15661559
}
15671560
}
15681561
}
@@ -1668,12 +1661,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
16681661
EX(call) = call->prev_execute_data;
16691662
if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) {
16701663
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) {
1671-
ZEND_VM_TAIL_CALL(zend_abstract_method_helper_SPEC(fbc ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
1664+
zend_abstract_method(fbc);
1665+
fcall_except:
1666+
UNDEF_RESULT();
1667+
zend_vm_stack_free_args(call);
1668+
goto fcall_end;
16721669
} else {
16731670
zend_deprecated_function(fbc);
16741671
if (UNEXPECTED(EG(exception) != NULL)) {
1675-
UNDEF_RESULT();
1676-
HANDLE_EXCEPTION();
1672+
goto fcall_except;
16771673
}
16781674
}
16791675
}

0 commit comments

Comments
 (0)