Skip to content

Commit 76622eb

Browse files
committed
Fix non-persistent arg info
Using zend_convert_internal_arg_info() at runtime breaks ZEND_RC_MOD_CHECK() assertions. Add a "persistent" param to make it possible to convert an internal arg info to a non-persistent arg info.
1 parent a8dc794 commit 76622eb

File tree

7 files changed

+131
-27
lines changed

7 files changed

+131
-27
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Declaring non persistent method with arg info
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
8+
$o = new _ZendTestClass();
9+
$o->testTmpMethodWithArgInfo(null);
10+
11+
echo new ReflectionFunction($o->testTmpMethodWithArgInfo(...));
12+
13+
?>
14+
--EXPECT--
15+
Closure [ <internal> public method testTmpMethodWithArgInfo ] {
16+
17+
- Parameters [2] {
18+
Parameter #0 [ <optional> Foo|Bar|null $tmpMethodParamName = null ]
19+
Parameter #1 [ <optional> string $tmpMethodParamWithStringDefaultValue = "tmpMethodParamWithStringDefaultValue" ]
20+
}
21+
}

Zend/zend_API.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2971,13 +2971,13 @@ static void zend_convert_internal_arg_info_type(zend_type *type, bool persistent
29712971

29722972
if (num_types == 1) {
29732973
/* Simple class type */
2974-
zend_string *str = zend_string_init_interned(class_name, strlen(class_name), true);
2974+
zend_string *str = zend_string_init_interned(class_name, strlen(class_name), persistent);
29752975
zend_alloc_ce_cache(str);
29762976
ZEND_TYPE_SET_PTR(*type, str);
29772977
type->type_mask |= _ZEND_TYPE_NAME_BIT;
29782978
} else {
29792979
/* Union type */
2980-
zend_type_list *list = pemalloc(ZEND_TYPE_LIST_SIZE(num_types), true);
2980+
zend_type_list *list = pemalloc(ZEND_TYPE_LIST_SIZE(num_types), persistent);
29812981
list->num_types = num_types;
29822982
ZEND_TYPE_SET_LIST(*type, list);
29832983
ZEND_TYPE_FULL_MASK(*type) |= _ZEND_TYPE_UNION_BIT;
@@ -2986,7 +2986,7 @@ static void zend_convert_internal_arg_info_type(zend_type *type, bool persistent
29862986
uint32_t j = 0;
29872987
while (true) {
29882988
const char *end = strchr(start, '|');
2989-
zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), true);
2989+
zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), persistent);
29902990
zend_alloc_ce_cache(str);
29912991
list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0);
29922992
if (!end) {
@@ -3010,12 +3010,12 @@ static void zend_convert_internal_arg_info_type(zend_type *type, bool persistent
30103010
}
30113011
}
30123012

3013-
ZEND_API void zend_convert_internal_arg_info(zend_arg_info *new_arg_info, const zend_internal_arg_info *arg_info, bool is_return_info)
3013+
ZEND_API void zend_convert_internal_arg_info(zend_arg_info *new_arg_info, const zend_internal_arg_info *arg_info, bool is_return_info, bool persistent)
30143014
{
30153015
if (!is_return_info) {
3016-
new_arg_info->name = zend_string_init_interned(arg_info->name, strlen(arg_info->name), true);
3016+
new_arg_info->name = zend_string_init_interned(arg_info->name, strlen(arg_info->name), persistent);
30173017
if (arg_info->default_value) {
3018-
new_arg_info->default_value = zend_string_init_interned(arg_info->default_value, strlen(arg_info->default_value), true);
3018+
new_arg_info->default_value = zend_string_init_interned(arg_info->default_value, strlen(arg_info->default_value), persistent);
30193019
} else {
30203020
new_arg_info->default_value = NULL;
30213021
}
@@ -3024,7 +3024,7 @@ ZEND_API void zend_convert_internal_arg_info(zend_arg_info *new_arg_info, const
30243024
new_arg_info->default_value = NULL;
30253025
}
30263026
new_arg_info->type = arg_info->type;
3027-
zend_convert_internal_arg_info_type(&new_arg_info->type);
3027+
zend_convert_internal_arg_info_type(&new_arg_info->type, persistent);
30283028
}
30293029

30303030
/* registers all functions in *library_functions in the function hash */
@@ -3236,7 +3236,8 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
32363236
new_arg_info = malloc(sizeof(zend_arg_info) * num_args);
32373237
reg_function->arg_info = new_arg_info + 1;
32383238
for (i = 0; i < num_args; i++) {
3239-
zend_convert_internal_arg_info(&new_arg_info[i], &arg_info[i], i == 0);
3239+
zend_convert_internal_arg_info(&new_arg_info[i], &arg_info[i],
3240+
i == 0, true);
32403241
}
32413242
}
32423243

