Improve exception handling for abstract/deprecated calls

Reuse existing arg freeing loop instead of duplicating it.

Additionally also handle deprecated in DO_FCALL_BY_NAME.
This commit is contained in:
Nikita Popov 2019-09-04 14:43:42 +02:00
parent 4bb7282742
commit b6f76aca54
5 changed files with 119 additions and 53 deletions

View File

@ -13,6 +13,14 @@ try {
echo $e->getMessage(), "\n";
}
$ret = new stdClass;
try {
$ret = Test::method(new stdClass);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
Cannot call abstract method Test::method()
Cannot call abstract method Test::method()

View File

@ -0,0 +1,43 @@
--TEST--
Check that arguments are freed when calling a deprecated function
--FILE--
<?php
set_error_handler(function($code, $msg) {
throw new Error($msg);
});
try {
ezmlm_hash(new stdClass);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
$ret = new stdClass;
try {
$ret = ezmlm_hash(new stdClass);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$fn = 'ezmlm_hash';
$fn(new stdClass);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
$ret = new stdClass;
try {
$fn = 'ezmlm_hash';
$ret = $fn(new stdClass);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
Function ezmlm_hash() is deprecated
Function ezmlm_hash() is deprecated
Function ezmlm_hash() is deprecated
Function ezmlm_hash() is deprecated

View File

@ -1132,7 +1132,6 @@ static zend_never_inline int zend_verify_internal_arg_types(zend_function *fbc,
dummy_cache_slot = NULL;
if (UNEXPECTED(!zend_verify_arg_type(fbc, i + 1, p, NULL, &dummy_cache_slot))) {
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
return 0;
}
p++;

View File

@ -4075,8 +4075,12 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL))
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
zend_deprecated_function(fbc);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
UNDEF_RESULT();
if (!RETURN_VALUE_USED(opline)) {
ret = &retval;
ZVAL_UNDEF(ret);
}
ZEND_VM_C_GOTO(fcall_end);
}
}
@ -4085,10 +4089,12 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL))
if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
&& UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
zend_vm_stack_free_call_frame(call);
zend_rethrow_exception(execute_data);
UNDEF_RESULT();
HANDLE_EXCEPTION();
UNDEF_RESULT();
if (!RETURN_VALUE_USED(opline)) {
ret = &retval;
ZVAL_UNDEF(ret);
}
ZEND_VM_C_GOTO(fcall_end);
}
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
@ -4106,6 +4112,8 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL))
#endif
EG(current_execute_data) = execute_data;
ZEND_VM_C_LABEL(fcall_end):
zend_vm_stack_free_args(call);
zend_vm_stack_free_call_frame(call);
@ -4128,6 +4136,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL))
zend_execute_data *call = EX(call);
zend_function *fbc = call->func;
zval *ret;
zval retval;
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
@ -4136,7 +4145,10 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL))
zend_abstract_method(fbc);
ZEND_VM_C_LABEL(fcall_except):
UNDEF_RESULT();
zend_vm_stack_free_args(call);
if (!RETURN_VALUE_USED(opline)) {
ret = &retval;
ZVAL_UNDEF(ret);
}
ZEND_VM_C_GOTO(fcall_end);
} else {
zend_deprecated_function(fbc);
@ -4167,15 +4179,12 @@ ZEND_VM_C_LABEL(fcall_except):
zend_execute_ex(call);
}
} else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) {
zval retval;
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
&& UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
UNDEF_RESULT();
ZEND_VM_C_GOTO(fcall_end);
ZEND_VM_C_GOTO(fcall_except);
}
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
@ -4198,15 +4207,13 @@ ZEND_VM_C_LABEL(fcall_except):
#endif
EG(current_execute_data) = execute_data;
zend_vm_stack_free_args(call);
ZEND_VM_C_LABEL(fcall_end):
zend_vm_stack_free_args(call);
if (!RETURN_VALUE_USED(opline)) {
i_zval_ptr_dtor(ret);
}
} else { /* ZEND_OVERLOADED_FUNCTION */
zval retval;
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
call->prev_execute_data = execute_data;
@ -4221,7 +4228,6 @@ ZEND_VM_C_LABEL(fcall_except):
}
}
ZEND_VM_C_LABEL(fcall_end):
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) {
OBJ_RELEASE(Z_OBJ(call->This));
}
@ -8291,14 +8297,13 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
EG(current_execute_data) = call->prev_execute_data;
ZEND_VM_C_LABEL(call_trampoline_end):
zend_vm_stack_free_args(call);
if (ret == &retval) {
zval_ptr_dtor(ret);
}
}
ZEND_VM_C_LABEL(call_trampoline_end):
execute_data = EG(current_execute_data);
if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {

View File

@ -1413,8 +1413,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
zend_deprecated_function(fbc);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
UNDEF_RESULT();
if (!0) {
ret = &retval;
ZVAL_UNDEF(ret);
}
goto fcall_end;
}
}
@ -1423,10 +1427,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
&& UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
zend_vm_stack_free_call_frame(call);
zend_rethrow_exception(execute_data);
UNDEF_RESULT();
HANDLE_EXCEPTION();
UNDEF_RESULT();
if (!0) {
ret = &retval;
ZVAL_UNDEF(ret);
}
goto fcall_end;
}
ret = 0 ? EX_VAR(opline->result.var) : &retval;
@ -1444,6 +1450,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
#endif
EG(current_execute_data) = execute_data;
fcall_end:
zend_vm_stack_free_args(call);
zend_vm_stack_free_call_frame(call);
@ -1489,8 +1497,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
zend_deprecated_function(fbc);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
UNDEF_RESULT();
if (!1) {
ret = &retval;
ZVAL_UNDEF(ret);
}
goto fcall_end;
}
}
@ -1499,10 +1511,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
&& UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
zend_vm_stack_free_call_frame(call);
zend_rethrow_exception(execute_data);
UNDEF_RESULT();
HANDLE_EXCEPTION();
UNDEF_RESULT();
if (!1) {
ret = &retval;
ZVAL_UNDEF(ret);
}
goto fcall_end;
}
ret = 1 ? EX_VAR(opline->result.var) : &retval;
@ -1520,6 +1534,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
#endif
EG(current_execute_data) = execute_data;
fcall_end:
zend_vm_stack_free_args(call);
zend_vm_stack_free_call_frame(call);
@ -1542,6 +1558,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
zend_execute_data *call = EX(call);
zend_function *fbc = call->func;
zval *ret;
zval retval;
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
@ -1550,7 +1567,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
zend_abstract_method(fbc);
fcall_except:
UNDEF_RESULT();
zend_vm_stack_free_args(call);
if (!0) {
ret = &retval;
ZVAL_UNDEF(ret);
}
goto fcall_end;
} else {
zend_deprecated_function(fbc);
@ -1581,15 +1601,12 @@ fcall_except:
zend_execute_ex(call);
}
} else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) {
zval retval;
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
&& UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
UNDEF_RESULT();
goto fcall_end;
goto fcall_except;
}
ret = 0 ? EX_VAR(opline->result.var) : &retval;
@ -1612,15 +1629,13 @@ fcall_except:
#endif
EG(current_execute_data) = execute_data;
zend_vm_stack_free_args(call);
fcall_end:
zend_vm_stack_free_args(call);
if (!0) {
i_zval_ptr_dtor(ret);
}
} else { /* ZEND_OVERLOADED_FUNCTION */
zval retval;
ret = 0 ? EX_VAR(opline->result.var) : &retval;
call->prev_execute_data = execute_data;
@ -1635,7 +1650,6 @@ fcall_except:
}
}
fcall_end:
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) {
OBJ_RELEASE(Z_OBJ(call->This));
}
@ -1656,6 +1670,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
zend_execute_data *call = EX(call);
zend_function *fbc = call->func;
zval *ret;
zval retval;
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
@ -1664,7 +1679,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
zend_abstract_method(fbc);
fcall_except:
UNDEF_RESULT();
zend_vm_stack_free_args(call);
if (!1) {
ret = &retval;
ZVAL_UNDEF(ret);
}
goto fcall_end;
} else {
zend_deprecated_function(fbc);
@ -1695,15 +1713,12 @@ fcall_except:
zend_execute_ex(call);
}
} else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) {
zval retval;
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
&& UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
UNDEF_RESULT();
goto fcall_end;
goto fcall_except;
}
ret = 1 ? EX_VAR(opline->result.var) : &retval;
@ -1726,15 +1741,13 @@ fcall_except:
#endif
EG(current_execute_data) = execute_data;
zend_vm_stack_free_args(call);
fcall_end:
zend_vm_stack_free_args(call);
if (!1) {
i_zval_ptr_dtor(ret);
}
} else { /* ZEND_OVERLOADED_FUNCTION */
zval retval;
ret = 1 ? EX_VAR(opline->result.var) : &retval;
call->prev_execute_data = execute_data;
@ -1749,7 +1762,6 @@ fcall_except:
}
}
fcall_end:
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) {
OBJ_RELEASE(Z_OBJ(call->This));
}
@ -2810,14 +2822,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z
EG(current_execute_data) = call->prev_execute_data;
call_trampoline_end:
zend_vm_stack_free_args(call);
if (ret == &retval) {
zval_ptr_dtor(ret);
}
}
call_trampoline_end:
execute_data = EG(current_execute_data);
if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {