From 71998e956e7ec823e22cf84dc43ff7606c1d0dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 7 Jan 2024 19:49:31 +0100 Subject: [PATCH 1/4] Add dedicated StreamBucket class --- ...flectionExtension_getClassNames_basic.phpt | 4 +- ext/standard/basic_functions.stub.php | 8 ++-- ext/standard/basic_functions_arginfo.h | 8 ++-- ext/standard/tests/file/bug39551.phpt | 11 ++++- ext/standard/user_filters.c | 42 +++++++++++-------- ext/standard/user_filters.stub.php | 8 ++++ ext/standard/user_filters_arginfo.h | 35 +++++++++++++++- 7 files changed, 87 insertions(+), 29 deletions(-) diff --git a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt index 5c4d1cb87f1d7..5a230936fff96 100644 --- a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt @@ -8,7 +8,7 @@ $standard = new ReflectionExtension('standard'); var_dump($standard->getClassNames()); ?> --EXPECT-- -array(4) { +array(5) { [0]=> string(22) "__PHP_Incomplete_Class" [1]=> @@ -16,5 +16,7 @@ array(4) { [2]=> string(15) "php_user_filter" [3]=> + string(12) "StreamBucket" + [4]=> string(9) "Directory" } diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index c3adaceff4390..dabb9fc1434a5 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -3744,19 +3744,19 @@ function get_headers(string $url, bool $associative = false, $context = null): a * @param resource $brigade * @refcount 1 */ -function stream_bucket_make_writeable($brigade): ?object {} +function stream_bucket_make_writeable($brigade): ?StreamBucket {} /** @param resource $brigade */ -function stream_bucket_prepend($brigade, object $bucket): void {} +function stream_bucket_prepend($brigade, StreamBucket $bucket): void {} /** @param resource $brigade */ -function stream_bucket_append($brigade, object $bucket): void {} +function stream_bucket_append($brigade, StreamBucket $bucket): void {} /** * @param resource $stream * @refcount 1 */ -function stream_bucket_new($stream, string $buffer): object {} +function stream_bucket_new($stream, string $buffer): StreamBucket {} /** * @return array diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index c420e5c29e0f7..4556e10ada82b 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 the .stub.php file instead. - * Stub hash: 954bf48ac1f24a14fd7508c8bf4b781883398499 */ + * Stub hash: 1ef54fdebc6a206c4af3438130db0cd12a62c8b6 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -2104,18 +2104,18 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_get_headers, 0, 1, MAY_BE_ARRAY| ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_bucket_make_writeable, 0, 1, IS_OBJECT, 1) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_stream_bucket_make_writeable, 0, 1, StreamBucket, 1) ZEND_ARG_INFO(0, brigade) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_bucket_prepend, 0, 2, IS_VOID, 0) ZEND_ARG_INFO(0, brigade) - ZEND_ARG_TYPE_INFO(0, bucket, IS_OBJECT, 0) + ZEND_ARG_OBJ_INFO(0, bucket, StreamBucket, 0) ZEND_END_ARG_INFO() #define arginfo_stream_bucket_append arginfo_stream_bucket_prepend -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_bucket_new, 0, 2, IS_OBJECT, 0) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_stream_bucket_new, 0, 2, StreamBucket, 0) ZEND_ARG_INFO(0, stream) ZEND_ARG_TYPE_INFO(0, buffer, IS_STRING, 0) ZEND_END_ARG_INFO() diff --git a/ext/standard/tests/file/bug39551.phpt b/ext/standard/tests/file/bug39551.phpt index e6982fcaab10a..ff853644d5907 100644 --- a/ext/standard/tests/file/bug39551.phpt +++ b/ext/standard/tests/file/bug39551.phpt @@ -4,6 +4,7 @@ Bug #39551 (Segfault with stream_bucket_new in user filter) ---EXPECT-- +--EXPECTF-- +object(StreamBucket)#%d (%d) { + ["bucket"]=> + resource(%d) of type (userfilter.bucket) + ["data"]=> + string(0) "" + ["datalen"]=> + int(0) +} Done diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index 9cdd1b26b7de6..9ad012fdd28f2 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -61,6 +61,7 @@ PHP_METHOD(php_user_filter, onClose) } static zend_class_entry *user_filter_class_entry; +static zend_class_entry *stream_bucket_class_entry; static ZEND_RSRC_DTOR_FUNC(php_bucket_dtor) { @@ -75,6 +76,7 @@ PHP_MINIT_FUNCTION(user_filters) { /* init the filter class ancestor */ user_filter_class_entry = register_class_php_user_filter(); + stream_bucket_class_entry = register_class_StreamBucket(); /* Filters will dispose of their brigades */ le_bucket_brigade = zend_register_list_destructors_ex(NULL, NULL, PHP_STREAM_BRIGADE_RES_NAME, module_number); @@ -351,16 +353,16 @@ PHP_FUNCTION(stream_bucket_make_writeable) RETURN_THROWS(); } - ZVAL_NULL(return_value); - if (brigade->head && (bucket = php_stream_bucket_make_writeable(brigade->head))) { ZVAL_RES(&zbucket, zend_register_resource(bucket, le_bucket)); - object_init(return_value); + object_init_ex(return_value, stream_bucket_class_entry); add_property_zval(return_value, "bucket", &zbucket); /* add_property_zval increments the refcount which is unwanted here */ zval_ptr_dtor(&zbucket); add_property_stringl(return_value, "data", bucket->buf, bucket->buflen); add_property_long(return_value, "datalen", bucket->buflen); + } else { + ZVAL_NULL(return_value); } } /* }}} */ @@ -369,38 +371,42 @@ PHP_FUNCTION(stream_bucket_make_writeable) static void php_stream_bucket_attach(int append, INTERNAL_FUNCTION_PARAMETERS) { zval *zbrigade, *zobject; - zval *pzbucket, *pzdata; + zval *pzbucket, *pzdata, rv; php_stream_bucket_brigade *brigade; php_stream_bucket *bucket; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_RESOURCE(zbrigade) - Z_PARAM_OBJECT(zobject) + Z_PARAM_OBJECT_OF_CLASS(zobject, stream_bucket_class_entry) ZEND_PARSE_PARAMETERS_END(); - if (NULL == (pzbucket = zend_hash_str_find_deref(Z_OBJPROP_P(zobject), "bucket", sizeof("bucket")-1))) { - zend_argument_value_error(2, "must be an object that has a \"bucket\" property"); + if ((brigade = (php_stream_bucket_brigade*)zend_fetch_resource( + Z_RES_P(zbrigade), PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade)) == NULL) { RETURN_THROWS(); } - if ((brigade = (php_stream_bucket_brigade*)zend_fetch_resource( - Z_RES_P(zbrigade), PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade)) == NULL) { + if (NULL == (pzbucket = zend_read_property(NULL, Z_OBJ_P(zobject), "bucket", sizeof("bucket")-1, false, &rv))) { + zend_argument_value_error(2, "must be an object that has a \"bucket\" property"); RETURN_THROWS(); } + ZVAL_DEREF(pzbucket); if ((bucket = (php_stream_bucket *)zend_fetch_resource_ex(pzbucket, PHP_STREAM_BUCKET_RES_NAME, le_bucket)) == NULL) { RETURN_THROWS(); } - if (NULL != (pzdata = zend_hash_str_find_deref(Z_OBJPROP_P(zobject), "data", sizeof("data")-1)) && Z_TYPE_P(pzdata) == IS_STRING) { - if (!bucket->own_buf) { - bucket = php_stream_bucket_make_writeable(bucket); - } - if (bucket->buflen != Z_STRLEN_P(pzdata)) { - bucket->buf = perealloc(bucket->buf, Z_STRLEN_P(pzdata), bucket->is_persistent); - bucket->buflen = Z_STRLEN_P(pzdata); + if (NULL != (pzdata = zend_read_property(NULL, Z_OBJ_P(zobject), "data", sizeof("data")-1, false, &rv))) { + ZVAL_DEREF(pzdata); + if (Z_TYPE_P(pzdata) == IS_STRING) { + if (!bucket->own_buf) { + bucket = php_stream_bucket_make_writeable(bucket); + } + if (bucket->buflen != Z_STRLEN_P(pzdata)) { + bucket->buf = perealloc(bucket->buf, Z_STRLEN_P(pzdata), bucket->is_persistent); + bucket->buflen = Z_STRLEN_P(pzdata); + } + memcpy(bucket->buf, Z_STRVAL_P(pzdata), bucket->buflen); } - memcpy(bucket->buf, Z_STRVAL_P(pzdata), bucket->buflen); } if (append) { @@ -454,7 +460,7 @@ PHP_FUNCTION(stream_bucket_new) bucket = php_stream_bucket_new(stream, pbuffer, buffer_len, 1, php_stream_is_persistent(stream)); ZVAL_RES(&zbucket, zend_register_resource(bucket, le_bucket)); - object_init(return_value); + object_init_ex(return_value, stream_bucket_class_entry); add_property_zval(return_value, "bucket", &zbucket); /* add_property_zval increments the refcount which is unwanted here */ zval_ptr_dtor(&zbucket); diff --git a/ext/standard/user_filters.stub.php b/ext/standard/user_filters.stub.php index 3c134ea1bb5b9..85ca510cc6cdc 100644 --- a/ext/standard/user_filters.stub.php +++ b/ext/standard/user_filters.stub.php @@ -55,3 +55,11 @@ public function onCreate(): bool {} /** @tentative-return-type */ public function onClose(): void {} } + +final class StreamBucket +{ + /** @var resource */ + public $bucket; + public string $data; + public int $datalen; +} diff --git a/ext/standard/user_filters_arginfo.h b/ext/standard/user_filters_arginfo.h index 53c535b4eb9af..806551b9ec32f 100644 --- a/ext/standard/user_filters_arginfo.h +++ b/ext/standard/user_filters_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1c03251b4e0b22056da43bf86087d6996454d2a0 */ + * Stub hash: d0658405953ef28aefd50aa52c106913aca18892 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_filter, 0, 4, IS_LONG, 0) ZEND_ARG_INFO(0, in) @@ -25,6 +25,10 @@ static const zend_function_entry class_php_user_filter_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_StreamBucket_methods[] = { + ZEND_FE_END +}; + static void register_user_filters_symbols(int module_number) { REGISTER_LONG_CONSTANT("PSFS_PASS_ON", PSFS_PASS_ON, CONST_PERSISTENT); @@ -62,3 +66,32 @@ static zend_class_entry *register_class_php_user_filter(void) return class_entry; } + +static zend_class_entry *register_class_StreamBucket(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "StreamBucket", class_StreamBucket_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL; + + zval property_bucket_default_value; + ZVAL_NULL(&property_bucket_default_value); + zend_string *property_bucket_name = zend_string_init("bucket", sizeof("bucket") - 1, 1); + zend_declare_typed_property(class_entry, property_bucket_name, &property_bucket_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0)); + zend_string_release(property_bucket_name); + + zval property_data_default_value; + ZVAL_UNDEF(&property_data_default_value); + zend_string *property_data_name = zend_string_init("data", sizeof("data") - 1, 1); + zend_declare_typed_property(class_entry, property_data_name, &property_data_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_data_name); + + zval property_datalen_default_value; + ZVAL_UNDEF(&property_datalen_default_value); + zend_string *property_datalen_name = zend_string_init("datalen", sizeof("datalen") - 1, 1); + zend_declare_typed_property(class_entry, property_datalen_name, &property_datalen_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_datalen_name); + + return class_entry; +} From ce1d776312f9b384e5080d3e5eeb1b21609fc95c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 11 Jan 2024 16:47:49 +0100 Subject: [PATCH 2/4] Use the zend_update_property_*() API instead of a add_property_*() --- ext/standard/user_filters.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index 9ad012fdd28f2..07d6606bae447 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -356,11 +356,11 @@ PHP_FUNCTION(stream_bucket_make_writeable) if (brigade->head && (bucket = php_stream_bucket_make_writeable(brigade->head))) { ZVAL_RES(&zbucket, zend_register_resource(bucket, le_bucket)); object_init_ex(return_value, stream_bucket_class_entry); - add_property_zval(return_value, "bucket", &zbucket); + zend_update_property(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("bucket"), &zbucket); /* add_property_zval increments the refcount which is unwanted here */ zval_ptr_dtor(&zbucket); - add_property_stringl(return_value, "data", bucket->buf, bucket->buflen); - add_property_long(return_value, "datalen", bucket->buflen); + zend_update_property_stringl(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("data"), bucket->buf, bucket->buflen); + zend_update_property_long(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("datalen"), bucket->buflen); } else { ZVAL_NULL(return_value); } @@ -461,11 +461,11 @@ PHP_FUNCTION(stream_bucket_new) ZVAL_RES(&zbucket, zend_register_resource(bucket, le_bucket)); object_init_ex(return_value, stream_bucket_class_entry); - add_property_zval(return_value, "bucket", &zbucket); + zend_update_property(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("bucket"), &zbucket); /* add_property_zval increments the refcount which is unwanted here */ zval_ptr_dtor(&zbucket); - add_property_stringl(return_value, "data", bucket->buf, bucket->buflen); - add_property_long(return_value, "datalen", bucket->buflen); + zend_update_property_stringl(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("data"), bucket->buf, bucket->buflen); + zend_update_property_long(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("datalen"), bucket->buflen); } /* }}} */ From 26ae64dfd14da55e8060e3c4836bd9ff23959396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 9 Apr 2024 22:39:57 +0200 Subject: [PATCH 3/4] Add $dataLength property --- ext/standard/tests/file/bug39551.phpt | 2 ++ ext/standard/user_filters.c | 2 ++ ext/standard/user_filters.stub.php | 9 ++++++++- ext/standard/user_filters_arginfo.h | 8 +++++++- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/file/bug39551.phpt b/ext/standard/tests/file/bug39551.phpt index ff853644d5907..5b0674342dea5 100644 --- a/ext/standard/tests/file/bug39551.phpt +++ b/ext/standard/tests/file/bug39551.phpt @@ -29,5 +29,7 @@ object(StreamBucket)#%d (%d) { string(0) "" ["datalen"]=> int(0) + ["dataLength"]=> + int(0) } Done diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index 07d6606bae447..064b29629c403 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -361,6 +361,7 @@ PHP_FUNCTION(stream_bucket_make_writeable) zval_ptr_dtor(&zbucket); zend_update_property_stringl(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("data"), bucket->buf, bucket->buflen); zend_update_property_long(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("datalen"), bucket->buflen); + zend_update_property_long(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("dataLength"), bucket->buflen); } else { ZVAL_NULL(return_value); } @@ -466,6 +467,7 @@ PHP_FUNCTION(stream_bucket_new) zval_ptr_dtor(&zbucket); zend_update_property_stringl(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("data"), bucket->buf, bucket->buflen); zend_update_property_long(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("datalen"), bucket->buflen); + zend_update_property_long(Z_OBJCE_P(return_value), Z_OBJ_P(return_value), ZEND_STRL("dataLength"), bucket->buflen); } /* }}} */ diff --git a/ext/standard/user_filters.stub.php b/ext/standard/user_filters.stub.php index 85ca510cc6cdc..acaa42bb33c84 100644 --- a/ext/standard/user_filters.stub.php +++ b/ext/standard/user_filters.stub.php @@ -58,8 +58,15 @@ public function onClose(): void {} final class StreamBucket { - /** @var resource */ + /** + * @var resource + * @readonly + */ public $bucket; + /** @readonly */ public string $data; + /** @readonly */ public int $datalen; + /** @readonly */ + public int $dataLength; } diff --git a/ext/standard/user_filters_arginfo.h b/ext/standard/user_filters_arginfo.h index 806551b9ec32f..a40a00f7ee476 100644 --- a/ext/standard/user_filters_arginfo.h +++ b/ext/standard/user_filters_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d0658405953ef28aefd50aa52c106913aca18892 */ + * Stub hash: 33264435fe01a2cc9aa21a4a087dbbf3c4007206 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_filter, 0, 4, IS_LONG, 0) ZEND_ARG_INFO(0, in) @@ -93,5 +93,11 @@ static zend_class_entry *register_class_StreamBucket(void) zend_declare_typed_property(class_entry, property_datalen_name, &property_datalen_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(property_datalen_name); + zval property_dataLength_default_value; + ZVAL_UNDEF(&property_dataLength_default_value); + zend_string *property_dataLength_name = zend_string_init("dataLength", sizeof("dataLength") - 1, 1); + zend_declare_typed_property(class_entry, property_dataLength_name, &property_dataLength_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_dataLength_name); + return class_entry; } From dfd3388a4be8847fe9f6c991491154b4942293fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 11 Apr 2024 17:15:16 +0200 Subject: [PATCH 4/4] Remove unnecessary check --- ext/standard/user_filters.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index 064b29629c403..f4ffae9559276 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -398,16 +398,14 @@ static void php_stream_bucket_attach(int append, INTERNAL_FUNCTION_PARAMETERS) if (NULL != (pzdata = zend_read_property(NULL, Z_OBJ_P(zobject), "data", sizeof("data")-1, false, &rv))) { ZVAL_DEREF(pzdata); - if (Z_TYPE_P(pzdata) == IS_STRING) { - if (!bucket->own_buf) { - bucket = php_stream_bucket_make_writeable(bucket); - } - if (bucket->buflen != Z_STRLEN_P(pzdata)) { - bucket->buf = perealloc(bucket->buf, Z_STRLEN_P(pzdata), bucket->is_persistent); - bucket->buflen = Z_STRLEN_P(pzdata); - } - memcpy(bucket->buf, Z_STRVAL_P(pzdata), bucket->buflen); + if (!bucket->own_buf) { + bucket = php_stream_bucket_make_writeable(bucket); + } + if (bucket->buflen != Z_STRLEN_P(pzdata)) { + bucket->buf = perealloc(bucket->buf, Z_STRLEN_P(pzdata), bucket->is_persistent); + bucket->buflen = Z_STRLEN_P(pzdata); } + memcpy(bucket->buf, Z_STRVAL_P(pzdata), bucket->buflen); } if (append) {