Zend/zend_API.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -931,7 +931,8 @@ ZEND_API bool zend_is_iterable(const zval *iterable);
931931
ZEND_API bool zend_is_countable(const zval *countable);
932932

933933
ZEND_API void zend_convert_internal_arg_info(zend_arg_info *new_arg_info,
934-
const zend_internal_arg_info *arg_info, bool is_return_info);
934+
const zend_internal_arg_info *arg_info, bool is_return_info,
935+
bool permanent);
935936

936937
ZEND_API zend_result zend_get_default_from_internal_arg_info(
937938
zval *default_value_zval, const zend_arg_info *arg_info);

Zend/zend_enum.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -641,12 +641,12 @@ ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *
641641
void zend_enum_startup(void)
642642
{
643643
for (size_t i = 0; i < sizeof(zarginfo_class_UnitEnum_cases)/sizeof(zend_arg_info); i++) {
644-
zend_convert_internal_arg_info(&zarginfo_class_UnitEnum_cases[i], &arginfo_class_UnitEnum_cases[i], i == 0);
644+
zend_convert_internal_arg_info(&zarginfo_class_UnitEnum_cases[i], &arginfo_class_UnitEnum_cases[i], i == 0, true);
645645
}
646646
for (size_t i = 0; i < sizeof(zarginfo_class_BackedEnum_from)/sizeof(zend_arg_info); i++) {
647-
zend_convert_internal_arg_info(&zarginfo_class_BackedEnum_from[i], &arginfo_class_BackedEnum_from[i], i == 0);
647+
zend_convert_internal_arg_info(&zarginfo_class_BackedEnum_from[i], &arginfo_class_BackedEnum_from[i], i == 0, true);
648648
}
649649
for (size_t i = 0; i < sizeof(zarginfo_class_BackedEnum_tryFrom)/sizeof(zend_arg_info); i++) {
650-
zend_convert_internal_arg_info(&zarginfo_class_BackedEnum_tryFrom[i], &arginfo_class_BackedEnum_tryFrom[i], i == 0);
650+
zend_convert_internal_arg_info(&zarginfo_class_BackedEnum_tryFrom[i], &arginfo_class_BackedEnum_tryFrom[i], i == 0, true);
651651
}
652652
}

Zend/zend_opcode.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,12 @@ ZEND_API void zend_type_release(zend_type type, bool persistent) {
123123
}
124124
}
125125

