diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 2541486c492a2..c97d9308e2081 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3045,7 +3045,6 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend internal_function->prop_info = NULL; internal_function->attributes = NULL; internal_function->frameless_function_infos = ptr->frameless_function_infos; - internal_function->fn_flags2 = 0; if (EG(active)) { // at run-time: this ought to only happen if registered with dl() or somehow temporarily at runtime ZEND_MAP_PTR_INIT(internal_function->run_time_cache, zend_arena_calloc(&CG(arena), 1, zend_internal_run_time_cache_reserved_size())); } else { @@ -3055,7 +3054,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend ZEND_MAP_PTR_INIT(internal_function->run_time_cache, NULL); #endif } - if (ptr->flags) { + if (ptr->flags & UINT32_MAX) { if (!(ptr->flags & ZEND_ACC_PPP_MASK)) { if (ptr->flags != ZEND_ACC_DEPRECATED && scope) { zend_error(error_type, "Invalid access level for %s::%s() - access must be exactly one of public, protected or private", ZSTR_VAL(scope->name), ptr->fname); @@ -3067,6 +3066,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend } else { internal_function->fn_flags = ZEND_ACC_PUBLIC; } + internal_function->fn_flags2 = ptr->flags >> 32; if (ptr->arg_info) { zend_internal_function_info *info = (zend_internal_function_info*)ptr->arg_info; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 17f7ce3263f84..9eaa1ec7b3c8b 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -36,7 +36,7 @@ typedef struct _zend_function_entry { zif_handler handler; const struct _zend_internal_arg_info *arg_info; uint32_t num_args; - uint32_t flags; + uint64_t flags; const zend_frameless_function_info *frameless_function_infos; const char *doc_comment; } zend_function_entry; @@ -74,6 +74,8 @@ typedef struct _zend_fcall_info_cache { #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(zif_##name) #define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(zim_##classname##_##name) +#define ZEND_FENTRY_FLAGS(flags, flags2) (((uint64_t)flags) | ((uint64_t)flags2 << 32)) + #define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags, NULL, NULL }, #define ZEND_RAW_FENTRY(zend_name, name, arg_info, flags, frameless_function_infos, doc_comment) { zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags, frameless_function_infos, doc_comment }, @@ -894,6 +896,8 @@ static zend_always_inline zend_result zend_forbid_dynamic_call(void) const zend_execute_data *ex = EG(current_execute_data); ZEND_ASSERT(ex != NULL && ex->func != NULL); + ZEND_ASSERT(ex->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS); + if (ZEND_CALL_INFO(ex) & ZEND_CALL_DYNAMIC) { zend_string *function_or_method_name = get_active_function_or_method_name(); zend_throw_error(NULL, "Cannot call %.*s() dynamically", diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 1fe4bcf3ee50e..c19bf2779fbf0 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -16,6 +16,7 @@ +----------------------------------------------------------------------+ */ +#include "php_version.h" #include "zend.h" #include "zend_API.h" #include "zend_attributes.h" diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php index 9b2267b531eb2..1d405587145dd 100644 --- a/Zend/zend_builtin_functions.stub.php +++ b/Zend/zend_builtin_functions.stub.php @@ -18,11 +18,16 @@ function die(string|int $status = 0): never {} /** @refcount 1 */ function zend_version(): string {} +/** @forbid-dynamic-calls */ function func_num_args(): int {} +/** @forbid-dynamic-calls */ function func_get_arg(int $position): mixed {} -/** @return array */ +/** + * @return array + * @forbid-dynamic-calls + */ function func_get_args(): array {} function strlen(string $string): int {} @@ -156,6 +161,7 @@ function get_defined_functions(bool $exclude_disabled = true): array {} /** * @return array * @refcount 1 + * @forbid-dynamic-calls */ function get_defined_vars(): array {} diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h index cb626ff430e60..b3af43fef340d 100644 --- a/Zend/zend_builtin_functions_arginfo.h +++ b/Zend/zend_builtin_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zend_builtin_functions.stub.php instead. - * Stub hash: 9b49f527064695c812cd204d9efc63c13681d942 */ + * Stub hash: 64c61862de86d9968930893bf21b516119724064 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_clone, 0, 1, IS_OBJECT, 0) ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) @@ -316,9 +316,21 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(exit, arginfo_exit) ZEND_RAW_FENTRY("die", zif_exit, arginfo_die, 0, NULL, NULL) ZEND_FE(zend_version, arginfo_zend_version) - ZEND_FE(func_num_args, arginfo_func_num_args) - ZEND_FE(func_get_arg, arginfo_func_get_arg) - ZEND_FE(func_get_args, arginfo_func_get_args) +#if (PHP_VERSION_ID >= 80600) + ZEND_RAW_FENTRY("func_num_args", zif_func_num_args, arginfo_func_num_args, ZEND_FENTRY_FLAGS(0, ZEND_ACC2_FORBID_DYN_CALLS), NULL, NULL) +#elif (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("func_num_args", zif_func_num_args, arginfo_func_num_args, 0, NULL, NULL) +#endif +#if (PHP_VERSION_ID >= 80600) + ZEND_RAW_FENTRY("func_get_arg", zif_func_get_arg, arginfo_func_get_arg, ZEND_FENTRY_FLAGS(0, ZEND_ACC2_FORBID_DYN_CALLS), NULL, NULL) +#elif (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("func_get_arg", zif_func_get_arg, arginfo_func_get_arg, 0, NULL, NULL) +#endif +#if (PHP_VERSION_ID >= 80600) + ZEND_RAW_FENTRY("func_get_args", zif_func_get_args, arginfo_func_get_args, ZEND_FENTRY_FLAGS(0, ZEND_ACC2_FORBID_DYN_CALLS), NULL, NULL) +#elif (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("func_get_args", zif_func_get_args, arginfo_func_get_args, 0, NULL, NULL) +#endif ZEND_FE(strlen, arginfo_strlen) ZEND_RAW_FENTRY("strcmp", zif_strcmp, arginfo_strcmp, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("strncmp", zif_strncmp, arginfo_strncmp, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) @@ -358,7 +370,11 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(get_declared_traits, arginfo_get_declared_traits) ZEND_FE(get_declared_interfaces, arginfo_get_declared_interfaces) ZEND_FE(get_defined_functions, arginfo_get_defined_functions) - ZEND_FE(get_defined_vars, arginfo_get_defined_vars) +#if (PHP_VERSION_ID >= 80600) + ZEND_RAW_FENTRY("get_defined_vars", zif_get_defined_vars, arginfo_get_defined_vars, ZEND_FENTRY_FLAGS(0, ZEND_ACC2_FORBID_DYN_CALLS), NULL, NULL) +#elif (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("get_defined_vars", zif_get_defined_vars, arginfo_get_defined_vars, 0, NULL, NULL) +#endif ZEND_FE(get_resource_type, arginfo_get_resource_type) ZEND_FE(get_resource_id, arginfo_get_resource_id) ZEND_FE(get_resources, arginfo_get_resources) diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 56090cddcafb8..314abede40635 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -749,6 +749,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */ { zend_closure *closure = (zend_closure*)ZEND_CLOSURE_OBJECT(EX(func)); closure->orig_internal_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + ZEND_ASSERT(!(closure->func.common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS) || EG(exception)); // Assign to EX(this) so that it is released after observer checks etc. ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_RELEASE_THIS); Z_OBJ(EX(This)) = &closure->std; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 3b85d52c1870a..8f67fee2a52ba 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -412,10 +412,11 @@ typedef struct _zend_oparray_context { /* op_array uses strict mode types | | | */ #define ZEND_ACC_STRICT_TYPES (1U << 31) /* | X | | */ /* | | | */ -/* Function Flags 2 (fn_flags2) (unused: 0-31) | | | */ +/* Function Flags 2 (fn_flags2) (unused: 1-31) | | | */ /* ============================ | | | */ /* | | | */ -/* #define ZEND_ACC2_EXAMPLE (1 << 0) | X | | */ +/* Function forbids dynamic calls | | | */ +#define ZEND_ACC2_FORBID_DYN_CALLS (1 << 0) /* | X | | */ #define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE) #define ZEND_ACC_PPP_SET_MASK (ZEND_ACC_PUBLIC_SET | ZEND_ACC_PROTECTED_SET | ZEND_ACC_PRIVATE_SET) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 5bead7034b9a4..0022eb4a1df8b 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1030,6 +1030,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ } ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(fci->retval) : !Z_ISREF_P(fci->retval)); + ZEND_ASSERT(!(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif ZEND_OBSERVER_FCALL_END(call, fci->retval); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 391b82241e477..1f7e09d1be358 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4160,6 +4160,8 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret); @@ -4291,6 +4293,8 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER)) ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -4422,6 +4426,8 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -9131,6 +9137,8 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER)) ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index a2e5eac491dc3..d5860da23b4c6 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1328,6 +1328,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif @@ -1396,6 +1398,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif @@ -1464,6 +1468,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif zend_observer_fcall_end(call, EG(exception) ? NULL : ret); @@ -1648,6 +1654,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -1764,6 +1772,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -1879,6 +1889,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -2013,6 +2025,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -2146,6 +2160,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -2276,6 +2292,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -3663,6 +3681,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALL_TRAMPOLI ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif @@ -3809,6 +3829,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALL_TRAMPOLI ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif zend_observer_fcall_end(call, EG(exception) ? NULL : ret); @@ -54080,6 +54102,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_ICA ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif @@ -54148,6 +54172,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_ICA ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif @@ -54216,6 +54242,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_IC ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif zend_observer_fcall_end(call, EG(exception) ? NULL : ret); @@ -54400,6 +54428,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -54516,6 +54546,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -54631,6 +54663,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FC ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -54765,6 +54799,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -54898,6 +54934,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -55028,6 +55066,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FC ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } ZEND_ASSERT(opline->result_type != IS_TMP_VAR || !Z_ISREF_P(ret)); #endif @@ -56299,6 +56339,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALL_TRAMPOLINE_SP ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif @@ -56445,6 +56487,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALL_TRAMPOLINE_SP ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); zend_verify_internal_func_info(call->func, ret); + ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) + || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)); } #endif zend_observer_fcall_end(call, EG(exception) ? NULL : ret); diff --git a/build/gen_stub.php b/build/gen_stub.php index db7f4d400ed78..619c4c905b60d 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -1184,6 +1184,32 @@ public function isEmpty(): bool { ); } + /** + * If we have ZEND_ACC2_ flags, represent as 'ZEND_FENTRY_FLAGS(flags1, flags2)'. + * Otherwise, represent as just 'flags1' (backwards compatible). + */ + private function formatFlags(array $flags): string { + $flags1 = []; + $flags2 = []; + foreach ($flags as $flag) { + if (str_starts_with($flag, 'ZEND_ACC2_')) { + $flags2[] = $flag; + } else { + $flags1[] = $flag; + } + } + + if ($flags2 !== []) { + return sprintf( + 'ZEND_FENTRY_FLAGS(%s, %s)', + $flags1 === [] ? 0 : implode("|", $flags1), + implode("|", $flags2), + ); + } + + return implode("|", $flags1); + } + public function generateVersionDependentFlagCode( string $codeTemplate, ?int $phpVersionIdMinimumCompatibility, @@ -1199,7 +1225,7 @@ public function generateVersionDependentFlagCode( if (empty($flagsByPhpVersions[$currentPhpVersion])) { return ''; } - return sprintf($codeTemplate, implode("|", $flagsByPhpVersions[$currentPhpVersion])); + return sprintf($codeTemplate, $this->formatFlags($flagsByPhpVersions[$currentPhpVersion])); } ksort($flagsByPhpVersions); @@ -1240,7 +1266,7 @@ public function generateVersionDependentFlagCode( reset($flagsByPhpVersions); $firstVersion = key($flagsByPhpVersions); if ($firstVersion === $phpVersionIdMinimumCompatibility) { - return sprintf($codeTemplate, implode("|", reset($flagsByPhpVersions))); + return sprintf($codeTemplate, $this->formatFlags(reset($flagsByPhpVersions))); } } @@ -1253,7 +1279,7 @@ public function generateVersionDependentFlagCode( $code .= "$if (PHP_VERSION_ID >= $version)\n"; - $code .= sprintf($codeTemplate, implode("|", $versionFlags)); + $code .= sprintf($codeTemplate, $this->formatFlags($versionFlags)); $code .= $endif; $i++; @@ -1278,6 +1304,7 @@ public function __construct( public ?FunctionOrMethodName $alias, private readonly bool $isDeprecated, private bool $supportsCompileTimeEval, + private bool $forbidDynamicCalls, public readonly bool $verify, public /* readonly */ array $args, public /* readonly */ ReturnInfo $return, @@ -1585,6 +1612,10 @@ private function getArginfoFlagsByPhpVersions(): VersionFlags $flags->addForVersionsAbove("ZEND_ACC_NODISCARD", PHP_85_VERSION_ID); } + if ($this->forbidDynamicCalls) { + $flags->addForVersionsAbove("ZEND_ACC2_FORBID_DYN_CALLS", PHP_86_VERSION_ID); + } + return $flags; } @@ -4771,6 +4802,7 @@ function parseFunctionLike( $alias = null; $isDeprecated = false; $supportsCompileTimeEval = false; + $forbidDynamicCalls = false; $verify = true; $docReturnType = null; $tentativeReturnType = false; @@ -4786,6 +4818,7 @@ function parseFunctionLike( $verify = !array_key_exists('no-verify', $tagMap); $tentativeReturnType = array_key_exists('tentative-return-type', $tagMap); $supportsCompileTimeEval = array_key_exists('compile-time-eval', $tagMap); + $forbidDynamicCalls = array_key_exists('forbid-dynamic-calls', $tagMap); $isUndocumentable = $isUndocumentable || array_key_exists('undocumentable', $tagMap); foreach ($tags as $tag) { @@ -4918,6 +4951,7 @@ function parseFunctionLike( $alias, $isDeprecated, $supportsCompileTimeEval, + $forbidDynamicCalls, $verify, $args, $return, diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index c437f0d7f6c2e..1999c9b92be1c 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -1641,7 +1641,10 @@ function in_array(mixed $needle, array $haystack, bool $strict = false): bool {} */ function array_search(mixed $needle, array $haystack, bool $strict = false): int|string|false {} -/** @prefer-ref $array */ +/** + * @prefer-ref $array + * @forbid-dynamic-calls + */ function extract(array &$array, int $flags = EXTR_OVERWRITE, string $prefix = ""): int {} /** @@ -1649,6 +1652,7 @@ function extract(array &$array, int $flags = EXTR_OVERWRITE, string $prefix = "" * @param array|string $var_names * @return array * @refcount 1 + * @forbid-dynamic-calls */ function compact($var_name, ...$var_names): array {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 1ba20c6b26ccd..e51a837ffa4d8 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: a846d7e3dd1f1cebd8c6257132c97b3758067127 + * Stub hash: 36b71aa7bbfe478a5e4af400b2822a77067efa2f * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) @@ -2944,8 +2944,16 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(array_walk_recursive, arginfo_array_walk_recursive) ZEND_RAW_FENTRY("in_array", zif_in_array, arginfo_in_array, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_in_array, NULL) ZEND_RAW_FENTRY("array_search", zif_array_search, arginfo_array_search, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) - ZEND_FE(extract, arginfo_extract) - ZEND_FE(compact, arginfo_compact) +#if (PHP_VERSION_ID >= 80600) + ZEND_RAW_FENTRY("extract", zif_extract, arginfo_extract, ZEND_FENTRY_FLAGS(0, ZEND_ACC2_FORBID_DYN_CALLS), NULL, NULL) +#elif (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("extract", zif_extract, arginfo_extract, 0, NULL, NULL) +#endif +#if (PHP_VERSION_ID >= 80600) + ZEND_RAW_FENTRY("compact", zif_compact, arginfo_compact, ZEND_FENTRY_FLAGS(0, ZEND_ACC2_FORBID_DYN_CALLS), NULL, NULL) +#elif (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("compact", zif_compact, arginfo_compact, 0, NULL, NULL) +#endif ZEND_FE(array_fill, arginfo_array_fill) ZEND_FE(array_fill_keys, arginfo_array_fill_keys) ZEND_FE(range, arginfo_range) diff --git a/ext/standard/basic_functions_decl.h b/ext/standard/basic_functions_decl.h index ab27bb64f0c70..b3eb25c5d9887 100644 --- a/ext/standard/basic_functions_decl.h +++ b/ext/standard/basic_functions_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: a846d7e3dd1f1cebd8c6257132c97b3758067127 */ + * Stub hash: 36b71aa7bbfe478a5e4af400b2822a77067efa2f */ -#ifndef ZEND_BASIC_FUNCTIONS_DECL_a846d7e3dd1f1cebd8c6257132c97b3758067127_H -#define ZEND_BASIC_FUNCTIONS_DECL_a846d7e3dd1f1cebd8c6257132c97b3758067127_H +#ifndef ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H +#define ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H typedef enum zend_enum_SortDirection { ZEND_ENUM_SortDirection_Ascending = 1, @@ -20,4 +20,4 @@ typedef enum zend_enum_RoundingMode { ZEND_ENUM_RoundingMode_PositiveInfinity = 8, } zend_enum_RoundingMode; -#endif /* ZEND_BASIC_FUNCTIONS_DECL_a846d7e3dd1f1cebd8c6257132c97b3758067127_H */ +#endif /* ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H */ diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index c9d367d5553fa..653630ed73b77 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -187,7 +187,9 @@ class ZendTestClassWithPropertyAttribute { } final class ZendTestForbidDynamicCall { + /** @forbid-dynamic-calls */ public function call(): void {} + /** @forbid-dynamic-calls */ public static function callStatic(): void {} } diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index a4da05df2ffb3..adcae16cdf616 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: dcb089a336c6c3e6c685762057dcedcb393508a7 + * Stub hash: e1fb73f5a5f455a3a1eb871e670f26b671da0407 * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_trigger_bailout, 0, 0, IS_NEVER, 0) @@ -590,8 +590,16 @@ static const zend_function_entry class_ZendTestChildClassWithMethodWithParameter }; static const zend_function_entry class_ZendTestForbidDynamicCall_methods[] = { +#if (PHP_VERSION_ID >= 80600) + ZEND_ME(ZendTestForbidDynamicCall, call, arginfo_class_ZendTestForbidDynamicCall_call, ZEND_FENTRY_FLAGS(ZEND_ACC_PUBLIC, ZEND_ACC2_FORBID_DYN_CALLS)) +#elif (PHP_VERSION_ID >= 80000) ZEND_ME(ZendTestForbidDynamicCall, call, arginfo_class_ZendTestForbidDynamicCall_call, ZEND_ACC_PUBLIC) +#endif +#if (PHP_VERSION_ID >= 80600) + ZEND_ME(ZendTestForbidDynamicCall, callStatic, arginfo_class_ZendTestForbidDynamicCall_callStatic, ZEND_FENTRY_FLAGS(ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, ZEND_ACC2_FORBID_DYN_CALLS)) +#elif (PHP_VERSION_ID >= 80000) ZEND_ME(ZendTestForbidDynamicCall, callStatic, arginfo_class_ZendTestForbidDynamicCall_callStatic, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) +#endif ZEND_FE_END }; diff --git a/ext/zend_test/test_decl.h b/ext/zend_test/test_decl.h index bc2ebaa93c3bd..ba6aab902280b 100644 --- a/ext/zend_test/test_decl.h +++ b/ext/zend_test/test_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: dcb089a336c6c3e6c685762057dcedcb393508a7 */ + * Stub hash: e1fb73f5a5f455a3a1eb871e670f26b671da0407 */ -#ifndef ZEND_TEST_DECL_dcb089a336c6c3e6c685762057dcedcb393508a7_H -#define ZEND_TEST_DECL_dcb089a336c6c3e6c685762057dcedcb393508a7_H +#ifndef ZEND_TEST_DECL_e1fb73f5a5f455a3a1eb871e670f26b671da0407_H +#define ZEND_TEST_DECL_e1fb73f5a5f455a3a1eb871e670f26b671da0407_H typedef enum zend_enum_ZendTestUnitEnum { ZEND_ENUM_ZendTestUnitEnum_Foo = 1, @@ -27,4 +27,4 @@ typedef enum zend_enum_ZendTestEnumWithInterface { ZEND_ENUM_ZendTestEnumWithInterface_Bar = 2, } zend_enum_ZendTestEnumWithInterface; -#endif /* ZEND_TEST_DECL_dcb089a336c6c3e6c685762057dcedcb393508a7_H */ +#endif /* ZEND_TEST_DECL_e1fb73f5a5f455a3a1eb871e670f26b671da0407_H */ diff --git a/ext/zend_test/test_legacy_arginfo.h b/ext/zend_test/test_legacy_arginfo.h index b446a0f9a2933..b42d524d7a8aa 100644 --- a/ext/zend_test/test_legacy_arginfo.h +++ b/ext/zend_test/test_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: dcb089a336c6c3e6c685762057dcedcb393508a7 + * Stub hash: e1fb73f5a5f455a3a1eb871e670f26b671da0407 * Has decl header: yes */ ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_trigger_bailout, 0, 0, 0) @@ -517,8 +517,16 @@ static const zend_function_entry class_ZendTestChildClassWithMethodWithParameter }; static const zend_function_entry class_ZendTestForbidDynamicCall_methods[] = { +#if (PHP_VERSION_ID >= 80600) + ZEND_ME(ZendTestForbidDynamicCall, call, arginfo_class_ZendTestForbidDynamicCall_call, ZEND_FENTRY_FLAGS(ZEND_ACC_PUBLIC, ZEND_ACC2_FORBID_DYN_CALLS)) +#elif (PHP_VERSION_ID >= 70000) ZEND_ME(ZendTestForbidDynamicCall, call, arginfo_class_ZendTestForbidDynamicCall_call, ZEND_ACC_PUBLIC) +#endif +#if (PHP_VERSION_ID >= 80600) + ZEND_ME(ZendTestForbidDynamicCall, callStatic, arginfo_class_ZendTestForbidDynamicCall_callStatic, ZEND_FENTRY_FLAGS(ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, ZEND_ACC2_FORBID_DYN_CALLS)) +#elif (PHP_VERSION_ID >= 70000) ZEND_ME(ZendTestForbidDynamicCall, callStatic, arginfo_class_ZendTestForbidDynamicCall_callStatic, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) +#endif ZEND_FE_END };