Zend: Add ZPP F type check for callables that do not free trampolines

As refetching it with the new FCC API does get tedious
This commit is contained in:
George Peter Banyard 2023-10-06 18:07:37 +01:00
parent d24f07cbfe
commit d86314939c
4 changed files with 39 additions and 17 deletions

View file

@ -19,6 +19,11 @@ PHP 8.4 INTERNALS UPGRADE NOTES
to do this at the call site anymore. Writing the handle should happen after
successful registration.
* ZPP now accepts a F or Z_PARAM_FUNC_NO_TRAMPOLINE_FREE type check.
This is identical to the 'f' or Z_PARAM_FUNC type check, except the FCC is
always initialized because it doesn't free trampolines.
Trampolines MUST be freed using zend_release_fcall_info_cache() or consumed.
========================
2. Build system changes
========================

View file

@ -981,6 +981,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
}
break;
case 'F':
case 'f':
{
zend_fcall_info *fci = va_arg(*va, zend_fcall_info *);
@ -995,10 +996,12 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
if (zend_fcall_info_init(arg, 0, fci, fcc, NULL, &is_callable_error) == SUCCESS) {
ZEND_ASSERT(!is_callable_error);
/* Release call trampolines: The function may not get called, in which case
* the trampoline will leak. Force it to be refetched during
* zend_call_function instead. */
zend_release_fcall_info_cache(fcc);
if (c == 'f') {
/* Release call trampolines: The function may not get called, in which case
* the trampoline will leak. Force it to be refetched during
* zend_call_function instead. */
zend_release_fcall_info_cache(fcc);
}
break;
}
@ -1109,7 +1112,7 @@ static zend_result zend_parse_va_args(uint32_t num_args, const char *type_spec,
case 'o': case 'O':
case 'z': case 'Z':
case 'C': case 'h':
case 'f': case 'A':
case 'f': case 'F': case 'A':
case 'H': case 'p':
case 'S': case 'P':
case 'L': case 'n':

View file

@ -1794,9 +1794,9 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
Z_PARAM_DOUBLE_EX(dest, is_null, 1, 0)
/* old "f" */
#define Z_PARAM_FUNC_EX(dest_fci, dest_fcc, check_null, deref) \
#define Z_PARAM_FUNC_EX(dest_fci, dest_fcc, check_null, deref, free_trampoline) \
Z_PARAM_PROLOGUE(deref, 0); \
if (UNEXPECTED(!zend_parse_arg_func(_arg, &dest_fci, &dest_fcc, check_null, &_error))) { \
if (UNEXPECTED(!zend_parse_arg_func(_arg, &dest_fci, &dest_fcc, check_null, &_error, free_trampoline))) { \
if (!_error) { \
_expected_type = check_null ? Z_EXPECTED_FUNC_OR_NULL : Z_EXPECTED_FUNC; \
_error_code = ZPP_ERROR_WRONG_ARG; \
@ -1807,13 +1807,19 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
} \
#define Z_PARAM_FUNC(dest_fci, dest_fcc) \
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 0, 0)
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 0, 0, true)
#define Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(dest_fci, dest_fcc) \
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 0, 0, false)
#define Z_PARAM_FUNC_OR_NULL(dest_fci, dest_fcc) \
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0)
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0, true)
#define Z_PARAM_FUNC_NO_TRAMPOLINE_FREE_OR_NULL(dest_fci, dest_fcc) \
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0, false)
#define Z_PARAM_FUNC_OR_NULL_WITH_ZVAL(dest_fci, dest_fcc, dest_zp) \
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0) \
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0, true) \
Z_PARAM_GET_PREV_ZVAL(dest_zp)
/* old "h" */
@ -2428,7 +2434,7 @@ static zend_always_inline bool zend_parse_arg_resource(zval *arg, zval **dest, b
return 1;
}
static zend_always_inline bool zend_parse_arg_func(zval *arg, zend_fcall_info *dest_fci, zend_fcall_info_cache *dest_fcc, bool check_null, char **error)
static zend_always_inline bool zend_parse_arg_func(zval *arg, zend_fcall_info *dest_fci, zend_fcall_info_cache *dest_fcc, bool check_null, char **error, bool free_trampoline)
{
if (check_null && UNEXPECTED(Z_TYPE_P(arg) == IS_NULL)) {
dest_fci->size = 0;
@ -2437,10 +2443,12 @@ static zend_always_inline bool zend_parse_arg_func(zval *arg, zend_fcall_info *d
} else if (UNEXPECTED(zend_fcall_info_init(arg, 0, dest_fci, dest_fcc, NULL, error) != SUCCESS)) {
return 0;
}
/* Release call trampolines: The function may not get called, in which case
* the trampoline will leak. Force it to be refetched during
* zend_call_function instead. */
zend_release_fcall_info_cache(dest_fcc);
if (free_trampoline) {
/* Release call trampolines: The function may not get called, in which case
* the trampoline will leak. Force it to be refetched during
* zend_call_function instead. */
zend_release_fcall_info_cache(dest_fcc);
}
return 1;
}

View file

@ -70,8 +70,14 @@ A - array or object (zval*)
b - boolean (bool)
C - class (zend_class_entry*)
d - double (double)
f - function or array containing php method call info (returned as
zend_fcall_info and zend_fcall_info_cache)
f - PHP callable containing php function/method call info (returned as
zend_fcall_info and zend_fcall_info_cache).
The FCC may be uninitialized if the callable is a trampoline.
F - PHP callable containing php function/method call info (returned as
zend_fcall_info and zend_fcall_info_cache).
The FCC will *always* be initialized, even if the callable is a trampoline.
A trampoline *must* be consumed or released with
zend_release_fcall_info_cache().
h - array (returned as HashTable*)
H - array or HASH_OF(object) (returned as HashTable*)
l - long (zend_long)