126-
ZEND_API void zend_free_internal_arg_info(zend_internal_function *function) {
126+
ZEND_API void zend_free_internal_arg_info(zend_internal_function *function,
127+
bool persistent) {
127128
if (function->arg_info) {
129+
ZEND_ASSERT((persistent || (function->fn_flags & ZEND_ACC_NEVER_CACHE))
130+
&& "Functions with non-persistent arg_info must be flagged ZEND_ACC_NEVER_CACHE");
131+
128132
uint32_t i;
129133
uint32_t num_args = function->num_args + 1;
130134
zend_arg_info *arg_info = function->arg_info - 1;
@@ -135,15 +139,16 @@ ZEND_API void zend_free_internal_arg_info(zend_internal_function *function) {
135139
for (i = 0 ; i < num_args; i++) {
136140
bool is_return_info = i == 0;
137141
if (!is_return_info) {
138-
zend_string_release_ex(arg_info[i].name, true);
142+
zend_string_release_ex(arg_info[i].name, persistent);
139143
if (arg_info[i].default_value) {
140-
zend_string_release_ex(arg_info[i].default_value, true);
144+
zend_string_release_ex(arg_info[i].default_value,
145+
persistent);
141146
}
142147
}
143-
zend_type_release(arg_info[i].type, /* persistent */ true);
148+
zend_type_release(arg_info[i].type, persistent);
144149
}
145150

146-
free(arg_info);
151+
pefree(arg_info, persistent);
147152
}
148153
}
149154

@@ -162,7 +167,7 @@ ZEND_API void zend_function_dtor(zval *zv)
162167

163168
/* For methods this will be called explicitly. */
164169
if (!function->common.scope) {
165-
zend_free_internal_arg_info(&function->internal_function);
170+
zend_free_internal_arg_info(&function->internal_function, true);
166171

167172
if (function->common.attributes) {
168173
zend_hash_release(function->common.attributes);
@@ -481,7 +486,7 @@ ZEND_API void destroy_zend_class(zval *zv)
481486

482487
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, fn) {
483488
if (fn->common.scope == ce) {
484-
zend_free_internal_arg_info(&fn->internal_function);
489+
zend_free_internal_arg_info(&fn->internal_function, true);
485490

486491
if (fn->common.attributes) {
487492
zend_hash_release(fn->common.attributes);

ext/pdo/pdo_dbh.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,7 +1321,7 @@ static void cls_method_dtor(zval *el) /* {{{ */ {
13211321
if (func->common.attributes) {
13221322
zend_hash_release(func->common.attributes);
13231323
}
1324-
zend_free_internal_arg_info(&func->internal_function);
1324+
zend_free_internal_arg_info(&func->internal_function, false);
13251325
efree(func);
13261326
}
13271327
/* }}} */
@@ -1337,7 +1337,7 @@ static void cls_method_pdtor(zval *el) /* {{{ */ {
13371337
if (func->common.attributes) {
13381338
zend_hash_release(func->common.attributes);
13391339
}
1340-
zend_free_internal_arg_info(&func->internal_function);
1340+
zend_free_internal_arg_info(&func->internal_function, true);
13411341
pefree(func, 1);
13421342
}
13431343
/* }}} */
@@ -1415,9 +1415,11 @@ bool pdo_hash_methods(pdo_dbh_object_t *dbh_obj, int kind)
14151415
num_arg_info++;
14161416
}
14171417

1418-
zend_arg_info *arg_info = safe_pemalloc(num_arg_info, sizeof(zend_arg_info), 0, true);
1418+
zend_arg_info *arg_info = safe_pemalloc(num_arg_info,
1419+
sizeof(zend_arg_info), 0, dbh->is_persistent);
14191420
for (uint32_t i = 0; i < num_arg_info; i++) {
1420-
zend_convert_internal_arg_info(&arg_info[i], &funcs->arg_info[i], i == 0);
1421+
zend_convert_internal_arg_info(&arg_info[i],
1422+
&funcs->arg_info[i], i == 0, dbh->is_persistent);
14211423
}
14221424

14231425
func.arg_info = arg_info + 1;

ext/zend_test/test.c

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@
3737
#include "Zend/Optimizer/zend_optimizer.h"
3838
#include "Zend/zend_alloc.h"
3939
#include "test_arginfo.h"
40+
#include "tmp_methods_arginfo.h"
4041
#include "zend_call_stack.h"
4142
#include "zend_exceptions.h"
4243
#include "zend_mm_custom_handlers.h"
4344
#include "ext/uri/php_uri.h"
45+
#include "zend_observer.h"
4446

4547
#if defined(HAVE_LIBXML) && !defined(PHP_WIN32)
4648
# include <libxml/globals.h>
@@ -1023,16 +1025,38 @@ static ZEND_FUNCTION(zend_test_log_err_debug)
10231025
php_log_err_with_severity(ZSTR_VAL(str), LOG_DEBUG);
10241026
}
10251027

1028+
typedef struct _zend_test_object {
1029+
zend_internal_function *tmp_method;
1030+
zend_object std;
1031+
} zend_test_object;
1032+
10261033
static zend_object *zend_test_class_new(zend_class_entry *class_type)
10271034
{
1028-
zend_object *obj = zend_objects_new(class_type);
1029-
object_properties_init(obj, class_type);
1030-
obj->handlers = &zend_test_class_handlers;
1031-
return obj;
1035+
zend_test_object *intern = zend_object_alloc(sizeof(zend_test_object), class_type);
1036+
zend_object_std_init(&intern->std, class_type);
1037+
object_properties_init(&intern->std, class_type);
1038+
return &intern->std;
1039+
}
1040+
1041+
static void zend_test_class_free_obj(zend_object *object)
1042+
{
1043+
zend_test_object *intern = (zend_test_object*)((char*)object - XtOffsetOf(zend_test_object, std));
1044+
1045+
if (intern->tmp_method) {
1046+
zend_internal_function *func = intern->tmp_method;
1047+
intern->tmp_method = NULL;
1048+
zend_string_release_ex(func->function_name, 0);
1049+
zend_free_internal_arg_info(func, false);
1050+
efree(func);
1051+
}
1052+
1053+
zend_object_std_dtor(object);
10321054
}
10331055

10341056
static zend_function *zend_test_class_method_get(zend_object **object, zend_string *name, const zval *key)
10351057
{
1058+
zend_test_object *intern = (zend_test_object*)((char*)(*object) - XtOffsetOf(zend_test_object, std));
1059+
10361060
if (zend_string_equals_literal_ci(name, "test")) {
10371061
zend_internal_function *fptr;
10381062

@@ -1050,6 +1074,41 @@ static zend_function *zend_test_class_method_get(zend_object **object, zend_stri
10501074
fptr->handler = ZEND_FN(zend_test_func);
10511075
fptr->doc_comment = NULL;
10521076

1077+
return (zend_function*)fptr;
1078+
} else if (zend_string_equals_literal_ci(name, "testTmpMethodWithArgInfo")) {
1079+
if (intern->tmp_method) {
1080+
return (zend_function*)intern->tmp_method;
1081+
}
1082+
1083+
const zend_function_entry *entry = &class_ZendTestTmpMethods_methods[0];
1084+
zend_internal_function *fptr = emalloc(sizeof(zend_internal_function));
1085+
memset(fptr, 0, sizeof(zend_internal_function));
1086+
fptr->type = ZEND_INTERNAL_FUNCTION;
1087+
fptr->handler = entry->handler;
1088+
fptr->function_name = zend_string_init(entry->fname, strlen(entry->fname), false);
1089+
fptr->scope = intern->std.ce;
1090+
fptr->prototype = NULL;
1091+
fptr->T = ZEND_OBSERVER_ENABLED;
1092+
fptr->fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_NEVER_CACHE;
1093+
1094+
zend_internal_function_info *info = (zend_internal_function_info*)entry->arg_info;
1095+
1096+
uint32_t num_arg_info = 1 + entry->num_args;
1097+
zend_arg_info *arg_info = safe_emalloc(num_arg_info, sizeof(zend_arg_info), 0);
1098+
for (uint32_t i = 0; i < num_arg_info; i++) {
1099+
zend_convert_internal_arg_info(&arg_info[i], &entry->arg_info[i], i == 0, false);
1100+
}
1101+
1102+
fptr->arg_info = arg_info + 1;
1103+
fptr->num_args = entry->num_args;
1104+
if (info->required_num_args == (uint32_t)-1) {
1105+
fptr->required_num_args = entry->num_args;
1106+
} else {
1107+
fptr->required_num_args = info->required_num_args;
1108+
}
1109+
1110+
intern->tmp_method = fptr;
1111+
10531112
return (zend_function*)fptr;
10541113
}
10551114
return zend_std_get_method(object, name, key);
@@ -1136,6 +1195,18 @@ static ZEND_METHOD(_ZendTestClass, variadicTest) {
11361195
object_init_ex(return_value, zend_get_called_scope(execute_data));
11371196
}
11381197

1198+
ZEND_METHOD(ZendTestTmpMethods, testTmpMethodWithArgInfo)
1199+
{
1200+
zend_object *obj;
1201+
zend_string *str;
1202+
1203+
ZEND_PARSE_PARAMETERS_START(0, 2);
1204+
Z_PARAM_OPTIONAL;
1205+
Z_PARAM_OBJ_OR_NULL(obj);
1206+
Z_PARAM_STR(str);
1207+
ZEND_PARSE_PARAMETERS_END();
1208+
}
1209+
11391210
static ZEND_METHOD(_ZendTestChildClass, returnsThrowable)
11401211
{
11411212
ZEND_PARSE_PARAMETERS_NONE();
@@ -1441,11 +1512,14 @@ PHP_MINIT_FUNCTION(zend_test)
14411512
register_ZendTestClass_dnf_property(zend_test_class);
14421513
zend_test_class->create_object = zend_test_class_new;
14431514
zend_test_class->get_static_method = zend_test_class_static_method_get;
1515+
zend_test_class->default_object_handlers = &zend_test_class_handlers;
14441516

14451517
zend_test_child_class = register_class__ZendTestChildClass(zend_test_class);
14461518

14471519
memcpy(&zend_test_class_handlers, &std_object_handlers, sizeof(zend_object_handlers));
14481520
zend_test_class_handlers.get_method = zend_test_class_method_get;
1521+
zend_test_class_handlers.free_obj = zend_test_class_free_obj;
1522+
zend_test_class_handlers.offset = XtOffsetOf(zend_test_object, std);
14491523

14501524
zend_test_gen_stub_flag_compatibility_test = register_class_ZendTestGenStubFlagCompatibilityTest();
14511525

0 commit comments

Comments
 (0)