Skip to content

Commit d863149

Browse files
committed
Zend: Add ZPP F type check for callables that do not free trampolines
As refetching it with the new FCC API does get tedious
1 parent d24f07c commit d863149

File tree

4 files changed

+39
-17
lines changed

4 files changed

+39
-17
lines changed

UPGRADING.INTERNALS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ PHP 8.4 INTERNALS UPGRADE NOTES
1919
to do this at the call site anymore. Writing the handle should happen after
2020
successful registration.
2121

22+
* ZPP now accepts a F or Z_PARAM_FUNC_NO_TRAMPOLINE_FREE type check.
23+
This is identical to the 'f' or Z_PARAM_FUNC type check, except the FCC is
24+
always initialized because it doesn't free trampolines.
25+
Trampolines MUST be freed using zend_release_fcall_info_cache() or consumed.
26+
2227
========================
2328
2. Build system changes
2429
========================

Zend/zend_API.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
981981
}
982982
break;
983983

984+
case 'F':
984985
case 'f':
985986
{
986987
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
995996

996997
if (zend_fcall_info_init(arg, 0, fci, fcc, NULL, &is_callable_error) == SUCCESS) {
997998
ZEND_ASSERT(!is_callable_error);
998-
/* Release call trampolines: The function may not get called, in which case
999-
* the trampoline will leak. Force it to be refetched during
1000-
* zend_call_function instead. */
1001-
zend_release_fcall_info_cache(fcc);
999+
if (c == 'f') {
1000+
/* Release call trampolines: The function may not get called, in which case
1001+
* the trampoline will leak. Force it to be refetched during
1002+
* zend_call_function instead. */
1003+
zend_release_fcall_info_cache(fcc);
1004+
}
10021005
break;
10031006
}
10041007

@@ -1109,7 +1112,7 @@ static zend_result zend_parse_va_args(uint32_t num_args, const char *type_spec,
11091112
case 'o': case 'O':
11101113
case 'z': case 'Z':
11111114
case 'C': case 'h':
1112-
case 'f': case 'A':
1115+
case 'f': case 'F': case 'A':
11131116
case 'H': case 'p':
11141117
case 'S': case 'P':
11151118
case 'L': case 'n':

Zend/zend_API.h

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,9 +1794,9 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
17941794
Z_PARAM_DOUBLE_EX(dest, is_null, 1, 0)
17951795

17961796
/* old "f" */
1797-
#define Z_PARAM_FUNC_EX(dest_fci, dest_fcc, check_null, deref) \
1797+
#define Z_PARAM_FUNC_EX(dest_fci, dest_fcc, check_null, deref, free_trampoline) \
17981798
Z_PARAM_PROLOGUE(deref, 0); \
1799-
if (UNEXPECTED(!zend_parse_arg_func(_arg, &dest_fci, &dest_fcc, check_null, &_error))) { \
1799+
if (UNEXPECTED(!zend_parse_arg_func(_arg, &dest_fci, &dest_fcc, check_null, &_error, free_trampoline))) { \
18001800
if (!_error) { \
18011801
_expected_type = check_null ? Z_EXPECTED_FUNC_OR_NULL : Z_EXPECTED_FUNC; \
18021802
_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 *
18071807
} \
18081808

18091809
#define Z_PARAM_FUNC(dest_fci, dest_fcc) \
1810-
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 0, 0)
1810+
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 0, 0, true)
1811+
1812+
#define Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(dest_fci, dest_fcc) \
1813+
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 0, 0, false)
18111814

18121815
#define Z_PARAM_FUNC_OR_NULL(dest_fci, dest_fcc) \
1813-
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0)
1816+
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0, true)
1817+
1818+
#define Z_PARAM_FUNC_NO_TRAMPOLINE_FREE_OR_NULL(dest_fci, dest_fcc) \
1819+
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0, false)
18141820

18151821
#define Z_PARAM_FUNC_OR_NULL_WITH_ZVAL(dest_fci, dest_fcc, dest_zp) \
1816-
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0) \
1822+
Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0, true) \
18171823
Z_PARAM_GET_PREV_ZVAL(dest_zp)
18181824

18191825
/* old "h" */
@@ -2428,7 +2434,7 @@ static zend_always_inline bool zend_parse_arg_resource(zval *arg, zval **dest, b
24282434
return 1;
24292435
}
24302436

2431-
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)
2437+
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)
24322438
{
24332439
if (check_null && UNEXPECTED(Z_TYPE_P(arg) == IS_NULL)) {
24342440
dest_fci->size = 0;
@@ -2437,10 +2443,12 @@ static zend_always_inline bool zend_parse_arg_func(zval *arg, zend_fcall_info *d
24372443
} else if (UNEXPECTED(zend_fcall_info_init(arg, 0, dest_fci, dest_fcc, NULL, error) != SUCCESS)) {
24382444
return 0;
24392445
}
2440-
/* Release call trampolines: The function may not get called, in which case
2441-
* the trampoline will leak. Force it to be refetched during
2442-
* zend_call_function instead. */
2443-
zend_release_fcall_info_cache(dest_fcc);
2446+
if (free_trampoline) {
2447+
/* Release call trampolines: The function may not get called, in which case
2448+
* the trampoline will leak. Force it to be refetched during
2449+
* zend_call_function instead. */
2450+
zend_release_fcall_info_cache(dest_fcc);
2451+
}
24442452
return 1;
24452453
}
24462454

docs/parameter-parsing-api.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,14 @@ A - array or object (zval*)
7070
b - boolean (bool)
7171
C - class (zend_class_entry*)
7272
d - double (double)
73-
f - function or array containing php method call info (returned as
74-
zend_fcall_info and zend_fcall_info_cache)
73+
f - PHP callable containing php function/method call info (returned as
74+
zend_fcall_info and zend_fcall_info_cache).
75+
The FCC may be uninitialized if the callable is a trampoline.
76+
F - PHP callable containing php function/method call info (returned as
77+
zend_fcall_info and zend_fcall_info_cache).
78+
The FCC will *always* be initialized, even if the callable is a trampoline.
79+
A trampoline *must* be consumed or released with
80+
zend_release_fcall_info_cache().
7581
h - array (returned as HashTable*)
7682
H - array or HASH_OF(object) (returned as HashTable*)
7783
l - long (zend_long)

0 commit comments

Comments
 (0)