From 3e982a8d9d5b50f06c989d45e27077de2195842d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Mon, 10 Nov 2025 22:41:14 +0100 Subject: [PATCH 001/252] standard: Remove redundant code in range() (#20432) If we use signed integers (which fit the unsigned chars), then we can avoid the extra checks. Also move an exception check to the proper place. --- ext/standard/array.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 3a782ec1ea40c..eac3d516e3f80 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3040,18 +3040,18 @@ PHP_FUNCTION(range) if (start_type == IS_STRING || end_type == IS_STRING) { php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be of type int when generating an array" " of characters, inputs converted to 0"); - } - if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } end_type = IS_LONG; start_type = IS_LONG; goto handle_numeric_inputs; } - /* Generate array of characters */ - unsigned char low = (unsigned char)Z_STRVAL_P(user_start)[0]; - unsigned char high = (unsigned char)Z_STRVAL_P(user_end)[0]; + /* Generate array of characters, as ints to make bounds checking possible in the loop condition */ + int low = Z_STRVAL_P(user_start)[0]; + int high = Z_STRVAL_P(user_end)[0]; /* Decreasing char range */ if (low > high) { @@ -3062,12 +3062,9 @@ PHP_FUNCTION(range) array_init_size(return_value, (uint32_t)(((low - high) / step) + 1)); zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - for (; low >= high; low -= (unsigned int)step) { + for (; low >= high; low -= step) { ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low)); ZEND_HASH_FILL_NEXT(); - if (((signed int)low - step) < 0) { - break; - } } } ZEND_HASH_FILL_END(); } else if (high > low) { /* Increasing char range */ @@ -3080,12 +3077,9 @@ PHP_FUNCTION(range) array_init_size(return_value, (uint32_t)(((high - low) / step) + 1)); zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - for (; low <= high; low += (unsigned int)step) { + for (; low <= high; low += step) { ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low)); ZEND_HASH_FILL_NEXT(); - if (((signed int)low + step) > 255) { - break; - } } } ZEND_HASH_FILL_END(); } else { From 15b996348abc2fb3b35ce384489a0d59b4644c97 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 9 Nov 2025 11:41:42 +0100 Subject: [PATCH 002/252] standard: Use specialised macro in array_merge() --- ext/standard/array.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index eac3d516e3f80..9a9ec99e3b12b 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -4311,7 +4311,7 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET bool copy = 1; zend_string *string_key; - ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) { + ZEND_HASH_MAP_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) { if (!string_key) { copy = 0; break; From e32c2c3e0b253094fb3553bf5bda4ee037b29c37 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 9 Nov 2025 11:42:16 +0100 Subject: [PATCH 003/252] standard: Use RETURN_COPY()/RETVAL_ARR() in array_merge() --- ext/standard/array.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 9a9ec99e3b12b..031e136870252 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -4304,8 +4304,7 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET if (ret) { if (HT_IS_PACKED(Z_ARRVAL_P(ret))) { if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) { - ZVAL_COPY(return_value, ret); - return; + RETURN_COPY(ret); } } else { bool copy = 1; @@ -4318,8 +4317,7 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET } } ZEND_HASH_FOREACH_END(); if (copy) { - ZVAL_COPY(return_value, ret); - return; + RETURN_COPY(ret); } } } @@ -4334,7 +4332,7 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET if (HT_IS_WITHOUT_HOLES(src) && zend_may_modify_arg_in_place(arg)) { dest = src; in_place = true; - ZVAL_ARR(return_value, dest); + RETVAL_ARR(dest); } else { array_init_size(return_value, count); dest = Z_ARRVAL_P(return_value); From aafb8a66239e20acd18122c0a0c75294855e286f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 9 Nov 2025 11:44:01 +0100 Subject: [PATCH 004/252] standard: Use booleans in array_merge() --- ext/standard/array.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 031e136870252..6de17f2bff7f9 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -4307,12 +4307,12 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET RETURN_COPY(ret); } } else { - bool copy = 1; + bool copy = true; zend_string *string_key; ZEND_HASH_MAP_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) { if (!string_key) { - copy = 0; + copy = false; break; } } ZEND_HASH_FOREACH_END(); From 4ee25395d5ebf6cffda3d71db51b571602c55a78 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:14:11 +0100 Subject: [PATCH 005/252] Fix GH-20442: Phar does not respect case-insensitiveness of __halt_compiler() when reading stub Functions are case insensitive. The flush code already takes this into account by checking for the __halt_compiler() symbol in a case insensitive manner; however the parsing code did not do that yet. Closes GH-20445. --- NEWS | 4 ++++ ext/phar/phar.c | 36 ++++-------------------------- ext/phar/tests/files/gh20442.phar | Bin 0 -> 144 bytes ext/phar/tests/gh20442.phpt | 18 +++++++++++++++ 4 files changed, 26 insertions(+), 32 deletions(-) create mode 100644 ext/phar/tests/files/gh20442.phar create mode 100644 ext/phar/tests/gh20442.phpt diff --git a/NEWS b/NEWS index ba45d98c37755..844c2851013fa 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,10 @@ PHP NEWS . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) +- Phar: + . Fixed bug GH-20442 (Phar does not respect case-insensitiveness of + __halt_compiler() when reading stub). (ndossche, TimWolla) + - Standard: . Fix memory leak in array_diff() with custom type checks. (ndossche) diff --git a/ext/phar/phar.c b/ext/phar/phar.c index a9aff9489df01..aa9a8821d8e8c 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1590,35 +1590,6 @@ int phar_open_from_filename(char *fname, size_t fname_len, char *alias, size_t a } /* }}}*/ -static inline char *phar_strnstr(const char *buf, size_t buf_len, const char *search, size_t search_len) /* {{{ */ -{ - const char *c; - ptrdiff_t so_far = 0; - - if (buf_len < search_len) { - return NULL; - } - - c = buf - 1; - - do { - if (!(c = memchr(c + 1, search[0], buf_len - search_len - so_far))) { - return (char *) NULL; - } - - so_far = c - buf; - - if (so_far >= (buf_len - search_len)) { - return (char *) NULL; - } - - if (!memcmp(c, search, search_len)) { - return (char *) c; - } - } while (1); -} -/* }}} */ - /** * Scan an open fp for the required __HALT_COMPILER(); ?> token and verify * that the manifest is proper, then pass it to phar_parse_pharfile(). SUCCESS @@ -1630,7 +1601,8 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char static const char zip_magic[] = "PK\x03\x04"; static const char gz_magic[] = "\x1f\x8b\x08"; static const char bz_magic[] = "BZh"; - char *pos, test = '\0'; + const char *pos; + char test = '\0'; int recursion_count = 3; // arbitrary limit to avoid too deep or even infinite recursion const int window_size = 1024; char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */ @@ -1779,14 +1751,14 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char } if (got >= 512) { - if (phar_is_tar(pos, fname)) { + if (phar_is_tar((char *) pos, fname)) { /* TODO: fix const correctness */ php_stream_rewind(fp); return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, is_data, compression, error); } } } - if (got > 0 && (pos = phar_strnstr(buffer, got + sizeof(token), token, sizeof(token)-1)) != NULL) { + if (got > 0 && (pos = php_memnistr(buffer, token, tokenlen, buffer + got + sizeof(token))) != NULL) { halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */ return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error); } diff --git a/ext/phar/tests/files/gh20442.phar b/ext/phar/tests/files/gh20442.phar new file mode 100644 index 0000000000000000000000000000000000000000..26fac67ede58722561e2e65f98d1c2780592f5c6 GIT binary patch literal 144 zcmcDqFUTn1N=?qlS5Wdu&B@7E2+uFdNl{d?=8BKcNX#jTPtMOR$jnJC($KV4u(#vo zGGbt0U<6`80SFCb0s#vU7Z)Y#gV-gSLYW{M1pb4R0>KTN>6x5|x)`%GawXK)*A`AS bE9&^Sd*6ya?!N)AKV^q81J$@Y`8ojr!b~J& literal 0 HcmV?d00001 diff --git a/ext/phar/tests/gh20442.phpt b/ext/phar/tests/gh20442.phpt new file mode 100644 index 0000000000000..e6862f3d33dbc --- /dev/null +++ b/ext/phar/tests/gh20442.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-20442 (Phar does not respect case-insensitiveness of __halt_compiler() when reading stub) +--EXTENSIONS-- +phar +--FILE-- +count()); +var_dump($phar->getStub()); + +?> +--EXPECT-- +int(1) +string(50) " +" From 33a2acba445aa50abddf62e247f394f35177124d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 9 Nov 2025 15:18:22 +0100 Subject: [PATCH 006/252] Fix GH-20435: SensitiveParameter doesn't work for named argument passing to variadic parameter Closes GH-20436. --- NEWS | 2 ++ Zend/tests/function_arguments/gh20435.phpt | 14 ++++++++++++++ Zend/zend_builtin_functions.c | 22 ++++++++++++++++++++-- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/function_arguments/gh20435.phpt diff --git a/NEWS b/NEWS index 844c2851013fa..28263cc2c0d24 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ PHP NEWS - Core: . Sync all boost.context files with release 1.86.0. (mvorisek) + . Fixed bug GH-20435 (SensitiveParameter doesn't work for named argument + passing to variadic parameter). (ndossche) - Date: . Fix crashes when trying to instantiate uninstantiable classes via date diff --git a/Zend/tests/function_arguments/gh20435.phpt b/Zend/tests/function_arguments/gh20435.phpt new file mode 100644 index 0000000000000..e360b873d3ced --- /dev/null +++ b/Zend/tests/function_arguments/gh20435.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-20435 (SensitiveParameter doesn't work for named argument passing to variadic parameter) +--FILE-- + +--EXPECTF-- +#0 %s(%d): test(2, b: Object(SensitiveParameterValue), c: Object(SensitiveParameterValue)) diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index a7e8a4fabf1f0..344983a6e2878 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1683,11 +1683,29 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) / if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { zend_string *name; zval *arg; + + ZEND_ASSERT(call->func->common.fn_flags & ZEND_ACC_VARIADIC); + + zend_attribute *attribute = zend_get_parameter_attribute_str( + call->func->common.attributes, + "sensitiveparameter", + sizeof("sensitiveparameter") - 1, + call->func->common.num_args + ); + bool is_sensitive = attribute != NULL; + SEPARATE_ARRAY(arg_array); ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(call->extra_named_params, name, arg) { ZVAL_DEREF(arg); - Z_TRY_ADDREF_P(arg); - zend_hash_add_new(Z_ARRVAL_P(arg_array), name, arg); + if (is_sensitive) { + zval redacted_arg; + object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value); + zend_call_method_with_1_params(Z_OBJ_P(&redacted_arg), zend_ce_sensitive_parameter_value, &zend_ce_sensitive_parameter_value->constructor, "__construct", NULL, arg); + zend_hash_add_new(Z_ARRVAL_P(arg_array), name, &redacted_arg); + } else { + Z_TRY_ADDREF_P(arg); + zend_hash_add_new(Z_ARRVAL_P(arg_array), name, arg); + } } ZEND_HASH_FOREACH_END(); } } From 5185d46ccc2f3858d343c5cf4edc5bc4220c7cd1 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 00:01:38 +0100 Subject: [PATCH 007/252] Fix zero-extension on range() char parameters --- ext/standard/array.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 6de17f2bff7f9..0389eb1840a96 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3049,9 +3049,9 @@ PHP_FUNCTION(range) goto handle_numeric_inputs; } - /* Generate array of characters, as ints to make bounds checking possible in the loop condition */ - int low = Z_STRVAL_P(user_start)[0]; - int high = Z_STRVAL_P(user_end)[0]; + /* Generate array of characters, as zero-extended ints to make bounds checking possible in the loop condition */ + int low = (unsigned char) Z_STRVAL_P(user_start)[0]; + int high = (unsigned char) Z_STRVAL_P(user_end)[0]; /* Decreasing char range */ if (low > high) { From da238e7a11101a042fe42147acf6ba0b23eea66a Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com> Date: Tue, 11 Nov 2025 00:31:58 +0100 Subject: [PATCH 008/252] ext/spl: convert `zend_parse_parameters_none()` to fast ZPP (#20441) --- ext/spl/php_spl.c | 8 +- ext/spl/spl_array.c | 60 ++++---------- ext/spl/spl_directory.c | 172 ++++++++++----------------------------- ext/spl/spl_dllist.c | 64 ++++----------- ext/spl/spl_fixedarray.c | 24 ++---- ext/spl/spl_heap.c | 68 ++++------------ ext/spl/spl_observer.c | 64 ++++----------- 7 files changed, 115 insertions(+), 345 deletions(-) diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 6de7a6d6635af..0acfcbc5c16d0 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -216,9 +216,7 @@ PHP_FUNCTION(class_uses) /* {{{ Return an array containing the names of all classes and interfaces defined in SPL */ PHP_FUNCTION(spl_classes) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); array_init(return_value); @@ -619,9 +617,7 @@ PHP_FUNCTION(spl_autoload_functions) { autoload_func_info *alfi; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); array_init(return_value); if (spl_autoload_functions) { diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index d430513ca11fa..3d6870a7ee953 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -753,9 +753,7 @@ PHP_METHOD(ArrayObject, getArrayCopy) zval *object = ZEND_THIS; spl_array_object *intern = Z_SPLARRAY_P(object); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_ARR(zend_array_dup(spl_array_get_hash_table(intern))); } /* }}} */ @@ -1086,9 +1084,7 @@ PHP_METHOD(ArrayObject, getIteratorClass) zval *object = ZEND_THIS; spl_array_object *intern = Z_SPLARRAY_P(object); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); zend_string_addref(intern->ce_get_iterator->name); RETURN_STR(intern->ce_get_iterator->name); @@ -1101,9 +1097,7 @@ PHP_METHOD(ArrayObject, getFlags) zval *object = ZEND_THIS; spl_array_object *intern = Z_SPLARRAY_P(object); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(intern->ar_flags & ~SPL_ARRAY_INT_MASK); } @@ -1150,9 +1144,7 @@ PHP_METHOD(ArrayObject, getIterator) zval *object = ZEND_THIS; spl_array_object *intern = Z_SPLARRAY_P(object); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_OBJ(spl_array_object_new_ex(intern->ce_get_iterator, Z_OBJ_P(object), 0)); } @@ -1203,9 +1195,7 @@ PHP_METHOD(ArrayObject, count) { spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(spl_array_object_count_elements_helper(intern)); } /* }}} */ @@ -1305,9 +1295,7 @@ PHP_METHOD(ArrayObject, serialize) php_serialize_data_t var_hash; smart_str buf = {0}; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); PHP_VAR_SERIALIZE_INIT(var_hash); @@ -1453,9 +1441,7 @@ PHP_METHOD(ArrayObject, __serialize) spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS); zval tmp; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); array_init(return_value); @@ -1559,9 +1545,7 @@ PHP_METHOD(ArrayObject, __unserialize) /* {{{ */ PHP_METHOD(ArrayObject, __debugInfo) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_ARR(spl_array_get_debug_info(Z_OBJ_P(ZEND_THIS))); } /* }}} */ @@ -1739,9 +1723,7 @@ PHP_METHOD(ArrayIterator, rewind) zval *object = ZEND_THIS; spl_array_object *intern = Z_SPLARRAY_P(object); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_array_rewind(intern); } @@ -1783,9 +1765,7 @@ PHP_METHOD(ArrayIterator, current) zval *entry; HashTable *aht = spl_array_get_hash_table(intern); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) { RETURN_NULL(); @@ -1812,9 +1792,7 @@ void spl_array_iterator_key(zval *object, zval *return_value) /* {{{ */ /* {{{ Return current array key */ PHP_METHOD(ArrayIterator, key) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_array_iterator_key(ZEND_THIS, return_value); } /* }}} */ @@ -1826,9 +1804,7 @@ PHP_METHOD(ArrayIterator, next) spl_array_object *intern = Z_SPLARRAY_P(object); HashTable *aht = spl_array_get_hash_table(intern); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_array_next_ex(intern, aht); } @@ -1841,9 +1817,7 @@ PHP_METHOD(ArrayIterator, valid) spl_array_object *intern = Z_SPLARRAY_P(object); HashTable *aht = spl_array_get_hash_table(intern); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_BOOL(zend_hash_has_more_elements_ex(aht, spl_array_get_pos_ptr(aht, intern)) == SUCCESS); } @@ -1858,9 +1832,7 @@ PHP_METHOD(RecursiveArrayIterator, hasChildren) spl_array_object *intern = Z_SPLARRAY_P(object); HashTable *aht = spl_array_get_hash_table(intern); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) { RETURN_FALSE; @@ -1898,9 +1870,7 @@ PHP_METHOD(RecursiveArrayIterator, getChildren) spl_array_object *intern = Z_SPLARRAY_P(object); HashTable *aht = spl_array_get_hash_table(intern); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if ((entry = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, intern))) == NULL) { RETURN_NULL(); diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index a769d627a54cd..c67a6ec4b2824 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -737,9 +737,7 @@ PHP_METHOD(DirectoryIterator, rewind) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern); intern->u.dir.index = 0; @@ -753,9 +751,7 @@ PHP_METHOD(DirectoryIterator, key) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern); RETURN_LONG(intern->u.dir.index); @@ -765,9 +761,7 @@ PHP_METHOD(DirectoryIterator, key) /* {{{ Return this (needed for Iterator interface) */ PHP_METHOD(DirectoryIterator, current) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS))); RETURN_OBJ_COPY(Z_OBJ_P(ZEND_THIS)); @@ -780,9 +774,7 @@ PHP_METHOD(DirectoryIterator, next) spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); bool skip_dots = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_SKIPDOTS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern); intern->u.dir.index++; @@ -831,9 +823,7 @@ PHP_METHOD(DirectoryIterator, valid) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern); RETURN_BOOL(intern->u.dir.entry.d_name[0] != '\0'); @@ -846,9 +836,7 @@ PHP_METHOD(SplFileInfo, getPath) spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); zend_string *path; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); path = spl_filesystem_object_get_path(intern); if (path) { @@ -865,9 +853,7 @@ PHP_METHOD(SplFileInfo, getFilename) spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); zend_string *path; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (!intern->file_name) { zend_throw_error(NULL, "Object not initialized"); @@ -894,9 +880,7 @@ PHP_METHOD(DirectoryIterator, getFilename) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern); RETURN_STRING(intern->u.dir.entry.d_name); @@ -914,9 +898,7 @@ PHP_METHOD(SplFileInfo, getExtension) size_t idx; zend_string *ret; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (!intern->file_name) { zend_throw_error(NULL, "Object not initialized"); @@ -959,9 +941,7 @@ PHP_METHOD(DirectoryIterator, getExtension) size_t idx; zend_string *fname; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern); fname = php_basename(intern->u.dir.entry.d_name, strlen(intern->u.dir.entry.d_name), NULL, 0); @@ -1039,9 +1019,7 @@ PHP_METHOD(SplFileInfo, getPathname) spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); zend_string *path; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); path = spl_filesystem_object_get_pathname(intern); if (path) { RETURN_STR_COPY(path); @@ -1056,9 +1034,7 @@ PHP_METHOD(FilesystemIterator, key) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (SPL_FILE_DIR_KEY(intern, SPL_FILE_DIR_KEY_AS_FILENAME)) { RETURN_STRING(intern->u.dir.entry.d_name); @@ -1076,9 +1052,7 @@ PHP_METHOD(FilesystemIterator, current) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (SPL_FILE_DIR_CURRENT(intern, SPL_FILE_DIR_CURRENT_AS_PATHNAME)) { if (spl_filesystem_object_get_file_name(intern) == FAILURE) { @@ -1101,9 +1075,7 @@ PHP_METHOD(DirectoryIterator, isDot) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern); RETURN_BOOL(spl_filesystem_is_dot(intern->u.dir.entry.d_name)); @@ -1137,9 +1109,7 @@ PHP_METHOD(SplFileInfo, func_name) \ { \ spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); \ zend_error_handling error_handling; \ - if (zend_parse_parameters_none() == FAILURE) { \ - RETURN_THROWS(); \ - } \ + ZEND_PARSE_PARAMETERS_NONE(); \ if (spl_filesystem_object_get_file_name(intern) == FAILURE) { \ RETURN_THROWS(); \ } \ @@ -1216,9 +1186,7 @@ PHP_METHOD(SplFileInfo, getLinkTarget) ssize_t ret; char buff[MAXPATHLEN]; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (intern->file_name == NULL) { if (spl_filesystem_object_get_file_name(intern) == FAILURE) { @@ -1263,9 +1231,7 @@ PHP_METHOD(SplFileInfo, getRealPath) char buff[MAXPATHLEN]; char *filename; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (intern->type == SPL_FS_DIR && !intern->file_name && intern->u.dir.entry.d_name[0]) { if (spl_filesystem_object_get_file_name(intern) == FAILURE) { @@ -1375,9 +1341,7 @@ PHP_METHOD(SplFileInfo, getPathInfo) /* {{{ */ PHP_METHOD(SplFileInfo, __debugInfo) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_ARR(spl_filesystem_object_get_debug_info(Z_OBJ_P(ZEND_THIS))); } /* }}} */ @@ -1385,9 +1349,7 @@ PHP_METHOD(SplFileInfo, __debugInfo) /* {{{ */ PHP_METHOD(SplFileInfo, _bad_state_ex) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); zend_throw_error(NULL, "The parent constructor was not called: the object is in an invalid state"); RETURN_THROWS(); } @@ -1406,9 +1368,7 @@ PHP_METHOD(FilesystemIterator, rewind) spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); bool skip_dots = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_SKIPDOTS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern->u.dir.index = 0; if (intern->u.dir.dirp) { @@ -1425,9 +1385,7 @@ PHP_METHOD(FilesystemIterator, getFlags) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(intern->flags & (SPL_FILE_DIR_KEY_MODE_MASK | SPL_FILE_DIR_CURRENT_MODE_MASK | SPL_FILE_DIR_OTHERS_MASK)); } /* }}} */ @@ -1491,9 +1449,7 @@ PHP_METHOD(RecursiveDirectoryIterator, getChildren) spl_filesystem_object *subdir; char slash = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_UNIXPATHS) ? '/' : DEFAULT_SLASH; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (spl_filesystem_object_get_file_name(intern) == FAILURE) { RETURN_THROWS(); @@ -1534,9 +1490,7 @@ PHP_METHOD(RecursiveDirectoryIterator, getSubPath) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (intern->u.dir.sub_path) { RETURN_STR_COPY(intern->u.dir.sub_path); @@ -1552,9 +1506,7 @@ PHP_METHOD(RecursiveDirectoryIterator, getSubPathname) spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); char slash = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_UNIXPATHS) ? '/' : DEFAULT_SLASH; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (intern->u.dir.sub_path) { RETURN_NEW_STR(strpprintf(0, "%s%c%s", ZSTR_VAL(intern->u.dir.sub_path), slash, intern->u.dir.entry.d_name)); @@ -1583,9 +1535,7 @@ PHP_METHOD(GlobIterator, count) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (EXPECTED(spl_intern_is_glob(intern))) { RETURN_LONG(php_glob_stream_get_count(intern->u.dir.dirp, NULL)); @@ -2100,9 +2050,7 @@ PHP_METHOD(SplFileObject, rewind) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_filesystem_file_rewind(ZEND_THIS, intern); } /* }}} */ @@ -2112,9 +2060,7 @@ PHP_METHOD(SplFileObject, eof) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern); @@ -2126,9 +2072,7 @@ PHP_METHOD(SplFileObject, valid) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) { RETURN_BOOL(intern->u.file.current_line || !Z_ISUNDEF(intern->u.file.current_zval)); @@ -2144,9 +2088,7 @@ PHP_METHOD(SplFileObject, fgets) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern); @@ -2161,9 +2103,7 @@ PHP_METHOD(SplFileObject, current) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern); @@ -2185,9 +2125,7 @@ PHP_METHOD(SplFileObject, key) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); /* Do not read the next line to support correct counting with fgetc() if (!intern->u.file.current_line) { @@ -2201,9 +2139,7 @@ PHP_METHOD(SplFileObject, next) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_filesystem_file_free_line(intern); if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) { @@ -2227,9 +2163,7 @@ PHP_METHOD(SplFileObject, getFlags) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(intern->flags & SPL_FILE_OBJECT_MASK); } /* }}} */ @@ -2258,9 +2192,7 @@ PHP_METHOD(SplFileObject, getMaxLineLen) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG((zend_long)intern->u.file.max_line_len); } /* }}} */ @@ -2268,9 +2200,7 @@ PHP_METHOD(SplFileObject, getMaxLineLen) /* {{{ Return false */ PHP_METHOD(SplFileObject, hasChildren) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_FALSE; } /* }}} */ @@ -2278,9 +2208,7 @@ PHP_METHOD(SplFileObject, hasChildren) /* {{{ Read NULL */ PHP_METHOD(SplFileObject, getChildren) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); /* return NULL */ } /* }}} */ @@ -2431,9 +2359,7 @@ PHP_METHOD(SplFileObject, getCsvControl) spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); char delimiter[2], enclosure[2], escape[2]; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); array_init(return_value); @@ -2476,9 +2402,7 @@ PHP_METHOD(SplFileObject, fflush) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern); @@ -2491,9 +2415,7 @@ PHP_METHOD(SplFileObject, ftell) spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); zend_long ret; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern); @@ -2527,9 +2449,7 @@ PHP_METHOD(SplFileObject, fgetc) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern); @@ -2552,9 +2472,7 @@ PHP_METHOD(SplFileObject, fpassthru) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern); @@ -2654,9 +2572,7 @@ PHP_METHOD(SplFileObject, fstat) { spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern); @@ -2722,9 +2638,7 @@ PHP_METHOD(SplFileObject, seek) PHP_METHOD(SplFileObject, __toString) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index 9401af3ae00de..867f492fab16d 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -511,9 +511,7 @@ PHP_METHOD(SplDoublyLinkedList, pop) { spl_dllist_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLDLLIST_P(ZEND_THIS); spl_ptr_llist_pop(intern->llist, return_value); @@ -530,9 +528,7 @@ PHP_METHOD(SplDoublyLinkedList, shift) { spl_dllist_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLDLLIST_P(ZEND_THIS); spl_ptr_llist_shift(intern->llist, return_value); @@ -550,9 +546,7 @@ PHP_METHOD(SplDoublyLinkedList, top) zval *value; spl_dllist_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLDLLIST_P(ZEND_THIS); value = spl_ptr_llist_last(intern->llist); @@ -572,9 +566,7 @@ PHP_METHOD(SplDoublyLinkedList, bottom) zval *value; spl_dllist_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLDLLIST_P(ZEND_THIS); value = spl_ptr_llist_first(intern->llist); @@ -594,9 +586,7 @@ PHP_METHOD(SplDoublyLinkedList, count) zend_long count; spl_dllist_object *intern = Z_SPLDLLIST_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); count = spl_ptr_llist_count(intern->llist); RETURN_LONG(count); @@ -608,9 +598,7 @@ PHP_METHOD(SplDoublyLinkedList, isEmpty) { zend_long count; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_dllist_object_count_elements(Z_OBJ_P(ZEND_THIS), &count); RETURN_BOOL(count == 0); @@ -646,9 +634,7 @@ PHP_METHOD(SplDoublyLinkedList, getIteratorMode) { spl_dllist_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLDLLIST_P(ZEND_THIS); @@ -914,9 +900,7 @@ PHP_METHOD(SplDoublyLinkedList, key) { spl_dllist_object *intern = Z_SPLDLLIST_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(intern->traverse_position); } @@ -927,9 +911,7 @@ PHP_METHOD(SplDoublyLinkedList, prev) { spl_dllist_object *intern = Z_SPLDLLIST_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_dllist_it_helper_move_forward(&intern->traverse_pointer, &intern->traverse_position, intern->llist, intern->flags ^ SPL_DLLIST_IT_LIFO); } @@ -940,9 +922,7 @@ PHP_METHOD(SplDoublyLinkedList, next) { spl_dllist_object *intern = Z_SPLDLLIST_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_dllist_it_helper_move_forward(&intern->traverse_pointer, &intern->traverse_position, intern->llist, intern->flags); } @@ -953,9 +933,7 @@ PHP_METHOD(SplDoublyLinkedList, valid) { spl_dllist_object *intern = Z_SPLDLLIST_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_BOOL(intern->traverse_pointer != NULL); } @@ -966,9 +944,7 @@ PHP_METHOD(SplDoublyLinkedList, rewind) { spl_dllist_object *intern = Z_SPLDLLIST_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_dllist_it_helper_rewind(&intern->traverse_pointer, &intern->traverse_position, intern->llist, intern->flags); } @@ -980,9 +956,7 @@ PHP_METHOD(SplDoublyLinkedList, current) spl_dllist_object *intern = Z_SPLDLLIST_P(ZEND_THIS); spl_ptr_llist_element *element = intern->traverse_pointer; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (element == NULL || Z_ISUNDEF(element->data)) { RETURN_NULL(); @@ -1001,9 +975,7 @@ PHP_METHOD(SplDoublyLinkedList, serialize) zval flags; php_serialize_data_t var_hash; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); PHP_VAR_SERIALIZE_INIT(var_hash); @@ -1099,9 +1071,7 @@ PHP_METHOD(SplDoublyLinkedList, __serialize) spl_ptr_llist_element *current = intern->llist->head; zval tmp; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); array_init(return_value); @@ -1205,9 +1175,7 @@ PHP_METHOD(SplDoublyLinkedList, add) /* {{{ */ PHP_METHOD(SplDoublyLinkedList, __debugInfo) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_ARR(spl_dllist_object_get_debug_info(Z_OBJ_P(ZEND_THIS))); } /* }}} */ diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index 61447e2d41bf4..a5e9ed166d8b3 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -562,9 +562,7 @@ PHP_METHOD(SplFixedArray, __wakeup) HashTable *intern_ht = zend_std_get_properties(Z_OBJ_P(ZEND_THIS)); zval *data; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (intern->array.size == 0) { int index = 0; @@ -589,9 +587,7 @@ PHP_METHOD(SplFixedArray, __serialize) zval *current; zend_string *key; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); HashTable *ht = zend_std_get_properties(&intern->std); uint32_t num_properties = zend_hash_num_elements(ht); @@ -666,9 +662,7 @@ PHP_METHOD(SplFixedArray, count) zval *object = ZEND_THIS; spl_fixedarray_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLFIXEDARRAY_P(object); RETURN_LONG(intern->array.size); @@ -678,9 +672,7 @@ PHP_METHOD(SplFixedArray, toArray) { spl_fixedarray_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLFIXEDARRAY_P(ZEND_THIS); @@ -774,9 +766,7 @@ PHP_METHOD(SplFixedArray, getSize) zval *object = ZEND_THIS; spl_fixedarray_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLFIXEDARRAY_P(object); RETURN_LONG(intern->array.size); @@ -871,9 +861,7 @@ PHP_METHOD(SplFixedArray, offsetUnset) /* Create a new iterator from a SplFixedArray instance. */ PHP_METHOD(SplFixedArray, getIterator) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); zend_create_internal_iterator_zval(return_value, ZEND_THIS); } diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index 5d36266393b04..85fc5f8bb698b 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -575,9 +575,7 @@ PHP_METHOD(SplHeap, count) zend_long count; spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); count = spl_ptr_heap_count(intern->heap); RETURN_LONG(count); @@ -589,9 +587,7 @@ PHP_METHOD(SplHeap, isEmpty) { spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_BOOL(spl_ptr_heap_count(intern->heap) == 0); } @@ -640,9 +636,7 @@ PHP_METHOD(SplHeap, extract) { spl_heap_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLHEAP_P(ZEND_THIS); @@ -705,9 +699,7 @@ PHP_METHOD(SplPriorityQueue, extract) spl_pqueue_elem elem; spl_heap_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLHEAP_P(ZEND_THIS); @@ -731,9 +723,7 @@ PHP_METHOD(SplPriorityQueue, top) spl_heap_object *intern; spl_pqueue_elem *elem; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLHEAP_P(ZEND_THIS); @@ -780,9 +770,7 @@ PHP_METHOD(SplPriorityQueue, getExtractFlags) { spl_heap_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLHEAP_P(ZEND_THIS); @@ -795,9 +783,7 @@ PHP_METHOD(SplHeap, recoverFromCorruption) { spl_heap_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLHEAP_P(ZEND_THIS); @@ -812,9 +798,7 @@ PHP_METHOD(SplHeap, isCorrupted) { spl_heap_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLHEAP_P(ZEND_THIS); @@ -841,9 +825,7 @@ PHP_METHOD(SplHeap, top) zval *value; spl_heap_object *intern; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); intern = Z_SPLHEAP_P(ZEND_THIS); @@ -970,9 +952,7 @@ PHP_METHOD(SplHeap, key) { spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(intern->heap->count - 1); } @@ -983,9 +963,7 @@ PHP_METHOD(SplHeap, next) { spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_ptr_heap_delete_top(intern->heap, NULL, ZEND_THIS); } @@ -996,9 +974,7 @@ PHP_METHOD(SplHeap, valid) { spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_BOOL(intern->heap->count != 0); } @@ -1007,9 +983,7 @@ PHP_METHOD(SplHeap, valid) /* {{{ Rewind the datastructure back to the start */ PHP_METHOD(SplHeap, rewind) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); /* do nothing, the iterator always points to the top element */ } /* }}} */ @@ -1019,9 +993,7 @@ PHP_METHOD(SplHeap, current) { spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (!intern->heap->count) { RETURN_NULL(); @@ -1037,9 +1009,7 @@ PHP_METHOD(SplPriorityQueue, current) { spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (!intern->heap->count) { RETURN_NULL(); @@ -1053,9 +1023,7 @@ PHP_METHOD(SplPriorityQueue, current) /* {{{ */ PHP_METHOD(SplHeap, __debugInfo) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_ARR(spl_heap_object_get_debug_info(spl_ce_SplHeap, Z_OBJ_P(ZEND_THIS))); } /* }}} */ @@ -1063,9 +1031,7 @@ PHP_METHOD(SplHeap, __debugInfo) /* {{{ */ PHP_METHOD(SplPriorityQueue, __debugInfo) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_ARR(spl_heap_object_get_debug_info(spl_ce_SplPriorityQueue, Z_OBJ_P(ZEND_THIS))); } /* }}} */ diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index 540e5c2c0ff94..801c091fbb42d 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -673,9 +673,7 @@ PHP_METHOD(SplObjectStorage, rewind) { spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); intern->index = 0; @@ -686,9 +684,7 @@ PHP_METHOD(SplObjectStorage, valid) { spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_BOOL(zend_hash_has_more_elements_ex(&intern->storage, &intern->pos) == SUCCESS); } /* }}} */ @@ -698,9 +694,7 @@ PHP_METHOD(SplObjectStorage, key) { spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(intern->index); } /* }}} */ @@ -711,9 +705,7 @@ PHP_METHOD(SplObjectStorage, current) spl_SplObjectStorageElement *element; spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) { zend_throw_exception(spl_ce_RuntimeException, "Called current() on invalid iterator", 0); @@ -728,9 +720,7 @@ PHP_METHOD(SplObjectStorage, getInfo) spl_SplObjectStorageElement *element; spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) { RETURN_NULL(); @@ -763,9 +753,7 @@ PHP_METHOD(SplObjectStorage, next) { spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); zend_hash_move_forward_ex(&intern->storage, &intern->pos); intern->index++; @@ -825,9 +813,7 @@ PHP_METHOD(SplObjectStorage, serialize) php_serialize_data_t var_hash; smart_str buf = {0}; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); PHP_VAR_SERIALIZE_INIT(var_hash); @@ -999,9 +985,7 @@ PHP_METHOD(SplObjectStorage, __serialize) spl_SplObjectStorageElement *elem; zval tmp; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); array_init(return_value); @@ -1069,9 +1053,7 @@ PHP_METHOD(SplObjectStorage, __unserialize) /* {{{ */ PHP_METHOD(SplObjectStorage, __debugInfo) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_ARR(spl_object_storage_debug_info(Z_OBJ_P(ZEND_THIS))); } @@ -1100,9 +1082,7 @@ PHP_METHOD(MultipleIterator, getFlags) { spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(intern->flags); } /* }}} */ @@ -1193,9 +1173,7 @@ PHP_METHOD(MultipleIterator, countIterators) { spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(zend_hash_num_elements(&intern->storage)); } @@ -1208,9 +1186,7 @@ PHP_METHOD(MultipleIterator, rewind) intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { @@ -1229,9 +1205,7 @@ PHP_METHOD(MultipleIterator, next) intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { @@ -1252,9 +1226,7 @@ PHP_METHOD(MultipleIterator, valid) intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); if (!zend_hash_num_elements(&intern->storage)) { RETURN_FALSE; @@ -1361,9 +1333,7 @@ PHP_METHOD(MultipleIterator, current) spl_SplObjectStorage *intern; intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_multiple_iterator_get_all(intern, SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT, return_value); } @@ -1375,9 +1345,7 @@ PHP_METHOD(MultipleIterator, key) spl_SplObjectStorage *intern; intern = Z_SPLOBJSTORAGE_P(ZEND_THIS); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); spl_multiple_iterator_get_all(intern, SPL_MULTIPLE_ITERATOR_GET_ALL_KEY, return_value); } From 37e82ea5d0da3a21d58e28ad28d29f704b1fb797 Mon Sep 17 00:00:00 2001 From: manuel Date: Wed, 24 Sep 2025 16:36:47 +0200 Subject: [PATCH 009/252] memory_limit is not always limited by max_memory_limit Make sure to always duplicate max_memory_limit ini value. Otherwise the alter ini routine may assume the value hasn't been overwritten, resulting in the user-specified value being set after the on_modify handler has run. Fixes GH-17951 Closes GH-19963 --- NEWS | 2 ++ tests/basic/gh17951_runtime_change_6.phpt | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/basic/gh17951_runtime_change_6.phpt diff --git a/NEWS b/NEWS index 5cda4161ea7b8..4ff3472d926b9 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ PHP NEWS . Sync all boost.context files with release 1.86.0. (mvorisek) . Fixed bug GH-20435 (SensitiveParameter doesn't work for named argument passing to variadic parameter). (ndossche) + . Fixed bug GH-17951 (memory_limit is not always limited by max_memory_limit). + (manuelm) - Standard: . Fix memory leak in array_diff() with custom type checks. (ndossche) diff --git a/tests/basic/gh17951_runtime_change_6.phpt b/tests/basic/gh17951_runtime_change_6.phpt new file mode 100644 index 0000000000000..0c62b0e837474 --- /dev/null +++ b/tests/basic/gh17951_runtime_change_6.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-17951 Runtime Change 6 +--INI-- +memory_limit=128M +max_memory_limit=512M +--FILE-- + +--EXPECTF-- +Warning: Failed to set memory_limit to 1073741824 bytes. Setting to max_memory_limit instead (currently: 536870912 bytes) in %s on line %d +512M +Warning: Failed to set memory_limit to 1073741824 bytes. Setting to max_memory_limit instead (currently: 536870912 bytes) in %s on line %d +512M +Warning: Failed to set memory_limit to 1073741824 bytes. Setting to max_memory_limit instead (currently: 536870912 bytes) in %s on line %d +512M From 9b752a3d2b4a15f61c0da61937792f9e56d72027 Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 11 Nov 2025 01:44:50 +0100 Subject: [PATCH 010/252] Commit the actual fix for GH-17951 Sorry, my (ilutov's) bad. I reverted the change to verify the test, but forgot to undo before pushing. --- main/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/main.c b/main/main.c index e33ef29d61bb7..cfa84ba0959bb 100644 --- a/main/main.c +++ b/main/main.c @@ -346,7 +346,7 @@ static PHP_INI_MH(OnChangeMemoryLimit) } zend_ini_entry *max_mem_limit_ini = zend_hash_str_find_ptr(EG(ini_directives), ZEND_STRL("max_memory_limit")); - entry->value = zend_string_copy(max_mem_limit_ini->value); + entry->value = zend_string_init(ZSTR_VAL(max_mem_limit_ini->value), ZSTR_LEN(max_mem_limit_ini->value), true); PG(memory_limit) = PG(max_memory_limit); return SUCCESS; From 08ba240c81811dda0dc672c9d6cacb3bb10c5741 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:19:29 +0100 Subject: [PATCH 011/252] simplexml: Avoid double lookups and unnecessary allocations in getNamespaces() (#20447) --- ext/simplexml/simplexml.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index d3248bb812086..16a8aaa22fc3a 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -1413,14 +1413,10 @@ PHP_METHOD(SimpleXMLElement, asXML) static inline void sxe_add_namespace_name_raw(zval *return_value, const char *prefix, const char *href) { - zend_string *key = zend_string_init(prefix, strlen(prefix), 0); - zval zv; - - if (!zend_hash_exists(Z_ARRVAL_P(return_value), key)) { - ZVAL_STRING(&zv, href); - zend_hash_add_new(Z_ARRVAL_P(return_value), key, &zv); + zval *zv = zend_hash_str_lookup(Z_ARRVAL_P(return_value), prefix, strlen(prefix)); + if (Z_ISNULL_P(zv)) { + ZVAL_STRING(zv, href); } - zend_string_release_ex(key, 0); } static inline void sxe_add_namespace_name(zval *return_value, xmlNsPtr ns) /* {{{ */ From ee9773bdc6484496d1f5cf258b0bf48c4ba1c93f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:40:04 +0100 Subject: [PATCH 012/252] Fix nightly failure due to OnChangeMemoryLimit changes (#20450) --- main/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/main/main.c b/main/main.c index cfa84ba0959bb..25cfc4de1e7d9 100644 --- a/main/main.c +++ b/main/main.c @@ -347,6 +347,7 @@ static PHP_INI_MH(OnChangeMemoryLimit) zend_ini_entry *max_mem_limit_ini = zend_hash_str_find_ptr(EG(ini_directives), ZEND_STRL("max_memory_limit")); entry->value = zend_string_init(ZSTR_VAL(max_mem_limit_ini->value), ZSTR_LEN(max_mem_limit_ini->value), true); + GC_MAKE_PERSISTENT_LOCAL(entry->value); PG(memory_limit) = PG(max_memory_limit); return SUCCESS; From 618e576614004c8853a3660435f8552b0cfbe77b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:41:18 +0100 Subject: [PATCH 013/252] Minor DOM cleanups --- ext/dom/element.c | 9 +-------- ext/dom/node.c | 17 ++++------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/ext/dom/element.c b/ext/dom/element.c index cf1a762768a87..797f215e173d1 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -1210,19 +1210,17 @@ Since: DOM Level 2 */ PHP_METHOD(DOMElement, getAttributeNodeNS) { - zval *id; xmlNodePtr elemp; xmlAttrPtr attrp; dom_object *intern; size_t uri_len, name_len; char *uri, *name; - id = ZEND_THIS; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) { RETURN_THROWS(); } - DOM_GET_OBJ(elemp, id, xmlNodePtr, intern); + DOM_GET_OBJ(elemp, ZEND_THIS, xmlNodePtr, intern); bool follow_spec = php_dom_follow_spec_intern(intern); if (follow_spec && uri_len == 0) { @@ -1239,16 +1237,11 @@ PHP_METHOD(DOMElement, getAttributeNodeNS) /* Keep parent alive, because we're a fake child. */ GC_ADDREF(&intern->std); (void) php_dom_create_fake_namespace_decl(elemp, nsptr, return_value, intern); - } else { - RETURN_NULL(); } - } else { - RETURN_NULL(); } } else { DOM_RET_OBJ((xmlNodePtr) attrp, intern); } - } /* }}} end dom_element_get_attribute_node_ns */ diff --git a/ext/dom/node.c b/ext/dom/node.c index 40aaf27669268..debc08c4958c7 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -1878,8 +1878,7 @@ static void dom_node_lookup_prefix(INTERNAL_FUNCTION_PARAMETERS, bool modern) case XML_DOCUMENT_FRAG_NODE: case XML_DOCUMENT_TYPE_NODE: case XML_DTD_NODE: - RETURN_NULL(); - break; + return; default: lookupp = nodep->parent; } @@ -1898,8 +1897,6 @@ static void dom_node_lookup_prefix(INTERNAL_FUNCTION_PARAMETERS, bool modern) } } } - - RETURN_NULL(); } PHP_METHOD(DOMNode, lookupPrefix) @@ -2065,16 +2062,14 @@ PHP_METHOD(DOMNode, lookupNamespaceURI) prefix = NULL; } const char *ns_uri = dom_locate_a_namespace(nodep, prefix); - if (ns_uri == NULL) { - RETURN_NULL(); - } else { + if (ns_uri != NULL) { RETURN_STRING(ns_uri); } } else { if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) { nodep = xmlDocGetRootElement((xmlDocPtr) nodep); if (nodep == NULL) { - RETURN_NULL(); + return; } } @@ -2083,8 +2078,6 @@ PHP_METHOD(DOMNode, lookupNamespaceURI) RETURN_STRING((char *) nsptr->href); } } - - RETURN_NULL(); } /* }}} end dom_node_lookup_namespace_uri */ @@ -2294,13 +2287,12 @@ static void dom_node_get_node_path(INTERNAL_FUNCTION_PARAMETERS, bool throw) zval *id; xmlNode *nodep; dom_object *intern; - char *value; ZEND_PARSE_PARAMETERS_NONE(); DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern); - value = (char *) xmlGetNodePath(nodep); + char *value = (char *) xmlGetNodePath(nodep); if (value == NULL) { /* This is only possible when an invalid argument is passed (e.g. namespace declaration, but that's not the case for this call site), * or on allocation failure. So in other words, this only happens on allocation failure. */ @@ -2308,7 +2300,6 @@ static void dom_node_get_node_path(INTERNAL_FUNCTION_PARAMETERS, bool throw) php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); RETURN_THROWS(); } - RETURN_NULL(); } else { RETVAL_STRING(value); xmlFree(value); From a596e05cf3563f5f43bf02d39f5c74ab47344273 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:44:13 +0100 Subject: [PATCH 014/252] phar: Make phar_is_tar() and referenced functions const correct (#20451) --- ext/phar/phar.c | 2 +- ext/phar/phar_internal.h | 2 +- ext/phar/tar.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/phar/phar.c b/ext/phar/phar.c index b7becf27e32db..51b28ec2b7499 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1719,7 +1719,7 @@ static zend_result phar_open_from_fp(php_stream* fp, char *fname, size_t fname_l } if (got >= 512) { - if (phar_is_tar((char *) pos, fname)) { /* TODO: fix const correctness */ + if (phar_is_tar(pos, fname)) { php_stream_rewind(fp); return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, compression, error); } diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index bd3d7158c8e82..5acb0873ebdbc 100644 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -443,7 +443,7 @@ zend_result phar_open_archive_fp(phar_archive_data *phar); zend_result phar_copy_on_write(phar_archive_data **pphar); /* tar functions in tar.c */ -bool phar_is_tar(char *buf, char *fname); +bool phar_is_tar(const char *buf, const char *fname); zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, char *alias, size_t alias_len, phar_archive_data** pphar, uint32_t compression, char **error); ZEND_ATTRIBUTE_NONNULL_ARGS(1, 7, 8) zend_result phar_open_or_create_tar(char *fname, size_t fname_len, char *alias, size_t alias_len, bool is_data, uint32_t options, phar_archive_data** pphar, char **error); ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_default_stub, char **error); diff --git a/ext/phar/tar.c b/ext/phar/tar.c index 4847597cce2ea..67994289d0a9f 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -87,10 +87,10 @@ static zend_result phar_tar_octal(char *buf, uint32_t val, size_t len) /* {{{ */ } /* }}} */ -static uint32_t phar_tar_checksum(char *buf, size_t len) /* {{{ */ +static uint32_t phar_tar_checksum(const char *buf, size_t len) /* {{{ */ { uint32_t sum = 0; - char *end = buf + len; + const char *end = buf + len; while (buf != end) { sum += (unsigned char)*buf; @@ -100,7 +100,7 @@ static uint32_t phar_tar_checksum(char *buf, size_t len) /* {{{ */ } /* }}} */ -bool phar_is_tar(char *buf, char *fname) /* {{{ */ +bool phar_is_tar(const char *buf, const char *fname) /* {{{ */ { tar_header *header = (tar_header *) buf; uint32_t checksum = phar_tar_number(header->checksum, sizeof(header->checksum)); From ddf584c71ac258a83e77a7cb0bc63747880d4361 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:46:29 +0100 Subject: [PATCH 015/252] [ci skip] Fix NEWS ordering --- NEWS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 4ff3472d926b9..3f519e5eec408 100644 --- a/NEWS +++ b/NEWS @@ -9,9 +9,6 @@ PHP NEWS . Fixed bug GH-17951 (memory_limit is not always limited by max_memory_limit). (manuelm) -- Standard: - . Fix memory leak in array_diff() with custom type checks. (ndossche) - - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) @@ -20,6 +17,9 @@ PHP NEWS . Fixed bug GH-20442 (Phar does not respect case-insensitiveness of __halt_compiler() when reading stub). (ndossche, TimWolla) +- Standard: + . Fix memory leak in array_diff() with custom type checks. (ndossche) + 06 Nov 2025, PHP 8.5.0RC4 - Core: From e504ab778c1d092b1d626f9df2e1d07aaef461ec Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:20:52 +0100 Subject: [PATCH 016/252] Fix memory leak when edge case is hit when registering xpath callback This can happen if you have a valid callable name with a NUL byte in it, on a non-interned string entry. This can be done by abusing anonymous classes. Closes GH-20452. --- NEWS | 4 ++++ ext/dom/tests/DOMXPath_callables_errors.phpt | 14 ++++++++++++++ ext/dom/xpath_callbacks.c | 6 ++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index b2f7d7956c3a3..f7b917410d874 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,10 @@ PHP NEWS . Fix crashes when trying to instantiate uninstantiable classes via date static constructors. (ndossche) +- DOM: + . Fix memory leak when edge case is hit when registering xpath callback. + (ndossche) + - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) diff --git a/ext/dom/tests/DOMXPath_callables_errors.phpt b/ext/dom/tests/DOMXPath_callables_errors.phpt index d11437398b158..10019474e7e7f 100644 --- a/ext/dom/tests/DOMXPath_callables_errors.phpt +++ b/ext/dom/tests/DOMXPath_callables_errors.phpt @@ -57,6 +57,19 @@ try { echo $e->getMessage(), "\n"; } +$x = new class { + public static function dump() {} +}; + +$classes = get_declared_classes(); + +try { + $str = str_repeat($classes[count($classes) - 1] . '::dump', random_int(1, 1)); + $xpath->registerPhpFunctions([$str]); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + ?> --EXPECT-- DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be a callable, function "nonexistent" not found or invalid function name @@ -67,3 +80,4 @@ DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be an array with DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be an array containing valid callback names DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be an array containing valid callback names DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be a valid callback name +DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be an array containing valid callback names diff --git a/ext/dom/xpath_callbacks.c b/ext/dom/xpath_callbacks.c index 283ccd8f7cc62..0bd6c3f331896 100644 --- a/ext/dom/xpath_callbacks.c +++ b/ext/dom/xpath_callbacks.c @@ -206,14 +206,16 @@ static zend_result php_dom_xpath_callback_ns_update_method_handler( ZVAL_PTR(®istered_value, fcc); if (!key) { - zend_string *str = zval_try_get_string(entry); + zend_string *tmp_str; + zend_string *str = zval_try_get_tmp_string(entry, &tmp_str); if (str && php_dom_xpath_is_callback_name_valid_and_throw(str, name_validation, true)) { zend_hash_update(&ns->functions, str, ®istered_value); if (register_func) { register_func(ctxt, namespace, str); } - zend_string_release_ex(str, false); + zend_tmp_string_release(tmp_str); } else { + zend_tmp_string_release(tmp_str); zend_fcc_dtor(fcc); efree(fcc); return FAILURE; From 8a0c300d02eff3fdadd73ce1c6de75a7a6cdca78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 11 Nov 2025 16:23:27 +0100 Subject: [PATCH 017/252] uri: Update to uriparser-0.9.9-59-gc3b4956 (#20437) This is specifically to backport uriparser/uriparser#276. Fixes php/php-src#20431. --- .../host_success_unset_existing.phpt | 4 +- ext/uri/uriparser/include/uriparser/Uri.h | 838 ++-- ext/uri/uriparser/include/uriparser/UriBase.h | 270 +- .../uriparser/include/uriparser/UriDefsAnsi.h | 10 +- .../include/uriparser/UriDefsConfig.h | 64 +- .../include/uriparser/UriDefsUnicode.h | 10 +- ext/uri/uriparser/include/uriparser/UriIp4.h | 82 +- ext/uri/uriparser/src/UriCommon.c | 1325 +++--- ext/uri/uriparser/src/UriCommon.h | 84 +- ext/uri/uriparser/src/UriCompare.c | 234 +- ext/uri/uriparser/src/UriConfig.h | 12 +- ext/uri/uriparser/src/UriConfig.h.in | 10 +- ext/uri/uriparser/src/UriCopy.c | 361 +- ext/uri/uriparser/src/UriCopy.h | 60 +- ext/uri/uriparser/src/UriEscape.c | 787 ++-- ext/uri/uriparser/src/UriFile.c | 335 +- ext/uri/uriparser/src/UriIp4.c | 421 +- ext/uri/uriparser/src/UriIp4Base.c | 67 +- ext/uri/uriparser/src/UriIp4Base.h | 16 +- ext/uri/uriparser/src/UriMemory.c | 724 ++- ext/uri/uriparser/src/UriMemory.h | 52 +- ext/uri/uriparser/src/UriNormalize.c | 1503 +++--- ext/uri/uriparser/src/UriNormalize.h | 57 +- ext/uri/uriparser/src/UriNormalizeBase.c | 146 +- ext/uri/uriparser/src/UriNormalizeBase.h | 10 +- ext/uri/uriparser/src/UriParse.c | 4086 +++++++++-------- ext/uri/uriparser/src/UriParseBase.c | 73 +- ext/uri/uriparser/src/UriParseBase.h | 12 +- ext/uri/uriparser/src/UriQuery.c | 792 ++-- ext/uri/uriparser/src/UriRecompose.c | 1073 ++--- ext/uri/uriparser/src/UriResolve.c | 597 +-- ext/uri/uriparser/src/UriSetFragment.c | 479 +- ext/uri/uriparser/src/UriSetHostAuto.c | 167 +- ext/uri/uriparser/src/UriSetHostBase.h | 6 +- ext/uri/uriparser/src/UriSetHostCommon.c | 410 +- ext/uri/uriparser/src/UriSetHostCommon.h | 63 +- ext/uri/uriparser/src/UriSetHostIp4.c | 96 +- ext/uri/uriparser/src/UriSetHostIp6.c | 224 +- ext/uri/uriparser/src/UriSetHostIpFuture.c | 227 +- ext/uri/uriparser/src/UriSetHostRegName.c | 363 +- ext/uri/uriparser/src/UriSetPath.c | 809 ++-- ext/uri/uriparser/src/UriSetPort.c | 234 +- ext/uri/uriparser/src/UriSetQuery.c | 473 +- ext/uri/uriparser/src/UriSetScheme.c | 409 +- ext/uri/uriparser/src/UriSetUserInfo.c | 479 +- ext/uri/uriparser/src/UriShorten.c | 628 +-- ext/uri/uriparser/src/UriVersion.c | 58 +- 47 files changed, 9339 insertions(+), 9901 deletions(-) diff --git a/ext/uri/tests/rfc3986/modification/host_success_unset_existing.phpt b/ext/uri/tests/rfc3986/modification/host_success_unset_existing.phpt index 521b6397ec450..8f79d723be5dd 100644 --- a/ext/uri/tests/rfc3986/modification/host_success_unset_existing.phpt +++ b/ext/uri/tests/rfc3986/modification/host_success_unset_existing.phpt @@ -18,6 +18,6 @@ var_dump($uri2->toString()); --EXPECT-- string(11) "example.com" NULL -string(7) "https:/" +string(6) "https:" NULL -string(7) "https:/" +string(6) "https:" diff --git a/ext/uri/uriparser/include/uriparser/Uri.h b/ext/uri/uriparser/include/uriparser/Uri.h index fbdb3d9a3798e..ea52097d6de58 100644 --- a/ext/uri/uriparser/include/uriparser/Uri.h +++ b/ext/uri/uriparser/include/uriparser/Uri.h @@ -45,47 +45,41 @@ */ #if (defined(URI_PASS_ANSI) && !defined(URI_H_ANSI)) \ - || (defined(URI_PASS_UNICODE) && !defined(URI_H_UNICODE)) \ - || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) + || (defined(URI_PASS_UNICODE) && !defined(URI_H_UNICODE)) \ + || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* What encodings are enabled? */ -#include "UriDefsConfig.h" -#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +# include "UriDefsConfig.h" +# if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "Uri.h" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "Uri.h" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "Uri.h" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "Uri.h" +# undef URI_PASS_UNICODE +# endif /* Only one pass for each encoding */ -#elif (defined(URI_PASS_ANSI) && !defined(URI_H_ANSI) \ - && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \ - && !defined(URI_H_UNICODE) && defined(URI_ENABLE_UNICODE)) -# ifdef URI_PASS_ANSI -# define URI_H_ANSI 1 -# include "UriDefsAnsi.h" -# else -# define URI_H_UNICODE 1 -# include "UriDefsUnicode.h" -# endif - - - -#ifdef __cplusplus +# elif (defined(URI_PASS_ANSI) && !defined(URI_H_ANSI) && defined(URI_ENABLE_ANSI)) \ + || (defined(URI_PASS_UNICODE) && !defined(URI_H_UNICODE) \ + && defined(URI_ENABLE_UNICODE)) +# ifdef URI_PASS_ANSI +# define URI_H_ANSI 1 +# include "UriDefsAnsi.h" +# else +# define URI_H_UNICODE 1 +# include "UriDefsUnicode.h" +# endif + +# ifdef __cplusplus extern "C" { -#endif - - - -#ifndef URI_DOXYGEN -# include "UriBase.h" -#endif - +# endif +# ifndef URI_DOXYGEN +# include "UriBase.h" +# endif /** * Specifies a range of characters within a string. @@ -99,12 +93,10 @@ extern "C" { * @since 0.3.0 */ typedef struct URI_TYPE(TextRangeStruct) { - const URI_CHAR * first; /**< Pointer to first character */ - const URI_CHAR * afterLast; /**< Pointer to character after the last one still in */ + const URI_CHAR * first; /**< Pointer to first character */ + const URI_CHAR * afterLast; /**< Pointer to character after the last one still in */ } URI_TYPE(TextRange); /**< @copydoc UriTextRangeStructA */ - - /** * Represents a path segment within a %URI path. * More precisely it is a node in a linked @@ -114,14 +106,13 @@ typedef struct URI_TYPE(TextRangeStruct) { * @since 0.3.0 */ typedef struct URI_TYPE(PathSegmentStruct) { - URI_TYPE(TextRange) text; /**< Path segment name */ - struct URI_TYPE(PathSegmentStruct) * next; /**< Pointer to the next path segment in the list, can be NULL if last already */ + URI_TYPE(TextRange) text; /**< Path segment name */ + struct URI_TYPE(PathSegmentStruct) * next; /**< Pointer to the next path segment in + the list, can be NULL if last already */ - void * reserved; /**< Reserved to the parser */ + void * reserved; /**< Reserved to the parser */ } URI_TYPE(PathSegment); /**< @copydoc UriPathSegmentStructA */ - - /** * Holds structured host information. * This is either a IPv4, IPv6, plain @@ -132,17 +123,16 @@ typedef struct URI_TYPE(PathSegmentStruct) { * @since 0.3.0 */ typedef struct URI_TYPE(HostDataStruct) { - UriIp4 * ip4; /**< IPv4 address */ - UriIp6 * ip6; /**< IPv6 address */ - URI_TYPE(TextRange) ipFuture; /**< IPvFuture address - @note - With non-NULL members in UriUriStructA.hostData context, - this text range's pointers must be identical to those - of UriUriStructA.hostText at all times. */ + UriIp4 * ip4; /**< IPv4 address */ + UriIp6 * ip6; /**< IPv6 address */ + URI_TYPE(TextRange) + ipFuture; /**< IPvFuture address +@note +With non-NULL members in UriUriStructA.hostData context, +this text range's pointers must be identical to those +of UriUriStructA.hostText at all times. */ } URI_TYPE(HostData); /**< @copydoc UriHostDataStructA */ - - /** * Represents an RFC 3986 %URI. * Missing components can be {NULL, NULL} ranges. @@ -153,24 +143,23 @@ typedef struct URI_TYPE(HostDataStruct) { * @since 0.3.0 */ typedef struct URI_TYPE(UriStruct) { - URI_TYPE(TextRange) scheme; /**< Scheme (e.g. "http") */ - URI_TYPE(TextRange) userInfo; /**< User info (e.g. "user:pass") */ - URI_TYPE(TextRange) hostText; /**< Host text (set for all hosts, excluding square brackets) */ - URI_TYPE(HostData) hostData; /**< Structured host type specific data */ - URI_TYPE(TextRange) portText; /**< Port (e.g. "80") */ - URI_TYPE(PathSegment) * pathHead; /**< Head of a linked list of path segments */ - URI_TYPE(PathSegment) * pathTail; /**< Tail of the list behind pathHead */ - URI_TYPE(TextRange) query; /**< Query without leading "?" */ - URI_TYPE(TextRange) fragment; /**< Query without leading "#" */ - UriBool absolutePath; /**< Absolute path flag, distincting "a" and "/a"; - always URI_FALSE for URIs with host */ - UriBool owner; /**< Memory owner flag */ - - void * reserved; /**< Reserved to the parser */ + URI_TYPE(TextRange) scheme; /**< Scheme (e.g. "http") */ + URI_TYPE(TextRange) userInfo; /**< User info (e.g. "user:pass") */ + URI_TYPE(TextRange) + hostText; /**< Host text (set for all hosts, excluding square brackets) */ + URI_TYPE(HostData) hostData; /**< Structured host type specific data */ + URI_TYPE(TextRange) portText; /**< Port (e.g. "80") */ + URI_TYPE(PathSegment) * pathHead; /**< Head of a linked list of path segments */ + URI_TYPE(PathSegment) * pathTail; /**< Tail of the list behind pathHead */ + URI_TYPE(TextRange) query; /**< Query without leading "?" */ + URI_TYPE(TextRange) fragment; /**< Query without leading "#" */ + UriBool absolutePath; /**< Absolute path flag, distincting "a" and "/a"; always + URI_FALSE for URIs with host */ + UriBool owner; /**< Memory owner flag */ + + void * reserved; /**< Reserved to the parser */ } URI_TYPE(Uri); /**< @copydoc UriUriStructA */ - - /** * Represents a state of the %URI parser. * Missing components can be NULL to reflect @@ -181,15 +170,14 @@ typedef struct URI_TYPE(UriStruct) { * @since 0.3.0 */ typedef struct URI_TYPE(ParserStateStruct) { - URI_TYPE(Uri) * uri; /**< Plug in the %URI structure to be filled while parsing here */ - int errorCode; /**< Code identifying the error which occurred */ - const URI_CHAR * errorPos; /**< Pointer to position in case of a syntax error */ + URI_TYPE(Uri) + *uri; /**< Plug in the %URI structure to be filled while parsing here */ + int errorCode; /**< Code identifying the error which occurred */ + const URI_CHAR * errorPos; /**< Pointer to position in case of a syntax error */ - void * reserved; /**< Reserved to the parser */ + void * reserved; /**< Reserved to the parser */ } URI_TYPE(ParserState); /**< @copydoc UriParserStateStructA */ - - /** * Represents a query element. * More precisely it is a node in a linked @@ -198,13 +186,13 @@ typedef struct URI_TYPE(ParserStateStruct) { * @since 0.7.0 */ typedef struct URI_TYPE(QueryListStruct) { - const URI_CHAR * key; /**< Key of the query element */ - const URI_CHAR * value; /**< Value of the query element, can be NULL */ + const URI_CHAR * key; /**< Key of the query element */ + const URI_CHAR * value; /**< Value of the query element, can be NULL */ - struct URI_TYPE(QueryListStruct) * next; /**< Pointer to the next key/value pair in the list, can be NULL if last already */ + struct URI_TYPE(QueryListStruct) * next; /**< Pointer to the next key/value pair in + the list, can be NULL if last already */ } URI_TYPE(QueryList); /**< @copydoc UriQueryListStructA */ - /** * Checks if a URI has the host component set. * @@ -215,8 +203,6 @@ typedef struct URI_TYPE(QueryListStruct) { */ URI_PUBLIC UriBool URI_FUNC(HasHost)(const URI_TYPE(Uri) * uri); - - /** * Converts an IPv6 text representation into 16 bytes. * @@ -232,11 +218,8 @@ URI_PUBLIC UriBool URI_FUNC(HasHost)(const URI_TYPE(Uri) * uri); * @see uriIsWellFormedHostIp6A * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(ParseIpSixAddress)(UriIp6 * output, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(ParseIpSixAddress)(UriIp6 * output, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Converts an IPv6 text representation into 16 bytes. @@ -252,12 +235,9 @@ URI_PUBLIC int URI_FUNC(ParseIpSixAddress)(UriIp6 * output, * @see uriIsWellFormedHostIp6MmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(ParseIpSixAddressMm)(UriIp6 * output, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(ParseIpSixAddressMm)(UriIp6 * output, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Parses a RFC 3986 %URI. @@ -265,7 +245,8 @@ URI_PUBLIC int URI_FUNC(ParseIpSixAddressMm)(UriIp6 * output, * * @param state INOUT: Parser state with set output %URI, must not be NULL * @param first IN: Pointer to the first character to parse, must not be NULL - * @param afterLast IN: Pointer to the character after the last to parse, must not be NULL + * @param afterLast IN: Pointer to the character after the last to parse, must + * not be NULL * @return 0 on success, error code otherwise * * @see uriParseUriA @@ -273,12 +254,11 @@ URI_PUBLIC int URI_FUNC(ParseIpSixAddressMm)(UriIp6 * output, * @see uriParseSingleUriExA * @see uriToStringA * @since 0.3.0 - * @deprecated Deprecated since 0.9.0, please migrate to uriParseSingleUriExA (with "Single"). + * @deprecated Deprecated since 0.9.0, please migrate to uriParseSingleUriExA (with + * "Single"). */ -URI_PUBLIC int URI_FUNC(ParseUriEx)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(ParseUriEx)(URI_TYPE(ParserState) * state, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Parses a RFC 3986 %URI. @@ -293,12 +273,10 @@ URI_PUBLIC int URI_FUNC(ParseUriEx)(URI_TYPE(ParserState) * state, * @see uriParseSingleUriExA * @see uriToStringA * @since 0.3.0 - * @deprecated Deprecated since 0.9.0, please migrate to uriParseSingleUriA (with "Single"). + * @deprecated Deprecated since 0.9.0, please migrate to uriParseSingleUriA (with + * "Single"). */ -URI_PUBLIC int URI_FUNC(ParseUri)(URI_TYPE(ParserState) * state, - const URI_CHAR * text); - - +URI_PUBLIC int URI_FUNC(ParseUri)(URI_TYPE(ParserState) * state, const URI_CHAR * text); /** * Parses a single RFC 3986 %URI. @@ -317,10 +295,8 @@ URI_PUBLIC int URI_FUNC(ParseUri)(URI_TYPE(ParserState) * state, * @see uriToStringA * @since 0.9.0 */ -URI_PUBLIC int URI_FUNC(ParseSingleUri)(URI_TYPE(Uri) * uri, - const URI_CHAR * text, const URI_CHAR ** errorPos); - - +URI_PUBLIC int URI_FUNC(ParseSingleUri)(URI_TYPE(Uri) * uri, const URI_CHAR * text, + const URI_CHAR ** errorPos); /** * Parses a single RFC 3986 %URI. @@ -342,11 +318,9 @@ URI_PUBLIC int URI_FUNC(ParseSingleUri)(URI_TYPE(Uri) * uri, * @see uriToStringA * @since 0.9.0 */ -URI_PUBLIC int URI_FUNC(ParseSingleUriEx)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, const URI_CHAR * afterLast, - const URI_CHAR ** errorPos); - - +URI_PUBLIC int URI_FUNC(ParseSingleUriEx)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + const URI_CHAR ** errorPos); /** * Parses a single RFC 3986 %URI. @@ -368,11 +342,10 @@ URI_PUBLIC int URI_FUNC(ParseSingleUriEx)(URI_TYPE(Uri) * uri, * @see uriToStringA * @since 0.9.0 */ -URI_PUBLIC int URI_FUNC(ParseSingleUriExMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, const URI_CHAR * afterLast, - const URI_CHAR ** errorPos, UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(ParseSingleUriExMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + const URI_CHAR ** errorPos, + UriMemoryManager * memory); /** * Frees all memory associated with the members @@ -381,8 +354,8 @@ URI_PUBLIC int URI_FUNC(ParseSingleUriExMm)(URI_TYPE(Uri) * uri, * Uses default libc-based memory manager. * * @remarks - * Calling on an all-zeros structure (e.g. through memset or calloc) is safe.
- * Calling on an uninitialized structure is not safe. + * Calling on an all-zeros structure (e.g. through memset or calloc) is + * safe.
Calling on an uninitialized structure is not safe. * * @param uri INOUT: %URI structure whose members should be freed * @@ -391,16 +364,14 @@ URI_PUBLIC int URI_FUNC(ParseSingleUriExMm)(URI_TYPE(Uri) * uri, */ URI_PUBLIC void URI_FUNC(FreeUriMembers)(URI_TYPE(Uri) * uri); - - /** * Frees all memory associated with the members * of the %URI structure. Note that the structure * itself is not freed, only its members. * * @remarks - * Calling on an all-zeros structure (e.g. through memset or calloc) is safe.
- * Calling on an uninitialized structure is not safe. + * Calling on an all-zeros structure (e.g. through memset or calloc) is + * safe.
Calling on an uninitialized structure is not safe. * * @param uri INOUT: %URI structure whose members should be freed * @param memory IN: Memory manager to use, NULL for default libc @@ -409,10 +380,7 @@ URI_PUBLIC void URI_FUNC(FreeUriMembers)(URI_TYPE(Uri) * uri); * @see uriFreeUriMembersA * @since 0.9.0 */ -URI_PUBLIC int URI_FUNC(FreeUriMembersMm)(URI_TYPE(Uri) * uri, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(FreeUriMembersMm)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); /** * Percent-encodes all but unreserved characters from the input string and @@ -443,10 +411,8 @@ URI_PUBLIC int URI_FUNC(FreeUriMembersMm)(URI_TYPE(Uri) * uri, * @since 0.5.2 */ URI_PUBLIC URI_CHAR * URI_FUNC(EscapeEx)(const URI_CHAR * inFirst, - const URI_CHAR * inAfterLast, URI_CHAR * out, - UriBool spaceToPlus, UriBool normalizeBreaks); - - + const URI_CHAR * inAfterLast, URI_CHAR * out, + UriBool spaceToPlus, UriBool normalizeBreaks); /** * Percent-encodes all but unreserved characters from the input string and @@ -476,9 +442,7 @@ URI_PUBLIC URI_CHAR * URI_FUNC(EscapeEx)(const URI_CHAR * inFirst, * @since 0.5.0 */ URI_PUBLIC URI_CHAR * URI_FUNC(Escape)(const URI_CHAR * in, URI_CHAR * out, - UriBool spaceToPlus, UriBool normalizeBreaks); - - + UriBool spaceToPlus, UriBool normalizeBreaks); /** * Unescapes percent-encoded groups in a given string. @@ -497,10 +461,9 @@ URI_PUBLIC URI_CHAR * URI_FUNC(Escape)(const URI_CHAR * in, URI_CHAR * out, * @see uriEscapeExA * @since 0.5.0 */ -URI_PUBLIC const URI_CHAR * URI_FUNC(UnescapeInPlaceEx)(URI_CHAR * inout, - UriBool plusToSpace, UriBreakConversion breakConversion); - - +URI_PUBLIC const URI_CHAR * + URI_FUNC(UnescapeInPlaceEx)(URI_CHAR * inout, UriBool plusToSpace, + UriBreakConversion breakConversion); /** * Unescapes percent-encoded groups in a given string. @@ -522,13 +485,11 @@ URI_PUBLIC const URI_CHAR * URI_FUNC(UnescapeInPlaceEx)(URI_CHAR * inout, */ URI_PUBLIC const URI_CHAR * URI_FUNC(UnescapeInPlace)(URI_CHAR * inout); - - /** * Performs reference resolution as described in - * section 5.2.2 of RFC 3986. - * Uses default libc-based memory manager. - * NOTE: On success you have to call uriFreeUriMembersA on \p absoluteDest manually later. + * section 5.2.2 of + * RFC 3986. Uses default libc-based memory manager. NOTE: On success you have to call + * uriFreeUriMembersA on \p absoluteDest manually later. * * @param absoluteDest OUT: Result %URI * @param relativeSource IN: Reference to resolve @@ -542,16 +503,14 @@ URI_PUBLIC const URI_CHAR * URI_FUNC(UnescapeInPlace)(URI_CHAR * inout); * @since 0.4.0 */ URI_PUBLIC int URI_FUNC(AddBaseUri)(URI_TYPE(Uri) * absoluteDest, - const URI_TYPE(Uri) * relativeSource, - const URI_TYPE(Uri) * absoluteBase); - - + const URI_TYPE(Uri) * relativeSource, + const URI_TYPE(Uri) * absoluteBase); /** * Performs reference resolution as described in - * section 5.2.2 of RFC 3986. - * Uses default libc-based memory manager. - * NOTE: On success you have to call uriFreeUriMembersA on \p absoluteDest manually later. + * section 5.2.2 of + * RFC 3986. Uses default libc-based memory manager. NOTE: On success you have to call + * uriFreeUriMembersA on \p absoluteDest manually later. * * @param absoluteDest OUT: Result %URI * @param relativeSource IN: Reference to resolve @@ -565,16 +524,15 @@ URI_PUBLIC int URI_FUNC(AddBaseUri)(URI_TYPE(Uri) * absoluteDest, * @since 0.8.1 */ URI_PUBLIC int URI_FUNC(AddBaseUriEx)(URI_TYPE(Uri) * absoluteDest, - const URI_TYPE(Uri) * relativeSource, - const URI_TYPE(Uri) * absoluteBase, - UriResolutionOptions options); - - + const URI_TYPE(Uri) * relativeSource, + const URI_TYPE(Uri) * absoluteBase, + UriResolutionOptions options); /** * Performs reference resolution as described in - * section 5.2.2 of RFC 3986. - * NOTE: On success you have to call uriFreeUriMembersMmA on \p absoluteDest manually later. + * section 5.2.2 of + * RFC 3986. NOTE: On success you have to call uriFreeUriMembersMmA on \p absoluteDest + * manually later. * * @param absoluteDest OUT: Result %URI * @param relativeSource IN: Reference to resolve @@ -590,11 +548,10 @@ URI_PUBLIC int URI_FUNC(AddBaseUriEx)(URI_TYPE(Uri) * absoluteDest, * @since 0.9.0 */ URI_PUBLIC int URI_FUNC(AddBaseUriExMm)(URI_TYPE(Uri) * absoluteDest, - const URI_TYPE(Uri) * relativeSource, - const URI_TYPE(Uri) * absoluteBase, - UriResolutionOptions options, UriMemoryManager * memory); - - + const URI_TYPE(Uri) * relativeSource, + const URI_TYPE(Uri) * absoluteBase, + UriResolutionOptions options, + UriMemoryManager * memory); /** * Tries to make a relative %URI (a reference) from an @@ -619,11 +576,9 @@ URI_PUBLIC int URI_FUNC(AddBaseUriExMm)(URI_TYPE(Uri) * absoluteDest, * @since 0.5.2 */ URI_PUBLIC int URI_FUNC(RemoveBaseUri)(URI_TYPE(Uri) * dest, - const URI_TYPE(Uri) * absoluteSource, - const URI_TYPE(Uri) * absoluteBase, - UriBool domainRootMode); - - + const URI_TYPE(Uri) * absoluteSource, + const URI_TYPE(Uri) * absoluteBase, + UriBool domainRootMode); /** * Tries to make a relative %URI (a reference) from an @@ -648,11 +603,10 @@ URI_PUBLIC int URI_FUNC(RemoveBaseUri)(URI_TYPE(Uri) * dest, * @since 0.9.0 */ URI_PUBLIC int URI_FUNC(RemoveBaseUriMm)(URI_TYPE(Uri) * dest, - const URI_TYPE(Uri) * absoluteSource, - const URI_TYPE(Uri) * absoluteBase, - UriBool domainRootMode, UriMemoryManager * memory); - - + const URI_TYPE(Uri) * absoluteSource, + const URI_TYPE(Uri) * absoluteBase, + UriBool domainRootMode, + UriMemoryManager * memory); /** * Checks two URIs for equivalence. Comparison is done @@ -665,10 +619,7 @@ URI_PUBLIC int URI_FUNC(RemoveBaseUriMm)(URI_TYPE(Uri) * dest, * * @since 0.4.0 */ -URI_PUBLIC UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, - const URI_TYPE(Uri) * b); - - +URI_PUBLIC UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b); /** * Calculates the number of characters needed to store the @@ -676,38 +627,39 @@ URI_PUBLIC UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, * terminator. * * @param uri IN: %URI to measure - * @param charsRequired OUT: Length of the string representation in characters excluding terminator + * @param charsRequired OUT: Length of the string representation in characters + * excluding terminator * @return Error code or 0 on success * * @see uriToStringA * @since 0.5.0 */ URI_PUBLIC int URI_FUNC(ToStringCharsRequired)(const URI_TYPE(Uri) * uri, - int * charsRequired); - - + int * charsRequired); /** * Converts a %URI structure back to text as described in - * section 5.3 of RFC 3986. + * section 5.3 of RFC + * 3986. * * NOTE: Scheme-based normalization - * (section 6.2.3 of RFC 3986) - * is not applied and is considered a responsibility of the application using uriparser. + * (section 6.2.3 of + * RFC 3986) is not applied and is considered a responsibility of the application + * using uriparser. * * @param dest OUT: Output destination * @param uri IN: %URI to convert - * @param maxChars IN: Maximum number of characters to copy including terminator - * @param charsWritten OUT: Number of characters written, can be lower than maxChars even if the %URI is too long! + * @param maxChars IN: Maximum number of characters to copy including + * terminator + * @param charsWritten OUT: Number of characters written, can be lower than + * maxChars even if the %URI is too long! * @return Error code or 0 on success * * @see uriToStringCharsRequiredA * @since 0.4.0 */ URI_PUBLIC int URI_FUNC(ToString)(URI_CHAR * dest, const URI_TYPE(Uri) * uri, - int maxChars, int * charsWritten); - - + int maxChars, int * charsWritten); /** * Copies a %URI structure. @@ -721,9 +673,8 @@ URI_PUBLIC int URI_FUNC(ToString)(URI_CHAR * dest, const URI_TYPE(Uri) * uri, * @since 0.9.9 */ URI_PUBLIC int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, - const URI_TYPE(Uri) * sourceUri, UriMemoryManager * memory); - - + const URI_TYPE(Uri) * sourceUri, + UriMemoryManager * memory); /** * Copies a %URI structure. @@ -735,9 +686,8 @@ URI_PUBLIC int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, * @see uriCopyUriMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(CopyUri)(URI_TYPE(Uri) * destUri, const URI_TYPE(Uri) * sourceUri); - - +URI_PUBLIC int URI_FUNC(CopyUri)(URI_TYPE(Uri) * destUri, + const URI_TYPE(Uri) * sourceUri); /** * Determines the components of a %URI that are not normalized. @@ -750,12 +700,10 @@ URI_PUBLIC int URI_FUNC(CopyUri)(URI_TYPE(Uri) * destUri, const URI_TYPE(Uri) * * @see uriNormalizeSyntaxExMmA * @see uriNormalizeSyntaxMaskRequiredExA * @since 0.5.0 - * @deprecated Deprecated since 0.9.0, please migrate to uriNormalizeSyntaxMaskRequiredExA (with "Ex"). + * @deprecated Deprecated since 0.9.0, please migrate to uriNormalizeSyntaxMaskRequiredExA + * (with "Ex"). */ -URI_PUBLIC unsigned int URI_FUNC(NormalizeSyntaxMaskRequired)( - const URI_TYPE(Uri) * uri); - - +URI_PUBLIC unsigned int URI_FUNC(NormalizeSyntaxMaskRequired)(const URI_TYPE(Uri) * uri); /** * Determines the components of a %URI that are not normalized. @@ -770,10 +718,8 @@ URI_PUBLIC unsigned int URI_FUNC(NormalizeSyntaxMaskRequired)( * @see uriNormalizeSyntaxMaskRequiredA * @since 0.9.0 */ -URI_PUBLIC int URI_FUNC(NormalizeSyntaxMaskRequiredEx)( - const URI_TYPE(Uri) * uri, unsigned int * outMask); - - +URI_PUBLIC int URI_FUNC(NormalizeSyntaxMaskRequiredEx)(const URI_TYPE(Uri) * uri, + unsigned int * outMask); /** * Normalizes a %URI using a normalization mask. @@ -792,10 +738,7 @@ URI_PUBLIC int URI_FUNC(NormalizeSyntaxMaskRequiredEx)( * @see uriNormalizeSyntaxMaskRequiredA * @since 0.5.0 */ -URI_PUBLIC int URI_FUNC(NormalizeSyntaxEx)(URI_TYPE(Uri) * uri, - unsigned int mask); - - +URI_PUBLIC int URI_FUNC(NormalizeSyntaxEx)(URI_TYPE(Uri) * uri, unsigned int mask); /** * Normalizes a %URI using a normalization mask. @@ -814,10 +757,8 @@ URI_PUBLIC int URI_FUNC(NormalizeSyntaxEx)(URI_TYPE(Uri) * uri, * @see uriNormalizeSyntaxMaskRequiredA * @since 0.9.0 */ -URI_PUBLIC int URI_FUNC(NormalizeSyntaxExMm)(URI_TYPE(Uri) * uri, - unsigned int mask, UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(NormalizeSyntaxExMm)(URI_TYPE(Uri) * uri, unsigned int mask, + UriMemoryManager * memory); /** * Normalizes all components of a %URI. @@ -836,8 +777,6 @@ URI_PUBLIC int URI_FUNC(NormalizeSyntaxExMm)(URI_TYPE(Uri) * uri, */ URI_PUBLIC int URI_FUNC(NormalizeSyntax)(URI_TYPE(Uri) * uri); - - /** * Converts a Unix filename to a %URI string. * The destination buffer must be large enough to hold 7 + 3 * len(filename) + 1 @@ -857,9 +796,7 @@ URI_PUBLIC int URI_FUNC(NormalizeSyntax)(URI_TYPE(Uri) * uri); * @since 0.5.2 */ URI_PUBLIC int URI_FUNC(UnixFilenameToUriString)(const URI_CHAR * filename, - URI_CHAR * uriString); - - + URI_CHAR * uriString); /** * Converts a Windows filename to a %URI string. @@ -880,9 +817,7 @@ URI_PUBLIC int URI_FUNC(UnixFilenameToUriString)(const URI_CHAR * filename, * @since 0.5.2 */ URI_PUBLIC int URI_FUNC(WindowsFilenameToUriString)(const URI_CHAR * filename, - URI_CHAR * uriString); - - + URI_CHAR * uriString); /** * Extracts a Unix filename from a %URI string. @@ -899,9 +834,7 @@ URI_PUBLIC int URI_FUNC(WindowsFilenameToUriString)(const URI_CHAR * filename, * @since 0.5.2 */ URI_PUBLIC int URI_FUNC(UriStringToUnixFilename)(const URI_CHAR * uriString, - URI_CHAR * filename); - - + URI_CHAR * filename); /** * Extracts a Windows filename from a %URI string. @@ -918,9 +851,7 @@ URI_PUBLIC int URI_FUNC(UriStringToUnixFilename)(const URI_CHAR * uriString, * @since 0.5.2 */ URI_PUBLIC int URI_FUNC(UriStringToWindowsFilename)(const URI_CHAR * uriString, - URI_CHAR * filename); - - + URI_CHAR * filename); /** * Calculates the number of characters needed to store the @@ -929,17 +860,16 @@ URI_PUBLIC int URI_FUNC(UriStringToWindowsFilename)(const URI_CHAR * uriString, * normalized to "%0D%0A". * * @param queryList IN: Query list to measure - * @param charsRequired OUT: Length of the string representation in characters excluding terminator + * @param charsRequired OUT: Length of the string representation in characters + * excluding terminator * @return Error code or 0 on success * * @see uriComposeQueryCharsRequiredExA * @see uriComposeQueryA * @since 0.7.0 */ -URI_PUBLIC int URI_FUNC(ComposeQueryCharsRequired)( - const URI_TYPE(QueryList) * queryList, int * charsRequired); - - +URI_PUBLIC int URI_FUNC(ComposeQueryCharsRequired)(const URI_TYPE(QueryList) * queryList, + int * charsRequired); /** * Calculates the number of characters needed to store the @@ -947,7 +877,8 @@ URI_PUBLIC int URI_FUNC(ComposeQueryCharsRequired)( * terminator. * * @param queryList IN: Query list to measure - * @param charsRequired OUT: Length of the string representation in characters excluding terminator + * @param charsRequired OUT: Length of the string representation in characters + * excluding terminator * @param spaceToPlus IN: Whether to convert ' ' to '+' or not * @param normalizeBreaks IN: Whether to convert CR and LF to CR-LF or not. * @return Error code or 0 on success @@ -956,11 +887,10 @@ URI_PUBLIC int URI_FUNC(ComposeQueryCharsRequired)( * @see uriComposeQueryExA * @since 0.7.0 */ -URI_PUBLIC int URI_FUNC(ComposeQueryCharsRequiredEx)( - const URI_TYPE(QueryList) * queryList, - int * charsRequired, UriBool spaceToPlus, UriBool normalizeBreaks); - - +URI_PUBLIC int + URI_FUNC(ComposeQueryCharsRequiredEx)(const URI_TYPE(QueryList) * queryList, + int * charsRequired, UriBool spaceToPlus, + UriBool normalizeBreaks); /** * Converts a query list structure back to a query string. @@ -970,8 +900,10 @@ URI_PUBLIC int URI_FUNC(ComposeQueryCharsRequiredEx)( * * @param dest OUT: Output destination * @param queryList IN: Query list to convert - * @param maxChars IN: Maximum number of characters to copy including terminator - * @param charsWritten OUT: Number of characters written, can be lower than maxChars even if the query list is too long! + * @param maxChars IN: Maximum number of characters to copy + * including terminator + * @param charsWritten OUT: Number of characters written, can be lower than + * maxChars even if the query list is too long! * @return Error code or 0 on success * * @see uriComposeQueryExA @@ -985,9 +917,8 @@ URI_PUBLIC int URI_FUNC(ComposeQueryCharsRequiredEx)( * @since 0.7.0 */ URI_PUBLIC int URI_FUNC(ComposeQuery)(URI_CHAR * dest, - const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten); - - + const URI_TYPE(QueryList) * queryList, int maxChars, + int * charsWritten); /** * Converts a query list structure back to a query string. @@ -995,8 +926,10 @@ URI_PUBLIC int URI_FUNC(ComposeQuery)(URI_CHAR * dest, * * @param dest OUT: Output destination * @param queryList IN: Query list to convert - * @param maxChars IN: Maximum number of characters to copy including terminator - * @param charsWritten OUT: Number of characters written, can be lower than maxChars even if the query list is too long! + * @param maxChars IN: Maximum number of characters to copy + * including terminator + * @param charsWritten OUT: Number of characters written, can be lower than + * maxChars even if the query list is too long! * @param spaceToPlus IN: Whether to convert ' ' to '+' or not * @param normalizeBreaks IN: Whether to convert CR and LF to CR-LF or not. * @return Error code or 0 on success @@ -1012,10 +945,9 @@ URI_PUBLIC int URI_FUNC(ComposeQuery)(URI_CHAR * dest, * @since 0.7.0 */ URI_PUBLIC int URI_FUNC(ComposeQueryEx)(URI_CHAR * dest, - const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten, - UriBool spaceToPlus, UriBool normalizeBreaks); - - + const URI_TYPE(QueryList) * queryList, + int maxChars, int * charsWritten, + UriBool spaceToPlus, UriBool normalizeBreaks); /** * Converts a query list structure back to a query string. @@ -1038,9 +970,7 @@ URI_PUBLIC int URI_FUNC(ComposeQueryEx)(URI_CHAR * dest, * @since 0.7.0 */ URI_PUBLIC int URI_FUNC(ComposeQueryMalloc)(URI_CHAR ** dest, - const URI_TYPE(QueryList) * queryList); - - + const URI_TYPE(QueryList) * queryList); /** * Converts a query list structure back to a query string. @@ -1063,10 +993,9 @@ URI_PUBLIC int URI_FUNC(ComposeQueryMalloc)(URI_CHAR ** dest, * @since 0.7.0 */ URI_PUBLIC int URI_FUNC(ComposeQueryMallocEx)(URI_CHAR ** dest, - const URI_TYPE(QueryList) * queryList, - UriBool spaceToPlus, UriBool normalizeBreaks); - - + const URI_TYPE(QueryList) * queryList, + UriBool spaceToPlus, + UriBool normalizeBreaks); /** * Converts a query list structure back to a query string. @@ -1089,11 +1018,10 @@ URI_PUBLIC int URI_FUNC(ComposeQueryMallocEx)(URI_CHAR ** dest, * @since 0.9.0 */ URI_PUBLIC int URI_FUNC(ComposeQueryMallocExMm)(URI_CHAR ** dest, - const URI_TYPE(QueryList) * queryList, - UriBool spaceToPlus, UriBool normalizeBreaks, - UriMemoryManager * memory); - - + const URI_TYPE(QueryList) * queryList, + UriBool spaceToPlus, + UriBool normalizeBreaks, + UriMemoryManager * memory); /** * Constructs a query list from the raw query string of a given URI. @@ -1113,10 +1041,9 @@ URI_PUBLIC int URI_FUNC(ComposeQueryMallocExMm)(URI_CHAR ** dest, * @see uriFreeQueryListMmA * @since 0.7.0 */ -URI_PUBLIC int URI_FUNC(DissectQueryMalloc)(URI_TYPE(QueryList) ** dest, - int * itemCount, const URI_CHAR * first, const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(DissectQueryMalloc)(URI_TYPE(QueryList) * *dest, int * itemCount, + const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Constructs a query list from the raw query string of a given URI. @@ -1136,11 +1063,11 @@ URI_PUBLIC int URI_FUNC(DissectQueryMalloc)(URI_TYPE(QueryList) ** dest, * @see uriFreeQueryListA * @since 0.7.0 */ -URI_PUBLIC int URI_FUNC(DissectQueryMallocEx)(URI_TYPE(QueryList) ** dest, - int * itemCount, const URI_CHAR * first, const URI_CHAR * afterLast, - UriBool plusToSpace, UriBreakConversion breakConversion); - - +URI_PUBLIC int URI_FUNC(DissectQueryMallocEx)(URI_TYPE(QueryList) * *dest, + int * itemCount, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriBool plusToSpace, + UriBreakConversion breakConversion); /** * Constructs a query list from the raw query string of a given URI. @@ -1161,12 +1088,12 @@ URI_PUBLIC int URI_FUNC(DissectQueryMallocEx)(URI_TYPE(QueryList) ** dest, * @see uriFreeQueryListMmA * @since 0.9.0 */ -URI_PUBLIC int URI_FUNC(DissectQueryMallocExMm)(URI_TYPE(QueryList) ** dest, - int * itemCount, const URI_CHAR * first, const URI_CHAR * afterLast, - UriBool plusToSpace, UriBreakConversion breakConversion, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(DissectQueryMallocExMm)(URI_TYPE(QueryList) * *dest, + int * itemCount, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriBool plusToSpace, + UriBreakConversion breakConversion, + UriMemoryManager * memory); /** * Frees all memory associated with the given query list. @@ -1179,8 +1106,6 @@ URI_PUBLIC int URI_FUNC(DissectQueryMallocExMm)(URI_TYPE(QueryList) ** dest, */ URI_PUBLIC void URI_FUNC(FreeQueryList)(URI_TYPE(QueryList) * queryList); - - /** * Frees all memory associated with the given query list. * The structure itself is freed as well. @@ -1193,9 +1118,7 @@ URI_PUBLIC void URI_FUNC(FreeQueryList)(URI_TYPE(QueryList) * queryList); * @since 0.9.0 */ URI_PUBLIC int URI_FUNC(FreeQueryListMm)(URI_TYPE(QueryList) * queryList, - UriMemoryManager * memory); - - + UriMemoryManager * memory); /** * Makes the %URI hold copies of strings so that it no longer depends @@ -1212,8 +1135,6 @@ URI_PUBLIC int URI_FUNC(FreeQueryListMm)(URI_TYPE(QueryList) * queryList, */ URI_PUBLIC int URI_FUNC(MakeOwner)(URI_TYPE(Uri) * uri); - - /** * Makes the %URI hold copies of strings so that it no longer depends * on the original %URI string. If the %URI is already owner of copies, @@ -1226,10 +1147,7 @@ URI_PUBLIC int URI_FUNC(MakeOwner)(URI_TYPE(Uri) * uri); * @see uriMakeOwnerA * @since 0.9.4 */ -URI_PUBLIC int URI_FUNC(MakeOwnerMm)(URI_TYPE(Uri) * uri, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(MakeOwnerMm)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); /** * Determines if the given text range contains a well-formed fragment @@ -1237,7 +1155,8 @@ URI_PUBLIC int URI_FUNC(MakeOwnerMm)(URI_TYPE(Uri) * uri, * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in - * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * @return URI_TRUE if non-NULL and well-formed, else + * URI_FALSE * * @see uriIsWellFormedHostIp4A * @see uriIsWellFormedHostIp6A @@ -1252,9 +1171,8 @@ URI_PUBLIC int URI_FUNC(MakeOwnerMm)(URI_TYPE(Uri) * uri, * @see uriSetFragmentMmA * @since 0.9.9 */ -URI_PUBLIC UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, const URI_CHAR * afterLast); - - +URI_PUBLIC UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Determines if the given text range contains a well-formed IPv4 address @@ -1262,7 +1180,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, const * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in - * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * @return URI_TRUE if non-NULL and well-formed, else + * URI_FALSE * * @see uriIsWellFormedFragmentA * @see uriIsWellFormedHostIp6A @@ -1277,9 +1196,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, const * @see uriSetHostIp4MmA * @since 0.9.9 */ -URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostIp4)(const URI_CHAR * first, const URI_CHAR * afterLast); - - +URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostIp4)(const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Determines if the given text range contains a well-formed IPv6 address @@ -1289,7 +1207,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostIp4)(const URI_CHAR * first, const U * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in - * @return URI_SUCCESS if non-NULL and well-formed, else an error code + * @return URI_SUCCESS if non-NULL and well-formed, else an error + * code * * @see uriIsWellFormedFragmentA * @see uriIsWellFormedHostIp4A @@ -1307,8 +1226,6 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostIp4)(const URI_CHAR * first, const U */ int URI_FUNC(IsWellFormedHostIp6)(const URI_CHAR * first, const URI_CHAR * afterLast); - - /** * Determines if the given text range contains a well-formed IPv6 address * according to RFC 3986 or not. @@ -1316,7 +1233,8 @@ int URI_FUNC(IsWellFormedHostIp6)(const URI_CHAR * first, const URI_CHAR * after * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in * @param memory IN: Memory manager to use, NULL for default libc - * @return URI_SUCCESS if non-NULL and well-formed, else an error code + * @return URI_SUCCESS if non-NULL and well-formed, else an error + * code * * @see uriIsWellFormedFragmentA * @see uriIsWellFormedHostIp4A @@ -1330,9 +1248,8 @@ int URI_FUNC(IsWellFormedHostIp6)(const URI_CHAR * first, const URI_CHAR * after * @see uriIsWellFormedUserInfoA * @since 0.9.9 */ -int URI_FUNC(IsWellFormedHostIp6Mm)(const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); - - +int URI_FUNC(IsWellFormedHostIp6Mm)(const URI_CHAR * first, const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Determines if the given text range contains a well-formed IPvFuture address @@ -1342,7 +1259,8 @@ int URI_FUNC(IsWellFormedHostIp6Mm)(const URI_CHAR * first, const URI_CHAR * aft * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in - * @return URI_SUCCESS if non-NULL and well-formed, else an error code + * @return URI_SUCCESS if non-NULL and well-formed, else an error + * code * * @see uriIsWellFormedFragmentA * @see uriIsWellFormedHostIp4A @@ -1358,9 +1276,8 @@ int URI_FUNC(IsWellFormedHostIp6Mm)(const URI_CHAR * first, const URI_CHAR * aft * @see uriSetHostIpFutureMmA * @since 0.9.9 */ -int URI_FUNC(IsWellFormedHostIpFuture)(const URI_CHAR * first, const URI_CHAR * afterLast); - - +int URI_FUNC(IsWellFormedHostIpFuture)(const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Determines if the given text range contains a well-formed IPvFuture address @@ -1369,7 +1286,8 @@ int URI_FUNC(IsWellFormedHostIpFuture)(const URI_CHAR * first, const URI_CHAR * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in * @param memory IN: Memory manager to use, NULL for default libc - * @return URI_SUCCESS if non-NULL and well-formed, else an error code + * @return URI_SUCCESS if non-NULL and well-formed, else an error + * code * * @see uriIsWellFormedFragmentA * @see uriIsWellFormedHostIp4A @@ -1385,9 +1303,9 @@ int URI_FUNC(IsWellFormedHostIpFuture)(const URI_CHAR * first, const URI_CHAR * * @see uriSetHostIpFutureMmA * @since 0.9.9 */ -int URI_FUNC(IsWellFormedHostIpFutureMm)(const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); - - +int URI_FUNC(IsWellFormedHostIpFutureMm)(const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Determines if the given text range contains a well-formed registered host name @@ -1395,7 +1313,8 @@ int URI_FUNC(IsWellFormedHostIpFutureMm)(const URI_CHAR * first, const URI_CHAR * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in - * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * @return URI_TRUE if non-NULL and well-formed, else + * URI_FALSE * * @see uriIsWellFormedFragmentA * @see uriIsWellFormedHostIp4A @@ -1410,9 +1329,8 @@ int URI_FUNC(IsWellFormedHostIpFutureMm)(const URI_CHAR * first, const URI_CHAR * @see uriSetHostRegNameMmA * @since 0.9.9 */ -URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, const URI_CHAR * afterLast); - - +URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Determines if the given text range contains a well-formed path @@ -1420,8 +1338,10 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, con * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in - * @param hasHost IN: Whether the target %URI has a non-NULL host set or not - * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * @param hasHost IN: Whether the target %URI has a non-NULL host set or + * not + * @return URI_TRUE if non-NULL and well-formed, else + * URI_FALSE * * @see uriIsWellFormedFragmentA * @see uriIsWellFormedHostIp4A @@ -1437,9 +1357,9 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, con * @see uriSetPathMmA * @since 0.9.9 */ -URI_PUBLIC UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, const URI_CHAR * afterLast, UriBool hasHost); - - +URI_PUBLIC UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, + const URI_CHAR * afterLast, + UriBool hasHost); /** * Determines if the given text range contains a well-formed port text @@ -1447,7 +1367,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, const URI_ * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in - * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * @return URI_TRUE if non-NULL and well-formed, else + * URI_FALSE * * @see uriIsWellFormedFragmentA * @see uriIsWellFormedHostIp4A @@ -1462,9 +1383,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, const URI_ * @see uriSetPortTextMmA * @since 0.9.9 */ -URI_PUBLIC UriBool URI_FUNC(IsWellFormedPort)(const URI_CHAR * first, const URI_CHAR * afterLast); - - +URI_PUBLIC UriBool URI_FUNC(IsWellFormedPort)(const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Determines if the given text range contains a well-formed query @@ -1472,7 +1392,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedPort)(const URI_CHAR * first, const URI_ * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in - * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * @return URI_TRUE if non-NULL and well-formed, else + * URI_FALSE * * @see uriIsWellFormedFragmentA * @see uriIsWellFormedHostIp4A @@ -1487,8 +1408,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedPort)(const URI_CHAR * first, const URI_ * @see uriSetQueryMmA * @since 0.9.9 */ -URI_PUBLIC UriBool URI_FUNC(IsWellFormedQuery)(const URI_CHAR * first, const URI_CHAR * afterLast); - +URI_PUBLIC UriBool URI_FUNC(IsWellFormedQuery)(const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Determines if the given text range contains a well-formed scheme @@ -1496,7 +1417,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedQuery)(const URI_CHAR * first, const URI * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in - * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * @return URI_TRUE if non-NULL and well-formed, else + * URI_FALSE * * @see uriIsWellFormedFragmentA * @see uriIsWellFormedHostIp4A @@ -1512,9 +1434,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedQuery)(const URI_CHAR * first, const URI * @see uriSetSchemeMmA * @since 0.9.9 */ -URI_PUBLIC UriBool URI_FUNC(IsWellFormedScheme)(const URI_CHAR * first, const URI_CHAR * afterLast); - - +URI_PUBLIC UriBool URI_FUNC(IsWellFormedScheme)(const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Determines if the given text range contains a well-formed user info @@ -1522,7 +1443,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedScheme)(const URI_CHAR * first, const UR * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in - * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * @return URI_TRUE if non-NULL and well-formed, else + * URI_FALSE * * @see uriIsWellFormedFragmentA * @see uriIsWellFormedHostIp4A @@ -1537,9 +1459,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedScheme)(const URI_CHAR * first, const UR * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, const URI_CHAR * afterLast); - - +URI_PUBLIC UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the fragment of the given %URI to the given value. @@ -1557,7 +1478,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, const * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @return Error code or 0 on success * * @see uriIsWellFormedFragmentA @@ -1573,11 +1495,8 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, const * @see uriSetUserInfoA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetFragment)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(SetFragment)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the fragment of the given %URI to the given value. @@ -1593,7 +1512,8 @@ URI_PUBLIC int URI_FUNC(SetFragment)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @param memory IN: Memory manager to use, NULL for default libc * @return Error code or 0 on success * @@ -1610,12 +1530,9 @@ URI_PUBLIC int URI_FUNC(SetFragment)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetFragmentMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(SetFragmentMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Sets the host of the given %URI to the given value. @@ -1633,7 +1550,8 @@ URI_PUBLIC int URI_FUNC(SetFragmentMm)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @return Error code or 0 on success * * @see uriIsWellFormedHostIp4A @@ -1654,11 +1572,8 @@ URI_PUBLIC int URI_FUNC(SetFragmentMm)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetHostAuto)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(SetHostAuto)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the host of the given %URI to the given value. @@ -1674,7 +1589,8 @@ URI_PUBLIC int URI_FUNC(SetHostAuto)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @param memory IN: Memory manager to use, NULL for default libc * @return Error code or 0 on success * @@ -1696,12 +1612,9 @@ URI_PUBLIC int URI_FUNC(SetHostAuto)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetHostAutoMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(SetHostAutoMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Sets the host of the given %URI to the given value. @@ -1719,7 +1632,8 @@ URI_PUBLIC int URI_FUNC(SetHostAutoMm)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @return Error code or 0 on success * * @see uriIsWellFormedHostRegNameA @@ -1735,11 +1649,8 @@ URI_PUBLIC int URI_FUNC(SetHostAutoMm)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetHostRegName)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(SetHostRegName)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the host of the given %URI to the given value. @@ -1755,7 +1666,8 @@ URI_PUBLIC int URI_FUNC(SetHostRegName)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @param memory IN: Memory manager to use, NULL for default libc * @return Error code or 0 on success * @@ -1772,12 +1684,9 @@ URI_PUBLIC int URI_FUNC(SetHostRegName)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetHostRegNameMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(SetHostRegNameMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Sets the host of the given %URI to the given value. @@ -1795,7 +1704,8 @@ URI_PUBLIC int URI_FUNC(SetHostRegNameMm)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @return Error code or 0 on success * * @see uriIsWellFormedHostIp4A @@ -1811,11 +1721,8 @@ URI_PUBLIC int URI_FUNC(SetHostRegNameMm)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetHostIp4)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(SetHostIp4)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the host of the given %URI to the given value. @@ -1831,7 +1738,8 @@ URI_PUBLIC int URI_FUNC(SetHostIp4)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @param memory IN: Memory manager to use, NULL for default libc * @return Error code or 0 on success * @@ -1848,12 +1756,9 @@ URI_PUBLIC int URI_FUNC(SetHostIp4)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetHostIp4Mm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(SetHostIp4Mm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Sets the host of the given %URI to the given value. @@ -1871,7 +1776,8 @@ URI_PUBLIC int URI_FUNC(SetHostIp4Mm)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @return Error code or 0 on success * * @see uriIsWellFormedHostIp6A @@ -1887,11 +1793,8 @@ URI_PUBLIC int URI_FUNC(SetHostIp4Mm)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetHostIp6)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(SetHostIp6)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the host of the given %URI to the given value. @@ -1907,7 +1810,8 @@ URI_PUBLIC int URI_FUNC(SetHostIp6)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @param memory IN: Memory manager to use, NULL for default libc * @return Error code or 0 on success * @@ -1924,12 +1828,9 @@ URI_PUBLIC int URI_FUNC(SetHostIp6)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetHostIp6Mm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(SetHostIp6Mm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Sets the host of the given %URI to the given value. @@ -1947,7 +1848,8 @@ URI_PUBLIC int URI_FUNC(SetHostIp6Mm)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @return Error code or 0 on success * * @see uriIsWellFormedHostIp4A @@ -1963,11 +1865,8 @@ URI_PUBLIC int URI_FUNC(SetHostIp6Mm)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetHostIpFuture)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(SetHostIpFuture)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the host of the given %URI to the given value. @@ -1983,7 +1882,8 @@ URI_PUBLIC int URI_FUNC(SetHostIpFuture)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @param memory IN: Memory manager to use, NULL for default libc * @return Error code or 0 on success * @@ -2000,12 +1900,9 @@ URI_PUBLIC int URI_FUNC(SetHostIpFuture)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetHostIpFutureMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(SetHostIpFutureMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Sets the path of the given %URI to the given value. @@ -2025,7 +1922,8 @@ URI_PUBLIC int URI_FUNC(SetHostIpFutureMm)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @return Error code or 0 on success * * @see uriIsWellFormedPathA @@ -2042,11 +1940,8 @@ URI_PUBLIC int URI_FUNC(SetHostIpFutureMm)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetPath)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(SetPath)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the path of the given %URI to the given value. @@ -2066,7 +1961,8 @@ URI_PUBLIC int URI_FUNC(SetPath)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @param memory IN: Memory manager to use, NULL for default libc * @return Error code or 0 on success * @@ -2084,12 +1980,8 @@ URI_PUBLIC int URI_FUNC(SetPath)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetPathMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(SetPathMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory); /** * Sets the port text of the given %URI to the given value. @@ -2109,7 +2001,8 @@ URI_PUBLIC int URI_FUNC(SetPathMm)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @return Error code or 0 on success * * @see uriIsWellFormedPortA @@ -2125,11 +2018,8 @@ URI_PUBLIC int URI_FUNC(SetPathMm)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetPortText)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(SetPortText)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the port text of the given %URI to the given value. @@ -2147,7 +2037,8 @@ URI_PUBLIC int URI_FUNC(SetPortText)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @param memory IN: Memory manager to use, NULL for default libc * @return Error code or 0 on success * @@ -2164,12 +2055,9 @@ URI_PUBLIC int URI_FUNC(SetPortText)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetPortTextMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(SetPortTextMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Sets the query of the given %URI to the given value. @@ -2187,7 +2075,8 @@ URI_PUBLIC int URI_FUNC(SetPortTextMm)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @return Error code or 0 on success * * @see uriIsWellFormedQueryA @@ -2203,11 +2092,8 @@ URI_PUBLIC int URI_FUNC(SetPortTextMm)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetQuery)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(SetQuery)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the query of the given %URI to the given value. @@ -2223,7 +2109,8 @@ URI_PUBLIC int URI_FUNC(SetQuery)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @param memory IN: Memory manager to use, NULL for default libc * @return Error code or 0 on success * @@ -2240,12 +2127,9 @@ URI_PUBLIC int URI_FUNC(SetQuery)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetQueryMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(SetQueryMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Sets the scheme of the given %URI to the given value. @@ -2263,7 +2147,8 @@ URI_PUBLIC int URI_FUNC(SetQueryMm)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @return Error code or 0 on success * * @see uriIsWellFormedSchemeA @@ -2279,11 +2164,8 @@ URI_PUBLIC int URI_FUNC(SetQueryMm)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetScheme)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(SetScheme)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the scheme of the given %URI to the given value. @@ -2299,7 +2181,8 @@ URI_PUBLIC int URI_FUNC(SetScheme)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @param memory IN: Memory manager to use, NULL for default libc * @return Error code or 0 on success * @@ -2316,12 +2199,9 @@ URI_PUBLIC int URI_FUNC(SetScheme)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetSchemeMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(SetSchemeMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Sets the user info of the given %URI to the given value. @@ -2341,7 +2221,8 @@ URI_PUBLIC int URI_FUNC(SetSchemeMm)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @return Error code or 0 on success * * @see uriIsWellFormedUserInfoA @@ -2357,11 +2238,8 @@ URI_PUBLIC int URI_FUNC(SetSchemeMm)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoMmA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetUserInfo)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast); - - +URI_PUBLIC int URI_FUNC(SetUserInfo)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast); /** * Sets the user info of the given %URI to the given value. @@ -2379,7 +2257,8 @@ URI_PUBLIC int URI_FUNC(SetUserInfo)(URI_TYPE(Uri) * uri, * * @param uri INOUT: %URI to modify * @param first IN: Pointer to first character, can be NULL - * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be + * NULL * @param memory IN: Memory manager to use, NULL for default libc * @return Error code or 0 on success * @@ -2396,12 +2275,9 @@ URI_PUBLIC int URI_FUNC(SetUserInfo)(URI_TYPE(Uri) * uri, * @see uriSetUserInfoA * @since 0.9.9 */ -URI_PUBLIC int URI_FUNC(SetUserInfoMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - +URI_PUBLIC int URI_FUNC(SetUserInfoMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); /** * Obtain the base runtime version of uriparser. @@ -2425,13 +2301,9 @@ URI_PUBLIC int URI_FUNC(SetUserInfoMm)(URI_TYPE(Uri) * uri, */ URI_PUBLIC const URI_CHAR * URI_FUNC(BaseRuntimeVersion)(void); - - -#ifdef __cplusplus +# ifdef __cplusplus } -#endif - +# endif - -#endif +# endif #endif diff --git a/ext/uri/uriparser/include/uriparser/UriBase.h b/ext/uri/uriparser/include/uriparser/UriBase.h index 92883278cff26..f8256951fe34a 100644 --- a/ext/uri/uriparser/include/uriparser/UriBase.h +++ b/ext/uri/uriparser/include/uriparser/UriBase.h @@ -43,152 +43,136 @@ */ #ifndef URI_BASE_H -#define URI_BASE_H 1 - - +# define URI_BASE_H 1 /* Version helper macro */ -#define URI_ANSI_TO_UNICODE_HELPER(x) L ## x -#define URI_ANSI_TO_UNICODE(x) URI_ANSI_TO_UNICODE_HELPER(x) - - +# define URI_ANSI_TO_UNICODE_HELPER(x) L##x +# define URI_ANSI_TO_UNICODE(x) URI_ANSI_TO_UNICODE_HELPER(x) /* Version */ -#define URI_VER_MAJOR 0 -#define URI_VER_MINOR 9 -#define URI_VER_RELEASE 9 -#define URI_VER_SUFFIX_ANSI "" -#define URI_VER_SUFFIX_UNICODE URI_ANSI_TO_UNICODE(URI_VER_SUFFIX_ANSI) - - +# define URI_VER_MAJOR 0 +# define URI_VER_MINOR 9 +# define URI_VER_RELEASE 9 +# define URI_VER_SUFFIX_ANSI "" +# define URI_VER_SUFFIX_UNICODE URI_ANSI_TO_UNICODE(URI_VER_SUFFIX_ANSI) /* More version helper macros */ -#define URI_INT_TO_ANSI_HELPER(x) #x -#define URI_INT_TO_ANSI(x) URI_INT_TO_ANSI_HELPER(x) +# define URI_INT_TO_ANSI_HELPER(x) #x +# define URI_INT_TO_ANSI(x) URI_INT_TO_ANSI_HELPER(x) -#define URI_INT_TO_UNICODE_HELPER(x) URI_ANSI_TO_UNICODE(#x) -#define URI_INT_TO_UNICODE(x) URI_INT_TO_UNICODE_HELPER(x) - -#define URI_VER_ANSI_HELPER(ma, mi, r, s) \ - URI_INT_TO_ANSI(ma) "." \ - URI_INT_TO_ANSI(mi) "." \ - URI_INT_TO_ANSI(r) \ - s - -#define URI_VER_UNICODE_HELPER(ma, mi, r, s) \ - URI_INT_TO_UNICODE(ma) L"." \ - URI_INT_TO_UNICODE(mi) L"." \ - URI_INT_TO_UNICODE(r) \ - s +# define URI_INT_TO_UNICODE_HELPER(x) URI_ANSI_TO_UNICODE(#x) +# define URI_INT_TO_UNICODE(x) URI_INT_TO_UNICODE_HELPER(x) +# define URI_VER_ANSI_HELPER(ma, mi, r, s) \ + URI_INT_TO_ANSI(ma) "." URI_INT_TO_ANSI(mi) "." URI_INT_TO_ANSI(r) s +# define URI_VER_UNICODE_HELPER(ma, mi, r, s) \ + URI_INT_TO_UNICODE(ma) L"." URI_INT_TO_UNICODE(mi) L"." URI_INT_TO_UNICODE(r) s /* Full version strings */ -#define URI_VER_ANSI URI_VER_ANSI_HELPER(URI_VER_MAJOR, URI_VER_MINOR, URI_VER_RELEASE, URI_VER_SUFFIX_ANSI) -#define URI_VER_UNICODE URI_VER_UNICODE_HELPER(URI_VER_MAJOR, URI_VER_MINOR, URI_VER_RELEASE, URI_VER_SUFFIX_UNICODE) - - +# define URI_VER_ANSI \ + URI_VER_ANSI_HELPER(URI_VER_MAJOR, URI_VER_MINOR, URI_VER_RELEASE, \ + URI_VER_SUFFIX_ANSI) +# define URI_VER_UNICODE \ + URI_VER_UNICODE_HELPER(URI_VER_MAJOR, URI_VER_MINOR, URI_VER_RELEASE, \ + URI_VER_SUFFIX_UNICODE) /* Unused parameter macro */ -#ifdef __GNUC__ -# define URI_UNUSED(x) unused_##x __attribute__((unused)) -#else -# define URI_UNUSED(x) x -#endif - - +# ifdef __GNUC__ +# define URI_UNUSED(x) unused_##x __attribute__((unused)) +# else +# define URI_UNUSED(x) x +# endif /* Import/export decorator */ -#if defined(_MSC_VER) -# if defined(URI_STATIC_BUILD) -# define URI_PUBLIC -# elif defined(URI_LIBRARY_BUILD) -# define URI_PUBLIC __declspec(dllexport) -# else -# define URI_PUBLIC __declspec(dllimport) -# endif -#else -# if ! defined(URI_LIBRARY_BUILD) || ! defined(URI_VISIBILITY) -# define URI_PUBLIC -# else -# define URI_PUBLIC __attribute__ ((visibility("default"))) -# endif -#endif - - +# if defined(_MSC_VER) +# if defined(URI_STATIC_BUILD) +# define URI_PUBLIC +# elif defined(URI_LIBRARY_BUILD) +# define URI_PUBLIC __declspec(dllexport) +# else +# define URI_PUBLIC __declspec(dllimport) +# endif +# else +# if !defined(URI_LIBRARY_BUILD) || !defined(URI_VISIBILITY) +# define URI_PUBLIC +# else +# define URI_PUBLIC __attribute__((visibility("default"))) +# endif +# endif typedef int UriBool; /**< Boolean type */ -#define URI_TRUE 1 -#define URI_FALSE 0 - - +# define URI_TRUE 1 +# define URI_FALSE 0 /* Shared errors */ -#define URI_SUCCESS 0 -#define URI_ERROR_SYNTAX 1 /* Parsed text violates expected format */ -#define URI_ERROR_NULL 2 /* One of the params passed was NULL - although it mustn't be */ -#define URI_ERROR_MALLOC 3 /* Requested memory could not be allocated */ -#define URI_ERROR_OUTPUT_TOO_LARGE 4 /* Some output is to large for the receiving buffer */ -#define URI_ERROR_NOT_IMPLEMENTED 8 /* The called function is not implemented yet */ -#define URI_ERROR_RANGE_INVALID 9 /* The parameters passed contained invalid ranges */ -#define URI_ERROR_MEMORY_MANAGER_INCOMPLETE 10 /* [>=0.9.0] The UriMemoryManager passed does not implement all needed functions */ - +# define URI_SUCCESS 0 +# define URI_ERROR_SYNTAX 1 /* Parsed text violates expected format */ +# define URI_ERROR_NULL \ + 2 /* One of the params passed was NULL although it mustn't be \ + */ +# define URI_ERROR_MALLOC 3 /* Requested memory could not be allocated */ +# define URI_ERROR_OUTPUT_TOO_LARGE \ + 4 /* Some output is to large for the receiving buffer */ +# define URI_ERROR_NOT_IMPLEMENTED 8 /* The called function is not implemented yet */ +# define URI_ERROR_RANGE_INVALID 9 /* The parameters passed contained invalid ranges */ +# define URI_ERROR_MEMORY_MANAGER_INCOMPLETE \ + 10 /* [>=0.9.0] The UriMemoryManager passed does not implement all needed \ + functions */ /* Errors specific to ToString */ -#define URI_ERROR_TOSTRING_TOO_LONG URI_ERROR_OUTPUT_TOO_LARGE /* Deprecated, test for URI_ERROR_OUTPUT_TOO_LARGE instead */ +# define URI_ERROR_TOSTRING_TOO_LONG \ + URI_ERROR_OUTPUT_TOO_LARGE /* Deprecated, test for URI_ERROR_OUTPUT_TOO_LARGE \ + instead */ /* Errors specific to AddBaseUri */ -#define URI_ERROR_ADDBASE_REL_BASE 5 /* Given base is not absolute */ +# define URI_ERROR_ADDBASE_REL_BASE 5 /* Given base is not absolute */ /* Errors specific to RemoveBaseUri */ -#define URI_ERROR_REMOVEBASE_REL_BASE 6 /* Given base is not absolute */ -#define URI_ERROR_REMOVEBASE_REL_SOURCE 7 /* Given base is not absolute */ +# define URI_ERROR_REMOVEBASE_REL_BASE 6 /* Given base is not absolute */ +# define URI_ERROR_REMOVEBASE_REL_SOURCE 7 /* Given base is not absolute */ /* Error specific to uriTestMemoryManager */ -#define URI_ERROR_MEMORY_MANAGER_FAULTY 11 /* [>=0.9.0] The UriMemoryManager given did not pass the test suite */ +# define URI_ERROR_MEMORY_MANAGER_FAULTY \ + 11 /* [>=0.9.0] The UriMemoryManager given did not pass the test suite */ /* Error specific to uriSetUserInfo */ -#define URI_ERROR_SETUSERINFO_HOST_NOT_SET 12 /* [>=0.9.9] The %URI given does not have the host set */ +# define URI_ERROR_SETUSERINFO_HOST_NOT_SET \ + 12 /* [>=0.9.9] The %URI given does not have the host set */ /* Error specific to uriSetPort */ -#define URI_ERROR_SETPORT_HOST_NOT_SET 13 /* [>=0.9.9] The %URI given does not have the host set */ +# define URI_ERROR_SETPORT_HOST_NOT_SET \ + 13 /* [>=0.9.9] The %URI given does not have the host set */ /* Error specific to uriSetHost* */ -#define URI_ERROR_SETHOST_USERINFO_SET 14 /* [>=0.9.9] The %URI given does have user info set */ -#define URI_ERROR_SETHOST_PORT_SET 15 /* [>=0.9.9] The %URI given does have a port set */ - - - -#ifndef URI_DOXYGEN -# include /* For NULL, snprintf */ -# include /* For wchar_t */ -# include /* For strlen, memset, memcpy */ -# include /* For malloc */ -#endif /* URI_DOXYGEN */ - - +# define URI_ERROR_SETHOST_USERINFO_SET \ + 14 /* [>=0.9.9] The %URI given does have user info set */ +# define URI_ERROR_SETHOST_PORT_SET \ + 15 /* [>=0.9.9] The %URI given does have a port set */ + +# ifndef URI_DOXYGEN +# include /* For NULL, snprintf */ +# include /* For wchar_t */ +# include /* For strlen, memset, memcpy */ +# include /* For malloc */ +# endif /* URI_DOXYGEN */ /** * Holds an IPv4 address. */ typedef struct UriIp4Struct { - unsigned char data[4]; /**< Each octet in one byte */ + unsigned char data[4]; /**< Each octet in one byte */ } UriIp4; /**< @copydoc UriIp4Struct */ - - /** * Holds an IPv6 address. */ typedef struct UriIp6Struct { - unsigned char data[16]; /**< Each quad in two bytes */ + unsigned char data[16]; /**< Each quad in two bytes */ } UriIp6; /**< @copydoc UriIp6Struct */ - -struct UriMemoryManagerStruct; /* forward declaration to break loop */ - +struct UriMemoryManagerStruct; /* forward declaration to break loop */ /** * Function signature that custom malloc(3) functions must conform to @@ -216,7 +200,8 @@ typedef void * (*UriFuncRealloc)(struct UriMemoryManagerStruct *, void *, size_t * * @since 0.9.0 */ -typedef void * (*UriFuncReallocarray)(struct UriMemoryManagerStruct *, void *, size_t, size_t); +typedef void * (*UriFuncReallocarray)(struct UriMemoryManagerStruct *, void *, size_t, + size_t); /** * Function signature that custom free(3) functions must conform to @@ -225,7 +210,6 @@ typedef void * (*UriFuncReallocarray)(struct UriMemoryManagerStruct *, void *, s */ typedef void (*UriFuncFree)(struct UriMemoryManagerStruct *, void *); - /** * Class-like interface of custom memory managers * @@ -236,56 +220,58 @@ typedef void (*UriFuncFree)(struct UriMemoryManagerStruct *, void *); * @since 0.9.0 */ typedef struct UriMemoryManagerStruct { - UriFuncMalloc malloc; /**< Pointer to custom malloc(3) */ - UriFuncCalloc calloc; /**< Pointer to custom calloc(3); to emulate using malloc and memset see uriEmulateCalloc */ - UriFuncRealloc realloc; /**< Pointer to custom realloc(3) */ - UriFuncReallocarray reallocarray; /**< Pointer to custom reallocarray(3); to emulate using realloc see uriEmulateReallocarray */ - UriFuncFree free; /**< Pointer to custom free(3) */ - void * userData; /**< Pointer to data that the other function members need access to */ + UriFuncMalloc malloc; /**< Pointer to custom malloc(3) */ + UriFuncCalloc calloc; /**< Pointer to custom calloc(3); to emulate using malloc and + memset see uriEmulateCalloc */ + UriFuncRealloc realloc; /**< Pointer to custom realloc(3) */ + UriFuncReallocarray reallocarray; /**< Pointer to custom reallocarray(3); to emulate + using realloc see uriEmulateReallocarray */ + UriFuncFree free; /**< Pointer to custom free(3) */ + void * + userData; /**< Pointer to data that the other function members need access to */ } UriMemoryManager; /**< @copydoc UriMemoryManagerStruct */ - /** * Specifies a line break conversion mode. */ typedef enum UriBreakConversionEnum { - URI_BR_TO_LF, /**< Convert to Unix line breaks ("\\x0a") */ - URI_BR_TO_CRLF, /**< Convert to Windows line breaks ("\\x0d\\x0a") */ - URI_BR_TO_CR, /**< Convert to Macintosh line breaks ("\\x0d") */ - URI_BR_TO_UNIX = URI_BR_TO_LF, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_LF */ - URI_BR_TO_WINDOWS = URI_BR_TO_CRLF, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_CRLF */ - URI_BR_TO_MAC = URI_BR_TO_CR, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_CR */ - URI_BR_DONT_TOUCH /**< Copy line breaks unmodified */ + URI_BR_TO_LF, /**< Convert to Unix line breaks ("\\x0a") */ + URI_BR_TO_CRLF, /**< Convert to Windows line breaks ("\\x0d\\x0a") */ + URI_BR_TO_CR, /**< Convert to Macintosh line breaks ("\\x0d") */ + URI_BR_TO_UNIX = URI_BR_TO_LF, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_LF */ + URI_BR_TO_WINDOWS = + URI_BR_TO_CRLF, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_CRLF */ + URI_BR_TO_MAC = URI_BR_TO_CR, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_CR */ + URI_BR_DONT_TOUCH /**< Copy line breaks unmodified */ } UriBreakConversion; /**< @copydoc UriBreakConversionEnum */ - - /** * Specifies which component of a %URI has to be normalized. */ typedef enum UriNormalizationMaskEnum { - URI_NORMALIZED = 0, /**< Do not normalize anything */ - URI_NORMALIZE_SCHEME = 1 << 0, /**< Normalize scheme (fix uppercase letters) */ - URI_NORMALIZE_USER_INFO = 1 << 1, /**< Normalize user info (fix uppercase percent-encodings) */ - URI_NORMALIZE_HOST = 1 << 2, /**< Normalize host (fix uppercase letters) */ - URI_NORMALIZE_PATH = 1 << 3, /**< Normalize path (fix uppercase percent-encodings and redundant dot segments) */ - URI_NORMALIZE_QUERY = 1 << 4, /**< Normalize query (fix uppercase percent-encodings) */ - URI_NORMALIZE_FRAGMENT = 1 << 5, /**< Normalize fragment (fix uppercase percent-encodings) */ - URI_NORMALIZE_PORT = 1 << 6 /**< Normalize port (drop leading zeros) @since 0.9.9 */ + URI_NORMALIZED = 0, /**< Do not normalize anything */ + URI_NORMALIZE_SCHEME = 1 << 0, /**< Normalize scheme (fix uppercase letters) */ + URI_NORMALIZE_USER_INFO = + 1 << 1, /**< Normalize user info (fix uppercase percent-encodings) */ + URI_NORMALIZE_HOST = 1 << 2, /**< Normalize host (fix uppercase letters) */ + URI_NORMALIZE_PATH = 1 << 3, /**< Normalize path (fix uppercase percent-encodings and + redundant dot segments) */ + URI_NORMALIZE_QUERY = + 1 << 4, /**< Normalize query (fix uppercase percent-encodings) */ + URI_NORMALIZE_FRAGMENT = + 1 << 5, /**< Normalize fragment (fix uppercase percent-encodings) */ + URI_NORMALIZE_PORT = 1 << 6 /**< Normalize port (drop leading zeros) @since 0.9.9 */ } UriNormalizationMask; /**< @copydoc UriNormalizationMaskEnum */ - - /** * Specifies how to resolve %URI references. */ typedef enum UriResolutionOptionsEnum { - URI_RESOLVE_STRICTLY = 0, /**< Full RFC conformance */ - URI_RESOLVE_IDENTICAL_SCHEME_COMPAT = 1 << 0 /**< Treat %URI to resolve with identical scheme as having no scheme */ + URI_RESOLVE_STRICTLY = 0, /**< Full RFC conformance */ + URI_RESOLVE_IDENTICAL_SCHEME_COMPAT = + 1 << 0 /**< Treat %URI to resolve with identical scheme as having no scheme */ } UriResolutionOptions; /**< @copydoc UriResolutionOptionsEnum */ - - /** * Wraps a memory manager backend that only provides malloc(3) and * free(3) to make a complete memory manager ready to be used. @@ -322,9 +308,7 @@ typedef enum UriResolutionOptionsEnum { * @since 0.9.0 */ URI_PUBLIC int uriCompleteMemoryManager(UriMemoryManager * memory, - UriMemoryManager * backend); - - + UriMemoryManager * backend); /** * Offers emulation of calloc(3) based on memory->malloc and memset. @@ -340,10 +324,7 @@ URI_PUBLIC int uriCompleteMemoryManager(UriMemoryManager * memory, * @see UriMemoryManager * @since 0.9.0 */ -URI_PUBLIC void * uriEmulateCalloc(UriMemoryManager * memory, - size_t nmemb, size_t size); - - +URI_PUBLIC void * uriEmulateCalloc(UriMemoryManager * memory, size_t nmemb, size_t size); /** * Offers emulation of reallocarray(3) based on memory->realloc. @@ -360,10 +341,8 @@ URI_PUBLIC void * uriEmulateCalloc(UriMemoryManager * memory, * @see UriMemoryManager * @since 0.9.0 */ -URI_PUBLIC void * uriEmulateReallocarray(UriMemoryManager * memory, - void * ptr, size_t nmemb, size_t size); - - +URI_PUBLIC void * uriEmulateReallocarray(UriMemoryManager * memory, void * ptr, + size_t nmemb, size_t size); /** * Run multiple tests against a given memory manager. @@ -392,8 +371,6 @@ URI_PUBLIC void * uriEmulateReallocarray(UriMemoryManager * memory, */ URI_PUBLIC int uriTestMemoryManager(UriMemoryManager * memory); - - /** * Run multiple tests against a given memory manager. * For example, one test @@ -419,8 +396,7 @@ URI_PUBLIC int uriTestMemoryManager(UriMemoryManager * memory); * @see uriTestMemoryManager * @since 0.9.10 */ -URI_PUBLIC int uriTestMemoryManagerEx(UriMemoryManager * memory, UriBool challengeAlignment); - - +URI_PUBLIC int uriTestMemoryManagerEx(UriMemoryManager * memory, + UriBool challengeAlignment); #endif /* URI_BASE_H */ diff --git a/ext/uri/uriparser/include/uriparser/UriDefsAnsi.h b/ext/uri/uriparser/include/uriparser/UriDefsAnsi.h index af581b91a2152..17aed1b3b59e8 100644 --- a/ext/uri/uriparser/include/uriparser/UriDefsAnsi.h +++ b/ext/uri/uriparser/include/uriparser/UriDefsAnsi.h @@ -46,24 +46,18 @@ /* Allow multi inclusion */ #include "UriDefsConfig.h" - - #undef URI_CHAR #define URI_CHAR char #undef _UT #define _UT(x) x - - #undef URI_FUNC #define URI_FUNC(x) uri##x##A #undef URI_TYPE #define URI_TYPE(x) Uri##x##A - - #undef URI_STRLEN #define URI_STRLEN strlen #undef URI_STRCPY @@ -76,7 +70,7 @@ /* TODO Remove on next source-compatibility break */ #undef URI_SNPRINTF #if (defined(__WIN32__) || defined(_WIN32) || defined(WIN32)) -# define URI_SNPRINTF _snprintf +# define URI_SNPRINTF _snprintf #else -# define URI_SNPRINTF snprintf +# define URI_SNPRINTF snprintf #endif diff --git a/ext/uri/uriparser/include/uriparser/UriDefsConfig.h b/ext/uri/uriparser/include/uriparser/UriDefsConfig.h index 51bc93eeddaae..79d485d90c380 100644 --- a/ext/uri/uriparser/include/uriparser/UriDefsConfig.h +++ b/ext/uri/uriparser/include/uriparser/UriDefsConfig.h @@ -43,59 +43,51 @@ */ #ifndef URI_DEFS_CONFIG_H -#define URI_DEFS_CONFIG_H 1 - - +# define URI_DEFS_CONFIG_H 1 /* Deny external overriding */ -#undef URI_ENABLE_ANSI /* Internal for !URI_NO_ANSI */ -#undef URI_ENABLE_UNICODE /* Internal for !URI_NO_UNICODE */ - - +# undef URI_ENABLE_ANSI /* Internal for !URI_NO_ANSI */ +# undef URI_ENABLE_UNICODE /* Internal for !URI_NO_UNICODE */ /* Encoding */ -#ifdef URI_NO_ANSI -# ifdef URI_NO_UNICODE +# ifdef URI_NO_ANSI +# ifdef URI_NO_UNICODE /* No encoding at all */ -# error URI_NO_ANSI and URI_NO_UNICODE cannot go together. -# else +# error URI_NO_ANSI and URI_NO_UNICODE cannot go together. +# else /* Wide strings only */ -# define URI_ENABLE_UNICODE 1 -# endif -#else -# ifdef URI_NO_UNICODE +# define URI_ENABLE_UNICODE 1 +# endif +# else +# ifdef URI_NO_UNICODE /* Narrow strings only */ -# define URI_ENABLE_ANSI 1 -# else +# define URI_ENABLE_ANSI 1 +# else /* Both narrow and wide strings */ -# define URI_ENABLE_ANSI 1 -# define URI_ENABLE_UNICODE 1 -# endif -#endif - - +# define URI_ENABLE_ANSI 1 +# define URI_ENABLE_UNICODE 1 +# endif +# endif /* Function inlining, not ANSI/ISO C! */ -#if defined(URI_DOXYGEN) -# define URI_INLINE -#elif defined(__INTEL_COMPILER) +# if defined(URI_DOXYGEN) +# define URI_INLINE +# elif defined(__INTEL_COMPILER) /* Intel C/C++ */ /* http://predef.sourceforge.net/precomp.html#sec20 */ /* http://www.intel.com/support/performancetools/c/windows/sb/CS-007751.htm#2 */ -# define URI_INLINE __forceinline -#elif defined(_MSC_VER) +# define URI_INLINE __forceinline +# elif defined(_MSC_VER) /* Microsoft Visual C++ */ /* http://predef.sourceforge.net/precomp.html#sec32 */ /* http://msdn2.microsoft.com/en-us/library/ms882281.aspx */ -# define URI_INLINE __forceinline -#elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) +# define URI_INLINE __forceinline +# elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) /* C99, "inline" is a keyword */ -# define URI_INLINE inline -#else +# define URI_INLINE inline +# else /* No inlining */ -# define URI_INLINE -#endif - - +# define URI_INLINE +# endif #endif /* URI_DEFS_CONFIG_H */ diff --git a/ext/uri/uriparser/include/uriparser/UriDefsUnicode.h b/ext/uri/uriparser/include/uriparser/UriDefsUnicode.h index 01421f5f861df..6fce6725e69b7 100644 --- a/ext/uri/uriparser/include/uriparser/UriDefsUnicode.h +++ b/ext/uri/uriparser/include/uriparser/UriDefsUnicode.h @@ -46,24 +46,18 @@ /* Allow multi inclusion */ #include "UriDefsConfig.h" - - #undef URI_CHAR #define URI_CHAR wchar_t #undef _UT #define _UT(x) L##x - - #undef URI_FUNC #define URI_FUNC(x) uri##x##W #undef URI_TYPE #define URI_TYPE(x) Uri##x##W - - #undef URI_STRLEN #define URI_STRLEN wcslen #undef URI_STRCPY @@ -76,7 +70,7 @@ /* TODO Remove on next source-compatibility break */ #undef URI_SNPRINTF #if (defined(__WIN32__) || defined(_WIN32) || defined(WIN32)) -# define URI_SNPRINTF _snwprintf +# define URI_SNPRINTF _snwprintf #else -# define URI_SNPRINTF swprintf +# define URI_SNPRINTF swprintf #endif diff --git a/ext/uri/uriparser/include/uriparser/UriIp4.h b/ext/uri/uriparser/include/uriparser/UriIp4.h index 4b847abe3ab7c..5f84b62dd2fce 100644 --- a/ext/uri/uriparser/include/uriparser/UriIp4.h +++ b/ext/uri/uriparser/include/uriparser/UriIp4.h @@ -44,48 +44,43 @@ */ #if (defined(URI_PASS_ANSI) && !defined(URI_IP4_TWICE_H_ANSI)) \ - || (defined(URI_PASS_UNICODE) && !defined(URI_IP4_TWICE_H_UNICODE)) \ - || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) + || (defined(URI_PASS_UNICODE) && !defined(URI_IP4_TWICE_H_UNICODE)) \ + || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* What encodings are enabled? */ -#include "UriDefsConfig.h" -#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +# include "UriDefsConfig.h" +# if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriIp4.h" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriIp4.h" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriIp4.h" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriIp4.h" +# undef URI_PASS_UNICODE +# endif /* Only one pass for each encoding */ -#elif (defined(URI_PASS_ANSI) && !defined(URI_IP4_TWICE_H_ANSI) \ - && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \ - && !defined(URI_IP4_TWICE_H_UNICODE) && defined(URI_ENABLE_UNICODE)) -# ifdef URI_PASS_ANSI -# define URI_IP4_TWICE_H_ANSI 1 -# include "UriDefsAnsi.h" -# else -# define URI_IP4_TWICE_H_UNICODE 1 -# include "UriDefsUnicode.h" -# include -# endif - - - -#ifdef __cplusplus +# elif (defined(URI_PASS_ANSI) && !defined(URI_IP4_TWICE_H_ANSI) \ + && defined(URI_ENABLE_ANSI)) \ + || (defined(URI_PASS_UNICODE) && !defined(URI_IP4_TWICE_H_UNICODE) \ + && defined(URI_ENABLE_UNICODE)) +# ifdef URI_PASS_ANSI +# define URI_IP4_TWICE_H_ANSI 1 +# include "UriDefsAnsi.h" +# else +# define URI_IP4_TWICE_H_UNICODE 1 +# include "UriDefsUnicode.h" +# include +# endif + +# ifdef __cplusplus extern "C" { -#endif - - - -#ifndef URI_DOXYGEN -# include "UriBase.h" -#endif - +# endif +# ifndef URI_DOXYGEN +# include "UriBase.h" +# endif /** * Converts an IPv4 text representation into four bytes. @@ -99,15 +94,12 @@ extern "C" { * @see uriParseIpSixAddressMmA */ URI_PUBLIC int URI_FUNC(ParseIpFourAddress)(unsigned char * octetOutput, - const URI_CHAR * first, const URI_CHAR * afterLast); - + const URI_CHAR * first, + const URI_CHAR * afterLast); - -#ifdef __cplusplus +# ifdef __cplusplus } -#endif +# endif - - -#endif +# endif #endif diff --git a/ext/uri/uriparser/src/UriCommon.c b/ext/uri/uriparser/src/UriCommon.c index a644eec475367..a594fcceed716 100644 --- a/ext/uri/uriparser/src/UriCommon.c +++ b/ext/uri/uriparser/src/UriCommon.c @@ -41,669 +41,663 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriCommon.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriCommon.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriCommon.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriCommon.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -#endif - - - -#include +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# endif +# include /*extern*/ const URI_CHAR * const URI_FUNC(SafeToPointTo) = _UT("X"); /*extern*/ const URI_CHAR * const URI_FUNC(ConstPwd) = _UT("."); /*extern*/ const URI_CHAR * const URI_FUNC(ConstParent) = _UT(".."); - - void URI_FUNC(ResetUri)(URI_TYPE(Uri) * uri) { - if (uri == NULL) { - return; - } - memset(uri, 0, sizeof(URI_TYPE(Uri))); + if (uri == NULL) { + return; + } + memset(uri, 0, sizeof(URI_TYPE(Uri))); } - - int URI_FUNC(FreeUriPath)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { - assert(uri != NULL); - assert(memory != NULL); - - if (uri->pathHead != NULL) { - URI_TYPE(PathSegment) * segWalk = uri->pathHead; - while (segWalk != NULL) { - URI_TYPE(PathSegment) * const next = segWalk->next; - if ((uri->owner == URI_TRUE) && (segWalk->text.first != segWalk->text.afterLast)) { - memory->free(memory, (URI_CHAR *)segWalk->text.first); - } - segWalk->text.first = NULL; - segWalk->text.afterLast = NULL; - segWalk->next = NULL; - memory->free(memory, segWalk); - segWalk = next; - } - uri->pathHead = NULL; - uri->pathTail = NULL; - } - - return URI_SUCCESS; + assert(uri != NULL); + assert(memory != NULL); + + if (uri->pathHead != NULL) { + URI_TYPE(PathSegment) * segWalk = uri->pathHead; + while (segWalk != NULL) { + URI_TYPE(PathSegment) * const next = segWalk->next; + if ((uri->owner == URI_TRUE) + && (segWalk->text.first != segWalk->text.afterLast)) { + memory->free(memory, (URI_CHAR *)segWalk->text.first); + } + segWalk->text.first = NULL; + segWalk->text.afterLast = NULL; + segWalk->next = NULL; + memory->free(memory, segWalk); + segWalk = next; + } + uri->pathHead = NULL; + uri->pathTail = NULL; + } + + return URI_SUCCESS; } - - /* Compares two text ranges for equal text content */ -int URI_FUNC(CompareRange)( - const URI_TYPE(TextRange) * a, - const URI_TYPE(TextRange) * b) { - int diff; - - /* NOTE: Both NULL means equal! */ - if ((a == NULL) || (b == NULL)) { - return ((a == NULL) ? 0 : 1) - ((b == NULL) ? 0 : 1); - } - - /* NOTE: Both NULL means equal! */ - if ((a->first == NULL) || (b->first == NULL)) { - return ((a->first == NULL) ? 0 : 1) - ((b->first == NULL) ? 0 : 1); - } - - diff = ((int)(a->afterLast - a->first) - (int)(b->afterLast - b->first)); - if (diff > 0) { - return 1; - } else if (diff < 0) { - return -1; - } - - diff = URI_STRNCMP(a->first, b->first, (a->afterLast - a->first)); - - if (diff > 0) { - return 1; - } else if (diff < 0) { - return -1; - } - - return diff; +int URI_FUNC(CompareRange)(const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b) { + int diff; + + /* NOTE: Both NULL means equal! */ + if ((a == NULL) || (b == NULL)) { + return ((a == NULL) ? 0 : 1) - ((b == NULL) ? 0 : 1); + } + + /* NOTE: Both NULL means equal! */ + if ((a->first == NULL) || (b->first == NULL)) { + return ((a->first == NULL) ? 0 : 1) - ((b->first == NULL) ? 0 : 1); + } + + diff = ((int)(a->afterLast - a->first) - (int)(b->afterLast - b->first)); + if (diff > 0) { + return 1; + } else if (diff < 0) { + return -1; + } + + diff = URI_STRNCMP(a->first, b->first, (a->afterLast - a->first)); + + if (diff > 0) { + return 1; + } else if (diff < 0) { + return -1; + } + + return diff; } - - UriBool URI_FUNC(CopyRange)(URI_TYPE(TextRange) * destRange, - const URI_TYPE(TextRange) * sourceRange, UriMemoryManager * memory) { - const int lenInChars = (int)(sourceRange->afterLast - sourceRange->first); - const int lenInBytes = lenInChars * sizeof(URI_CHAR); - URI_CHAR * dup = memory->malloc(memory, lenInBytes); - if (dup == NULL) { - return URI_FALSE; - } - memcpy(dup, sourceRange->first, lenInBytes); - destRange->first = dup; - destRange->afterLast = dup + lenInChars; - - return URI_TRUE; + const URI_TYPE(TextRange) * sourceRange, + UriMemoryManager * memory) { + const int lenInChars = (int)(sourceRange->afterLast - sourceRange->first); + const int lenInBytes = lenInChars * sizeof(URI_CHAR); + URI_CHAR * dup = memory->malloc(memory, lenInBytes); + if (dup == NULL) { + return URI_FALSE; + } + memcpy(dup, sourceRange->first, lenInBytes); + destRange->first = dup; + destRange->afterLast = dup + lenInChars; + + return URI_TRUE; } - - UriBool URI_FUNC(CopyRangeAsNeeded)(URI_TYPE(TextRange) * destRange, - const URI_TYPE(TextRange) * sourceRange, UriMemoryManager * memory) { - if (sourceRange->first == NULL) { - destRange->first = NULL; - destRange->afterLast = NULL; - } else if (sourceRange->first == sourceRange->afterLast) { - destRange->first = URI_FUNC(SafeToPointTo); - destRange->afterLast = URI_FUNC(SafeToPointTo); - } else { - return URI_FUNC(CopyRange)(destRange, sourceRange, memory); - } - - return URI_TRUE; + const URI_TYPE(TextRange) * sourceRange, + UriMemoryManager * memory) { + if (sourceRange->first == NULL) { + destRange->first = NULL; + destRange->afterLast = NULL; + } else if (sourceRange->first == sourceRange->afterLast) { + destRange->first = URI_FUNC(SafeToPointTo); + destRange->afterLast = URI_FUNC(SafeToPointTo); + } else { + return URI_FUNC(CopyRange)(destRange, sourceRange, memory); + } + + return URI_TRUE; } - - -UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri, - UriBool relative, UriBool pathOwned, UriMemoryManager * memory) { - URI_TYPE(PathSegment) * walker; - if ((uri == NULL) || (uri->pathHead == NULL)) { - return URI_TRUE; - } - - walker = uri->pathHead; - walker->reserved = NULL; /* Prev pointer */ - do { - UriBool removeSegment = URI_FALSE; - int len = (int)(walker->text.afterLast - walker->text.first); - switch (len) { - case 1: - if ((walker->text.first)[0] == _UT('.')) { - /* "." segment -> remove if not essential */ - URI_TYPE(PathSegment) * const prev = walker->reserved; - URI_TYPE(PathSegment) * const nextBackup = walker->next; - - /* - * Is this dot segment essential, - * i.e. is there a chance of changing semantics by dropping this dot segment? - * - * For example, changing "./http://foo" into "http://foo" would change semantics - * and hence the dot segment is essential to that case and cannot be removed. - * - * Other examples that would change semantics are: - * - cutting "/.//" down to "//" - * - cutting "scheme:/.//" down to "scheme://". - */ - removeSegment = URI_TRUE; - if ((walker == uri->pathHead) && (walker->next != NULL)) { - /* Detect case "/.//" (with or without scheme) */ - if ((walker->next->text.first == walker->next->text.afterLast) - && (URI_FUNC(HasHost)(uri) == URI_FALSE)) { - removeSegment = URI_FALSE; - /* Detect case "./withcolon:" */ - } else if (relative) { - const URI_CHAR * ch = walker->next->text.first; - for (; ch < walker->next->text.afterLast; ch++) { - if (*ch == _UT(':')) { - removeSegment = URI_FALSE; - break; - } - } - } - } - - if (removeSegment) { - /* .. then let's go remove that segment. */ - /* Last segment? */ - if (walker->next != NULL) { - /* Not last segment, i.e. first or middle segment - * OLD: (prev|NULL) <- walker <- next - * NEW: (prev|NULL) <----------- next */ - walker->next->reserved = prev; - - if (prev == NULL) { - /* First but not last segment - * OLD: head -> walker -> next - * NEW: head -----------> next */ - uri->pathHead = walker->next; - } else { - /* Middle segment - * OLD: prev -> walker -> next - * NEW: prev -----------> next */ - prev->next = walker->next; - } - - if (pathOwned && (walker->text.first != walker->text.afterLast)) { - memory->free(memory, (URI_CHAR *)walker->text.first); - } - memory->free(memory, walker); - } else { - /* Last segment */ - if (pathOwned && (walker->text.first != walker->text.afterLast)) { - memory->free(memory, (URI_CHAR *)walker->text.first); - } - - if (prev == NULL) { - /* Last and first */ - if (URI_FUNC(HasHost)(uri)) { - /* Replace "." with empty segment to represent trailing slash */ - walker->text.first = URI_FUNC(SafeToPointTo); - walker->text.afterLast = URI_FUNC(SafeToPointTo); - } else { - memory->free(memory, walker); - - uri->pathHead = NULL; - uri->pathTail = NULL; - } - } else { - /* Last but not first, replace "." with empty segment to represent trailing slash */ - walker->text.first = URI_FUNC(SafeToPointTo); - walker->text.afterLast = URI_FUNC(SafeToPointTo); - } - } - - walker = nextBackup; - } - } - break; - - case 2: - if (((walker->text.first)[0] == _UT('.')) - && ((walker->text.first)[1] == _UT('.'))) { - /* Path ".." -> remove this and the previous segment */ - URI_TYPE(PathSegment) * const prev = walker->reserved; - URI_TYPE(PathSegment) * prevPrev; - URI_TYPE(PathSegment) * const nextBackup = walker->next; - - removeSegment = URI_TRUE; - if (relative) { - if (prev == NULL) { - /* We cannot remove traversal beyond because the - * URI is relative and may be resolved later. - * So we can simplify "a/../b/d" to "b/d" but - * we cannot simplify "../b/d" (outside of reference resolution). */ - removeSegment = URI_FALSE; - } else if ((prev != NULL) - && ((prev->text.afterLast - prev->text.first) == 2) - && ((prev->text.first)[0] == _UT('.')) - && ((prev->text.first)[1] == _UT('.'))) { - /* We need to protect against mis-simplifying "a/../../b" to "a/b". */ - removeSegment = URI_FALSE; - } - } - - if (removeSegment) { - if (prev != NULL) { - /* Not first segment */ - prevPrev = prev->reserved; - if (prevPrev != NULL) { - /* Not even prev is the first one - * OLD: prevPrev -> prev -> walker -> (next|NULL) - * NEW: prevPrev -------------------> (next|NULL) */ - prevPrev->next = walker->next; - if (walker->next != NULL) { - /* Update parent relationship as well - * OLD: prevPrev <- prev <- walker <- next - * NEW: prevPrev <------------------- next */ - walker->next->reserved = prevPrev; - } else { - /* Last segment -> insert "" segment to represent trailing slash, update tail */ - URI_TYPE(PathSegment) * const segment = memory->calloc(memory, 1, sizeof(URI_TYPE(PathSegment))); - if (segment == NULL) { - if (pathOwned && (walker->text.first != walker->text.afterLast)) { - memory->free(memory, (URI_CHAR *)walker->text.first); - } - memory->free(memory, walker); - - if (pathOwned && (prev->text.first != prev->text.afterLast)) { - memory->free(memory, (URI_CHAR *)prev->text.first); - } - memory->free(memory, prev); - - return URI_FALSE; /* Raises malloc error */ - } - segment->text.first = URI_FUNC(SafeToPointTo); - segment->text.afterLast = URI_FUNC(SafeToPointTo); - prevPrev->next = segment; - uri->pathTail = segment; - } - - if (pathOwned && (walker->text.first != walker->text.afterLast)) { - memory->free(memory, (URI_CHAR *)walker->text.first); - } - memory->free(memory, walker); - - if (pathOwned && (prev->text.first != prev->text.afterLast)) { - memory->free(memory, (URI_CHAR *)prev->text.first); - } - memory->free(memory, prev); - - walker = nextBackup; - } else { - /* Prev is the first segment */ - if (walker->next != NULL) { - uri->pathHead = walker->next; - walker->next->reserved = NULL; - - if (pathOwned && (walker->text.first != walker->text.afterLast)) { - memory->free(memory, (URI_CHAR *)walker->text.first); - } - memory->free(memory, walker); - } else { - /* Reuse segment for "" path segment to represent trailing slash, update tail */ - URI_TYPE(PathSegment) * const segment = walker; - if (pathOwned && (segment->text.first != segment->text.afterLast)) { - memory->free(memory, (URI_CHAR *)segment->text.first); - } - segment->text.first = URI_FUNC(SafeToPointTo); - segment->text.afterLast = URI_FUNC(SafeToPointTo); - uri->pathHead = segment; - uri->pathTail = segment; - } - - if (pathOwned && (prev->text.first != prev->text.afterLast)) { - memory->free(memory, (URI_CHAR *)prev->text.first); - } - memory->free(memory, prev); - - walker = nextBackup; - } - } else { - URI_TYPE(PathSegment) * const anotherNextBackup = walker->next; - int freeWalker = URI_TRUE; - - /* First segment */ - if (walker->next != NULL) { - /* First segment of multiple -> update head - * OLD: head -> walker -> next - * NEW: head -----------> next */ - uri->pathHead = walker->next; - - /* Update parent link as well - * OLD: head <- walker <- next - * NEW: head <----------- next */ - walker->next->reserved = NULL; - } else { - if (uri->absolutePath) { - /* First and only segment -> update head - * OLD: head -> walker -> NULL - * NEW: head -----------> NULL */ - uri->pathHead = NULL; - - /* Last segment -> update tail - * OLD: tail -> walker - * NEW: tail -> NULL */ - uri->pathTail = NULL; - } else { - /* Reuse segment for "" path segment to represent trailing slash, - * then update head and tail */ - if (pathOwned && (walker->text.first != walker->text.afterLast)) { - memory->free(memory, (URI_CHAR *)walker->text.first); - } - walker->text.first = URI_FUNC(SafeToPointTo); - walker->text.afterLast = URI_FUNC(SafeToPointTo); - freeWalker = URI_FALSE; - } - } - - if (freeWalker) { - if (pathOwned && (walker->text.first != walker->text.afterLast)) { - memory->free(memory, (URI_CHAR *)walker->text.first); - } - memory->free(memory, walker); - } - - walker = anotherNextBackup; - } - } - } - break; - } /* end of switch */ - - if (!removeSegment) { - /* .. then let's move to the next element, and start again. */ - if (walker->next != NULL) { - walker->next->reserved = walker; - } else { - /* Last segment -> update tail */ - uri->pathTail = walker; - } - walker = walker->next; - } - } while (walker != NULL); - - return URI_TRUE; +UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri, UriBool relative, + UriBool pathOwned, UriMemoryManager * memory) { + URI_TYPE(PathSegment) * walker; + if ((uri == NULL) || (uri->pathHead == NULL)) { + return URI_TRUE; + } + + walker = uri->pathHead; + walker->reserved = NULL; /* Prev pointer */ + do { + UriBool removeSegment = URI_FALSE; + int len = (int)(walker->text.afterLast - walker->text.first); + switch (len) { + case 1: + if ((walker->text.first)[0] == _UT('.')) { + /* "." segment -> remove if not essential */ + URI_TYPE(PathSegment) * const prev = walker->reserved; + URI_TYPE(PathSegment) * const nextBackup = walker->next; + + /* + * Is this dot segment essential, + * i.e. is there a chance of changing semantics by dropping this dot + * segment? + * + * For example, changing "./http://foo" into "http://foo" would change + * semantics and hence the dot segment is essential to that case and + * cannot be removed. + * + * Other examples that would change semantics are: + * - cutting "/.//" down to "//" + * - cutting "scheme:/.//" down to "scheme://". + */ + removeSegment = URI_TRUE; + if ((walker == uri->pathHead) && (walker->next != NULL)) { + /* Detect case "/.//" (with or without scheme) */ + if ((walker->next->text.first == walker->next->text.afterLast) + && (URI_FUNC(HasHost)(uri) == URI_FALSE)) { + removeSegment = URI_FALSE; + /* Detect case "./withcolon:" */ + } else if (relative) { + const URI_CHAR * ch = walker->next->text.first; + for (; ch < walker->next->text.afterLast; ch++) { + if (*ch == _UT(':')) { + removeSegment = URI_FALSE; + break; + } + } + } + } + + if (removeSegment) { + /* .. then let's go remove that segment. */ + /* Last segment? */ + if (walker->next != NULL) { + /* Not last segment, i.e. first or middle segment + * OLD: (prev|NULL) <- walker <- next + * NEW: (prev|NULL) <----------- next */ + walker->next->reserved = prev; + + if (prev == NULL) { + /* First but not last segment + * OLD: head -> walker -> next + * NEW: head -----------> next */ + uri->pathHead = walker->next; + } else { + /* Middle segment + * OLD: prev -> walker -> next + * NEW: prev -----------> next */ + prev->next = walker->next; + } + + if (pathOwned && (walker->text.first != walker->text.afterLast)) { + memory->free(memory, (URI_CHAR *)walker->text.first); + } + memory->free(memory, walker); + } else { + /* Last segment */ + if (pathOwned && (walker->text.first != walker->text.afterLast)) { + memory->free(memory, (URI_CHAR *)walker->text.first); + } + + if (prev == NULL) { + /* Last and first */ + if (URI_FUNC(HasHost)(uri)) { + /* Replace "." with empty segment to represent trailing + * slash */ + walker->text.first = URI_FUNC(SafeToPointTo); + walker->text.afterLast = URI_FUNC(SafeToPointTo); + } else { + memory->free(memory, walker); + + uri->pathHead = NULL; + uri->pathTail = NULL; + } + } else { + /* Last but not first, replace "." with empty segment to + * represent trailing slash */ + walker->text.first = URI_FUNC(SafeToPointTo); + walker->text.afterLast = URI_FUNC(SafeToPointTo); + } + } + + walker = nextBackup; + } + } + break; + + case 2: + if (((walker->text.first)[0] == _UT('.')) + && ((walker->text.first)[1] == _UT('.'))) { + /* Path ".." -> remove this and the previous segment */ + URI_TYPE(PathSegment) * const prev = walker->reserved; + URI_TYPE(PathSegment) * prevPrev; + URI_TYPE(PathSegment) * const nextBackup = walker->next; + + removeSegment = URI_TRUE; + if (relative) { + if (prev == NULL) { + /* We cannot remove traversal beyond because the + * URI is relative and may be resolved later. + * So we can simplify "a/../b/d" to "b/d" but + * we cannot simplify "../b/d" (outside of reference resolution). + */ + removeSegment = URI_FALSE; + } else if ((prev != NULL) + && ((prev->text.afterLast - prev->text.first) == 2) + && ((prev->text.first)[0] == _UT('.')) + && ((prev->text.first)[1] == _UT('.'))) { + /* We need to protect against mis-simplifying "a/../../b" to + * "a/b". */ + removeSegment = URI_FALSE; + } + } + + if (removeSegment) { + if (prev != NULL) { + /* Not first segment */ + prevPrev = prev->reserved; + if (prevPrev != NULL) { + /* Not even prev is the first one + * OLD: prevPrev -> prev -> walker -> (next|NULL) + * NEW: prevPrev -------------------> (next|NULL) */ + prevPrev->next = walker->next; + if (walker->next != NULL) { + /* Update parent relationship as well + * OLD: prevPrev <- prev <- walker <- next + * NEW: prevPrev <------------------- next */ + walker->next->reserved = prevPrev; + } else { + /* Last segment -> insert "" segment to represent trailing + * slash, update tail */ + URI_TYPE(PathSegment) * const segment = memory->calloc( + memory, 1, sizeof(URI_TYPE(PathSegment))); + if (segment == NULL) { + if (pathOwned + && (walker->text.first + != walker->text.afterLast)) { + memory->free(memory, + (URI_CHAR *)walker->text.first); + } + memory->free(memory, walker); + + if (pathOwned + && (prev->text.first != prev->text.afterLast)) { + memory->free(memory, + (URI_CHAR *)prev->text.first); + } + memory->free(memory, prev); + + return URI_FALSE; /* Raises malloc error */ + } + segment->text.first = URI_FUNC(SafeToPointTo); + segment->text.afterLast = URI_FUNC(SafeToPointTo); + prevPrev->next = segment; + uri->pathTail = segment; + } + + if (pathOwned + && (walker->text.first != walker->text.afterLast)) { + memory->free(memory, (URI_CHAR *)walker->text.first); + } + memory->free(memory, walker); + + if (pathOwned && (prev->text.first != prev->text.afterLast)) { + memory->free(memory, (URI_CHAR *)prev->text.first); + } + memory->free(memory, prev); + + walker = nextBackup; + } else { + /* Prev is the first segment */ + if (walker->next != NULL) { + uri->pathHead = walker->next; + walker->next->reserved = NULL; + + if (pathOwned + && (walker->text.first != walker->text.afterLast)) { + memory->free(memory, (URI_CHAR *)walker->text.first); + } + memory->free(memory, walker); + } else { + /* Reuse segment for "" path segment to represent trailing + * slash, update tail */ + URI_TYPE(PathSegment) * const segment = walker; + if (pathOwned + && (segment->text.first != segment->text.afterLast)) { + memory->free(memory, (URI_CHAR *)segment->text.first); + } + segment->text.first = URI_FUNC(SafeToPointTo); + segment->text.afterLast = URI_FUNC(SafeToPointTo); + uri->pathHead = segment; + uri->pathTail = segment; + } + + if (pathOwned && (prev->text.first != prev->text.afterLast)) { + memory->free(memory, (URI_CHAR *)prev->text.first); + } + memory->free(memory, prev); + + walker = nextBackup; + } + } else { + URI_TYPE(PathSegment) * const anotherNextBackup = walker->next; + int freeWalker = URI_TRUE; + + /* First segment */ + if (walker->next != NULL) { + /* First segment of multiple -> update head + * OLD: head -> walker -> next + * NEW: head -----------> next */ + uri->pathHead = walker->next; + + /* Update parent link as well + * OLD: head <- walker <- next + * NEW: head <----------- next */ + walker->next->reserved = NULL; + } else { + if (uri->absolutePath) { + /* First and only segment -> update head + * OLD: head -> walker -> NULL + * NEW: head -----------> NULL */ + uri->pathHead = NULL; + + /* Last segment -> update tail + * OLD: tail -> walker + * NEW: tail -> NULL */ + uri->pathTail = NULL; + } else { + /* Reuse segment for "" path segment to represent trailing + * slash, then update head and tail */ + if (pathOwned + && (walker->text.first != walker->text.afterLast)) { + memory->free(memory, (URI_CHAR *)walker->text.first); + } + walker->text.first = URI_FUNC(SafeToPointTo); + walker->text.afterLast = URI_FUNC(SafeToPointTo); + freeWalker = URI_FALSE; + } + } + + if (freeWalker) { + if (pathOwned + && (walker->text.first != walker->text.afterLast)) { + memory->free(memory, (URI_CHAR *)walker->text.first); + } + memory->free(memory, walker); + } + + walker = anotherNextBackup; + } + } + } + break; + } /* end of switch */ + + if (!removeSegment) { + /* .. then let's move to the next element, and start again. */ + if (walker->next != NULL) { + walker->next->reserved = walker; + } else { + /* Last segment -> update tail */ + uri->pathTail = walker; + } + walker = walker->next; + } + } while (walker != NULL); + + return URI_TRUE; } - - /* Properly removes "." and ".." path segments */ UriBool URI_FUNC(RemoveDotSegmentsAbsolute)(URI_TYPE(Uri) * uri, - UriMemoryManager * memory) { - const UriBool ABSOLUTE = URI_FALSE; - if (uri == NULL) { - return URI_TRUE; - } - return URI_FUNC(RemoveDotSegmentsEx)(uri, ABSOLUTE, uri->owner, memory); + UriMemoryManager * memory) { + const UriBool ABSOLUTE = URI_FALSE; + if (uri == NULL) { + return URI_TRUE; + } + return URI_FUNC(RemoveDotSegmentsEx)(uri, ABSOLUTE, uri->owner, memory); } - - unsigned char URI_FUNC(HexdigToInt)(URI_CHAR hexdig) { - switch (hexdig) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): - return (unsigned char)(9 + hexdig - _UT('9')); - - case _UT('a'): - case _UT('b'): - case _UT('c'): - case _UT('d'): - case _UT('e'): - case _UT('f'): - return (unsigned char)(15 + hexdig - _UT('f')); - - case _UT('A'): - case _UT('B'): - case _UT('C'): - case _UT('D'): - case _UT('E'): - case _UT('F'): - return (unsigned char)(15 + hexdig - _UT('F')); - - default: - return 0; - } + switch (hexdig) { + case _UT('0'): + case _UT('1'): + case _UT('2'): + case _UT('3'): + case _UT('4'): + case _UT('5'): + case _UT('6'): + case _UT('7'): + case _UT('8'): + case _UT('9'): + return (unsigned char)(9 + hexdig - _UT('9')); + + case _UT('a'): + case _UT('b'): + case _UT('c'): + case _UT('d'): + case _UT('e'): + case _UT('f'): + return (unsigned char)(15 + hexdig - _UT('f')); + + case _UT('A'): + case _UT('B'): + case _UT('C'): + case _UT('D'): + case _UT('E'): + case _UT('F'): + return (unsigned char)(15 + hexdig - _UT('F')); + + default: + return 0; + } } - - URI_CHAR URI_FUNC(HexToLetterEx)(unsigned int value, UriBool uppercase) { - switch (value) { - case 0: return _UT('0'); - case 1: return _UT('1'); - case 2: return _UT('2'); - case 3: return _UT('3'); - case 4: return _UT('4'); - case 5: return _UT('5'); - case 6: return _UT('6'); - case 7: return _UT('7'); - case 8: return _UT('8'); - case 9: return _UT('9'); - - case 10: return (uppercase == URI_TRUE) ? _UT('A') : _UT('a'); - case 11: return (uppercase == URI_TRUE) ? _UT('B') : _UT('b'); - case 12: return (uppercase == URI_TRUE) ? _UT('C') : _UT('c'); - case 13: return (uppercase == URI_TRUE) ? _UT('D') : _UT('d'); - case 14: return (uppercase == URI_TRUE) ? _UT('E') : _UT('e'); - default: return (uppercase == URI_TRUE) ? _UT('F') : _UT('f'); - } + switch (value) { + case 0: + return _UT('0'); + case 1: + return _UT('1'); + case 2: + return _UT('2'); + case 3: + return _UT('3'); + case 4: + return _UT('4'); + case 5: + return _UT('5'); + case 6: + return _UT('6'); + case 7: + return _UT('7'); + case 8: + return _UT('8'); + case 9: + return _UT('9'); + + case 10: + return (uppercase == URI_TRUE) ? _UT('A') : _UT('a'); + case 11: + return (uppercase == URI_TRUE) ? _UT('B') : _UT('b'); + case 12: + return (uppercase == URI_TRUE) ? _UT('C') : _UT('c'); + case 13: + return (uppercase == URI_TRUE) ? _UT('D') : _UT('d'); + case 14: + return (uppercase == URI_TRUE) ? _UT('E') : _UT('e'); + default: + return (uppercase == URI_TRUE) ? _UT('F') : _UT('f'); + } } - - /* Checks if a URI has the host component set. */ UriBool URI_FUNC(HasHost)(const URI_TYPE(Uri) * uri) { - /* NOTE: .hostData.ipFuture.first is not being checked, * - * because we do check .hostText.first and * - * .hostData.ipFuture.first has to be identical to * - * .hostText.first if set, and hence there is * - * no more information to be gained. */ - return (uri != NULL) - && ((uri->hostText.first != NULL) - || (uri->hostData.ip4 != NULL) - || (uri->hostData.ip6 != NULL) - ); + /* NOTE: .hostData.ipFuture.first is not being checked, * + * because we do check .hostText.first and * + * .hostData.ipFuture.first has to be identical to * + * .hostText.first if set, and hence there is * + * no more information to be gained. */ + return (uri != NULL) + && ((uri->hostText.first != NULL) || (uri->hostData.ip4 != NULL) + || (uri->hostData.ip6 != NULL)); } - - /* Copies the path segment list from one URI to another. */ -UriBool URI_FUNC(CopyPath)(URI_TYPE(Uri) * dest, - const URI_TYPE(Uri) * source, UriMemoryManager * memory) { - if (source->pathHead == NULL) { - /* No path component */ - dest->pathHead = NULL; - dest->pathTail = NULL; - } else { - /* Copy list but not the text contained */ - URI_TYPE(PathSegment) * sourceWalker = source->pathHead; - URI_TYPE(PathSegment) * destPrev = NULL; - do { - URI_TYPE(PathSegment) * cur = memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); - if (cur == NULL) { - /* Fix broken list */ - if (destPrev != NULL) { - destPrev->next = NULL; - } - return URI_FALSE; /* Raises malloc error */ - } - - /* From this functions usage we know that * - * the dest URI cannot be uri->owner */ - cur->text = sourceWalker->text; - if (destPrev == NULL) { - /* First segment ever */ - dest->pathHead = cur; - } else { - destPrev->next = cur; - } - destPrev = cur; - sourceWalker = sourceWalker->next; - } while (sourceWalker != NULL); - dest->pathTail = destPrev; - dest->pathTail->next = NULL; - } - - dest->absolutePath = source->absolutePath; - return URI_TRUE; +UriBool URI_FUNC(CopyPath)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * source, + UriMemoryManager * memory) { + if (source->pathHead == NULL) { + /* No path component */ + dest->pathHead = NULL; + dest->pathTail = NULL; + } else { + /* Copy list but not the text contained */ + URI_TYPE(PathSegment) * sourceWalker = source->pathHead; + URI_TYPE(PathSegment) * destPrev = NULL; + do { + URI_TYPE(PathSegment) * cur = + memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); + if (cur == NULL) { + /* Fix broken list */ + if (destPrev != NULL) { + destPrev->next = NULL; + } + return URI_FALSE; /* Raises malloc error */ + } + + /* From this functions usage we know that * + * the dest URI cannot be uri->owner */ + cur->text = sourceWalker->text; + if (destPrev == NULL) { + /* First segment ever */ + dest->pathHead = cur; + } else { + destPrev->next = cur; + } + destPrev = cur; + sourceWalker = sourceWalker->next; + } while (sourceWalker != NULL); + dest->pathTail = destPrev; + dest->pathTail->next = NULL; + } + + dest->absolutePath = source->absolutePath; + return URI_TRUE; } - - /* Copies the authority part of an URI over to another. */ -UriBool URI_FUNC(CopyAuthority)(URI_TYPE(Uri) * dest, - const URI_TYPE(Uri) * source, UriMemoryManager * memory) { - /* From this functions usage we know that * - * the dest URI cannot be uri->owner */ - - /* Copy userInfo */ - dest->userInfo = source->userInfo; - - /* Copy hostText */ - dest->hostText = source->hostText; - - /* Copy hostData */ - if (source->hostData.ip4 != NULL) { - dest->hostData.ip4 = memory->malloc(memory, sizeof(UriIp4)); - if (dest->hostData.ip4 == NULL) { - return URI_FALSE; /* Raises malloc error */ - } - *(dest->hostData.ip4) = *(source->hostData.ip4); - dest->hostData.ip6 = NULL; - dest->hostData.ipFuture.first = NULL; - dest->hostData.ipFuture.afterLast = NULL; - } else if (source->hostData.ip6 != NULL) { - dest->hostData.ip4 = NULL; - dest->hostData.ip6 = memory->malloc(memory, sizeof(UriIp6)); - if (dest->hostData.ip6 == NULL) { - return URI_FALSE; /* Raises malloc error */ - } - *(dest->hostData.ip6) = *(source->hostData.ip6); - dest->hostData.ipFuture.first = NULL; - dest->hostData.ipFuture.afterLast = NULL; - } else { - dest->hostData.ip4 = NULL; - dest->hostData.ip6 = NULL; - dest->hostData.ipFuture = source->hostData.ipFuture; - } - - /* Copy portText */ - dest->portText = source->portText; - - return URI_TRUE; +UriBool URI_FUNC(CopyAuthority)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * source, + UriMemoryManager * memory) { + /* From this functions usage we know that * + * the dest URI cannot be uri->owner */ + + /* Copy userInfo */ + dest->userInfo = source->userInfo; + + /* Copy hostText */ + dest->hostText = source->hostText; + + /* Copy hostData */ + if (source->hostData.ip4 != NULL) { + dest->hostData.ip4 = memory->malloc(memory, sizeof(UriIp4)); + if (dest->hostData.ip4 == NULL) { + return URI_FALSE; /* Raises malloc error */ + } + *(dest->hostData.ip4) = *(source->hostData.ip4); + dest->hostData.ip6 = NULL; + dest->hostData.ipFuture.first = NULL; + dest->hostData.ipFuture.afterLast = NULL; + } else if (source->hostData.ip6 != NULL) { + dest->hostData.ip4 = NULL; + dest->hostData.ip6 = memory->malloc(memory, sizeof(UriIp6)); + if (dest->hostData.ip6 == NULL) { + return URI_FALSE; /* Raises malloc error */ + } + *(dest->hostData.ip6) = *(source->hostData.ip6); + dest->hostData.ipFuture.first = NULL; + dest->hostData.ipFuture.afterLast = NULL; + } else { + dest->hostData.ip4 = NULL; + dest->hostData.ip6 = NULL; + dest->hostData.ipFuture = source->hostData.ipFuture; + } + + /* Copy portText */ + dest->portText = source->portText; + + return URI_TRUE; } - - -UriBool URI_FUNC(FixAmbiguity)(URI_TYPE(Uri) * uri, - UriMemoryManager * memory) { - URI_TYPE(PathSegment) * segment; - - if ( /* Case 1: absolute path, empty first segment */ - (uri->absolutePath - && (uri->pathHead != NULL) - && (uri->pathHead->text.afterLast == uri->pathHead->text.first)) - - /* Case 2: relative path, empty first and second segment */ - || (!uri->absolutePath - && (uri->pathHead != NULL) - && (uri->pathHead->next != NULL) - && (uri->pathHead->text.afterLast == uri->pathHead->text.first) - && (uri->pathHead->next->text.afterLast == uri->pathHead->next->text.first))) { - /* NOOP */ - } else { - return URI_TRUE; - } - - segment = memory->malloc(memory, 1 * sizeof(URI_TYPE(PathSegment))); - if (segment == NULL) { - return URI_FALSE; /* Raises malloc error */ - } - - /* Insert "." segment in front */ - segment->next = uri->pathHead; - segment->text.first = URI_FUNC(ConstPwd); - segment->text.afterLast = URI_FUNC(ConstPwd) + 1; - uri->pathHead = segment; - return URI_TRUE; +UriBool URI_FUNC(FixAmbiguity)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { + URI_TYPE(PathSegment) * segment; + + if (/* Case 1: absolute path, empty first segment */ + (uri->absolutePath && (uri->pathHead != NULL) + && (uri->pathHead->text.afterLast == uri->pathHead->text.first)) + + /* Case 2: relative path, empty first and second segment */ + || (!uri->absolutePath && (uri->pathHead != NULL) && (uri->pathHead->next != NULL) + && (uri->pathHead->text.afterLast == uri->pathHead->text.first) + && (uri->pathHead->next->text.afterLast + == uri->pathHead->next->text.first))) { + /* NOOP */ + } else { + return URI_TRUE; + } + + segment = memory->malloc(memory, 1 * sizeof(URI_TYPE(PathSegment))); + if (segment == NULL) { + return URI_FALSE; /* Raises malloc error */ + } + + /* Insert "." segment in front */ + segment->next = uri->pathHead; + segment->text.first = URI_FUNC(ConstPwd); + segment->text.afterLast = URI_FUNC(ConstPwd) + 1; + uri->pathHead = segment; + return URI_TRUE; } +static UriBool URI_FUNC(PrependNewDotSegment)(URI_TYPE(Uri) * uri, + UriMemoryManager * memory) { + assert(uri != NULL); + assert(memory != NULL); + URI_TYPE(PathSegment) * const segment = + memory->malloc(memory, 1 * sizeof(URI_TYPE(PathSegment))); -static UriBool URI_FUNC(PrependNewDotSegment)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { - assert(uri != NULL); - assert(memory != NULL); + if (segment == NULL) { + return URI_FALSE; /* i.e. raise malloc error */ + } - { - URI_TYPE(PathSegment) * const segment = memory->malloc(memory, 1 * sizeof(URI_TYPE(PathSegment))); + segment->next = uri->pathHead; - if (segment == NULL) { - return URI_FALSE; /* i.e. raise malloc error */ - } + URI_TYPE(TextRange) dotRange; + dotRange.first = URI_FUNC(ConstPwd); + dotRange.afterLast = URI_FUNC(ConstPwd) + 1; - segment->next = uri->pathHead; + if (uri->owner == URI_TRUE) { + if (URI_FUNC(CopyRange)(&(segment->text), &dotRange, memory) == URI_FALSE) { + memory->free(memory, segment); + return URI_FALSE; /* i.e. raise malloc error */ + } + } else { + segment->text = dotRange; /* copies all members */ + } - { - URI_TYPE(TextRange) dotRange; - dotRange.first = URI_FUNC(ConstPwd); - dotRange.afterLast = URI_FUNC(ConstPwd) + 1; + uri->pathHead = segment; - if (uri->owner == URI_TRUE) { - if (URI_FUNC(CopyRange)(&(segment->text), &dotRange, memory) == URI_FALSE) { - memory->free(memory, segment); - return URI_FALSE; /* i.e. raise malloc error */ - } - } else { - segment->text = dotRange; /* copies all members */ - } - } - - uri->pathHead = segment; - } - - return URI_TRUE; + return URI_TRUE; } - - /* When dropping a scheme from a URI without a host and with a colon (":") * in the first path segment, a consecutive reparse would rightfully * mis-classify the first path segment as a scheme due to the colon. @@ -721,44 +715,37 @@ static UriBool URI_FUNC(PrependNewDotSegment)(URI_TYPE(Uri) * uri, UriMemoryMana * Returns URI_TRUE for (a) nothing to do or (b) successful changes. * Returns URI_FALSE to signal out-of-memory. */ -UriBool URI_FUNC(FixPathNoScheme)(URI_TYPE(Uri) * uri, - UriMemoryManager * memory) { - assert(uri != NULL); - assert(memory != NULL); - - if ((uri->absolutePath == URI_TRUE) - || (uri->pathHead == NULL) - || (uri->scheme.first != NULL) - || URI_FUNC(HasHost)(uri)) { - return URI_TRUE; /* i.e. nothing to do */ - } - - /* Check for troublesome first path segment containing a colon */ - { - UriBool colonFound = URI_FALSE; - const URI_CHAR * walker = uri->pathHead->text.first; - - while (walker < uri->pathHead->text.afterLast) { - if (walker[0] == _UT(':')) { - colonFound = URI_TRUE; - break; - } - walker++; - } - - assert((walker == uri->pathHead->text.afterLast) || (colonFound == URI_TRUE)); - - if (colonFound == URI_FALSE) { - return URI_TRUE; /* i.e. nothing to do */ - } - } - - /* Insert "." segment in front */ - return URI_FUNC(PrependNewDotSegment)(uri, memory); +UriBool URI_FUNC(FixPathNoScheme)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { + assert(uri != NULL); + assert(memory != NULL); + + if ((uri->absolutePath == URI_TRUE) || (uri->pathHead == NULL) + || (uri->scheme.first != NULL) || URI_FUNC(HasHost)(uri)) { + return URI_TRUE; /* i.e. nothing to do */ + } + + /* Check for troublesome first path segment containing a colon */ + UriBool colonFound = URI_FALSE; + const URI_CHAR * walker = uri->pathHead->text.first; + + while (walker < uri->pathHead->text.afterLast) { + if (walker[0] == _UT(':')) { + colonFound = URI_TRUE; + break; + } + walker++; + } + + assert((walker == uri->pathHead->text.afterLast) || (colonFound == URI_TRUE)); + + if (colonFound == URI_FALSE) { + return URI_TRUE; /* i.e. nothing to do */ + } + + /* Insert "." segment in front */ + return URI_FUNC(PrependNewDotSegment)(uri, memory); } - - /* When dropping a host from a URI without a scheme, an absolute path * and and empty first path segment, a consecutive reparse would rightfully * mis-classify the first path segment as a host marker due to the "//". @@ -774,38 +761,30 @@ UriBool URI_FUNC(FixPathNoScheme)(URI_TYPE(Uri) * uri, * Returns URI_FALSE to signal out-of-memory. */ UriBool URI_FUNC(EnsureThatPathIsNotMistakenForHost)(URI_TYPE(Uri) * uri, - UriMemoryManager * memory) { - assert(uri != NULL); - assert(memory != NULL); - - if ((URI_FUNC(HasHost)(uri) == URI_TRUE) - || (uri->absolutePath == URI_FALSE) - || (uri->pathHead == NULL) - || (uri->pathHead == uri->pathTail) /* i.e. no second slash */ - || (uri->pathHead->text.first != uri->pathHead->text.afterLast)) { - return URI_TRUE; /* i.e. nothing to do */ - } - - /* Insert "." segment in front */ - return URI_FUNC(PrependNewDotSegment)(uri, memory); + UriMemoryManager * memory) { + assert(uri != NULL); + assert(memory != NULL); + + if ((URI_FUNC(HasHost)(uri) == URI_TRUE) || (uri->absolutePath == URI_FALSE) + || (uri->pathHead == NULL) + || (uri->pathHead == uri->pathTail) /* i.e. no second slash */ + || (uri->pathHead->text.first != uri->pathHead->text.afterLast)) { + return URI_TRUE; /* i.e. nothing to do */ + } + + /* Insert "." segment in front */ + return URI_FUNC(PrependNewDotSegment)(uri, memory); } - - -void URI_FUNC(FixEmptyTrailSegment)(URI_TYPE(Uri) * uri, - UriMemoryManager * memory) { - /* Fix path if only one empty segment */ - if (!uri->absolutePath - && !URI_FUNC(HasHost)(uri) - && (uri->pathHead != NULL) - && (uri->pathHead->next == NULL) - && (uri->pathHead->text.first == uri->pathHead->text.afterLast)) { - memory->free(memory, uri->pathHead); - uri->pathHead = NULL; - uri->pathTail = NULL; - } +void URI_FUNC(FixEmptyTrailSegment)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { + /* Fix path if only one empty segment */ + if (!uri->absolutePath && !URI_FUNC(HasHost)(uri) && (uri->pathHead != NULL) + && (uri->pathHead->next == NULL) + && (uri->pathHead->text.first == uri->pathHead->text.afterLast)) { + memory->free(memory, uri->pathHead); + uri->pathHead = NULL; + uri->pathTail = NULL; + } } - - #endif diff --git a/ext/uri/uriparser/src/UriCommon.h b/ext/uri/uriparser/src/UriCommon.h index 96beb087a03d3..d141935f19e5a 100644 --- a/ext/uri/uriparser/src/UriCommon.h +++ b/ext/uri/uriparser/src/UriCommon.h @@ -38,35 +38,34 @@ */ #if (defined(URI_PASS_ANSI) && !defined(URI_COMMON_H_ANSI)) \ - || (defined(URI_PASS_UNICODE) && !defined(URI_COMMON_H_UNICODE)) \ - || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) + || (defined(URI_PASS_UNICODE) && !defined(URI_COMMON_H_UNICODE)) \ + || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* What encodings are enabled? */ -#include -#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +# include +# if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriCommon.h" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriCommon.h" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriCommon.h" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriCommon.h" +# undef URI_PASS_UNICODE +# endif /* Only one pass for each encoding */ -#elif (defined(URI_PASS_ANSI) && !defined(URI_COMMON_H_ANSI) \ - && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \ - && !defined(URI_COMMON_H_UNICODE) && defined(URI_ENABLE_UNICODE)) -# ifdef URI_PASS_ANSI -# define URI_COMMON_H_ANSI 1 -# include -# else -# define URI_COMMON_H_UNICODE 1 -# include -# endif - - +# elif (defined(URI_PASS_ANSI) && !defined(URI_COMMON_H_ANSI) \ + && defined(URI_ENABLE_ANSI)) \ + || (defined(URI_PASS_UNICODE) && !defined(URI_COMMON_H_UNICODE) \ + && defined(URI_ENABLE_UNICODE)) +# ifdef URI_PASS_ANSI +# define URI_COMMON_H_ANSI 1 +# include +# else +# define URI_COMMON_H_UNICODE 1 +# include +# endif /* Used to point to from empty path segments. * X.first and X.afterLast must be the same non-NULL value then. */ @@ -74,40 +73,37 @@ extern const URI_CHAR * const URI_FUNC(SafeToPointTo); extern const URI_CHAR * const URI_FUNC(ConstPwd); extern const URI_CHAR * const URI_FUNC(ConstParent); - - void URI_FUNC(ResetUri)(URI_TYPE(Uri) * uri); int URI_FUNC(FreeUriPath)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); -int URI_FUNC(CompareRange)( - const URI_TYPE(TextRange) * a, - const URI_TYPE(TextRange) * b); +int URI_FUNC(CompareRange)(const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b); UriBool URI_FUNC(CopyRange)(URI_TYPE(TextRange) * destRange, - const URI_TYPE(TextRange) * sourceRange, UriMemoryManager * memory); + const URI_TYPE(TextRange) * sourceRange, + UriMemoryManager * memory); UriBool URI_FUNC(CopyRangeAsNeeded)(URI_TYPE(TextRange) * destRange, - const URI_TYPE(TextRange) * sourceRange, UriMemoryManager * memory); + const URI_TYPE(TextRange) * sourceRange, + UriMemoryManager * memory); UriBool URI_FUNC(RemoveDotSegmentsAbsolute)(URI_TYPE(Uri) * uri, - UriMemoryManager * memory); -UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri, - UriBool relative, UriBool pathOwned, UriMemoryManager * memory); + UriMemoryManager * memory); +UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri, UriBool relative, + UriBool pathOwned, UriMemoryManager * memory); unsigned char URI_FUNC(HexdigToInt)(URI_CHAR hexdig); URI_CHAR URI_FUNC(HexToLetterEx)(unsigned int value, UriBool uppercase); UriBool URI_FUNC(CopyPath)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * source, - UriMemoryManager * memory); -UriBool URI_FUNC(CopyAuthority)(URI_TYPE(Uri) * dest, - const URI_TYPE(Uri) * source, UriMemoryManager * memory); + UriMemoryManager * memory); +UriBool URI_FUNC(CopyAuthority)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * source, + UriMemoryManager * memory); UriBool URI_FUNC(FixAmbiguity)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); UriBool URI_FUNC(FixPathNoScheme)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); -UriBool URI_FUNC(EnsureThatPathIsNotMistakenForHost)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); -void URI_FUNC(FixEmptyTrailSegment)(URI_TYPE(Uri) * uri, - UriMemoryManager * memory); - +UriBool URI_FUNC(EnsureThatPathIsNotMistakenForHost)(URI_TYPE(Uri) * uri, + UriMemoryManager * memory); +void URI_FUNC(FixEmptyTrailSegment)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); -#endif +# endif #endif diff --git a/ext/uri/uriparser/src/UriCompare.c b/ext/uri/uriparser/src/UriCompare.c index bca3db92361d9..781100e7e6f49 100644 --- a/ext/uri/uriparser/src/UriCompare.c +++ b/ext/uri/uriparser/src/UriCompare.c @@ -41,128 +41,120 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriCompare.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriCompare.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriCompare.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriCompare.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include -# include "UriCommon.h" -#endif - - - -UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, - const URI_TYPE(Uri) * b) { - /* NOTE: Both NULL means equal! */ - if ((a == NULL) || (b == NULL)) { - return ((a == NULL) && (b == NULL)) ? URI_TRUE : URI_FALSE; - } - - /* scheme */ - if (URI_FUNC(CompareRange)(&(a->scheme), &(b->scheme))) { - return URI_FALSE; - } - - /* absolutePath */ - if ((a->scheme.first == NULL)&& (a->absolutePath != b->absolutePath)) { - return URI_FALSE; - } - - /* userInfo */ - if (URI_FUNC(CompareRange)(&(a->userInfo), &(b->userInfo))) { - return URI_FALSE; - } - - /* Host */ - if (((a->hostData.ip4 == NULL) != (b->hostData.ip4 == NULL)) - || ((a->hostData.ip6 == NULL) != (b->hostData.ip6 == NULL)) - || ((a->hostData.ipFuture.first == NULL) - != (b->hostData.ipFuture.first == NULL))) { - return URI_FALSE; - } - - if (a->hostData.ip4 != NULL) { - if (memcmp(a->hostData.ip4->data, b->hostData.ip4->data, 4)) { - return URI_FALSE; - } - } - - if (a->hostData.ip6 != NULL) { - if (memcmp(a->hostData.ip6->data, b->hostData.ip6->data, 16)) { - return URI_FALSE; - } - } - - if (a->hostData.ipFuture.first != NULL) { - if (URI_FUNC(CompareRange)(&(a->hostData.ipFuture), &(b->hostData.ipFuture))) { - return URI_FALSE; - } - } - - if ((a->hostData.ip4 == NULL) - && (a->hostData.ip6 == NULL) - && (a->hostData.ipFuture.first == NULL)) { - if (URI_FUNC(CompareRange)(&(a->hostText), &(b->hostText))) { - return URI_FALSE; - } - } - - /* portText */ - if (URI_FUNC(CompareRange)(&(a->portText), &(b->portText))) { - return URI_FALSE; - } - - /* Path */ - if ((a->pathHead == NULL) != (b->pathHead == NULL)) { - return URI_FALSE; - } - - if (a->pathHead != NULL) { - URI_TYPE(PathSegment) * walkA = a->pathHead; - URI_TYPE(PathSegment) * walkB = b->pathHead; - do { - if (URI_FUNC(CompareRange)(&(walkA->text), &(walkB->text))) { - return URI_FALSE; - } - if ((walkA->next == NULL) != (walkB->next == NULL)) { - return URI_FALSE; - } - walkA = walkA->next; - walkB = walkB->next; - } while (walkA != NULL); - } - - /* query */ - if (URI_FUNC(CompareRange)(&(a->query), &(b->query))) { - return URI_FALSE; - } - - /* fragment */ - if (URI_FUNC(CompareRange)(&(a->fragment), &(b->fragment))) { - return URI_FALSE; - } - - return URI_TRUE; /* Equal*/ +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include +# include "UriCommon.h" +# endif + +UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b) { + /* NOTE: Both NULL means equal! */ + if ((a == NULL) || (b == NULL)) { + return ((a == NULL) && (b == NULL)) ? URI_TRUE : URI_FALSE; + } + + /* scheme */ + if (URI_FUNC(CompareRange)(&(a->scheme), &(b->scheme))) { + return URI_FALSE; + } + + /* absolutePath */ + if ((a->scheme.first == NULL) && (a->absolutePath != b->absolutePath)) { + return URI_FALSE; + } + + /* userInfo */ + if (URI_FUNC(CompareRange)(&(a->userInfo), &(b->userInfo))) { + return URI_FALSE; + } + + /* Host */ + if (((a->hostData.ip4 == NULL) != (b->hostData.ip4 == NULL)) + || ((a->hostData.ip6 == NULL) != (b->hostData.ip6 == NULL)) + || ((a->hostData.ipFuture.first == NULL) + != (b->hostData.ipFuture.first == NULL))) { + return URI_FALSE; + } + + if (a->hostData.ip4 != NULL) { + if (memcmp(a->hostData.ip4->data, b->hostData.ip4->data, 4)) { + return URI_FALSE; + } + } + + if (a->hostData.ip6 != NULL) { + if (memcmp(a->hostData.ip6->data, b->hostData.ip6->data, 16)) { + return URI_FALSE; + } + } + + if (a->hostData.ipFuture.first != NULL) { + if (URI_FUNC(CompareRange)(&(a->hostData.ipFuture), &(b->hostData.ipFuture))) { + return URI_FALSE; + } + } + + if ((a->hostData.ip4 == NULL) && (a->hostData.ip6 == NULL) + && (a->hostData.ipFuture.first == NULL)) { + if (URI_FUNC(CompareRange)(&(a->hostText), &(b->hostText))) { + return URI_FALSE; + } + } + + /* portText */ + if (URI_FUNC(CompareRange)(&(a->portText), &(b->portText))) { + return URI_FALSE; + } + + /* Path */ + if ((a->pathHead == NULL) != (b->pathHead == NULL)) { + return URI_FALSE; + } + + if (a->pathHead != NULL) { + URI_TYPE(PathSegment) * walkA = a->pathHead; + URI_TYPE(PathSegment) * walkB = b->pathHead; + do { + if (URI_FUNC(CompareRange)(&(walkA->text), &(walkB->text))) { + return URI_FALSE; + } + if ((walkA->next == NULL) != (walkB->next == NULL)) { + return URI_FALSE; + } + walkA = walkA->next; + walkB = walkB->next; + } while (walkA != NULL); + } + + /* query */ + if (URI_FUNC(CompareRange)(&(a->query), &(b->query))) { + return URI_FALSE; + } + + /* fragment */ + if (URI_FUNC(CompareRange)(&(a->fragment), &(b->fragment))) { + return URI_FALSE; + } + + return URI_TRUE; /* Equal*/ } - - #endif diff --git a/ext/uri/uriparser/src/UriConfig.h b/ext/uri/uriparser/src/UriConfig.h index 8c58a3222e210..e265c0a9a1154 100644 --- a/ext/uri/uriparser/src/UriConfig.h +++ b/ext/uri/uriparser/src/UriConfig.h @@ -37,17 +37,13 @@ */ #if !defined(URI_CONFIG_H) -# define URI_CONFIG_H 1 +# define URI_CONFIG_H 1 - - -#define PACKAGE_VERSION "@PROJECT_VERSION@" +# define PACKAGE_VERSION "@PROJECT_VERSION@" /* -#define HAVE_WPRINTF* +#define HAVE_WPRINTF #define HAVE_REALLOCARRAY */ - - -#endif /* !defined(URI_CONFIG_H) */ +#endif /* !defined(URI_CONFIG_H) */ diff --git a/ext/uri/uriparser/src/UriConfig.h.in b/ext/uri/uriparser/src/UriConfig.h.in index a16a93381c102..b9a85a8fe02af 100644 --- a/ext/uri/uriparser/src/UriConfig.h.in +++ b/ext/uri/uriparser/src/UriConfig.h.in @@ -37,15 +37,11 @@ */ #if !defined(URI_CONFIG_H) -# define URI_CONFIG_H 1 +# define URI_CONFIG_H 1 - - -#define PACKAGE_VERSION "@PROJECT_VERSION@" +# define PACKAGE_VERSION "@PROJECT_VERSION@" #cmakedefine HAVE_WPRINTF #cmakedefine HAVE_REALLOCARRAY - - -#endif /* !defined(URI_CONFIG_H) */ +#endif /* !defined(URI_CONFIG_H) */ diff --git a/ext/uri/uriparser/src/UriCopy.c b/ext/uri/uriparser/src/UriCopy.c index 85f2a8aa17926..3bc6b0dbe3f4c 100644 --- a/ext/uri/uriparser/src/UriCopy.c +++ b/ext/uri/uriparser/src/UriCopy.c @@ -48,192 +48,195 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriCopy.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriCopy.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriCopy.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriCopy.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -# include "UriMemory.h" -# include "UriNormalize.h" -# include "UriCopy.h" -#endif - - +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +# include "UriNormalize.h" +# include "UriCopy.h" +# endif static void URI_FUNC(PreventLeakageAfterCopy)(URI_TYPE(Uri) * uri, - unsigned int revertMask, UriMemoryManager * memory) { - URI_FUNC(PreventLeakage)(uri, revertMask, memory); - - if (uri->hostData.ip4 != NULL) { - memory->free(memory, uri->hostData.ip4); - uri->hostData.ip4 = NULL; - } else if (uri->hostData.ip6 != NULL) { - memory->free(memory, uri->hostData.ip6); - uri->hostData.ip6 = NULL; - } - - if (revertMask & URI_NORMALIZE_PORT) { - if (uri->portText.first != uri->portText.afterLast) { - memory->free(memory, (URI_CHAR *)uri->portText.first); - } - uri->portText.first = NULL; - uri->portText.afterLast = NULL; - } + unsigned int revertMask, + UriMemoryManager * memory) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + + if (uri->hostData.ip4 != NULL) { + memory->free(memory, uri->hostData.ip4); + uri->hostData.ip4 = NULL; + } else if (uri->hostData.ip6 != NULL) { + memory->free(memory, uri->hostData.ip6); + uri->hostData.ip6 = NULL; + } + + if (revertMask & URI_NORMALIZE_PORT) { + if (uri->portText.first != uri->portText.afterLast) { + memory->free(memory, (URI_CHAR *)uri->portText.first); + } + uri->portText.first = NULL; + uri->portText.afterLast = NULL; + } } - - -int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, - const URI_TYPE(Uri) * sourceUri, UriMemoryManager * memory) { - unsigned int revertMask = URI_NORMALIZED; - - if (sourceUri == NULL || destUri == NULL) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - URI_FUNC(ResetUri)(destUri); - - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->scheme, &sourceUri->scheme, memory) == URI_FALSE) { - return URI_ERROR_MALLOC; - } - - revertMask |= URI_NORMALIZE_SCHEME; - - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->userInfo, &sourceUri->userInfo, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); - return URI_ERROR_MALLOC; - } - - revertMask |= URI_NORMALIZE_USER_INFO; - - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostText, &sourceUri->hostText, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); - return URI_ERROR_MALLOC; - } - - revertMask |= URI_NORMALIZE_HOST; - - if (sourceUri->hostData.ip4 == NULL) { - destUri->hostData.ip4 = NULL; - } else { - destUri->hostData.ip4 = memory->malloc(memory, sizeof(UriIp4)); - if (destUri->hostData.ip4 == NULL) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); - return URI_ERROR_MALLOC; - } - *(destUri->hostData.ip4) = *(sourceUri->hostData.ip4); - } - - if (sourceUri->hostData.ip6 == NULL) { - destUri->hostData.ip6 = NULL; - } else { - destUri->hostData.ip6 = memory->malloc(memory, sizeof(UriIp6)); - if (destUri->hostData.ip6 == NULL) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); - return URI_ERROR_MALLOC; - } - *(destUri->hostData.ip6) = *(sourceUri->hostData.ip6); - } - - if (sourceUri->hostData.ipFuture.first != NULL) { - destUri->hostData.ipFuture.first = destUri->hostText.first; - destUri->hostData.ipFuture.afterLast = destUri->hostText.afterLast; - } else if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostData.ipFuture, &sourceUri->hostData.ipFuture, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); - return URI_ERROR_MALLOC; - } - - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->portText, &sourceUri->portText, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); - return URI_ERROR_MALLOC; - } - - revertMask |= URI_NORMALIZE_PORT; - - destUri->pathHead = NULL; - destUri->pathTail = NULL; - - if (sourceUri->pathHead != NULL) { - URI_TYPE(PathSegment) * sourceWalker = sourceUri->pathHead; - URI_TYPE(PathSegment) * destPrev = NULL; - - while (sourceWalker != NULL) { - URI_TYPE(PathSegment) * destWalker = memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); - if (destWalker == NULL) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); - return URI_ERROR_MALLOC; - } - - destWalker->text.first = NULL; - destWalker->text.afterLast = NULL; - destWalker->next = NULL; - destWalker->reserved = NULL; - - if (destUri->pathHead == NULL) { - destUri->pathHead = destWalker; - revertMask |= URI_NORMALIZE_PATH; - } - - if (URI_FUNC(CopyRangeAsNeeded)(&destWalker->text, &sourceWalker->text, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); - return URI_ERROR_MALLOC; - } - - if (destPrev != NULL) { - destPrev->next = destWalker; - } - - destPrev = destWalker; - sourceWalker = sourceWalker->next; - - destUri->pathTail = destWalker; - } - } - - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->query, &sourceUri->query, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); - return URI_ERROR_MALLOC; - } - - revertMask |= URI_NORMALIZE_QUERY; - - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->fragment, &sourceUri->fragment, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); - return URI_ERROR_MALLOC; - } - - destUri->absolutePath = sourceUri->absolutePath; - destUri->owner = URI_TRUE; - destUri->reserved = NULL; - - return URI_SUCCESS; +int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, const URI_TYPE(Uri) * sourceUri, + UriMemoryManager * memory) { + unsigned int revertMask = URI_NORMALIZED; + + if (sourceUri == NULL || destUri == NULL) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + URI_FUNC(ResetUri)(destUri); + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->scheme, &sourceUri->scheme, memory) + == URI_FALSE) { + return URI_ERROR_MALLOC; + } + + revertMask |= URI_NORMALIZE_SCHEME; + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->userInfo, &sourceUri->userInfo, memory) + == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); + return URI_ERROR_MALLOC; + } + + revertMask |= URI_NORMALIZE_USER_INFO; + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostText, &sourceUri->hostText, memory) + == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); + return URI_ERROR_MALLOC; + } + + revertMask |= URI_NORMALIZE_HOST; + + if (sourceUri->hostData.ip4 == NULL) { + destUri->hostData.ip4 = NULL; + } else { + destUri->hostData.ip4 = memory->malloc(memory, sizeof(UriIp4)); + if (destUri->hostData.ip4 == NULL) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); + return URI_ERROR_MALLOC; + } + *(destUri->hostData.ip4) = *(sourceUri->hostData.ip4); + } + + if (sourceUri->hostData.ip6 == NULL) { + destUri->hostData.ip6 = NULL; + } else { + destUri->hostData.ip6 = memory->malloc(memory, sizeof(UriIp6)); + if (destUri->hostData.ip6 == NULL) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); + return URI_ERROR_MALLOC; + } + *(destUri->hostData.ip6) = *(sourceUri->hostData.ip6); + } + + if (sourceUri->hostData.ipFuture.first != NULL) { + destUri->hostData.ipFuture.first = destUri->hostText.first; + destUri->hostData.ipFuture.afterLast = destUri->hostText.afterLast; + } else if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostData.ipFuture, + &sourceUri->hostData.ipFuture, memory) + == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); + return URI_ERROR_MALLOC; + } + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->portText, &sourceUri->portText, memory) + == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); + return URI_ERROR_MALLOC; + } + + revertMask |= URI_NORMALIZE_PORT; + + destUri->pathHead = NULL; + destUri->pathTail = NULL; + + if (sourceUri->pathHead != NULL) { + URI_TYPE(PathSegment) * sourceWalker = sourceUri->pathHead; + URI_TYPE(PathSegment) * destPrev = NULL; + + while (sourceWalker != NULL) { + URI_TYPE(PathSegment) * destWalker = + memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); + if (destWalker == NULL) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); + return URI_ERROR_MALLOC; + } + + destWalker->text.first = NULL; + destWalker->text.afterLast = NULL; + destWalker->next = NULL; + destWalker->reserved = NULL; + + if (destUri->pathHead == NULL) { + destUri->pathHead = destWalker; + revertMask |= URI_NORMALIZE_PATH; + } + + if (URI_FUNC(CopyRangeAsNeeded)(&destWalker->text, &sourceWalker->text, + memory) + == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); + return URI_ERROR_MALLOC; + } + + if (destPrev != NULL) { + destPrev->next = destWalker; + } + + destPrev = destWalker; + sourceWalker = sourceWalker->next; + + destUri->pathTail = destWalker; + } + } + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->query, &sourceUri->query, memory) + == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); + return URI_ERROR_MALLOC; + } + + revertMask |= URI_NORMALIZE_QUERY; + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->fragment, &sourceUri->fragment, memory) + == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); + return URI_ERROR_MALLOC; + } + + destUri->absolutePath = sourceUri->absolutePath; + destUri->owner = URI_TRUE; + destUri->reserved = NULL; + + return URI_SUCCESS; } - - -int URI_FUNC(CopyUri)(URI_TYPE(Uri) * destUri, - const URI_TYPE(Uri) * sourceUri) { - return URI_FUNC(CopyUriMm)(destUri, sourceUri, NULL); +int URI_FUNC(CopyUri)(URI_TYPE(Uri) * destUri, const URI_TYPE(Uri) * sourceUri) { + return URI_FUNC(CopyUriMm)(destUri, sourceUri, NULL); } #endif diff --git a/ext/uri/uriparser/src/UriCopy.h b/ext/uri/uriparser/src/UriCopy.h index 952b1df4f9cb3..430f5dc6da24a 100644 --- a/ext/uri/uriparser/src/UriCopy.h +++ b/ext/uri/uriparser/src/UriCopy.h @@ -39,40 +39,38 @@ */ #if (defined(URI_PASS_ANSI) && !defined(URI_COPY_H_ANSI)) \ - || (defined(URI_PASS_UNICODE) && !defined(URI_COPY_H_UNICODE)) \ - || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) + || (defined(URI_PASS_UNICODE) && !defined(URI_COPY_H_UNICODE)) \ + || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* What encodings are enabled? */ -#include -#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +# include +# if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriCopy.h" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriCopy.h" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriCopy.h" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriCopy.h" +# undef URI_PASS_UNICODE +# endif /* Only one pass for each encoding */ -#elif (defined(URI_PASS_ANSI) && !defined(URI_COPY_H_ANSI) \ - && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \ - && !defined(URI_COPY_H_UNICODE) && defined(URI_ENABLE_UNICODE)) -# ifdef URI_PASS_ANSI -# define URI_COPY_H_ANSI 1 -# include -# else -# define URI_COPY_H_UNICODE 1 -# include -# endif +# elif (defined(URI_PASS_ANSI) && !defined(URI_COPY_H_ANSI) \ + && defined(URI_ENABLE_ANSI)) \ + || (defined(URI_PASS_UNICODE) && !defined(URI_COPY_H_UNICODE) \ + && defined(URI_ENABLE_UNICODE)) +# ifdef URI_PASS_ANSI +# define URI_COPY_H_ANSI 1 +# include +# else +# define URI_COPY_H_UNICODE 1 +# include +# endif +int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, const URI_TYPE(Uri) * sourceUri, + UriMemoryManager * memory); +int URI_FUNC(CopyUri)(URI_TYPE(Uri) * destUri, const URI_TYPE(Uri) * sourceUri); - -int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, - const URI_TYPE(Uri) * sourceUri, UriMemoryManager * memory); -int URI_FUNC(CopyUri)(URI_TYPE(Uri) * destUri, - const URI_TYPE(Uri) * sourceUri); - -#endif +# endif #endif diff --git a/ext/uri/uriparser/src/UriEscape.c b/ext/uri/uriparser/src/UriEscape.c index 366955f4adad4..b23050783fb33 100644 --- a/ext/uri/uriparser/src/UriEscape.c +++ b/ext/uri/uriparser/src/UriEscape.c @@ -41,416 +41,399 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriEscape.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriEscape.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriEscape.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriEscape.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -#endif - - - -URI_CHAR * URI_FUNC(Escape)(const URI_CHAR * in, URI_CHAR * out, - UriBool spaceToPlus, UriBool normalizeBreaks) { - return URI_FUNC(EscapeEx)(in, NULL, out, spaceToPlus, normalizeBreaks); +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# endif + +URI_CHAR * URI_FUNC(Escape)(const URI_CHAR * in, URI_CHAR * out, UriBool spaceToPlus, + UriBool normalizeBreaks) { + return URI_FUNC(EscapeEx)(in, NULL, out, spaceToPlus, normalizeBreaks); } - - -URI_CHAR * URI_FUNC(EscapeEx)(const URI_CHAR * inFirst, - const URI_CHAR * inAfterLast, URI_CHAR * out, - UriBool spaceToPlus, UriBool normalizeBreaks) { - const URI_CHAR * read = inFirst; - URI_CHAR * write = out; - UriBool prevWasCr = URI_FALSE; - if ((out == NULL) || (inFirst == out)) { - return NULL; - } else if (inFirst == NULL) { - if (out != NULL) { - out[0] = _UT('\0'); - } - return out; - } - - for (;;) { - if ((inAfterLast != NULL) && (read >= inAfterLast)) { - write[0] = _UT('\0'); - return write; - } - - switch (read[0]) { - case _UT('\0'): - write[0] = _UT('\0'); - return write; - - case _UT(' '): - if (spaceToPlus) { - write[0] = _UT('+'); - write++; - } else { - write[0] = _UT('%'); - write[1] = _UT('2'); - write[2] = _UT('0'); - write += 3; - } - prevWasCr = URI_FALSE; - break; - - case _UT('a'): /* ALPHA */ - case _UT('A'): - case _UT('b'): - case _UT('B'): - case _UT('c'): - case _UT('C'): - case _UT('d'): - case _UT('D'): - case _UT('e'): - case _UT('E'): - case _UT('f'): - case _UT('F'): - case _UT('g'): - case _UT('G'): - case _UT('h'): - case _UT('H'): - case _UT('i'): - case _UT('I'): - case _UT('j'): - case _UT('J'): - case _UT('k'): - case _UT('K'): - case _UT('l'): - case _UT('L'): - case _UT('m'): - case _UT('M'): - case _UT('n'): - case _UT('N'): - case _UT('o'): - case _UT('O'): - case _UT('p'): - case _UT('P'): - case _UT('q'): - case _UT('Q'): - case _UT('r'): - case _UT('R'): - case _UT('s'): - case _UT('S'): - case _UT('t'): - case _UT('T'): - case _UT('u'): - case _UT('U'): - case _UT('v'): - case _UT('V'): - case _UT('w'): - case _UT('W'): - case _UT('x'): - case _UT('X'): - case _UT('y'): - case _UT('Y'): - case _UT('z'): - case _UT('Z'): - case _UT('0'): /* DIGIT */ - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): - case _UT('-'): /* "-" / "." / "_" / "~" */ - case _UT('.'): - case _UT('_'): - case _UT('~'): - /* Copy unmodified */ - write[0] = read[0]; - write++; - - prevWasCr = URI_FALSE; - break; - - case _UT('\x0a'): - if (normalizeBreaks) { - if (!prevWasCr) { - write[0] = _UT('%'); - write[1] = _UT('0'); - write[2] = _UT('D'); - write[3] = _UT('%'); - write[4] = _UT('0'); - write[5] = _UT('A'); - write += 6; - } - } else { - write[0] = _UT('%'); - write[1] = _UT('0'); - write[2] = _UT('A'); - write += 3; - } - prevWasCr = URI_FALSE; - break; - - case _UT('\x0d'): - if (normalizeBreaks) { - write[0] = _UT('%'); - write[1] = _UT('0'); - write[2] = _UT('D'); - write[3] = _UT('%'); - write[4] = _UT('0'); - write[5] = _UT('A'); - write += 6; - } else { - write[0] = _UT('%'); - write[1] = _UT('0'); - write[2] = _UT('D'); - write += 3; - } - prevWasCr = URI_TRUE; - break; - - default: - /* Percent encode */ - { - const unsigned char code = (unsigned char)read[0]; - /* Uppercase recommended in (last sentence of) section 2.1 * - * of RFC 3986: * - * https://datatracker.ietf.org/doc/html/rfc3986#section-2.1 */ - write[0] = _UT('%'); - write[1] = URI_FUNC(HexToLetterEx)(code >> 4, URI_TRUE); - write[2] = URI_FUNC(HexToLetterEx)(code & 0x0f, URI_TRUE); - write += 3; - } - prevWasCr = URI_FALSE; - break; - } - - read++; - } +URI_CHAR * URI_FUNC(EscapeEx)(const URI_CHAR * inFirst, const URI_CHAR * inAfterLast, + URI_CHAR * out, UriBool spaceToPlus, + UriBool normalizeBreaks) { + const URI_CHAR * read = inFirst; + URI_CHAR * write = out; + UriBool prevWasCr = URI_FALSE; + if ((out == NULL) || (inFirst == out)) { + return NULL; + } else if (inFirst == NULL) { + if (out != NULL) { + out[0] = _UT('\0'); + } + return out; + } + + for (;;) { + if ((inAfterLast != NULL) && (read >= inAfterLast)) { + write[0] = _UT('\0'); + return write; + } + + switch (read[0]) { + case _UT('\0'): + write[0] = _UT('\0'); + return write; + + case _UT(' '): + if (spaceToPlus) { + write[0] = _UT('+'); + write++; + } else { + write[0] = _UT('%'); + write[1] = _UT('2'); + write[2] = _UT('0'); + write += 3; + } + prevWasCr = URI_FALSE; + break; + + case _UT('a'): /* ALPHA */ + case _UT('A'): + case _UT('b'): + case _UT('B'): + case _UT('c'): + case _UT('C'): + case _UT('d'): + case _UT('D'): + case _UT('e'): + case _UT('E'): + case _UT('f'): + case _UT('F'): + case _UT('g'): + case _UT('G'): + case _UT('h'): + case _UT('H'): + case _UT('i'): + case _UT('I'): + case _UT('j'): + case _UT('J'): + case _UT('k'): + case _UT('K'): + case _UT('l'): + case _UT('L'): + case _UT('m'): + case _UT('M'): + case _UT('n'): + case _UT('N'): + case _UT('o'): + case _UT('O'): + case _UT('p'): + case _UT('P'): + case _UT('q'): + case _UT('Q'): + case _UT('r'): + case _UT('R'): + case _UT('s'): + case _UT('S'): + case _UT('t'): + case _UT('T'): + case _UT('u'): + case _UT('U'): + case _UT('v'): + case _UT('V'): + case _UT('w'): + case _UT('W'): + case _UT('x'): + case _UT('X'): + case _UT('y'): + case _UT('Y'): + case _UT('z'): + case _UT('Z'): + case _UT('0'): /* DIGIT */ + case _UT('1'): + case _UT('2'): + case _UT('3'): + case _UT('4'): + case _UT('5'): + case _UT('6'): + case _UT('7'): + case _UT('8'): + case _UT('9'): + case _UT('-'): /* "-" / "." / "_" / "~" */ + case _UT('.'): + case _UT('_'): + case _UT('~'): + /* Copy unmodified */ + write[0] = read[0]; + write++; + + prevWasCr = URI_FALSE; + break; + + case _UT('\x0a'): + if (normalizeBreaks) { + if (!prevWasCr) { + write[0] = _UT('%'); + write[1] = _UT('0'); + write[2] = _UT('D'); + write[3] = _UT('%'); + write[4] = _UT('0'); + write[5] = _UT('A'); + write += 6; + } + } else { + write[0] = _UT('%'); + write[1] = _UT('0'); + write[2] = _UT('A'); + write += 3; + } + prevWasCr = URI_FALSE; + break; + + case _UT('\x0d'): + if (normalizeBreaks) { + write[0] = _UT('%'); + write[1] = _UT('0'); + write[2] = _UT('D'); + write[3] = _UT('%'); + write[4] = _UT('0'); + write[5] = _UT('A'); + write += 6; + } else { + write[0] = _UT('%'); + write[1] = _UT('0'); + write[2] = _UT('D'); + write += 3; + } + prevWasCr = URI_TRUE; + break; + + default: + /* Percent encode */ + { + const unsigned char code = (unsigned char)read[0]; + /* Uppercase recommended in (last sentence of) section 2.1 * + * of RFC 3986: * + * https://datatracker.ietf.org/doc/html/rfc3986#section-2.1 */ + write[0] = _UT('%'); + write[1] = URI_FUNC(HexToLetterEx)(code >> 4, URI_TRUE); + write[2] = URI_FUNC(HexToLetterEx)(code & 0x0f, URI_TRUE); + write += 3; + } + prevWasCr = URI_FALSE; + break; + } + + read++; + } } - - const URI_CHAR * URI_FUNC(UnescapeInPlace)(URI_CHAR * inout) { - return URI_FUNC(UnescapeInPlaceEx)(inout, URI_FALSE, URI_BR_DONT_TOUCH); + return URI_FUNC(UnescapeInPlaceEx)(inout, URI_FALSE, URI_BR_DONT_TOUCH); } - - -const URI_CHAR * URI_FUNC(UnescapeInPlaceEx)(URI_CHAR * inout, - UriBool plusToSpace, UriBreakConversion breakConversion) { - URI_CHAR * read = inout; - URI_CHAR * write = inout; - UriBool prevWasCr = URI_FALSE; - - if (inout == NULL) { - return NULL; - } - - for (;;) { - switch (read[0]) { - case _UT('\0'): - if (read > write) { - write[0] = _UT('\0'); - } - return write; - - case _UT('%'): - switch (read[1]) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): - case _UT('a'): - case _UT('b'): - case _UT('c'): - case _UT('d'): - case _UT('e'): - case _UT('f'): - case _UT('A'): - case _UT('B'): - case _UT('C'): - case _UT('D'): - case _UT('E'): - case _UT('F'): - switch (read[2]) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): - case _UT('a'): - case _UT('b'): - case _UT('c'): - case _UT('d'): - case _UT('e'): - case _UT('f'): - case _UT('A'): - case _UT('B'): - case _UT('C'): - case _UT('D'): - case _UT('E'): - case _UT('F'): - { - /* Percent group found */ - const unsigned char left = URI_FUNC(HexdigToInt)(read[1]); - const unsigned char right = URI_FUNC(HexdigToInt)(read[2]); - const int code = 16 * left + right; - switch (code) { - case 10: - switch (breakConversion) { - case URI_BR_TO_LF: - if (!prevWasCr) { - write[0] = (URI_CHAR)10; - write++; - } - break; - - case URI_BR_TO_CRLF: - if (!prevWasCr) { - write[0] = (URI_CHAR)13; - write[1] = (URI_CHAR)10; - write += 2; - } - break; - - case URI_BR_TO_CR: - if (!prevWasCr) { - write[0] = (URI_CHAR)13; - write++; - } - break; - - case URI_BR_DONT_TOUCH: - default: - write[0] = (URI_CHAR)10; - write++; - - } - prevWasCr = URI_FALSE; - break; - - case 13: - switch (breakConversion) { - case URI_BR_TO_LF: - write[0] = (URI_CHAR)10; - write++; - break; - - case URI_BR_TO_CRLF: - write[0] = (URI_CHAR)13; - write[1] = (URI_CHAR)10; - write += 2; - break; - - case URI_BR_TO_CR: - write[0] = (URI_CHAR)13; - write++; - break; - - case URI_BR_DONT_TOUCH: - default: - write[0] = (URI_CHAR)13; - write++; - - } - prevWasCr = URI_TRUE; - break; - - default: - write[0] = (URI_CHAR)(code); - write++; - - prevWasCr = URI_FALSE; - - } - read += 3; - } - break; - - default: - /* Copy two chars unmodified and */ - /* look at this char again */ - if (read > write) { - write[0] = read[0]; - write[1] = read[1]; - } - read += 2; - write += 2; - - prevWasCr = URI_FALSE; - } - break; - - default: - /* Copy one char unmodified and */ - /* look at this char again */ - if (read > write) { - write[0] = read[0]; - } - read++; - write++; - - prevWasCr = URI_FALSE; - } - break; - - case _UT('+'): - if (plusToSpace) { - /* Convert '+' to ' ' */ - write[0] = _UT(' '); - } else { - /* Copy one char unmodified */ - if (read > write) { - write[0] = read[0]; - } - } - read++; - write++; - - prevWasCr = URI_FALSE; - break; - - default: - /* Copy one char unmodified */ - if (read > write) { - write[0] = read[0]; - } - read++; - write++; - - prevWasCr = URI_FALSE; - } - } +const URI_CHAR * URI_FUNC(UnescapeInPlaceEx)(URI_CHAR * inout, UriBool plusToSpace, + UriBreakConversion breakConversion) { + URI_CHAR * read = inout; + URI_CHAR * write = inout; + UriBool prevWasCr = URI_FALSE; + + if (inout == NULL) { + return NULL; + } + + for (;;) { + switch (read[0]) { + case _UT('\0'): + if (read > write) { + write[0] = _UT('\0'); + } + return write; + + case _UT('%'): + switch (read[1]) { + case _UT('0'): + case _UT('1'): + case _UT('2'): + case _UT('3'): + case _UT('4'): + case _UT('5'): + case _UT('6'): + case _UT('7'): + case _UT('8'): + case _UT('9'): + case _UT('a'): + case _UT('b'): + case _UT('c'): + case _UT('d'): + case _UT('e'): + case _UT('f'): + case _UT('A'): + case _UT('B'): + case _UT('C'): + case _UT('D'): + case _UT('E'): + case _UT('F'): + switch (read[2]) { + case _UT('0'): + case _UT('1'): + case _UT('2'): + case _UT('3'): + case _UT('4'): + case _UT('5'): + case _UT('6'): + case _UT('7'): + case _UT('8'): + case _UT('9'): + case _UT('a'): + case _UT('b'): + case _UT('c'): + case _UT('d'): + case _UT('e'): + case _UT('f'): + case _UT('A'): + case _UT('B'): + case _UT('C'): + case _UT('D'): + case _UT('E'): + case _UT('F'): { + /* Percent group found */ + const unsigned char left = URI_FUNC(HexdigToInt)(read[1]); + const unsigned char right = URI_FUNC(HexdigToInt)(read[2]); + const int code = 16 * left + right; + switch (code) { + case 10: + switch (breakConversion) { + case URI_BR_TO_LF: + if (!prevWasCr) { + write[0] = (URI_CHAR)10; + write++; + } + break; + + case URI_BR_TO_CRLF: + if (!prevWasCr) { + write[0] = (URI_CHAR)13; + write[1] = (URI_CHAR)10; + write += 2; + } + break; + + case URI_BR_TO_CR: + if (!prevWasCr) { + write[0] = (URI_CHAR)13; + write++; + } + break; + + case URI_BR_DONT_TOUCH: + default: + write[0] = (URI_CHAR)10; + write++; + } + prevWasCr = URI_FALSE; + break; + + case 13: + switch (breakConversion) { + case URI_BR_TO_LF: + write[0] = (URI_CHAR)10; + write++; + break; + + case URI_BR_TO_CRLF: + write[0] = (URI_CHAR)13; + write[1] = (URI_CHAR)10; + write += 2; + break; + + case URI_BR_TO_CR: + write[0] = (URI_CHAR)13; + write++; + break; + + case URI_BR_DONT_TOUCH: + default: + write[0] = (URI_CHAR)13; + write++; + } + prevWasCr = URI_TRUE; + break; + + default: + write[0] = (URI_CHAR)(code); + write++; + + prevWasCr = URI_FALSE; + } + read += 3; + } break; + + default: + /* Copy two chars unmodified and */ + /* look at this char again */ + if (read > write) { + write[0] = read[0]; + write[1] = read[1]; + } + read += 2; + write += 2; + + prevWasCr = URI_FALSE; + } + break; + + default: + /* Copy one char unmodified and */ + /* look at this char again */ + if (read > write) { + write[0] = read[0]; + } + read++; + write++; + + prevWasCr = URI_FALSE; + } + break; + + case _UT('+'): + if (plusToSpace) { + /* Convert '+' to ' ' */ + write[0] = _UT(' '); + } else { + /* Copy one char unmodified */ + if (read > write) { + write[0] = read[0]; + } + } + read++; + write++; + + prevWasCr = URI_FALSE; + break; + + default: + /* Copy one char unmodified */ + if (read > write) { + write[0] = read[0]; + } + read++; + write++; + + prevWasCr = URI_FALSE; + } + } } - - #endif diff --git a/ext/uri/uriparser/src/UriFile.c b/ext/uri/uriparser/src/UriFile.c index 232957d3c8079..7510e701c9f5c 100644 --- a/ext/uri/uriparser/src/UriFile.c +++ b/ext/uri/uriparser/src/UriFile.c @@ -41,202 +41,181 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriFile.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriFile.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriFile.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriFile.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -#endif - - - -#include /* for size_t, avoiding stddef.h for older MSVCs */ +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif +# ifndef URI_DOXYGEN +# include +# endif +# include /* for size_t, avoiding stddef.h for older MSVCs */ static URI_INLINE int URI_FUNC(FilenameToUriString)(const URI_CHAR * filename, - URI_CHAR * uriString, UriBool fromUnix) { - const URI_CHAR * input = filename; - const URI_CHAR * lastSep = input - 1; - UriBool firstSegment = URI_TRUE; - URI_CHAR * output = uriString; - UriBool absolute; - UriBool is_windows_network; - - if ((filename == NULL) || (uriString == NULL)) { - return URI_ERROR_NULL; - } - - is_windows_network = (filename[0] == _UT('\\')) && (filename[1] == _UT('\\')); - absolute = fromUnix - ? (filename[0] == _UT('/')) - : (((filename[0] != _UT('\0')) && (filename[1] == _UT(':'))) - || is_windows_network); - - if (absolute) { - const URI_CHAR * const prefix = fromUnix - ? _UT("file://") - : is_windows_network - ? _UT("file:") - : _UT("file:///"); - const size_t prefixLen = URI_STRLEN(prefix); - - /* Copy prefix */ - memcpy(uriString, prefix, prefixLen * sizeof(URI_CHAR)); - output += prefixLen; - } - - /* Copy and escape on the fly */ - for (;;) { - if ((input[0] == _UT('\0')) - || (fromUnix && input[0] == _UT('/')) - || (!fromUnix && input[0] == _UT('\\'))) { - /* Copy text after last separator */ - if (lastSep + 1 < input) { - if (!fromUnix && absolute && (firstSegment == URI_TRUE)) { - /* Quick hack to not convert "C:" to "C%3A" */ - const int charsToCopy = (int)(input - (lastSep + 1)); - memcpy(output, lastSep + 1, charsToCopy * sizeof(URI_CHAR)); - output += charsToCopy; - } else { - output = URI_FUNC(EscapeEx)(lastSep + 1, input, output, - URI_FALSE, URI_FALSE); - } - } - firstSegment = URI_FALSE; - } - - if (input[0] == _UT('\0')) { - output[0] = _UT('\0'); - break; - } else if (fromUnix && (input[0] == _UT('/'))) { - /* Copy separators unmodified */ - output[0] = _UT('/'); - output++; - lastSep = input; - } else if (!fromUnix && (input[0] == _UT('\\'))) { - /* Convert backslashes to forward slashes */ - output[0] = _UT('/'); - output++; - lastSep = input; - } - input++; - } - - return URI_SUCCESS; + URI_CHAR * uriString, + UriBool fromUnix) { + const URI_CHAR * input = filename; + const URI_CHAR * lastSep = input - 1; + UriBool firstSegment = URI_TRUE; + URI_CHAR * output = uriString; + UriBool absolute; + UriBool is_windows_network; + + if ((filename == NULL) || (uriString == NULL)) { + return URI_ERROR_NULL; + } + + is_windows_network = (filename[0] == _UT('\\')) && (filename[1] == _UT('\\')); + absolute = fromUnix ? (filename[0] == _UT('/')) + : (((filename[0] != _UT('\0')) && (filename[1] == _UT(':'))) + || is_windows_network); + + if (absolute) { + const URI_CHAR * const prefix = fromUnix ? _UT("file://") + : is_windows_network ? _UT("file:") + : _UT("file:///"); + const size_t prefixLen = URI_STRLEN(prefix); + + /* Copy prefix */ + memcpy(uriString, prefix, prefixLen * sizeof(URI_CHAR)); + output += prefixLen; + } + + /* Copy and escape on the fly */ + for (;;) { + if ((input[0] == _UT('\0')) || (fromUnix && input[0] == _UT('/')) + || (!fromUnix && input[0] == _UT('\\'))) { + /* Copy text after last separator */ + if (lastSep + 1 < input) { + if (!fromUnix && absolute && (firstSegment == URI_TRUE)) { + /* Quick hack to not convert "C:" to "C%3A" */ + const int charsToCopy = (int)(input - (lastSep + 1)); + memcpy(output, lastSep + 1, charsToCopy * sizeof(URI_CHAR)); + output += charsToCopy; + } else { + output = URI_FUNC(EscapeEx)(lastSep + 1, input, output, URI_FALSE, + URI_FALSE); + } + } + firstSegment = URI_FALSE; + } + + if (input[0] == _UT('\0')) { + output[0] = _UT('\0'); + break; + } else if (fromUnix && (input[0] == _UT('/'))) { + /* Copy separators unmodified */ + output[0] = _UT('/'); + output++; + lastSep = input; + } else if (!fromUnix && (input[0] == _UT('\\'))) { + /* Convert backslashes to forward slashes */ + output[0] = _UT('/'); + output++; + lastSep = input; + } + input++; + } + + return URI_SUCCESS; } - - static URI_INLINE int URI_FUNC(UriStringToFilename)(const URI_CHAR * uriString, - URI_CHAR * filename, UriBool toUnix) { - if ((uriString == NULL) || (filename == NULL)) { - return URI_ERROR_NULL; - } - - { - const UriBool file_unknown_slashes = - URI_STRNCMP(uriString, _UT("file:"), URI_STRLEN(_UT("file:"))) == 0; - const UriBool file_one_or_more_slashes = file_unknown_slashes - && (URI_STRNCMP(uriString, _UT("file:/"), URI_STRLEN(_UT("file:/"))) == 0); - const UriBool file_two_or_more_slashes = file_one_or_more_slashes - && (URI_STRNCMP(uriString, _UT("file://"), URI_STRLEN(_UT("file://"))) == 0); - const UriBool file_three_or_more_slashes = file_two_or_more_slashes - && (URI_STRNCMP(uriString, _UT("file:///"), URI_STRLEN(_UT("file:///"))) == 0); - - const size_t charsToSkip = file_two_or_more_slashes - ? file_three_or_more_slashes - ? toUnix - /* file:///bin/bash */ - ? URI_STRLEN(_UT("file://")) - /* file:///E:/Documents%20and%20Settings */ - : URI_STRLEN(_UT("file:///")) - /* file://Server01/Letter.txt */ - : URI_STRLEN(_UT("file://")) - : ((file_one_or_more_slashes && toUnix) - /* file:/bin/bash */ - /* https://tools.ietf.org/html/rfc8089#appendix-B */ - ? URI_STRLEN(_UT("file:")) - : ((! toUnix && file_unknown_slashes && ! file_one_or_more_slashes) - /* file:c:/path/to/file */ - /* https://tools.ietf.org/html/rfc8089#appendix-E.2 */ - ? URI_STRLEN(_UT("file:")) - : 0)); - const size_t charsToCopy = URI_STRLEN(uriString + charsToSkip) + 1; - - const UriBool is_windows_network_with_authority = - (toUnix == URI_FALSE) - && file_two_or_more_slashes - && ! file_three_or_more_slashes; - - URI_CHAR * const unescape_target = is_windows_network_with_authority - ? (filename + 2) - : filename; - - if (is_windows_network_with_authority) { - filename[0] = '\\'; - filename[1] = '\\'; - } - - memcpy(unescape_target, uriString + charsToSkip, charsToCopy * sizeof(URI_CHAR)); - URI_FUNC(UnescapeInPlaceEx)(filename, URI_FALSE, URI_BR_DONT_TOUCH); - } - - /* Convert forward slashes to backslashes */ - if (!toUnix) { - URI_CHAR * walker = filename; - while (walker[0] != _UT('\0')) { - if (walker[0] == _UT('/')) { - walker[0] = _UT('\\'); - } - walker++; - } - } - - return URI_SUCCESS; + URI_CHAR * filename, UriBool toUnix) { + if ((uriString == NULL) || (filename == NULL)) { + return URI_ERROR_NULL; + } + + const UriBool file_unknown_slashes = + URI_STRNCMP(uriString, _UT("file:"), URI_STRLEN(_UT("file:"))) == 0; + const UriBool file_one_or_more_slashes = + file_unknown_slashes + && (URI_STRNCMP(uriString, _UT("file:/"), URI_STRLEN(_UT("file:/"))) == 0); + const UriBool file_two_or_more_slashes = + file_one_or_more_slashes + && (URI_STRNCMP(uriString, _UT("file://"), URI_STRLEN(_UT("file://"))) == 0); + const UriBool file_three_or_more_slashes = + file_two_or_more_slashes + && (URI_STRNCMP(uriString, _UT("file:///"), URI_STRLEN(_UT("file:///"))) == 0); + + const size_t charsToSkip = + file_two_or_more_slashes + ? file_three_or_more_slashes ? toUnix + /* file:///bin/bash */ + ? URI_STRLEN(_UT("file://")) + /* file:///E:/Documents%20and%20Settings */ + : URI_STRLEN(_UT("file:///")) + /* file://Server01/Letter.txt */ + : URI_STRLEN(_UT("file://")) + : ((file_one_or_more_slashes && toUnix) + /* file:/bin/bash */ + /* https://tools.ietf.org/html/rfc8089#appendix-B */ + ? URI_STRLEN(_UT("file:")) + : ((!toUnix && file_unknown_slashes && !file_one_or_more_slashes) + /* file:c:/path/to/file */ + /* https://tools.ietf.org/html/rfc8089#appendix-E.2 */ + ? URI_STRLEN(_UT("file:")) + : 0)); + const size_t charsToCopy = URI_STRLEN(uriString + charsToSkip) + 1; + + const UriBool is_windows_network_with_authority = + (toUnix == URI_FALSE) && file_two_or_more_slashes && !file_three_or_more_slashes; + + URI_CHAR * const unescape_target = + is_windows_network_with_authority ? (filename + 2) : filename; + + if (is_windows_network_with_authority) { + filename[0] = '\\'; + filename[1] = '\\'; + } + + memcpy(unescape_target, uriString + charsToSkip, charsToCopy * sizeof(URI_CHAR)); + URI_FUNC(UnescapeInPlaceEx)(filename, URI_FALSE, URI_BR_DONT_TOUCH); + + /* Convert forward slashes to backslashes */ + if (!toUnix) { + URI_CHAR * walker = filename; + while (walker[0] != _UT('\0')) { + if (walker[0] == _UT('/')) { + walker[0] = _UT('\\'); + } + walker++; + } + } + + return URI_SUCCESS; } - - int URI_FUNC(UnixFilenameToUriString)(const URI_CHAR * filename, URI_CHAR * uriString) { - return URI_FUNC(FilenameToUriString)(filename, uriString, URI_TRUE); + return URI_FUNC(FilenameToUriString)(filename, uriString, URI_TRUE); } - - -int URI_FUNC(WindowsFilenameToUriString)(const URI_CHAR * filename, URI_CHAR * uriString) { - return URI_FUNC(FilenameToUriString)(filename, uriString, URI_FALSE); +int URI_FUNC(WindowsFilenameToUriString)(const URI_CHAR * filename, + URI_CHAR * uriString) { + return URI_FUNC(FilenameToUriString)(filename, uriString, URI_FALSE); } - - int URI_FUNC(UriStringToUnixFilename)(const URI_CHAR * uriString, URI_CHAR * filename) { - return URI_FUNC(UriStringToFilename)(uriString, filename, URI_TRUE); + return URI_FUNC(UriStringToFilename)(uriString, filename, URI_TRUE); } - - -int URI_FUNC(UriStringToWindowsFilename)(const URI_CHAR * uriString, URI_CHAR * filename) { - return URI_FUNC(UriStringToFilename)(uriString, filename, URI_FALSE); +int URI_FUNC(UriStringToWindowsFilename)(const URI_CHAR * uriString, + URI_CHAR * filename) { + return URI_FUNC(UriStringToFilename)(uriString, filename, URI_FALSE); } - - #endif diff --git a/ext/uri/uriparser/src/UriIp4.c b/ext/uri/uriparser/src/UriIp4.c index a794265269da5..162a75a556d49 100644 --- a/ext/uri/uriparser/src/UriIp4.c +++ b/ext/uri/uriparser/src/UriIp4.c @@ -47,97 +47,93 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriIp4.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriIp4.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriIp4.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriIp4.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriIp4Base.h" -# include -#endif - - +# ifdef URI_PASS_ANSI +# include +# else +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriIp4Base.h" +# include +# endif /* Prototypes */ static const URI_CHAR * URI_FUNC(ParseDecOctet)(UriIp4Parser * parser, - const URI_CHAR * first, const URI_CHAR * afterLast); + const URI_CHAR * first, + const URI_CHAR * afterLast); static const URI_CHAR * URI_FUNC(ParseDecOctetOne)(UriIp4Parser * parser, - const URI_CHAR * first, const URI_CHAR * afterLast); + const URI_CHAR * first, + const URI_CHAR * afterLast); static const URI_CHAR * URI_FUNC(ParseDecOctetTwo)(UriIp4Parser * parser, - const URI_CHAR * first, const URI_CHAR * afterLast); + const URI_CHAR * first, + const URI_CHAR * afterLast); static const URI_CHAR * URI_FUNC(ParseDecOctetThree)(UriIp4Parser * parser, - const URI_CHAR * first, const URI_CHAR * afterLast); + const URI_CHAR * first, + const URI_CHAR * afterLast); static const URI_CHAR * URI_FUNC(ParseDecOctetFour)(UriIp4Parser * parser, - const URI_CHAR * first, const URI_CHAR * afterLast); - - + const URI_CHAR * first, + const URI_CHAR * afterLast); /* * [ipFourAddress]->[decOctet]<.>[decOctet]<.>[decOctet]<.>[decOctet] */ -int URI_FUNC(ParseIpFourAddress)(unsigned char * octetOutput, - const URI_CHAR * first, const URI_CHAR * afterLast) { - const URI_CHAR * after; - UriIp4Parser parser; - - /* Essential checks */ - if ((octetOutput == NULL) || (first == NULL) - || (afterLast <= first)) { - return URI_ERROR_SYNTAX; - } - - /* Reset parser */ - parser.stackCount = 0; - - /* Octet #1 */ - after = URI_FUNC(ParseDecOctet)(&parser, first, afterLast); - if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) { - return URI_ERROR_SYNTAX; - } - uriStackToOctet(&parser, octetOutput); - - /* Octet #2 */ - after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast); - if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) { - return URI_ERROR_SYNTAX; - } - uriStackToOctet(&parser, octetOutput + 1); - - /* Octet #3 */ - after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast); - if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) { - return URI_ERROR_SYNTAX; - } - uriStackToOctet(&parser, octetOutput + 2); - - /* Octet #4 */ - after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast); - if (after != afterLast) { - return URI_ERROR_SYNTAX; - } - uriStackToOctet(&parser, octetOutput + 3); - - return URI_SUCCESS; +int URI_FUNC(ParseIpFourAddress)(unsigned char * octetOutput, const URI_CHAR * first, + const URI_CHAR * afterLast) { + const URI_CHAR * after; + UriIp4Parser parser; + + /* Essential checks */ + if ((octetOutput == NULL) || (first == NULL) || (afterLast <= first)) { + return URI_ERROR_SYNTAX; + } + + /* Reset parser */ + parser.stackCount = 0; + + /* Octet #1 */ + after = URI_FUNC(ParseDecOctet)(&parser, first, afterLast); + if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) { + return URI_ERROR_SYNTAX; + } + uriStackToOctet(&parser, octetOutput); + + /* Octet #2 */ + after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast); + if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) { + return URI_ERROR_SYNTAX; + } + uriStackToOctet(&parser, octetOutput + 1); + + /* Octet #3 */ + after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast); + if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) { + return URI_ERROR_SYNTAX; + } + uriStackToOctet(&parser, octetOutput + 2); + + /* Octet #4 */ + after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast); + if (after != afterLast) { + return URI_ERROR_SYNTAX; + } + uriStackToOctet(&parser, octetOutput + 3); + + return URI_SUCCESS; } - - /* * [decOctet]-><0> * [decOctet]-><1>[decOctetOne] @@ -151,72 +147,72 @@ int URI_FUNC(ParseIpFourAddress)(unsigned char * octetOutput, * [decOctet]-><9>[decOctetThree] */ static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctet)(UriIp4Parser * parser, - const URI_CHAR * first, const URI_CHAR * afterLast) { - if (first >= afterLast) { - return NULL; - } - - switch (*first) { - case _UT('0'): - uriPushToStack(parser, 0); - return first + 1; - - case _UT('1'): - uriPushToStack(parser, 1); - return (const URI_CHAR *)URI_FUNC(ParseDecOctetOne)(parser, first + 1, afterLast); - - case _UT('2'): - uriPushToStack(parser, 2); - return (const URI_CHAR *)URI_FUNC(ParseDecOctetTwo)(parser, first + 1, afterLast); - - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): - uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); - return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, afterLast); - - default: - return NULL; - } + const URI_CHAR * first, + const URI_CHAR * afterLast) { + if (first >= afterLast) { + return NULL; + } + + switch (*first) { + case _UT('0'): + uriPushToStack(parser, 0); + return first + 1; + + case _UT('1'): + uriPushToStack(parser, 1); + return (const URI_CHAR *)URI_FUNC(ParseDecOctetOne)(parser, first + 1, afterLast); + + case _UT('2'): + uriPushToStack(parser, 2); + return (const URI_CHAR *)URI_FUNC(ParseDecOctetTwo)(parser, first + 1, afterLast); + + case _UT('3'): + case _UT('4'): + case _UT('5'): + case _UT('6'): + case _UT('7'): + case _UT('8'): + case _UT('9'): + uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); + return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, + afterLast); + + default: + return NULL; + } } - - /* * [decOctetOne]-> * [decOctetOne]->[DIGIT][decOctetThree] */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetOne)(UriIp4Parser * parser, - const URI_CHAR * first, const URI_CHAR * afterLast) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): - uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); - return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, afterLast); - - default: - return first; - } +static URI_INLINE const URI_CHAR * +URI_FUNC(ParseDecOctetOne)(UriIp4Parser * parser, const URI_CHAR * first, + const URI_CHAR * afterLast) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('0'): + case _UT('1'): + case _UT('2'): + case _UT('3'): + case _UT('4'): + case _UT('5'): + case _UT('6'): + case _UT('7'): + case _UT('8'): + case _UT('9'): + uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); + return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, + afterLast); + + default: + return first; + } } - - /* * [decOctetTwo]-> * [decOctetTwo]-><0>[decOctetThree] @@ -229,71 +225,71 @@ static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetOne)(UriIp4Parser * par * [decOctetTwo]-><7> * [decOctetTwo]-><8> * [decOctetTwo]-><9> -*/ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetTwo)(UriIp4Parser * parser, - const URI_CHAR * first, const URI_CHAR * afterLast) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); - return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, afterLast); - - case _UT('5'): - uriPushToStack(parser, 5); - return (const URI_CHAR *)URI_FUNC(ParseDecOctetFour)(parser, first + 1, afterLast); - - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): - uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); - return first + 1; - - default: - return first; - } + */ +static URI_INLINE const URI_CHAR * +URI_FUNC(ParseDecOctetTwo)(UriIp4Parser * parser, const URI_CHAR * first, + const URI_CHAR * afterLast) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('0'): + case _UT('1'): + case _UT('2'): + case _UT('3'): + case _UT('4'): + uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); + return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, + afterLast); + + case _UT('5'): + uriPushToStack(parser, 5); + return (const URI_CHAR *)URI_FUNC(ParseDecOctetFour)(parser, first + 1, + afterLast); + + case _UT('6'): + case _UT('7'): + case _UT('8'): + case _UT('9'): + uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); + return first + 1; + + default: + return first; + } } - - /* * [decOctetThree]-> * [decOctetThree]->[DIGIT] */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetThree)(UriIp4Parser * parser, - const URI_CHAR * first, const URI_CHAR * afterLast) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): - uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); - return first + 1; - - default: - return first; - } +static URI_INLINE const URI_CHAR * +URI_FUNC(ParseDecOctetThree)(UriIp4Parser * parser, const URI_CHAR * first, + const URI_CHAR * afterLast) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('0'): + case _UT('1'): + case _UT('2'): + case _UT('3'): + case _UT('4'): + case _UT('5'): + case _UT('6'): + case _UT('7'): + case _UT('8'): + case _UT('9'): + uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); + return first + 1; + + default: + return first; + } } - - /* * [decOctetFour]-> * [decOctetFour]-><0> @@ -303,27 +299,26 @@ static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetThree)(UriIp4Parser * p * [decOctetFour]-><4> * [decOctetFour]-><5> */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetFour)(UriIp4Parser * parser, - const URI_CHAR * first, const URI_CHAR * afterLast) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); - return first + 1; - - default: - return first; - } +static URI_INLINE const URI_CHAR * +URI_FUNC(ParseDecOctetFour)(UriIp4Parser * parser, const URI_CHAR * first, + const URI_CHAR * afterLast) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('0'): + case _UT('1'): + case _UT('2'): + case _UT('3'): + case _UT('4'): + case _UT('5'): + uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); + return first + 1; + + default: + return first; + } } - - #endif diff --git a/ext/uri/uriparser/src/UriIp4Base.c b/ext/uri/uriparser/src/UriIp4Base.c index ded7c32c9940e..900234cd782d3 100644 --- a/ext/uri/uriparser/src/UriIp4Base.c +++ b/ext/uri/uriparser/src/UriIp4Base.c @@ -43,54 +43,45 @@ */ #ifndef URI_DOXYGEN -# include "UriIp4Base.h" +# include "UriIp4Base.h" #endif - - void uriStackToOctet(UriIp4Parser * parser, unsigned char * octet) { - switch (parser->stackCount) { - case 1: - *octet = parser->stackOne; - break; + switch (parser->stackCount) { + case 1: + *octet = parser->stackOne; + break; - case 2: - *octet = parser->stackOne * 10 - + parser->stackTwo; - break; + case 2: + *octet = parser->stackOne * 10 + parser->stackTwo; + break; - case 3: - *octet = parser->stackOne * 100 - + parser->stackTwo * 10 - + parser->stackThree; - break; + case 3: + *octet = parser->stackOne * 100 + parser->stackTwo * 10 + parser->stackThree; + break; - default: - ; - } - parser->stackCount = 0; + default:; + } + parser->stackCount = 0; } - - void uriPushToStack(UriIp4Parser * parser, unsigned char digit) { - switch (parser->stackCount) { - case 0: - parser->stackOne = digit; - parser->stackCount = 1; - break; + switch (parser->stackCount) { + case 0: + parser->stackOne = digit; + parser->stackCount = 1; + break; - case 1: - parser->stackTwo = digit; - parser->stackCount = 2; - break; + case 1: + parser->stackTwo = digit; + parser->stackCount = 2; + break; - case 2: - parser->stackThree = digit; - parser->stackCount = 3; - break; + case 2: + parser->stackThree = digit; + parser->stackCount = 3; + break; - default: - ; - } + default:; + } } diff --git a/ext/uri/uriparser/src/UriIp4Base.h b/ext/uri/uriparser/src/UriIp4Base.h index 4867850224033..1a2ff148b628b 100644 --- a/ext/uri/uriparser/src/UriIp4Base.h +++ b/ext/uri/uriparser/src/UriIp4Base.h @@ -38,22 +38,16 @@ */ #ifndef URI_IP4_BASE_H -#define URI_IP4_BASE_H 1 - - +# define URI_IP4_BASE_H 1 typedef struct UriIp4ParserStruct { - unsigned char stackCount; - unsigned char stackOne; - unsigned char stackTwo; - unsigned char stackThree; + unsigned char stackCount; + unsigned char stackOne; + unsigned char stackTwo; + unsigned char stackThree; } UriIp4Parser; - - void uriPushToStack(UriIp4Parser * parser, unsigned char digit); void uriStackToOctet(UriIp4Parser * parser, unsigned char * octet); - - #endif /* URI_IP4_BASE_H */ diff --git a/ext/uri/uriparser/src/UriMemory.c b/ext/uri/uriparser/src/UriMemory.c index 503b9f9787ff4..3caf8199dc45d 100644 --- a/ext/uri/uriparser/src/UriMemory.c +++ b/ext/uri/uriparser/src/UriMemory.c @@ -42,469 +42,423 @@ * Holds memory manager implementation. */ -#include "UriConfig.h" /* for HAVE_REALLOCARRAY */ +#include "UriConfig.h" /* for HAVE_REALLOCARRAY */ #ifdef HAVE_REALLOCARRAY -# ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -# endif -# ifdef __NetBSD__ -# define _OPENBSD_SOURCE 1 -# endif +# ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +# endif +# ifdef __NetBSD__ +# define _OPENBSD_SOURCE 1 +# endif #endif #include #include - - #ifndef URI_DOXYGEN -# include "UriMemory.h" +# include "UriMemory.h" #endif - - -#define URI_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define URI_MAX(a, b) (((a) > (b)) ? (a) : (b)) /* NOTE: This intends to mimic MALLOC_ALIGNMENT of glibc */ -#define URI_MALLOC_ALIGNMENT URI_MAX(2 * sizeof(size_t), sizeof(long double)) - -#define URI_MALLOC_PADDING (URI_MALLOC_ALIGNMENT - sizeof(size_t)) - +#define URI_MALLOC_ALIGNMENT URI_MAX(2 * sizeof(size_t), sizeof(long double)) +#define URI_MALLOC_PADDING (URI_MALLOC_ALIGNMENT - sizeof(size_t)) #define URI_CHECK_ALLOC_OVERFLOW(total_size, nmemb, size) \ - do { \ - /* check for unsigned overflow */ \ - if ((nmemb != 0) && (total_size / nmemb != size)) { \ - errno = ENOMEM; \ - return NULL; \ - } \ - } while (0) - - - -static void * uriDefaultMalloc(UriMemoryManager * URI_UNUSED(memory), - size_t size) { - return malloc(size); + do { \ + /* check for unsigned overflow */ \ + if ((nmemb != 0) && (total_size / nmemb != size)) { \ + errno = ENOMEM; \ + return NULL; \ + } \ + } while (0) + +static void * uriDefaultMalloc(UriMemoryManager * URI_UNUSED(memory), size_t size) { + return malloc(size); } - - -static void * uriDefaultCalloc(UriMemoryManager * URI_UNUSED(memory), - size_t nmemb, size_t size) { - return calloc(nmemb, size); +static void * uriDefaultCalloc(UriMemoryManager * URI_UNUSED(memory), size_t nmemb, + size_t size) { + return calloc(nmemb, size); } - - -static void * uriDefaultRealloc(UriMemoryManager * URI_UNUSED(memory), - void * ptr, size_t size) { - return realloc(ptr, size); +static void * uriDefaultRealloc(UriMemoryManager * URI_UNUSED(memory), void * ptr, + size_t size) { + return realloc(ptr, size); } - - -static void * uriDefaultReallocarray(UriMemoryManager * URI_UNUSED(memory), - void * ptr, size_t nmemb, size_t size) { +static void * uriDefaultReallocarray(UriMemoryManager * URI_UNUSED(memory), void * ptr, + size_t nmemb, size_t size) { #ifdef HAVE_REALLOCARRAY - return reallocarray(ptr, nmemb, size); + return reallocarray(ptr, nmemb, size); #else - const size_t total_size = nmemb * size; + const size_t total_size = nmemb * size; - URI_CHECK_ALLOC_OVERFLOW(total_size, nmemb, size); /* may return */ + URI_CHECK_ALLOC_OVERFLOW(total_size, nmemb, size); /* may return */ - return realloc(ptr, total_size); + return realloc(ptr, total_size); #endif } - - -static void uriDefaultFree(UriMemoryManager * URI_UNUSED(memory), - void * ptr) { - free(ptr); +static void uriDefaultFree(UriMemoryManager * URI_UNUSED(memory), void * ptr) { + free(ptr); } - - UriBool uriMemoryManagerIsComplete(const UriMemoryManager * memory) { - return (memory - && memory->malloc - && memory->calloc - && memory->realloc - && memory->reallocarray - && memory->free) ? URI_TRUE : URI_FALSE; + return (memory && memory->malloc && memory->calloc && memory->realloc + && memory->reallocarray && memory->free) + ? URI_TRUE + : URI_FALSE; } - - void * uriEmulateCalloc(UriMemoryManager * memory, size_t nmemb, size_t size) { - void * buffer; - const size_t total_size = nmemb * size; - - if (memory == NULL) { - errno = EINVAL; - return NULL; - } - - URI_CHECK_ALLOC_OVERFLOW(total_size, nmemb, size); /* may return */ - - buffer = memory->malloc(memory, total_size); - if (buffer == NULL) { - /* errno set by malloc */ - return NULL; - } - memset(buffer, 0, total_size); - return buffer; + void * buffer; + const size_t total_size = nmemb * size; + + if (memory == NULL) { + errno = EINVAL; + return NULL; + } + + URI_CHECK_ALLOC_OVERFLOW(total_size, nmemb, size); /* may return */ + + buffer = memory->malloc(memory, total_size); + if (buffer == NULL) { + /* errno set by malloc */ + return NULL; + } + memset(buffer, 0, total_size); + return buffer; } +void * uriEmulateReallocarray(UriMemoryManager * memory, void * ptr, size_t nmemb, + size_t size) { + const size_t total_size = nmemb * size; + if (memory == NULL) { + errno = EINVAL; + return NULL; + } -void * uriEmulateReallocarray(UriMemoryManager * memory, - void * ptr, size_t nmemb, size_t size) { - const size_t total_size = nmemb * size; - - if (memory == NULL) { - errno = EINVAL; - return NULL; - } + URI_CHECK_ALLOC_OVERFLOW(total_size, nmemb, size); /* may return */ - URI_CHECK_ALLOC_OVERFLOW(total_size, nmemb, size); /* may return */ - - return memory->realloc(memory, ptr, total_size); + return memory->realloc(memory, ptr, total_size); } +static void * uriDecorateMalloc(UriMemoryManager * memory, size_t size) { + UriMemoryManager * backend; + const size_t extraBytes = sizeof(size_t) + URI_MALLOC_PADDING; + void * buffer; + if (memory == NULL) { + errno = EINVAL; + return NULL; + } -static void * uriDecorateMalloc(UriMemoryManager * memory, - size_t size) { - UriMemoryManager * backend; - const size_t extraBytes = sizeof(size_t) + URI_MALLOC_PADDING; - void * buffer; - - if (memory == NULL) { - errno = EINVAL; - return NULL; - } + /* check for unsigned overflow */ + if (size > ((size_t)-1) - extraBytes) { + errno = ENOMEM; + return NULL; + } - /* check for unsigned overflow */ - if (size > ((size_t)-1) - extraBytes) { - errno = ENOMEM; - return NULL; - } + backend = (UriMemoryManager *)memory->userData; + if (backend == NULL) { + errno = EINVAL; + return NULL; + } - backend = (UriMemoryManager *)memory->userData; - if (backend == NULL) { - errno = EINVAL; - return NULL; - } + buffer = backend->malloc(backend, extraBytes + size); + if (buffer == NULL) { + return NULL; + } - buffer = backend->malloc(backend, extraBytes + size); - if (buffer == NULL) { - return NULL; - } + *(size_t *)buffer = size; - *(size_t *)buffer = size; - - return (char *)buffer + extraBytes; + return (char *)buffer + extraBytes; } +static void * uriDecorateRealloc(UriMemoryManager * memory, void * ptr, size_t size) { + void * newBuffer; + size_t prevSize; + if (memory == NULL) { + errno = EINVAL; + return NULL; + } -static void * uriDecorateRealloc(UriMemoryManager * memory, - void * ptr, size_t size) { - void * newBuffer; - size_t prevSize; - - if (memory == NULL) { - errno = EINVAL; - return NULL; - } + /* man realloc: "If ptr is NULL, then the call is equivalent to + * malloc(size), for *all* values of size" */ + if (ptr == NULL) { + return memory->malloc(memory, size); + } - /* man realloc: "If ptr is NULL, then the call is equivalent to - * malloc(size), for *all* values of size" */ - if (ptr == NULL) { - return memory->malloc(memory, size); - } + /* man realloc: "If size is equal to zero, and ptr is *not* NULL, + * then the call is equivalent to free(ptr)." */ + if (size == 0) { + memory->free(memory, ptr); + return NULL; + } - /* man realloc: "If size is equal to zero, and ptr is *not* NULL, - * then the call is equivalent to free(ptr)." */ - if (size == 0) { - memory->free(memory, ptr); - return NULL; - } + prevSize = *((size_t *)((char *)ptr - sizeof(size_t) - URI_MALLOC_PADDING)); - prevSize = *((size_t *)((char *)ptr - sizeof(size_t) - URI_MALLOC_PADDING)); + /* Anything to do? */ + /* mull-ignore-next cxx_le_to_lt */ + if (size <= prevSize) { + return ptr; + } - /* Anything to do? */ - if (size <= prevSize) { - return ptr; - } + newBuffer = memory->malloc(memory, size); + if (newBuffer == NULL) { + /* errno set by malloc */ + return NULL; + } - newBuffer = memory->malloc(memory, size); - if (newBuffer == NULL) { - /* errno set by malloc */ - return NULL; - } + memcpy(newBuffer, ptr, prevSize); - memcpy(newBuffer, ptr, prevSize); + memory->free(memory, ptr); - memory->free(memory, ptr); - - return newBuffer; + return newBuffer; } - - static void uriDecorateFree(UriMemoryManager * memory, void * ptr) { - UriMemoryManager * backend; + UriMemoryManager * backend; - if ((ptr == NULL) || (memory == NULL)) { - return; - } + if ((ptr == NULL) || (memory == NULL)) { + return; + } - backend = (UriMemoryManager *)memory->userData; - if (backend == NULL) { - return; - } + backend = (UriMemoryManager *)memory->userData; + if (backend == NULL) { + return; + } - backend->free(backend, (char *)ptr - sizeof(size_t) - URI_MALLOC_PADDING); + backend->free(backend, (char *)ptr - sizeof(size_t) - URI_MALLOC_PADDING); } +int uriCompleteMemoryManager(UriMemoryManager * memory, UriMemoryManager * backend) { + if ((memory == NULL) || (backend == NULL)) { + return URI_ERROR_NULL; + } + if ((backend->malloc == NULL) || (backend->free == NULL)) { + return URI_ERROR_MEMORY_MANAGER_INCOMPLETE; + } -int uriCompleteMemoryManager(UriMemoryManager * memory, - UriMemoryManager * backend) { - if ((memory == NULL) || (backend == NULL)) { - return URI_ERROR_NULL; - } - - if ((backend->malloc == NULL) || (backend->free == NULL)) { - return URI_ERROR_MEMORY_MANAGER_INCOMPLETE; - } - - memory->calloc = uriEmulateCalloc; - memory->reallocarray = uriEmulateReallocarray; + memory->calloc = uriEmulateCalloc; + memory->reallocarray = uriEmulateReallocarray; - memory->malloc = uriDecorateMalloc; - memory->realloc = uriDecorateRealloc; - memory->free = uriDecorateFree; + memory->malloc = uriDecorateMalloc; + memory->realloc = uriDecorateRealloc; + memory->free = uriDecorateFree; - memory->userData = backend; + memory->userData = backend; - return URI_SUCCESS; + return URI_SUCCESS; } - - +/* mull-off */ int uriTestMemoryManagerEx(UriMemoryManager * memory, UriBool challengeAlignment) { - const size_t mallocSize = 7; - const size_t callocNmemb = 3; - const size_t callocSize = 5; - const size_t callocTotalSize = callocNmemb * callocSize; - const size_t reallocSize = 11; - const size_t reallocarrayNmemb = 5; - const size_t reallocarraySize = 7; - const size_t reallocarrayTotal = reallocarrayNmemb * reallocarraySize; - size_t index; - char * buffer; - - if (memory == NULL) { - return URI_ERROR_NULL; - } - - if (uriMemoryManagerIsComplete(memory) != URI_TRUE) { - return URI_ERROR_MEMORY_MANAGER_INCOMPLETE; - } - - /* malloc + free*/ - buffer = memory->malloc(memory, mallocSize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - buffer[mallocSize - 1] = '\xF1'; - memory->free(memory, buffer); - buffer = NULL; - - /* calloc + free */ - buffer = memory->calloc(memory, callocNmemb, callocSize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - for (index = 0; index < callocTotalSize; index++) { /* all zeros? */ - if (buffer[index] != '\0') { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - } - buffer[callocTotalSize - 1] = '\xF2'; - memory->free(memory, buffer); - buffer = NULL; - - /* malloc + realloc + free */ - buffer = memory->malloc(memory, mallocSize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - for (index = 0; index < mallocSize; index++) { - buffer[index] = '\xF3'; - } - buffer = memory->realloc(memory, buffer, reallocSize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - for (index = 0; index < mallocSize; index++) { /* previous content? */ - if (buffer[index] != '\xF3') { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - } - buffer[reallocSize - 1] = '\xF4'; - memory->free(memory, buffer); - buffer = NULL; - - /* malloc + realloc ptr!=NULL size==0 (equals free) */ - buffer = memory->malloc(memory, mallocSize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - buffer[mallocSize - 1] = '\xF5'; - memory->realloc(memory, buffer, 0); - buffer = NULL; - - /* realloc ptr==NULL size!=0 (equals malloc) + free */ - buffer = memory->realloc(memory, NULL, mallocSize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - buffer[mallocSize - 1] = '\xF6'; - memory->free(memory, buffer); - buffer = NULL; - - /* realloc ptr==NULL size==0 (equals malloc) + free */ - buffer = memory->realloc(memory, NULL, 0); - if (buffer != NULL) { - memory->free(memory, buffer); - buffer = NULL; - } - - /* malloc + reallocarray + free */ - buffer = memory->malloc(memory, mallocSize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - for (index = 0; index < mallocSize; index++) { - buffer[index] = '\xF7'; - } - buffer = memory->reallocarray(memory, buffer, reallocarrayNmemb, - reallocarraySize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - for (index = 0; index < mallocSize; index++) { /* previous content? */ - if (buffer[index] != '\xF7') { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - } - buffer[reallocarrayTotal - 1] = '\xF8'; - memory->free(memory, buffer); - buffer = NULL; - - /* malloc + reallocarray ptr!=NULL nmemb==0 size!=0 (equals free) */ - buffer = memory->malloc(memory, mallocSize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - buffer[mallocSize - 1] = '\xF9'; - memory->reallocarray(memory, buffer, 0, reallocarraySize); - buffer = NULL; - - /* malloc + reallocarray ptr!=NULL nmemb!=0 size==0 (equals free) */ - buffer = memory->malloc(memory, mallocSize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - buffer[mallocSize - 1] = '\xFA'; - memory->reallocarray(memory, buffer, reallocarrayNmemb, 0); - buffer = NULL; - - /* malloc + reallocarray ptr!=NULL nmemb==0 size==0 (equals free) */ - buffer = memory->malloc(memory, mallocSize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - buffer[mallocSize - 1] = '\xFB'; - memory->reallocarray(memory, buffer, 0, 0); - buffer = NULL; - - /* reallocarray ptr==NULL nmemb!=0 size!=0 (equals malloc) + free */ - buffer = memory->reallocarray(memory, NULL, callocNmemb, callocSize); - if (buffer == NULL) { - return URI_ERROR_MEMORY_MANAGER_FAULTY; - } - buffer[callocTotalSize - 1] = '\xFC'; - memory->free(memory, buffer); - buffer = NULL; - - /* reallocarray ptr==NULL nmemb==0 size!=0 (equals malloc) + free */ - buffer = memory->reallocarray(memory, NULL, 0, callocSize); - if (buffer != NULL) { - memory->free(memory, buffer); - buffer = NULL; - } - - /* reallocarray ptr==NULL nmemb!=0 size==0 (equals malloc) + free */ - buffer = memory->reallocarray(memory, NULL, callocNmemb, 0); - if (buffer != NULL) { - memory->free(memory, buffer); - buffer = NULL; - } - - /* reallocarray ptr==NULL nmemb==0 size==0 (equals malloc) + free */ - buffer = memory->reallocarray(memory, NULL, 0, 0); - if (buffer != NULL) { - memory->free(memory, buffer); - buffer = NULL; - } - - /* challenge pointer alignment */ - if (challengeAlignment == URI_TRUE) { - long double * ptr = memory->malloc(memory, 4 * sizeof(long double)); - if (ptr != NULL) { - ptr[0] = 0.0L; - ptr[1] = 1.1L; - ptr[2] = 2.2L; - ptr[3] = 3.3L; - - { - long double * const ptrNew = memory->realloc(memory, ptr, 8 * sizeof(long double)); - if (ptrNew != NULL) { - ptr = ptrNew; - ptr[4] = 4.4L; - ptr[5] = 5.5L; - ptr[6] = 6.6L; - ptr[7] = 7.7L; - } - } - - memory->free(memory, ptr); - } - } - - return URI_SUCCESS; + const size_t mallocSize = 7; + const size_t callocNmemb = 3; + const size_t callocSize = 5; + const size_t callocTotalSize = callocNmemb * callocSize; + const size_t reallocSize = 11; + const size_t reallocarrayNmemb = 5; + const size_t reallocarraySize = 7; + const size_t reallocarrayTotal = reallocarrayNmemb * reallocarraySize; + size_t index; + char * buffer; + + if (memory == NULL) { + return URI_ERROR_NULL; + } + + if (uriMemoryManagerIsComplete(memory) != URI_TRUE) { + return URI_ERROR_MEMORY_MANAGER_INCOMPLETE; + } + + /* malloc + free*/ + buffer = memory->malloc(memory, mallocSize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + buffer[mallocSize - 1] = '\xF1'; + memory->free(memory, buffer); + buffer = NULL; + + /* calloc + free */ + buffer = memory->calloc(memory, callocNmemb, callocSize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + for (index = 0; index < callocTotalSize; index++) { /* all zeros? */ + if (buffer[index] != '\0') { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + } + buffer[callocTotalSize - 1] = '\xF2'; + memory->free(memory, buffer); + buffer = NULL; + + /* malloc + realloc + free */ + buffer = memory->malloc(memory, mallocSize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + for (index = 0; index < mallocSize; index++) { + buffer[index] = '\xF3'; + } + buffer = memory->realloc(memory, buffer, reallocSize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + for (index = 0; index < mallocSize; index++) { /* previous content? */ + if (buffer[index] != '\xF3') { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + } + buffer[reallocSize - 1] = '\xF4'; + memory->free(memory, buffer); + buffer = NULL; + + /* malloc + realloc ptr!=NULL size==0 (equals free) */ + buffer = memory->malloc(memory, mallocSize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + buffer[mallocSize - 1] = '\xF5'; + memory->realloc(memory, buffer, 0); + buffer = NULL; + + /* realloc ptr==NULL size!=0 (equals malloc) + free */ + buffer = memory->realloc(memory, NULL, mallocSize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + buffer[mallocSize - 1] = '\xF6'; + memory->free(memory, buffer); + buffer = NULL; + + /* realloc ptr==NULL size==0 (equals malloc) + free */ + buffer = memory->realloc(memory, NULL, 0); + if (buffer != NULL) { + memory->free(memory, buffer); + buffer = NULL; + } + + /* malloc + reallocarray + free */ + buffer = memory->malloc(memory, mallocSize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + for (index = 0; index < mallocSize; index++) { + buffer[index] = '\xF7'; + } + buffer = memory->reallocarray(memory, buffer, reallocarrayNmemb, reallocarraySize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + for (index = 0; index < mallocSize; index++) { /* previous content? */ + if (buffer[index] != '\xF7') { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + } + buffer[reallocarrayTotal - 1] = '\xF8'; + memory->free(memory, buffer); + buffer = NULL; + + /* malloc + reallocarray ptr!=NULL nmemb==0 size!=0 (equals free) */ + buffer = memory->malloc(memory, mallocSize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + buffer[mallocSize - 1] = '\xF9'; + memory->reallocarray(memory, buffer, 0, reallocarraySize); + buffer = NULL; + + /* malloc + reallocarray ptr!=NULL nmemb!=0 size==0 (equals free) */ + buffer = memory->malloc(memory, mallocSize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + buffer[mallocSize - 1] = '\xFA'; + memory->reallocarray(memory, buffer, reallocarrayNmemb, 0); + buffer = NULL; + + /* malloc + reallocarray ptr!=NULL nmemb==0 size==0 (equals free) */ + buffer = memory->malloc(memory, mallocSize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + buffer[mallocSize - 1] = '\xFB'; + memory->reallocarray(memory, buffer, 0, 0); + buffer = NULL; + + /* reallocarray ptr==NULL nmemb!=0 size!=0 (equals malloc) + free */ + buffer = memory->reallocarray(memory, NULL, callocNmemb, callocSize); + if (buffer == NULL) { + return URI_ERROR_MEMORY_MANAGER_FAULTY; + } + buffer[callocTotalSize - 1] = '\xFC'; + memory->free(memory, buffer); + buffer = NULL; + + /* reallocarray ptr==NULL nmemb==0 size!=0 (equals malloc) + free */ + buffer = memory->reallocarray(memory, NULL, 0, callocSize); + if (buffer != NULL) { + memory->free(memory, buffer); + buffer = NULL; + } + + /* reallocarray ptr==NULL nmemb!=0 size==0 (equals malloc) + free */ + buffer = memory->reallocarray(memory, NULL, callocNmemb, 0); + if (buffer != NULL) { + memory->free(memory, buffer); + buffer = NULL; + } + + /* reallocarray ptr==NULL nmemb==0 size==0 (equals malloc) + free */ + buffer = memory->reallocarray(memory, NULL, 0, 0); + if (buffer != NULL) { + memory->free(memory, buffer); + buffer = NULL; + } + + /* challenge pointer alignment */ + if (challengeAlignment == URI_TRUE) { + long double * ptr = memory->malloc(memory, 4 * sizeof(long double)); + if (ptr != NULL) { + ptr[0] = 0.0L; + ptr[1] = 1.1L; + ptr[2] = 2.2L; + ptr[3] = 3.3L; + + long double * const ptrNew = + memory->realloc(memory, ptr, 8 * sizeof(long double)); + if (ptrNew != NULL) { + ptr = ptrNew; + ptr[4] = 4.4L; + ptr[5] = 5.5L; + ptr[6] = 6.6L; + ptr[7] = 7.7L; + } + + memory->free(memory, ptr); + } + } + + return URI_SUCCESS; } - - +/* mull-on */ int uriTestMemoryManager(UriMemoryManager * memory) { - return uriTestMemoryManagerEx(memory, /*challengeAlignment=*/ URI_FALSE); + return uriTestMemoryManagerEx(memory, /*challengeAlignment=*/URI_FALSE); } - - /*extern*/ UriMemoryManager defaultMemoryManager = { - uriDefaultMalloc, - uriDefaultCalloc, - uriDefaultRealloc, - uriDefaultReallocarray, - uriDefaultFree, - NULL /* userData */ + uriDefaultMalloc, uriDefaultCalloc, uriDefaultRealloc, + uriDefaultReallocarray, uriDefaultFree, NULL /* userData */ }; diff --git a/ext/uri/uriparser/src/UriMemory.h b/ext/uri/uriparser/src/UriMemory.h index a930f93ac3c35..b03ca4a093b05 100644 --- a/ext/uri/uriparser/src/UriMemory.h +++ b/ext/uri/uriparser/src/UriMemory.h @@ -38,41 +38,31 @@ */ #ifndef URI_MEMORY_H -#define URI_MEMORY_H 1 - - - -#ifndef URI_DOXYGEN -# include -#endif - - - -#define URI_CHECK_MEMORY_MANAGER(memory) \ - do { \ - if (memory == NULL) { \ - memory = &defaultMemoryManager; \ - } else if (uriMemoryManagerIsComplete(memory) != URI_TRUE) { \ - return URI_ERROR_MEMORY_MANAGER_INCOMPLETE; \ - } \ - } while (0) - - - -#ifdef __cplusplus -# define URIPARSER_EXTERN extern "C" -#else -# define URIPARSER_EXTERN extern -#endif +# define URI_MEMORY_H 1 + +# ifndef URI_DOXYGEN +# include +# endif + +# define URI_CHECK_MEMORY_MANAGER(memory) \ + do { \ + if (memory == NULL) { \ + memory = &defaultMemoryManager; \ + } else if (uriMemoryManagerIsComplete(memory) != URI_TRUE) { \ + return URI_ERROR_MEMORY_MANAGER_INCOMPLETE; \ + } \ + } while (0) + +# ifdef __cplusplus +# define URIPARSER_EXTERN extern "C" +# else +# define URIPARSER_EXTERN extern +# endif URIPARSER_EXTERN UriMemoryManager defaultMemoryManager; -#undef URIPARSER_EXTERN - - +# undef URIPARSER_EXTERN UriBool uriMemoryManagerIsComplete(const UriMemoryManager * memory); - - #endif /* URI_MEMORY_H */ diff --git a/ext/uri/uriparser/src/UriNormalize.c b/ext/uri/uriparser/src/UriNormalize.c index 631d9a05a4d95..8c812d37a0a7e 100644 --- a/ext/uri/uriparser/src/UriNormalize.c +++ b/ext/uri/uriparser/src/UriNormalize.c @@ -47,865 +47,818 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriNormalize.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriNormalize.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriNormalize.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriNormalize.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriNormalizeBase.h" -# include "UriCommon.h" -# include "UriMemory.h" -#endif - - - -#include - - +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriNormalizeBase.h" +# include "UriCommon.h" +# include "UriMemory.h" +# endif + +# include static int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, unsigned int inMask, - unsigned int * outMask, UriMemoryManager * memory); + unsigned int * outMask, + UriMemoryManager * memory); -static UriBool URI_FUNC(MakeRangeOwner)(unsigned int * revertMask, - unsigned int maskTest, URI_TYPE(TextRange) * range, - UriMemoryManager * memory); -static UriBool URI_FUNC(MakeOwnerEngine)(URI_TYPE(Uri) * uri, - unsigned int * revertMask, UriMemoryManager * memory); +static UriBool URI_FUNC(MakeRangeOwner)(unsigned int * revertMask, unsigned int maskTest, + URI_TYPE(TextRange) * range, + UriMemoryManager * memory); +static UriBool URI_FUNC(MakeOwnerEngine)(URI_TYPE(Uri) * uri, unsigned int * revertMask, + UriMemoryManager * memory); static void URI_FUNC(FixPercentEncodingInplace)(const URI_CHAR * first, - const URI_CHAR ** afterLast); + const URI_CHAR ** afterLast); static UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** first, - const URI_CHAR ** afterLast, UriMemoryManager * memory); -static void URI_FUNC(FixPercentEncodingEngine)( - const URI_CHAR * inFirst, const URI_CHAR * inAfterLast, - const URI_CHAR * outFirst, const URI_CHAR ** outAfterLast); + const URI_CHAR ** afterLast, + UriMemoryManager * memory); +static void URI_FUNC(FixPercentEncodingEngine)(const URI_CHAR * inFirst, + const URI_CHAR * inAfterLast, + const URI_CHAR * outFirst, + const URI_CHAR ** outAfterLast); static UriBool URI_FUNC(ContainsUppercaseLetters)(const URI_CHAR * first, - const URI_CHAR * afterLast); + const URI_CHAR * afterLast); static UriBool URI_FUNC(ContainsUglyPercentEncoding)(const URI_CHAR * first, - const URI_CHAR * afterLast); + const URI_CHAR * afterLast); static void URI_FUNC(LowercaseInplace)(const URI_CHAR * first, - const URI_CHAR * afterLast); + const URI_CHAR * afterLast); static void URI_FUNC(LowercaseInplaceExceptPercentEncoding)(const URI_CHAR * first, - const URI_CHAR * afterLast); + const URI_CHAR * afterLast); static UriBool URI_FUNC(LowercaseMalloc)(const URI_CHAR ** first, - const URI_CHAR ** afterLast, UriMemoryManager * memory); - - - -void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri, - unsigned int revertMask, UriMemoryManager * memory) { - if (revertMask & URI_NORMALIZE_SCHEME) { - /* NOTE: A scheme cannot be the empty string - * so no need to compare .first with .afterLast, here. */ - memory->free(memory, (URI_CHAR *)uri->scheme.first); - uri->scheme.first = NULL; - uri->scheme.afterLast = NULL; - } - - if (revertMask & URI_NORMALIZE_USER_INFO) { - if (uri->userInfo.first != uri->userInfo.afterLast) { - memory->free(memory, (URI_CHAR *)uri->userInfo.first); - } - uri->userInfo.first = NULL; - uri->userInfo.afterLast = NULL; - } - - if (revertMask & URI_NORMALIZE_HOST) { - if (uri->hostData.ipFuture.first != NULL) { - /* IPvFuture */ - /* NOTE: An IPvFuture address cannot be the empty string - * so no need to compare .first with .afterLast, here. */ - memory->free(memory, (URI_CHAR *)uri->hostData.ipFuture.first); - uri->hostData.ipFuture.first = NULL; - uri->hostData.ipFuture.afterLast = NULL; - uri->hostText.first = NULL; - uri->hostText.afterLast = NULL; - } else if (uri->hostText.first != NULL) { - /* Regname */ - if (uri->hostText.first != uri->hostText.afterLast) { - memory->free(memory, (URI_CHAR *)uri->hostText.first); - } - uri->hostText.first = NULL; - uri->hostText.afterLast = NULL; - } - } - - /* NOTE: Port cannot happen! */ - - if (revertMask & URI_NORMALIZE_PATH) { - URI_TYPE(PathSegment) * walker = uri->pathHead; - while (walker != NULL) { - URI_TYPE(PathSegment) * const next = walker->next; - if (walker->text.afterLast > walker->text.first) { - memory->free(memory, (URI_CHAR *)walker->text.first); - } - memory->free(memory, walker); - walker = next; - } - uri->pathHead = NULL; - uri->pathTail = NULL; - } - - if (revertMask & URI_NORMALIZE_QUERY) { - if (uri->query.first != uri->query.afterLast) { - memory->free(memory, (URI_CHAR *)uri->query.first); - } - uri->query.first = NULL; - uri->query.afterLast = NULL; - } - - if (revertMask & URI_NORMALIZE_FRAGMENT) { - if (uri->fragment.first != uri->fragment.afterLast) { - memory->free(memory, (URI_CHAR *)uri->fragment.first); - } - uri->fragment.first = NULL; - uri->fragment.afterLast = NULL; - } + const URI_CHAR ** afterLast, + UriMemoryManager * memory); + +void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri, unsigned int revertMask, + UriMemoryManager * memory) { + if (revertMask & URI_NORMALIZE_SCHEME) { + /* NOTE: A scheme cannot be the empty string + * so no need to compare .first with .afterLast, here. */ + memory->free(memory, (URI_CHAR *)uri->scheme.first); + uri->scheme.first = NULL; + uri->scheme.afterLast = NULL; + } + + if (revertMask & URI_NORMALIZE_USER_INFO) { + if (uri->userInfo.first != uri->userInfo.afterLast) { + memory->free(memory, (URI_CHAR *)uri->userInfo.first); + } + uri->userInfo.first = NULL; + uri->userInfo.afterLast = NULL; + } + + if (revertMask & URI_NORMALIZE_HOST) { + if (uri->hostData.ipFuture.first != NULL) { + /* IPvFuture */ + /* NOTE: An IPvFuture address cannot be the empty string + * so no need to compare .first with .afterLast, here. */ + memory->free(memory, (URI_CHAR *)uri->hostData.ipFuture.first); + uri->hostData.ipFuture.first = NULL; + uri->hostData.ipFuture.afterLast = NULL; + uri->hostText.first = NULL; + uri->hostText.afterLast = NULL; + } else if (uri->hostText.first != NULL) { + /* Regname */ + if (uri->hostText.first != uri->hostText.afterLast) { + memory->free(memory, (URI_CHAR *)uri->hostText.first); + } + uri->hostText.first = NULL; + uri->hostText.afterLast = NULL; + } + } + + /* NOTE: Port cannot happen! */ + + if (revertMask & URI_NORMALIZE_PATH) { + URI_TYPE(PathSegment) * walker = uri->pathHead; + while (walker != NULL) { + URI_TYPE(PathSegment) * const next = walker->next; + if (walker->text.afterLast > walker->text.first) { + memory->free(memory, (URI_CHAR *)walker->text.first); + } + memory->free(memory, walker); + walker = next; + } + uri->pathHead = NULL; + uri->pathTail = NULL; + } + + if (revertMask & URI_NORMALIZE_QUERY) { + if (uri->query.first != uri->query.afterLast) { + memory->free(memory, (URI_CHAR *)uri->query.first); + } + uri->query.first = NULL; + uri->query.afterLast = NULL; + } + + if (revertMask & URI_NORMALIZE_FRAGMENT) { + if (uri->fragment.first != uri->fragment.afterLast) { + memory->free(memory, (URI_CHAR *)uri->fragment.first); + } + uri->fragment.first = NULL; + uri->fragment.afterLast = NULL; + } } - - static URI_INLINE UriBool URI_FUNC(ContainsUppercaseLetters)(const URI_CHAR * first, - const URI_CHAR * afterLast) { - if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) { - const URI_CHAR * i = first; - for (; i < afterLast; i++) { - /* 6.2.2.1 Case Normalization: uppercase letters in scheme or host */ - if ((*i >= _UT('A')) && (*i <= _UT('Z'))) { - return URI_TRUE; - } - } - } - return URI_FALSE; + const URI_CHAR * afterLast) { + if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) { + const URI_CHAR * i = first; + for (; i < afterLast; i++) { + /* 6.2.2.1 Case Normalization: uppercase letters in scheme or host */ + if ((*i >= _UT('A')) && (*i <= _UT('Z'))) { + return URI_TRUE; + } + } + } + return URI_FALSE; } - - -static URI_INLINE UriBool URI_FUNC(ContainsUglyPercentEncoding)(const URI_CHAR * first, - const URI_CHAR * afterLast) { - if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) { - const URI_CHAR * i = first; - for (; i + 2 < afterLast; i++) { - if (i[0] == _UT('%')) { - /* 6.2.2.1 Case Normalization: * - * lowercase percent-encodings */ - if (((i[1] >= _UT('a')) && (i[1] <= _UT('f'))) - || ((i[2] >= _UT('a')) && (i[2] <= _UT('f')))) { - return URI_TRUE; - } else { - /* 6.2.2.2 Percent-Encoding Normalization: * - * percent-encoded unreserved characters */ - const unsigned char left = URI_FUNC(HexdigToInt)(i[1]); - const unsigned char right = URI_FUNC(HexdigToInt)(i[2]); - const int code = 16 * left + right; - if (uriIsUnreserved(code)) { - return URI_TRUE; - } - } - } - } - } - return URI_FALSE; +static URI_INLINE UriBool URI_FUNC(ContainsUglyPercentEncoding)( + const URI_CHAR * first, const URI_CHAR * afterLast) { + if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) { + const URI_CHAR * i = first; + for (; i + 2 < afterLast; i++) { + if (i[0] == _UT('%')) { + /* 6.2.2.1 Case Normalization: * + * lowercase percent-encodings */ + if (((i[1] >= _UT('a')) && (i[1] <= _UT('f'))) + || ((i[2] >= _UT('a')) && (i[2] <= _UT('f')))) { + return URI_TRUE; + } else { + /* 6.2.2.2 Percent-Encoding Normalization: * + * percent-encoded unreserved characters */ + const unsigned char left = URI_FUNC(HexdigToInt)(i[1]); + const unsigned char right = URI_FUNC(HexdigToInt)(i[2]); + const int code = 16 * left + right; + if (uriIsUnreserved(code)) { + return URI_TRUE; + } + } + } + } + } + return URI_FALSE; } - - static URI_INLINE void URI_FUNC(LowercaseInplace)(const URI_CHAR * first, - const URI_CHAR * afterLast) { - if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) { - URI_CHAR * i = (URI_CHAR *)first; - const int lowerUpperDiff = (_UT('a') - _UT('A')); - for (; i < afterLast; i++) { - if ((*i >= _UT('A')) && (*i <=_UT('Z'))) { - *i = (URI_CHAR)(*i + lowerUpperDiff); - } - } - } + const URI_CHAR * afterLast) { + if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) { + URI_CHAR * i = (URI_CHAR *)first; + const int lowerUpperDiff = (_UT('a') - _UT('A')); + for (; i < afterLast; i++) { + if ((*i >= _UT('A')) && (*i <= _UT('Z'))) { + *i = (URI_CHAR)(*i + lowerUpperDiff); + } + } + } } - - -static URI_INLINE void URI_FUNC(LowercaseInplaceExceptPercentEncoding)(const URI_CHAR * first, - const URI_CHAR * afterLast) { - if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) { - URI_CHAR * i = (URI_CHAR *)first; - const int lowerUpperDiff = (_UT('a') - _UT('A')); - for (; i < afterLast; i++) { - if ((*i >= _UT('A')) && (*i <=_UT('Z'))) { - *i = (URI_CHAR)(*i + lowerUpperDiff); - } else if (*i == _UT('%')) { - if (i + 3 >= afterLast) { - return; - } - i += 2; - } - } - } +static URI_INLINE void +URI_FUNC(LowercaseInplaceExceptPercentEncoding)(const URI_CHAR * first, + const URI_CHAR * afterLast) { + if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) { + URI_CHAR * i = (URI_CHAR *)first; + const int lowerUpperDiff = (_UT('a') - _UT('A')); + for (; i < afterLast; i++) { + if ((*i >= _UT('A')) && (*i <= _UT('Z'))) { + *i = (URI_CHAR)(*i + lowerUpperDiff); + } else if (*i == _UT('%')) { + if (i + 3 >= afterLast) { + return; + } + i += 2; + } + } + } } - - static URI_INLINE UriBool URI_FUNC(LowercaseMalloc)(const URI_CHAR ** first, - const URI_CHAR ** afterLast, UriMemoryManager * memory) { - int lenInChars; - const int lowerUpperDiff = (_UT('a') - _UT('A')); - URI_CHAR * buffer; - int i = 0; - - if ((first == NULL) || (afterLast == NULL) || (*first == NULL) - || (*afterLast == NULL)) { - return URI_FALSE; - } - - lenInChars = (int)(*afterLast - *first); - if (lenInChars == 0) { - return URI_TRUE; - } else if (lenInChars < 0) { - return URI_FALSE; - } - - buffer = memory->malloc(memory, lenInChars * sizeof(URI_CHAR)); - if (buffer == NULL) { - return URI_FALSE; - } - - for (; i < lenInChars; i++) { - if (((*first)[i] >= _UT('A')) && ((*first)[i] <=_UT('Z'))) { - buffer[i] = (URI_CHAR)((*first)[i] + lowerUpperDiff); - } else { - buffer[i] = (*first)[i]; - } - } - - *first = buffer; - *afterLast = buffer + lenInChars; - return URI_TRUE; + const URI_CHAR ** afterLast, + UriMemoryManager * memory) { + int lenInChars; + const int lowerUpperDiff = (_UT('a') - _UT('A')); + URI_CHAR * buffer; + int i = 0; + + if ((first == NULL) || (afterLast == NULL) || (*first == NULL) + || (*afterLast == NULL)) { + return URI_FALSE; + } + + lenInChars = (int)(*afterLast - *first); + if (lenInChars == 0) { + return URI_TRUE; + } else if (lenInChars < 0) { + return URI_FALSE; + } + + buffer = memory->malloc(memory, lenInChars * sizeof(URI_CHAR)); + if (buffer == NULL) { + return URI_FALSE; + } + + for (; i < lenInChars; i++) { + if (((*first)[i] >= _UT('A')) && ((*first)[i] <= _UT('Z'))) { + buffer[i] = (URI_CHAR)((*first)[i] + lowerUpperDiff); + } else { + buffer[i] = (*first)[i]; + } + } + + *first = buffer; + *afterLast = buffer + lenInChars; + return URI_TRUE; } - - /* NOTE: Implementation must stay inplace-compatible */ -static URI_INLINE void URI_FUNC(FixPercentEncodingEngine)( - const URI_CHAR * inFirst, const URI_CHAR * inAfterLast, - const URI_CHAR * outFirst, const URI_CHAR ** outAfterLast) { - URI_CHAR * write = (URI_CHAR *)outFirst; - const int lenInChars = (int)(inAfterLast - inFirst); - int i = 0; - - /* All but last two */ - for (; i + 2 < lenInChars; i++) { - if (inFirst[i] != _UT('%')) { - write[0] = inFirst[i]; - write++; - } else { - /* 6.2.2.2 Percent-Encoding Normalization: * - * percent-encoded unreserved characters */ - const URI_CHAR one = inFirst[i + 1]; - const URI_CHAR two = inFirst[i + 2]; - const unsigned char left = URI_FUNC(HexdigToInt)(one); - const unsigned char right = URI_FUNC(HexdigToInt)(two); - const int code = 16 * left + right; - if (uriIsUnreserved(code)) { - write[0] = (URI_CHAR)(code); - write++; - } else { - /* 6.2.2.1 Case Normalization: * - * uppercase percent-encodings */ - write[0] = _UT('%'); - write[1] = URI_FUNC(HexToLetterEx)(left, URI_TRUE); - write[2] = URI_FUNC(HexToLetterEx)(right, URI_TRUE); - write += 3; - } - - i += 2; /* For the two chars of the percent group we just ate */ - } - } - - /* Last two */ - for (; i < lenInChars; i++) { - write[0] = inFirst[i]; - write++; - } - - *outAfterLast = write; +static URI_INLINE void +URI_FUNC(FixPercentEncodingEngine)(const URI_CHAR * inFirst, const URI_CHAR * inAfterLast, + const URI_CHAR * outFirst, + const URI_CHAR ** outAfterLast) { + URI_CHAR * write = (URI_CHAR *)outFirst; + const int lenInChars = (int)(inAfterLast - inFirst); + int i = 0; + + /* All but last two */ + for (; i + 2 < lenInChars; i++) { + if (inFirst[i] != _UT('%')) { + write[0] = inFirst[i]; + write++; + } else { + /* 6.2.2.2 Percent-Encoding Normalization: * + * percent-encoded unreserved characters */ + const URI_CHAR one = inFirst[i + 1]; + const URI_CHAR two = inFirst[i + 2]; + const unsigned char left = URI_FUNC(HexdigToInt)(one); + const unsigned char right = URI_FUNC(HexdigToInt)(two); + const int code = 16 * left + right; + if (uriIsUnreserved(code)) { + write[0] = (URI_CHAR)(code); + write++; + } else { + /* 6.2.2.1 Case Normalization: * + * uppercase percent-encodings */ + write[0] = _UT('%'); + write[1] = URI_FUNC(HexToLetterEx)(left, URI_TRUE); + write[2] = URI_FUNC(HexToLetterEx)(right, URI_TRUE); + write += 3; + } + + i += 2; /* For the two chars of the percent group we just ate */ + } + } + + /* Last two */ + for (; i < lenInChars; i++) { + write[0] = inFirst[i]; + write++; + } + + *outAfterLast = write; } - - static URI_INLINE void URI_FUNC(FixPercentEncodingInplace)(const URI_CHAR * first, - const URI_CHAR ** afterLast) { - /* Death checks */ - if ((first == NULL) || (afterLast == NULL) || (*afterLast == NULL)) { - return; - } - - /* Fix inplace */ - URI_FUNC(FixPercentEncodingEngine)(first, *afterLast, first, afterLast); + const URI_CHAR ** afterLast) { + /* Death checks */ + if ((first == NULL) || (afterLast == NULL) || (*afterLast == NULL)) { + return; + } + + /* Fix inplace */ + URI_FUNC(FixPercentEncodingEngine)(first, *afterLast, first, afterLast); } - - static URI_INLINE UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** first, - const URI_CHAR ** afterLast, UriMemoryManager * memory) { - int lenInChars; - URI_CHAR * buffer; - - /* Death checks */ - if ((first == NULL) || (afterLast == NULL) - || (*first == NULL) || (*afterLast == NULL)) { - return URI_FALSE; - } - - /* Old text length */ - lenInChars = (int)(*afterLast - *first); - if (lenInChars == 0) { - return URI_TRUE; - } else if (lenInChars < 0) { - return URI_FALSE; - } - - /* New buffer */ - buffer = memory->malloc(memory, lenInChars * sizeof(URI_CHAR)); - if (buffer == NULL) { - return URI_FALSE; - } - - /* Fix on copy */ - URI_FUNC(FixPercentEncodingEngine)(*first, *afterLast, buffer, afterLast); - *first = buffer; - return URI_TRUE; + const URI_CHAR ** afterLast, + UriMemoryManager * memory) { + int lenInChars; + URI_CHAR * buffer; + + /* Death checks */ + if ((first == NULL) || (afterLast == NULL) || (*first == NULL) + || (*afterLast == NULL)) { + return URI_FALSE; + } + + /* Old text length */ + lenInChars = (int)(*afterLast - *first); + if (lenInChars == 0) { + return URI_TRUE; + } else if (lenInChars < 0) { + return URI_FALSE; + } + + /* New buffer */ + buffer = memory->malloc(memory, lenInChars * sizeof(URI_CHAR)); + if (buffer == NULL) { + return URI_FALSE; + } + + /* Fix on copy */ + URI_FUNC(FixPercentEncodingEngine)(*first, *afterLast, buffer, afterLast); + *first = buffer; + return URI_TRUE; } - - static URI_INLINE UriBool URI_FUNC(MakeRangeOwner)(unsigned int * revertMask, - unsigned int maskTest, URI_TYPE(TextRange) * range, - UriMemoryManager * memory) { - if (((*revertMask & maskTest) == 0) - && (range->first != NULL) - && (range->afterLast != NULL) - && (range->afterLast > range->first)) { - if (URI_FUNC(CopyRange)(range, range, memory) == URI_FALSE) { - return URI_FALSE; - } - *revertMask |= maskTest; - } - return URI_TRUE; + unsigned int maskTest, + URI_TYPE(TextRange) * range, + UriMemoryManager * memory) { + if (((*revertMask & maskTest) == 0) && (range->first != NULL) + && (range->afterLast != NULL) && (range->afterLast > range->first)) { + if (URI_FUNC(CopyRange)(range, range, memory) == URI_FALSE) { + return URI_FALSE; + } + *revertMask |= maskTest; + } + return URI_TRUE; } - - static URI_INLINE UriBool URI_FUNC(MakeOwnerEngine)(URI_TYPE(Uri) * uri, - unsigned int * revertMask, UriMemoryManager * memory) { - URI_TYPE(PathSegment) * walker = uri->pathHead; - if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_SCHEME, - &(uri->scheme), memory) - || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_USER_INFO, - &(uri->userInfo), memory) - || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_QUERY, - &(uri->query), memory) - || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_FRAGMENT, - &(uri->fragment), memory)) { - return URI_FALSE; /* Raises malloc error */ - } - - /* Host */ - if ((*revertMask & URI_NORMALIZE_HOST) == 0) { - if (uri->hostData.ipFuture.first != NULL) { - /* IPvFuture */ - if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_HOST, - &(uri->hostData.ipFuture), memory)) { - return URI_FALSE; /* Raises malloc error */ - } - uri->hostText.first = uri->hostData.ipFuture.first; - uri->hostText.afterLast = uri->hostData.ipFuture.afterLast; - } else if (uri->hostText.first != NULL) { - /* Regname */ - if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_HOST, - &(uri->hostText), memory)) { - return URI_FALSE; /* Raises malloc error */ - } - } - } - - /* Path */ - if ((*revertMask & URI_NORMALIZE_PATH) == 0) { - while (walker != NULL) { - if (!URI_FUNC(MakeRangeOwner)(revertMask, 0, &(walker->text), memory)) { - /* Free allocations done so far and kill path */ - - /* Kill path to one before walker (if any) */ - URI_TYPE(PathSegment) * ranger = uri->pathHead; - while (ranger != walker) { - URI_TYPE(PathSegment) * const next = ranger->next; - if ((ranger->text.first != NULL) - && (ranger->text.afterLast != NULL) - && (ranger->text.afterLast > ranger->text.first)) { - memory->free(memory, (URI_CHAR *)ranger->text.first); - } - memory->free(memory, ranger); - ranger = next; - } - - /* Kill path from walker */ - while (walker != NULL) { - URI_TYPE(PathSegment) * const next = walker->next; - memory->free(memory, walker); - walker = next; - } - - uri->pathHead = NULL; - uri->pathTail = NULL; - return URI_FALSE; /* Raises malloc error */ - } - walker = walker->next; - } - *revertMask |= URI_NORMALIZE_PATH; - } - - /* Port text, must come last so we don't have to undo that one if it fails. * - * Otherwise we would need and extra enum flag for it although the port * - * cannot go unnormalized... */ - if (!URI_FUNC(MakeRangeOwner)(revertMask, 0, &(uri->portText), memory)) { - return URI_FALSE; /* Raises malloc error */ - } - - return URI_TRUE; + unsigned int * revertMask, + UriMemoryManager * memory) { + URI_TYPE(PathSegment) * walker = uri->pathHead; + if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_SCHEME, &(uri->scheme), + memory) + || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_USER_INFO, + &(uri->userInfo), memory) + || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_QUERY, &(uri->query), + memory) + || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_FRAGMENT, &(uri->fragment), + memory)) { + return URI_FALSE; /* Raises malloc error */ + } + + /* Host */ + if ((*revertMask & URI_NORMALIZE_HOST) == 0) { + if (uri->hostData.ipFuture.first != NULL) { + /* IPvFuture */ + if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_HOST, + &(uri->hostData.ipFuture), memory)) { + return URI_FALSE; /* Raises malloc error */ + } + uri->hostText.first = uri->hostData.ipFuture.first; + uri->hostText.afterLast = uri->hostData.ipFuture.afterLast; + } else if (uri->hostText.first != NULL) { + /* Regname */ + if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_HOST, + &(uri->hostText), memory)) { + return URI_FALSE; /* Raises malloc error */ + } + } + } + + /* Path */ + if ((*revertMask & URI_NORMALIZE_PATH) == 0) { + while (walker != NULL) { + if (!URI_FUNC(MakeRangeOwner)(revertMask, 0, &(walker->text), memory)) { + /* Free allocations done so far and kill path */ + + /* Kill path to one before walker (if any) */ + URI_TYPE(PathSegment) * ranger = uri->pathHead; + while (ranger != walker) { + URI_TYPE(PathSegment) * const next = ranger->next; + if ((ranger->text.first != NULL) && (ranger->text.afterLast != NULL) + && (ranger->text.afterLast > ranger->text.first)) { + memory->free(memory, (URI_CHAR *)ranger->text.first); + } + memory->free(memory, ranger); + ranger = next; + } + + /* Kill path from walker */ + while (walker != NULL) { + URI_TYPE(PathSegment) * const next = walker->next; + memory->free(memory, walker); + walker = next; + } + + uri->pathHead = NULL; + uri->pathTail = NULL; + return URI_FALSE; /* Raises malloc error */ + } + walker = walker->next; + } + *revertMask |= URI_NORMALIZE_PATH; + } + + /* Port text, must come last so we don't have to undo that one if it fails. * + * Otherwise we would need and extra enum flag for it although the port * + * cannot go unnormalized... */ + if (!URI_FUNC(MakeRangeOwner)(revertMask, 0, &(uri->portText), memory)) { + return URI_FALSE; /* Raises malloc error */ + } + + return URI_TRUE; } - - unsigned int URI_FUNC(NormalizeSyntaxMaskRequired)(const URI_TYPE(Uri) * uri) { - unsigned int outMask = URI_NORMALIZED; /* for NULL uri */ - URI_FUNC(NormalizeSyntaxMaskRequiredEx)(uri, &outMask); - return outMask; + unsigned int outMask = URI_NORMALIZED; /* for NULL uri */ + URI_FUNC(NormalizeSyntaxMaskRequiredEx)(uri, &outMask); + return outMask; } - - int URI_FUNC(NormalizeSyntaxMaskRequiredEx)(const URI_TYPE(Uri) * uri, - unsigned int * outMask) { - UriMemoryManager * const memory = NULL; /* no use of memory manager */ - -#if defined(__GNUC__) && ((__GNUC__ > 4) \ - || ((__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 2))) - /* Slower code that fixes a warning, not sure if this is a smart idea */ - URI_TYPE(Uri) writeableClone; -#endif - - if ((uri == NULL) || (outMask == NULL)) { - return URI_ERROR_NULL; - } - -#if defined(__GNUC__) && ((__GNUC__ > 4) \ - || ((__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 2))) - /* Slower code that fixes a warning, not sure if this is a smart idea */ - memcpy(&writeableClone, uri, 1 * sizeof(URI_TYPE(Uri))); - URI_FUNC(NormalizeSyntaxEngine)(&writeableClone, 0, outMask, memory); -#else - URI_FUNC(NormalizeSyntaxEngine)((URI_TYPE(Uri) *)uri, 0, outMask, memory); -#endif - return URI_SUCCESS; + unsigned int * outMask) { + UriMemoryManager * const memory = NULL; /* no use of memory manager */ + +# if defined(__GNUC__) \ + && ((__GNUC__ > 4) \ + || ((__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 2))) + /* Slower code that fixes a warning, not sure if this is a smart idea */ + URI_TYPE(Uri) writeableClone; +# endif + + if ((uri == NULL) || (outMask == NULL)) { + return URI_ERROR_NULL; + } + +# if defined(__GNUC__) \ + && ((__GNUC__ > 4) \ + || ((__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 2))) + /* Slower code that fixes a warning, not sure if this is a smart idea */ + memcpy(&writeableClone, uri, 1 * sizeof(URI_TYPE(Uri))); + URI_FUNC(NormalizeSyntaxEngine)(&writeableClone, 0, outMask, memory); +# else + URI_FUNC(NormalizeSyntaxEngine)((URI_TYPE(Uri) *)uri, 0, outMask, memory); +# endif + return URI_SUCCESS; } - - int URI_FUNC(NormalizeSyntaxEx)(URI_TYPE(Uri) * uri, unsigned int mask) { - return URI_FUNC(NormalizeSyntaxExMm)(uri, mask, NULL); + return URI_FUNC(NormalizeSyntaxExMm)(uri, mask, NULL); } - - int URI_FUNC(NormalizeSyntaxExMm)(URI_TYPE(Uri) * uri, unsigned int mask, - UriMemoryManager * memory) { - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - return URI_FUNC(NormalizeSyntaxEngine)(uri, mask, NULL, memory); + UriMemoryManager * memory) { + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + return URI_FUNC(NormalizeSyntaxEngine)(uri, mask, NULL, memory); } - - int URI_FUNC(NormalizeSyntax)(URI_TYPE(Uri) * uri) { - return URI_FUNC(NormalizeSyntaxEx)(uri, (unsigned int)-1); + return URI_FUNC(NormalizeSyntaxEx)(uri, (unsigned int)-1); } - -static const URI_CHAR * URI_FUNC(PastLeadingZeros)(const URI_CHAR * first, const URI_CHAR * afterLast) { - assert(first != NULL); - assert(afterLast != NULL); - assert(first != afterLast); - - { - /* Find the first non-zero character */ - const URI_CHAR * remainderFirst = first; - while ((remainderFirst < afterLast) && (remainderFirst[0] == _UT('0'))) { - remainderFirst++; - } - - /* Is the string /all/ zeros? */ - if (remainderFirst == afterLast) { - /* Yes, and length is >=1 because we ruled out the empty string earlier; - * pull back onto rightmost zero */ - assert(remainderFirst > first); - remainderFirst--; - assert(remainderFirst[0] == _UT('0')); - } - - return remainderFirst; - } +static const URI_CHAR * URI_FUNC(PastLeadingZeros)(const URI_CHAR * first, + const URI_CHAR * afterLast) { + assert(first != NULL); + assert(afterLast != NULL); + assert(first != afterLast); + + /* Find the first non-zero character */ + const URI_CHAR * remainderFirst = first; + while ((remainderFirst < afterLast) && (remainderFirst[0] == _UT('0'))) { + remainderFirst++; + } + + /* Is the string /all/ zeros? */ + if (remainderFirst == afterLast) { + /* Yes, and length is >=1 because we ruled out the empty string earlier; + * pull back onto rightmost zero */ + assert(remainderFirst > first); + remainderFirst--; + assert(remainderFirst[0] == _UT('0')); + } + + return remainderFirst; } +static void URI_FUNC(DropLeadingZerosInplace)(URI_CHAR * first, + const URI_CHAR ** afterLast) { + assert(first != NULL); + assert(afterLast != NULL); + assert(*afterLast != NULL); + if (first == *afterLast) { + return; + } -static void URI_FUNC(DropLeadingZerosInplace)(URI_CHAR * first, const URI_CHAR ** afterLast) { - assert(first != NULL); - assert(afterLast != NULL); - assert(*afterLast != NULL); - - if (first == *afterLast) { - return; - } - - { - const URI_CHAR * const remainderFirst = URI_FUNC(PastLeadingZeros)(first, *afterLast); + const URI_CHAR * const remainderFirst = URI_FUNC(PastLeadingZeros)(first, *afterLast); - if (remainderFirst > first) { - const size_t remainderLen = *afterLast - remainderFirst; - memmove(first, remainderFirst, remainderLen * sizeof(URI_CHAR)); - first[remainderLen] = _UT('\0'); - *afterLast = first + remainderLen; - } - } + if (remainderFirst > first) { + const size_t remainderLen = *afterLast - remainderFirst; + memmove(first, remainderFirst, remainderLen * sizeof(URI_CHAR)); + first[remainderLen] = _UT('\0'); + *afterLast = first + remainderLen; + } } +static void URI_FUNC(AdvancePastLeadingZeros)(const URI_CHAR ** first, + const URI_CHAR * afterLast) { + assert(first != NULL); + assert(*first != NULL); + assert(afterLast != NULL); + if (*first == afterLast) { + return; + } -static void URI_FUNC(AdvancePastLeadingZeros)( - const URI_CHAR ** first, const URI_CHAR * afterLast) { - assert(first != NULL); - assert(*first != NULL); - assert(afterLast != NULL); + const URI_CHAR * const remainderFirst = URI_FUNC(PastLeadingZeros)(*first, afterLast); - if (*first == afterLast) { - return; - } - - { - const URI_CHAR * const remainderFirst = URI_FUNC(PastLeadingZeros)(*first, afterLast); - - /* Cut off leading zeros */ - *first = remainderFirst; - } + /* Cut off leading zeros */ + *first = remainderFirst; } - - static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, - unsigned int inMask, unsigned int * outMask, - UriMemoryManager * memory) { - unsigned int revertMask = URI_NORMALIZED; - - /* Not just doing inspection? -> memory manager required! */ - if (outMask == NULL) { - assert(memory != NULL); - } - - if (uri == NULL) { - if (outMask != NULL) { - *outMask = URI_NORMALIZED; - return URI_SUCCESS; - } else { - return URI_ERROR_NULL; - } - } - - if (outMask != NULL) { - /* Reset mask */ - *outMask = URI_NORMALIZED; - } else if (inMask == URI_NORMALIZED) { - /* Nothing to do */ - return URI_SUCCESS; - } - - /* Scheme, host */ - if (outMask != NULL) { - const UriBool normalizeScheme = URI_FUNC(ContainsUppercaseLetters)( - uri->scheme.first, uri->scheme.afterLast); - const UriBool normalizeHostCase = URI_FUNC(ContainsUppercaseLetters)( - uri->hostText.first, uri->hostText.afterLast); - if (normalizeScheme) { - *outMask |= URI_NORMALIZE_SCHEME; - } - - if (normalizeHostCase) { - *outMask |= URI_NORMALIZE_HOST; - } else { - const UriBool normalizeHostPrecent = URI_FUNC(ContainsUglyPercentEncoding)( - uri->hostText.first, uri->hostText.afterLast); - if (normalizeHostPrecent) { - *outMask |= URI_NORMALIZE_HOST; - } - } - } else { - /* Scheme */ - if ((inMask & URI_NORMALIZE_SCHEME) && (uri->scheme.first != NULL)) { - if (uri->owner) { - URI_FUNC(LowercaseInplace)(uri->scheme.first, uri->scheme.afterLast); - } else { - if (!URI_FUNC(LowercaseMalloc)(&(uri->scheme.first), &(uri->scheme.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, revertMask, memory); - return URI_ERROR_MALLOC; - } - revertMask |= URI_NORMALIZE_SCHEME; - } - } - - /* Host */ - if (inMask & URI_NORMALIZE_HOST) { - if (uri->hostData.ipFuture.first != NULL) { - /* IPvFuture */ - if (uri->owner) { - URI_FUNC(LowercaseInplace)(uri->hostData.ipFuture.first, - uri->hostData.ipFuture.afterLast); - } else { - if (!URI_FUNC(LowercaseMalloc)(&(uri->hostData.ipFuture.first), - &(uri->hostData.ipFuture.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, revertMask, memory); - return URI_ERROR_MALLOC; - } - revertMask |= URI_NORMALIZE_HOST; - } - uri->hostText.first = uri->hostData.ipFuture.first; - uri->hostText.afterLast = uri->hostData.ipFuture.afterLast; - } else if ((uri->hostText.first != NULL) - && (uri->hostData.ip4 == NULL)) { - /* Regname or IPv6 */ - if (uri->owner) { - URI_FUNC(FixPercentEncodingInplace)(uri->hostText.first, - &(uri->hostText.afterLast)); - } else { - if (!URI_FUNC(FixPercentEncodingMalloc)( - &(uri->hostText.first), - &(uri->hostText.afterLast), - memory)) { - URI_FUNC(PreventLeakage)(uri, revertMask, memory); - return URI_ERROR_MALLOC; - } - revertMask |= URI_NORMALIZE_HOST; - } - - URI_FUNC(LowercaseInplaceExceptPercentEncoding)(uri->hostText.first, - uri->hostText.afterLast); - } - } - } - - /* Port */ - if (outMask != NULL) { - /* Is there a port even? */ - if (uri->portText.first != NULL) { - /* Determine whether the port is already normalized, i.e. either "", "0" or no leading zeros */ - const size_t portLen = uri->portText.afterLast - uri->portText.first; - if ((portLen > 1) && (uri->portText.first[0] == _UT('0'))) { - *outMask |= URI_NORMALIZE_PORT; - } - } - } else { - /* Normalize the port, i.e. drop leading zeros (except for string "0") */ - if ((inMask & URI_NORMALIZE_PORT) && (uri->portText.first != NULL)) { - if (uri->owner) { - URI_FUNC(DropLeadingZerosInplace)((URI_CHAR *)uri->portText.first, &(uri->portText.afterLast)); - } else { - URI_FUNC(AdvancePastLeadingZeros)(&(uri->portText.first), uri->portText.afterLast); - } - } - } - - /* User info */ - if (outMask != NULL) { - const UriBool normalizeUserInfo = URI_FUNC(ContainsUglyPercentEncoding)( - uri->userInfo.first, uri->userInfo.afterLast); - if (normalizeUserInfo) { - *outMask |= URI_NORMALIZE_USER_INFO; - } - } else { - if ((inMask & URI_NORMALIZE_USER_INFO) && (uri->userInfo.first != NULL)) { - if (uri->owner) { - URI_FUNC(FixPercentEncodingInplace)(uri->userInfo.first, &(uri->userInfo.afterLast)); - } else { - if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->userInfo.first), - &(uri->userInfo.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, revertMask, memory); - return URI_ERROR_MALLOC; - } - revertMask |= URI_NORMALIZE_USER_INFO; - } - } - } - - /* Path */ - if (outMask != NULL) { - const URI_TYPE(PathSegment) * walker = uri->pathHead; - while (walker != NULL) { - const URI_CHAR * const first = walker->text.first; - const URI_CHAR * const afterLast = walker->text.afterLast; - if ((first != NULL) - && (afterLast != NULL) - && (afterLast > first) - && ( - (((afterLast - first) == 1) - && (first[0] == _UT('.'))) - || - (((afterLast - first) == 2) - && (first[0] == _UT('.')) - && (first[1] == _UT('.'))) - || - URI_FUNC(ContainsUglyPercentEncoding)(first, afterLast) - )) { - *outMask |= URI_NORMALIZE_PATH; - break; - } - walker = walker->next; - } - } else if (inMask & URI_NORMALIZE_PATH) { - URI_TYPE(PathSegment) * walker; - const UriBool relative = ((uri->scheme.first == NULL) - && !uri->absolutePath) ? URI_TRUE : URI_FALSE; - - /* Fix percent-encoding for each segment */ - walker = uri->pathHead; - if (uri->owner) { - while (walker != NULL) { - URI_FUNC(FixPercentEncodingInplace)(walker->text.first, &(walker->text.afterLast)); - walker = walker->next; - } - } else { - while (walker != NULL) { - if (!URI_FUNC(FixPercentEncodingMalloc)(&(walker->text.first), - &(walker->text.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, revertMask, memory); - return URI_ERROR_MALLOC; - } - walker = walker->next; - } - revertMask |= URI_NORMALIZE_PATH; - } - - /* 6.2.2.3 Path Segment Normalization */ - if (!URI_FUNC(RemoveDotSegmentsEx)(uri, relative, - (uri->owner == URI_TRUE) - || ((revertMask & URI_NORMALIZE_PATH) != 0), - memory)) { - URI_FUNC(PreventLeakage)(uri, revertMask, memory); - return URI_ERROR_MALLOC; - } - URI_FUNC(FixEmptyTrailSegment)(uri, memory); - } - - /* Query, fragment */ - if (outMask != NULL) { - const UriBool normalizeQuery = URI_FUNC(ContainsUglyPercentEncoding)( - uri->query.first, uri->query.afterLast); - const UriBool normalizeFragment = URI_FUNC(ContainsUglyPercentEncoding)( - uri->fragment.first, uri->fragment.afterLast); - if (normalizeQuery) { - *outMask |= URI_NORMALIZE_QUERY; - } - - if (normalizeFragment) { - *outMask |= URI_NORMALIZE_FRAGMENT; - } - } else { - /* Query */ - if ((inMask & URI_NORMALIZE_QUERY) && (uri->query.first != NULL)) { - if (uri->owner) { - URI_FUNC(FixPercentEncodingInplace)(uri->query.first, &(uri->query.afterLast)); - } else { - if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->query.first), - &(uri->query.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, revertMask, memory); - return URI_ERROR_MALLOC; - } - revertMask |= URI_NORMALIZE_QUERY; - } - } - - /* Fragment */ - if ((inMask & URI_NORMALIZE_FRAGMENT) && (uri->fragment.first != NULL)) { - if (uri->owner) { - URI_FUNC(FixPercentEncodingInplace)(uri->fragment.first, &(uri->fragment.afterLast)); - } else { - if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->fragment.first), - &(uri->fragment.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, revertMask, memory); - return URI_ERROR_MALLOC; - } - revertMask |= URI_NORMALIZE_FRAGMENT; - } - } - } - - /* Dup all not duped yet */ - if ((outMask == NULL) && !uri->owner) { - if (!URI_FUNC(MakeOwnerEngine)(uri, &revertMask, memory)) { - URI_FUNC(PreventLeakage)(uri, revertMask, memory); - return URI_ERROR_MALLOC; - } - uri->owner = URI_TRUE; - } - - return URI_SUCCESS; + unsigned int inMask, + unsigned int * outMask, + UriMemoryManager * memory) { + unsigned int revertMask = URI_NORMALIZED; + + /* Not just doing inspection? -> memory manager required! */ + if (outMask == NULL) { + assert(memory != NULL); + } + + if (uri == NULL) { + if (outMask != NULL) { + *outMask = URI_NORMALIZED; + return URI_SUCCESS; + } else { + return URI_ERROR_NULL; + } + } + + if (outMask != NULL) { + /* Reset mask */ + *outMask = URI_NORMALIZED; + } else if (inMask == URI_NORMALIZED) { + /* Nothing to do */ + return URI_SUCCESS; + } + + /* Scheme, host */ + if (outMask != NULL) { + const UriBool normalizeScheme = + URI_FUNC(ContainsUppercaseLetters)(uri->scheme.first, uri->scheme.afterLast); + const UriBool normalizeHostCase = URI_FUNC(ContainsUppercaseLetters)( + uri->hostText.first, uri->hostText.afterLast); + if (normalizeScheme) { + *outMask |= URI_NORMALIZE_SCHEME; + } + + if (normalizeHostCase) { + *outMask |= URI_NORMALIZE_HOST; + } else { + const UriBool normalizeHostPrecent = URI_FUNC(ContainsUglyPercentEncoding)( + uri->hostText.first, uri->hostText.afterLast); + if (normalizeHostPrecent) { + *outMask |= URI_NORMALIZE_HOST; + } + } + } else { + /* Scheme */ + if ((inMask & URI_NORMALIZE_SCHEME) && (uri->scheme.first != NULL)) { + if (uri->owner) { + URI_FUNC(LowercaseInplace)(uri->scheme.first, uri->scheme.afterLast); + } else { + if (!URI_FUNC(LowercaseMalloc)(&(uri->scheme.first), + &(uri->scheme.afterLast), memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + return URI_ERROR_MALLOC; + } + revertMask |= URI_NORMALIZE_SCHEME; + } + } + + /* Host */ + if (inMask & URI_NORMALIZE_HOST) { + if (uri->hostData.ipFuture.first != NULL) { + /* IPvFuture */ + if (uri->owner) { + URI_FUNC(LowercaseInplace)(uri->hostData.ipFuture.first, + uri->hostData.ipFuture.afterLast); + } else { + if (!URI_FUNC(LowercaseMalloc)(&(uri->hostData.ipFuture.first), + &(uri->hostData.ipFuture.afterLast), + memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + return URI_ERROR_MALLOC; + } + revertMask |= URI_NORMALIZE_HOST; + } + uri->hostText.first = uri->hostData.ipFuture.first; + uri->hostText.afterLast = uri->hostData.ipFuture.afterLast; + } else if ((uri->hostText.first != NULL) && (uri->hostData.ip4 == NULL)) { + /* Regname or IPv6 */ + if (uri->owner) { + URI_FUNC(FixPercentEncodingInplace)(uri->hostText.first, + &(uri->hostText.afterLast)); + } else { + if (!URI_FUNC(FixPercentEncodingMalloc)( + &(uri->hostText.first), &(uri->hostText.afterLast), memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + return URI_ERROR_MALLOC; + } + revertMask |= URI_NORMALIZE_HOST; + } + + URI_FUNC(LowercaseInplaceExceptPercentEncoding)(uri->hostText.first, + uri->hostText.afterLast); + } + } + } + + /* Port */ + if (outMask != NULL) { + /* Is there a port even? */ + if (uri->portText.first != NULL) { + /* Determine whether the port is already normalized, i.e. either "", "0" or no + * leading zeros */ + const size_t portLen = uri->portText.afterLast - uri->portText.first; + if ((portLen > 1) && (uri->portText.first[0] == _UT('0'))) { + *outMask |= URI_NORMALIZE_PORT; + } + } + } else { + /* Normalize the port, i.e. drop leading zeros (except for string "0") */ + if ((inMask & URI_NORMALIZE_PORT) && (uri->portText.first != NULL)) { + if (uri->owner) { + URI_FUNC(DropLeadingZerosInplace)((URI_CHAR *)uri->portText.first, + &(uri->portText.afterLast)); + } else { + URI_FUNC(AdvancePastLeadingZeros)(&(uri->portText.first), + uri->portText.afterLast); + } + } + } + + /* User info */ + if (outMask != NULL) { + const UriBool normalizeUserInfo = URI_FUNC(ContainsUglyPercentEncoding)( + uri->userInfo.first, uri->userInfo.afterLast); + if (normalizeUserInfo) { + *outMask |= URI_NORMALIZE_USER_INFO; + } + } else { + if ((inMask & URI_NORMALIZE_USER_INFO) && (uri->userInfo.first != NULL)) { + if (uri->owner) { + URI_FUNC(FixPercentEncodingInplace)(uri->userInfo.first, + &(uri->userInfo.afterLast)); + } else { + if (!URI_FUNC(FixPercentEncodingMalloc)( + &(uri->userInfo.first), &(uri->userInfo.afterLast), memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + return URI_ERROR_MALLOC; + } + revertMask |= URI_NORMALIZE_USER_INFO; + } + } + } + + /* Path */ + if (outMask != NULL) { + const URI_TYPE(PathSegment) * walker = uri->pathHead; + while (walker != NULL) { + const URI_CHAR * const first = walker->text.first; + const URI_CHAR * const afterLast = walker->text.afterLast; + if ((first != NULL) && (afterLast != NULL) && (afterLast > first) + && ((((afterLast - first) == 1) && (first[0] == _UT('.'))) + || (((afterLast - first) == 2) && (first[0] == _UT('.')) + && (first[1] == _UT('.'))) + || URI_FUNC(ContainsUglyPercentEncoding)(first, afterLast))) { + *outMask |= URI_NORMALIZE_PATH; + break; + } + walker = walker->next; + } + } else if (inMask & URI_NORMALIZE_PATH) { + URI_TYPE(PathSegment) * walker; + const UriBool relative = + ((uri->scheme.first == NULL) && !uri->absolutePath) ? URI_TRUE : URI_FALSE; + + /* Fix percent-encoding for each segment */ + walker = uri->pathHead; + if (uri->owner) { + while (walker != NULL) { + URI_FUNC(FixPercentEncodingInplace)(walker->text.first, + &(walker->text.afterLast)); + walker = walker->next; + } + } else { + while (walker != NULL) { + if (!URI_FUNC(FixPercentEncodingMalloc)( + &(walker->text.first), &(walker->text.afterLast), memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + return URI_ERROR_MALLOC; + } + walker = walker->next; + } + revertMask |= URI_NORMALIZE_PATH; + } + + /* 6.2.2.3 Path Segment Normalization */ + if (!URI_FUNC(RemoveDotSegmentsEx)( + uri, relative, + (uri->owner == URI_TRUE) || ((revertMask & URI_NORMALIZE_PATH) != 0), + memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + return URI_ERROR_MALLOC; + } + URI_FUNC(FixEmptyTrailSegment)(uri, memory); + } + + /* Query, fragment */ + if (outMask != NULL) { + const UriBool normalizeQuery = + URI_FUNC(ContainsUglyPercentEncoding)(uri->query.first, uri->query.afterLast); + const UriBool normalizeFragment = URI_FUNC(ContainsUglyPercentEncoding)( + uri->fragment.first, uri->fragment.afterLast); + if (normalizeQuery) { + *outMask |= URI_NORMALIZE_QUERY; + } + + if (normalizeFragment) { + *outMask |= URI_NORMALIZE_FRAGMENT; + } + } else { + /* Query */ + if ((inMask & URI_NORMALIZE_QUERY) && (uri->query.first != NULL)) { + if (uri->owner) { + URI_FUNC(FixPercentEncodingInplace)(uri->query.first, + &(uri->query.afterLast)); + } else { + if (!URI_FUNC(FixPercentEncodingMalloc)( + &(uri->query.first), &(uri->query.afterLast), memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + return URI_ERROR_MALLOC; + } + revertMask |= URI_NORMALIZE_QUERY; + } + } + + /* Fragment */ + if ((inMask & URI_NORMALIZE_FRAGMENT) && (uri->fragment.first != NULL)) { + if (uri->owner) { + URI_FUNC(FixPercentEncodingInplace)(uri->fragment.first, + &(uri->fragment.afterLast)); + } else { + if (!URI_FUNC(FixPercentEncodingMalloc)( + &(uri->fragment.first), &(uri->fragment.afterLast), memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + return URI_ERROR_MALLOC; + } + revertMask |= URI_NORMALIZE_FRAGMENT; + } + } + } + + /* Dup all not duped yet */ + if ((outMask == NULL) && !uri->owner) { + if (!URI_FUNC(MakeOwnerEngine)(uri, &revertMask, memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + return URI_ERROR_MALLOC; + } + uri->owner = URI_TRUE; + } + + return URI_SUCCESS; } - - int URI_FUNC(MakeOwnerMm)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { - unsigned int revertMask = URI_NORMALIZED; + unsigned int revertMask = URI_NORMALIZED; - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - if (uri == NULL) { - return URI_ERROR_NULL; - } + if (uri == NULL) { + return URI_ERROR_NULL; + } - if (uri->owner == URI_TRUE) { - return URI_SUCCESS; - } + if (uri->owner == URI_TRUE) { + return URI_SUCCESS; + } - if (! URI_FUNC(MakeOwnerEngine)(uri, &revertMask, memory)) { - URI_FUNC(PreventLeakage)(uri, revertMask, memory); - return URI_ERROR_MALLOC; - } + if (!URI_FUNC(MakeOwnerEngine)(uri, &revertMask, memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + return URI_ERROR_MALLOC; + } - uri->owner = URI_TRUE; + uri->owner = URI_TRUE; - return URI_SUCCESS; + return URI_SUCCESS; } - - int URI_FUNC(MakeOwner)(URI_TYPE(Uri) * uri) { - return URI_FUNC(MakeOwnerMm)(uri, NULL); + return URI_FUNC(MakeOwnerMm)(uri, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriNormalize.h b/ext/uri/uriparser/src/UriNormalize.h index cb58085b7d318..41c87a382c369 100644 --- a/ext/uri/uriparser/src/UriNormalize.h +++ b/ext/uri/uriparser/src/UriNormalize.h @@ -39,38 +39,37 @@ */ #if (defined(URI_PASS_ANSI) && !defined(URI_NORMALIZE_H_ANSI)) \ - || (defined(URI_PASS_UNICODE) && !defined(URI_NORMALIZE_H_UNICODE)) \ - || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) + || (defined(URI_PASS_UNICODE) && !defined(URI_NORMALIZE_H_UNICODE)) \ + || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* What encodings are enabled? */ -#include -#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +# include +# if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriNormalize.h" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriNormalize.h" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriNormalize.h" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriNormalize.h" +# undef URI_PASS_UNICODE +# endif /* Only one pass for each encoding */ -#elif (defined(URI_PASS_ANSI) && !defined(URI_NORMALIZE_H_ANSI) \ - && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \ - && !defined(URI_NORMALIZE_H_UNICODE) && defined(URI_ENABLE_UNICODE)) -# ifdef URI_PASS_ANSI -# define URI_NORMALIZE_H_ANSI 1 -# include -# else -# define URI_NORMALIZE_H_UNICODE 1 -# include -# endif +# elif (defined(URI_PASS_ANSI) && !defined(URI_NORMALIZE_H_ANSI) \ + && defined(URI_ENABLE_ANSI)) \ + || (defined(URI_PASS_UNICODE) && !defined(URI_NORMALIZE_H_UNICODE) \ + && defined(URI_ENABLE_UNICODE)) +# ifdef URI_PASS_ANSI +# define URI_NORMALIZE_H_ANSI 1 +# include +# else +# define URI_NORMALIZE_H_UNICODE 1 +# include +# endif +void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri, unsigned int revertMask, + UriMemoryManager * memory); - -void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri, - unsigned int revertMask, UriMemoryManager * memory); - -#endif +# endif #endif diff --git a/ext/uri/uriparser/src/UriNormalizeBase.c b/ext/uri/uriparser/src/UriNormalizeBase.c index a74fcd3f8bfbf..9c2acee1ee759 100644 --- a/ext/uri/uriparser/src/UriNormalizeBase.c +++ b/ext/uri/uriparser/src/UriNormalizeBase.c @@ -38,82 +38,80 @@ */ #ifndef URI_DOXYGEN -# include "UriNormalizeBase.h" +# include "UriNormalizeBase.h" #endif - - UriBool uriIsUnreserved(int code) { - switch (code) { - case L'a': /* ALPHA */ - case L'A': - case L'b': - case L'B': - case L'c': - case L'C': - case L'd': - case L'D': - case L'e': - case L'E': - case L'f': - case L'F': - case L'g': - case L'G': - case L'h': - case L'H': - case L'i': - case L'I': - case L'j': - case L'J': - case L'k': - case L'K': - case L'l': - case L'L': - case L'm': - case L'M': - case L'n': - case L'N': - case L'o': - case L'O': - case L'p': - case L'P': - case L'q': - case L'Q': - case L'r': - case L'R': - case L's': - case L'S': - case L't': - case L'T': - case L'u': - case L'U': - case L'v': - case L'V': - case L'w': - case L'W': - case L'x': - case L'X': - case L'y': - case L'Y': - case L'z': - case L'Z': - case L'0': /* DIGIT */ - case L'1': - case L'2': - case L'3': - case L'4': - case L'5': - case L'6': - case L'7': - case L'8': - case L'9': - case L'-': /* "-" / "." / "_" / "~" */ - case L'.': - case L'_': - case L'~': - return URI_TRUE; + switch (code) { + case L'a': /* ALPHA */ + case L'A': + case L'b': + case L'B': + case L'c': + case L'C': + case L'd': + case L'D': + case L'e': + case L'E': + case L'f': + case L'F': + case L'g': + case L'G': + case L'h': + case L'H': + case L'i': + case L'I': + case L'j': + case L'J': + case L'k': + case L'K': + case L'l': + case L'L': + case L'm': + case L'M': + case L'n': + case L'N': + case L'o': + case L'O': + case L'p': + case L'P': + case L'q': + case L'Q': + case L'r': + case L'R': + case L's': + case L'S': + case L't': + case L'T': + case L'u': + case L'U': + case L'v': + case L'V': + case L'w': + case L'W': + case L'x': + case L'X': + case L'y': + case L'Y': + case L'z': + case L'Z': + case L'0': /* DIGIT */ + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + case L'-': /* "-" / "." / "_" / "~" */ + case L'.': + case L'_': + case L'~': + return URI_TRUE; - default: - return URI_FALSE; - } + default: + return URI_FALSE; + } } diff --git a/ext/uri/uriparser/src/UriNormalizeBase.h b/ext/uri/uriparser/src/UriNormalizeBase.h index 8651c8616649f..3ff49112a82cc 100644 --- a/ext/uri/uriparser/src/UriNormalizeBase.h +++ b/ext/uri/uriparser/src/UriNormalizeBase.h @@ -38,16 +38,10 @@ */ #ifndef URI_NORMALIZE_BASE_H -#define URI_NORMALIZE_BASE_H 1 - - - -#include - +# define URI_NORMALIZE_BASE_H 1 +# include UriBool uriIsUnreserved(int code); - - #endif /* URI_NORMALIZE_BASE_H */ diff --git a/ext/uri/uriparser/src/UriParse.c b/ext/uri/uriparser/src/UriParse.c index 8bb18f65ac166..db48b380464b1 100644 --- a/ext/uri/uriparser/src/UriParse.c +++ b/ext/uri/uriparser/src/UriParse.c @@ -47,839 +47,920 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriParse.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriParse.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriParse.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriParse.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include -# include "UriCommon.h" -# include "UriMemory.h" -# include "UriParseBase.h" -#endif - - - -#define URI_SET_DIGIT \ - _UT('0'): \ - case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - -#define URI_SET_HEX_LETTER_UPPER \ - _UT('A'): \ - case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - -#define URI_SET_HEX_LETTER_LOWER \ - _UT('a'): \ - case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - -#define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - -#define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - - - -static const URI_CHAR * URI_FUNC(ParseAuthority)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast); -static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast); -static const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseIpLit2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseIPv6address2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseOwnHost)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParsePartHelperTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParsePathAbsNoLeadSlash)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParsePathRootless)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParsePchar)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParsePctEncoded)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast); -static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseSegmentNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseUriReference)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseUriTail)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseUriTailTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); - -static UriBool URI_FUNC(OnExitOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, UriMemoryManager * memory); -static UriBool URI_FUNC(OnExitOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, UriMemoryManager * memory); -static UriBool URI_FUNC(OnExitOwnPortUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, UriMemoryManager * memory); -static UriBool URI_FUNC(OnExitSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, UriMemoryManager * memory); +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include +# include "UriCommon.h" +# include "UriMemory.h" +# include "UriParseBase.h" +# endif + +# define URI_SET_DIGIT \ + _UT('0') : case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + +# define URI_SET_HEX_LETTER_UPPER \ + _UT('A') : case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + +# define URI_SET_HEX_LETTER_LOWER \ + _UT('a') : case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + +# define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + +# define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + +static const URI_CHAR * URI_FUNC(ParseAuthority)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast); +static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast); +static const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseIpLit2)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseIPv6address2)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseOwnHost)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParsePartHelperTwo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParsePathAbsNoLeadSlash)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParsePathRootless)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParsePchar)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParsePctEncoded)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast); +static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseSegmentNz)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseUriReference)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseUriTail)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseUriTailTwo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + +static UriBool URI_FUNC(OnExitOwnHost2)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + UriMemoryManager * memory); +static UriBool URI_FUNC(OnExitOwnHostUserInfo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + UriMemoryManager * memory); +static UriBool URI_FUNC(OnExitOwnPortUserInfo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + UriMemoryManager * memory); +static UriBool URI_FUNC(OnExitSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + UriMemoryManager * memory); static void URI_FUNC(OnExitPartHelperTwo)(URI_TYPE(ParserState) * state); static void URI_FUNC(ResetParserStateExceptUri)(URI_TYPE(ParserState) * state); static UriBool URI_FUNC(PushPathSegment)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory); - -static void URI_FUNC(StopSyntax)(URI_TYPE(ParserState) * state, const URI_CHAR * errorPos, UriMemoryManager * memory); -static void URI_FUNC(StopMalloc)(URI_TYPE(ParserState) * state, UriMemoryManager * memory); - -static int URI_FUNC(ParseUriExMm)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory); + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); +static void URI_FUNC(StopSyntax)(URI_TYPE(ParserState) * state, const URI_CHAR * errorPos, + UriMemoryManager * memory); +static void URI_FUNC(StopMalloc)(URI_TYPE(ParserState) * state, + UriMemoryManager * memory); +static int URI_FUNC(ParseUriExMm)(URI_TYPE(ParserState) * state, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory); static URI_INLINE void URI_FUNC(StopSyntax)(URI_TYPE(ParserState) * state, - const URI_CHAR * errorPos, UriMemoryManager * memory) { - URI_FUNC(FreeUriMembersMm)(state->uri, memory); - state->errorPos = errorPos; - state->errorCode = URI_ERROR_SYNTAX; + const URI_CHAR * errorPos, + UriMemoryManager * memory) { + URI_FUNC(FreeUriMembersMm)(state->uri, memory); + state->errorPos = errorPos; + state->errorCode = URI_ERROR_SYNTAX; } - - -static URI_INLINE void URI_FUNC(StopMalloc)(URI_TYPE(ParserState) * state, UriMemoryManager * memory) { - URI_FUNC(FreeUriMembersMm)(state->uri, memory); - state->errorPos = NULL; - state->errorCode = URI_ERROR_MALLOC; +static URI_INLINE void URI_FUNC(StopMalloc)(URI_TYPE(ParserState) * state, + UriMemoryManager * memory) { + URI_FUNC(FreeUriMembersMm)(state->uri, memory); + state->errorPos = NULL; + state->errorCode = URI_ERROR_MALLOC; } - - /* * [authority]-><[>[ipLit2][authorityTwo] * [authority]->[ownHostUserInfoNz] * [authority]-> */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseAuthority)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - /* "" regname host */ - state->uri->hostText.first = URI_FUNC(SafeToPointTo); - state->uri->hostText.afterLast = URI_FUNC(SafeToPointTo); - return afterLast; - } - - switch (*first) { - case _UT('['): - { - const URI_CHAR * const afterIpLit2 - = URI_FUNC(ParseIpLit2)(state, first + 1, afterLast, memory); - if (afterIpLit2 == NULL) { - return NULL; - } - state->uri->hostText.first = first + 1; /* HOST BEGIN */ - return URI_FUNC(ParseAuthorityTwo)(state, afterIpLit2, afterLast); - } - - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - state->uri->userInfo.first = first; /* USERINFO BEGIN */ - return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast, memory); - - default: - /* "" regname host */ - state->uri->hostText.first = URI_FUNC(SafeToPointTo); - state->uri->hostText.afterLast = URI_FUNC(SafeToPointTo); - return first; - } +static URI_INLINE const URI_CHAR * URI_FUNC(ParseAuthority)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + /* "" regname host */ + state->uri->hostText.first = URI_FUNC(SafeToPointTo); + state->uri->hostText.afterLast = URI_FUNC(SafeToPointTo); + return afterLast; + } + + switch (*first) { + case _UT('['): { + const URI_CHAR * const afterIpLit2 = + URI_FUNC(ParseIpLit2)(state, first + 1, afterLast, memory); + if (afterIpLit2 == NULL) { + return NULL; + } + state->uri->hostText.first = first + 1; /* HOST BEGIN */ + return URI_FUNC(ParseAuthorityTwo)(state, afterIpLit2, afterLast); + } + + case _UT('!'): + case _UT('$'): + case _UT('%'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('-'): + case _UT('*'): + case _UT(','): + case _UT('.'): + case _UT(':'): + case _UT(';'): + case _UT('@'): + case _UT('\''): + case _UT('_'): + case _UT('~'): + case _UT('+'): + case _UT('='): + case URI_SET_DIGIT: + case URI_SET_ALPHA: + state->uri->userInfo.first = first; /* USERINFO BEGIN */ + return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast, memory); + + default: + /* "" regname host */ + state->uri->hostText.first = URI_FUNC(SafeToPointTo); + state->uri->hostText.afterLast = URI_FUNC(SafeToPointTo); + return first; + } } - - /* * [authorityTwo]-><:>[port] * [authorityTwo]-> */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT(':'): - { - const URI_CHAR * const afterPort = URI_FUNC(ParsePort)(state, first + 1, afterLast); - if (afterPort == NULL) { - return NULL; - } - state->uri->portText.first = first + 1; /* PORT BEGIN */ - state->uri->portText.afterLast = afterPort; /* PORT END */ - return afterPort; - } - - default: - return first; - } +static URI_INLINE const URI_CHAR * +URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, + const URI_CHAR * afterLast) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT(':'): { + const URI_CHAR * const afterPort = + URI_FUNC(ParsePort)(state, first + 1, afterLast); + if (afterPort == NULL) { + return NULL; + } + state->uri->portText.first = first + 1; /* PORT BEGIN */ + state->uri->portText.afterLast = afterPort; /* PORT END */ + return afterPort; + } + + default: + return first; + } } - - /* * [hexZero]->[HEXDIG][hexZero] * [hexZero]-> */ -static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case URI_SET_HEXDIG: - return URI_FUNC(ParseHexZero)(state, first + 1, afterLast); - - default: - return first; - } +static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case URI_SET_HEXDIG: + return URI_FUNC(ParseHexZero)(state, first + 1, afterLast); + + default: + return first; + } } - - /* * [hierPart]->[pathRootless] * [hierPart]->[partHelperTwo] * [hierPart]-> */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseHierPart)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - return URI_FUNC(ParsePathRootless)(state, first, afterLast, memory); - - case _UT('/'): - return URI_FUNC(ParsePartHelperTwo)(state, first + 1, afterLast, memory); - - default: - return first; - } +static URI_INLINE const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('!'): + case _UT('$'): + case _UT('%'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('-'): + case _UT('*'): + case _UT(','): + case _UT('.'): + case _UT(':'): + case _UT(';'): + case _UT('@'): + case _UT('\''): + case _UT('_'): + case _UT('~'): + case _UT('+'): + case _UT('='): + case URI_SET_DIGIT: + case URI_SET_ALPHA: + return URI_FUNC(ParsePathRootless)(state, first, afterLast, memory); + + case _UT('/'): + return URI_FUNC(ParsePartHelperTwo)(state, first + 1, afterLast, memory); + + default: + return first; + } } - - /* * [ipFutLoop]->[subDelims][ipFutStopGo] * [ipFutLoop]->[unreserved][ipFutStopGo] * [ipFutLoop]-><:>[ipFutStopGo] */ static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - return URI_FUNC(ParseIpFutStopGo)(state, first + 1, afterLast, memory); - - default: - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + switch (*first) { + case _UT('!'): + case _UT('$'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('-'): + case _UT('*'): + case _UT(','): + case _UT('.'): + case _UT(':'): + case _UT(';'): + case _UT('\''): + case _UT('_'): + case _UT('~'): + case _UT('+'): + case _UT('='): + case URI_SET_DIGIT: + case URI_SET_ALPHA: + return URI_FUNC(ParseIpFutStopGo)(state, first + 1, afterLast, memory); + + default: + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } } - - /* * [ipFutStopGo]->[ipFutLoop] * [ipFutStopGo]-> */ -static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)( - URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - return URI_FUNC(ParseIpFutLoop)(state, first, afterLast, memory); - - default: - return first; - } +static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('!'): + case _UT('$'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('-'): + case _UT('*'): + case _UT(','): + case _UT('.'): + case _UT(':'): + case _UT(';'): + case _UT('\''): + case _UT('_'): + case _UT('~'): + case _UT('+'): + case _UT('='): + case URI_SET_DIGIT: + case URI_SET_ALPHA: + return URI_FUNC(ParseIpFutLoop)(state, first, afterLast, memory); + + default: + return first; + } } - - /* * [ipFuture]->[HEXDIG][hexZero]<.>[ipFutLoop] */ static const URI_CHAR * URI_FUNC(ParseIpFuture)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - /* - First character has already been - checked before entering this rule. - - switch (*first) { - case _UT('v'): - case _UT('V'): - */ - if (afterLast - first < 2) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - switch (first[1]) { - case URI_SET_HEXDIG: - { - const URI_CHAR * afterIpFutLoop; - const URI_CHAR * const afterHexZero - = URI_FUNC(ParseHexZero)(state, first + 2, afterLast); - if (afterHexZero == NULL) { - return NULL; - } - if (afterHexZero >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - if (*afterHexZero != _UT('.')) { - URI_FUNC(StopSyntax)(state, afterHexZero, memory); - return NULL; - } - state->uri->hostText.first = first; /* HOST BEGIN */ - state->uri->hostData.ipFuture.first = first; /* IPFUTURE BEGIN */ - afterIpFutLoop = URI_FUNC(ParseIpFutLoop)(state, afterHexZero + 1, afterLast, memory); - if (afterIpFutLoop == NULL) { - return NULL; - } - state->uri->hostText.afterLast = afterIpFutLoop; /* HOST END */ - state->uri->hostData.ipFuture.afterLast = afterIpFutLoop; /* IPFUTURE END */ - return afterIpFutLoop; - } - - default: - URI_FUNC(StopSyntax)(state, first + 1, memory); - return NULL; - } - - /* - default: - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - */ + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + /* + First character has already been + checked before entering this rule. + + switch (*first) { + case _UT('v'): + case _UT('V'): + */ + if (afterLast - first < 2) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + switch (first[1]) { + case URI_SET_HEXDIG: { + const URI_CHAR * afterIpFutLoop; + const URI_CHAR * const afterHexZero = + URI_FUNC(ParseHexZero)(state, first + 2, afterLast); + if (afterHexZero == NULL) { + return NULL; + } + if (afterHexZero >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + if (*afterHexZero != _UT('.')) { + URI_FUNC(StopSyntax)(state, afterHexZero, memory); + return NULL; + } + state->uri->hostText.first = first; /* HOST BEGIN */ + state->uri->hostData.ipFuture.first = first; /* IPFUTURE BEGIN */ + afterIpFutLoop = + URI_FUNC(ParseIpFutLoop)(state, afterHexZero + 1, afterLast, memory); + if (afterIpFutLoop == NULL) { + return NULL; + } + state->uri->hostText.afterLast = afterIpFutLoop; /* HOST END */ + state->uri->hostData.ipFuture.afterLast = afterIpFutLoop; /* IPFUTURE END */ + return afterIpFutLoop; + } + + default: + URI_FUNC(StopSyntax)(state, first + 1, memory); + return NULL; + } + + /* + default: + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + */ } - - /* * [ipLit2]->[ipFuture]<]> * [ipLit2]->[IPv6address2] */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseIpLit2)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - switch (*first) { - /* The leading "v" of IPvFuture is case-insensitive. */ - case _UT('v'): - case _UT('V'): - { - const URI_CHAR * const afterIpFuture - = URI_FUNC(ParseIpFuture)(state, first, afterLast, memory); - if (afterIpFuture == NULL) { - return NULL; - } - if (afterIpFuture >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - if (*afterIpFuture != _UT(']')) { - URI_FUNC(StopSyntax)(state, afterIpFuture, memory); - return NULL; - } - return afterIpFuture + 1; - } - - case _UT(':'): - case _UT(']'): - case URI_SET_HEXDIG: - state->uri->hostData.ip6 = memory->malloc(memory, 1 * sizeof(UriIp6)); /* Freed when stopping on parse error */ - if (state->uri->hostData.ip6 == NULL) { - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return URI_FUNC(ParseIPv6address2)(state, first, afterLast, memory); - - default: - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } +static URI_INLINE const URI_CHAR * URI_FUNC(ParseIpLit2)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + switch (*first) { + /* The leading "v" of IPvFuture is case-insensitive. */ + case _UT('v'): + case _UT('V'): { + const URI_CHAR * const afterIpFuture = + URI_FUNC(ParseIpFuture)(state, first, afterLast, memory); + if (afterIpFuture == NULL) { + return NULL; + } + if (afterIpFuture >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + if (*afterIpFuture != _UT(']')) { + URI_FUNC(StopSyntax)(state, afterIpFuture, memory); + return NULL; + } + return afterIpFuture + 1; + } + + case _UT(':'): + case _UT(']'): + case URI_SET_HEXDIG: + state->uri->hostData.ip6 = memory->malloc( + memory, 1 * sizeof(UriIp6)); /* Freed when stopping on parse error */ + if (state->uri->hostData.ip6 == NULL) { + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return URI_FUNC(ParseIPv6address2)(state, first, afterLast, memory); + + default: + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } } - - /* * [IPv6address2]->..<]> */ -static const URI_CHAR * URI_FUNC(ParseIPv6address2)( - URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - int zipperEver = 0; - int quadsDone = 0; - int digitCount = 0; - unsigned char digitHistory[4]; - int ip4OctetsDone = 0; - - unsigned char quadsAfterZipper[14]; - int quadsAfterZipperCount = 0; - - - for (;;) { - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - /* Inside IPv4 part? */ - if (ip4OctetsDone > 0) { - /* Eat rest of IPv4 address */ - for (;;) { - switch (*first) { - case URI_SET_DIGIT: - if (digitCount == 4) { - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - digitHistory[digitCount++] = (unsigned char)(9 + *first - _UT('9')); - break; - - case _UT('.'): - if ((ip4OctetsDone == 4) /* NOTE! */ - || (digitCount == 0) - || (digitCount == 4)) { - /* Invalid digit or octet count */ - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } else if ((digitCount > 1) - && (digitHistory[0] == 0)) { - /* Leading zero */ - URI_FUNC(StopSyntax)(state, first - digitCount, memory); - return NULL; - } else if ((digitCount == 3) - && (100 * digitHistory[0] - + 10 * digitHistory[1] - + digitHistory[2] > 255)) { - /* Octet value too large */ - if (digitHistory[0] > 2) { - URI_FUNC(StopSyntax)(state, first - 3, memory); - } else if (digitHistory[1] > 5) { - URI_FUNC(StopSyntax)(state, first - 2, memory); - } else { - URI_FUNC(StopSyntax)(state, first - 1, memory); - } - return NULL; - } - - /* Copy IPv4 octet */ - state->uri->hostData.ip6->data[16 - 4 + ip4OctetsDone] = uriGetOctetValue(digitHistory, digitCount); - digitCount = 0; - ip4OctetsDone++; - break; - - case _UT(']'): - if ((ip4OctetsDone != 3) /* NOTE! */ - || (digitCount == 0) - || (digitCount == 4)) { - /* Invalid digit or octet count */ - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } else if ((digitCount > 1) - && (digitHistory[0] == 0)) { - /* Leading zero */ - URI_FUNC(StopSyntax)(state, first - digitCount, memory); - return NULL; - } else if ((digitCount == 3) - && (100 * digitHistory[0] - + 10 * digitHistory[1] - + digitHistory[2] > 255)) { - /* Octet value too large */ - if (digitHistory[0] > 2) { - URI_FUNC(StopSyntax)(state, first - 3, memory); - } else if (digitHistory[1] > 5) { - URI_FUNC(StopSyntax)(state, first - 2, memory); - } else { - URI_FUNC(StopSyntax)(state, first - 1, memory); - } - return NULL; - } - - state->uri->hostText.afterLast = first; /* HOST END */ - - /* Copy missing quads right before IPv4 */ - memcpy(state->uri->hostData.ip6->data + 16 - 4 - 2 * quadsAfterZipperCount, - quadsAfterZipper, 2 * quadsAfterZipperCount); - - /* Copy last IPv4 octet */ - state->uri->hostData.ip6->data[16 - 4 + 3] = uriGetOctetValue(digitHistory, digitCount); - - return first + 1; - - default: - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - first++; - - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - } - } else { - /* Eat while no dot in sight */ - int letterAmong = 0; - int walking = 1; - do { - switch (*first) { - case URI_SET_HEX_LETTER_LOWER: - letterAmong = 1; - if (digitCount == 4) { - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - digitHistory[digitCount] = (unsigned char)(15 + *first - _UT('f')); - digitCount++; - break; - - case URI_SET_HEX_LETTER_UPPER: - letterAmong = 1; - if (digitCount == 4) { - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - digitHistory[digitCount] = (unsigned char)(15 + *first - _UT('F')); - digitCount++; - break; - - case URI_SET_DIGIT: - if (digitCount == 4) { - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - digitHistory[digitCount] = (unsigned char)(9 + *first - _UT('9')); - digitCount++; - break; - - case _UT(':'): - { - int setZipper = 0; - - if (digitCount > 0) { - if (zipperEver) { - uriWriteQuadToDoubleByte(digitHistory, digitCount, quadsAfterZipper + 2 * quadsAfterZipperCount); - quadsAfterZipperCount++; - } else { - uriWriteQuadToDoubleByte(digitHistory, digitCount, state->uri->hostData.ip6->data + 2 * quadsDone); - } - quadsDone++; - digitCount = 0; - } - letterAmong = 0; - - /* Too many quads? */ - if (quadsDone >= 8 - zipperEver) { - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - - /* "::"? */ - if (afterLast - first < 2) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - if (first[1] == _UT(':')) { - const int resetOffset = 2 * (quadsDone + (digitCount > 0)); - - first++; - if (zipperEver) { - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; /* "::.+::" */ - } - - /* Zero everything after zipper */ - memset(state->uri->hostData.ip6->data + resetOffset, 0, 16 - resetOffset); - setZipper = 1; - - /* ":::+"? */ - if (afterLast - first < 2) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; /* No ']' yet */ - } - if (first[1] == _UT(':')) { - URI_FUNC(StopSyntax)(state, first + 1, memory); - return NULL; /* ":::+ "*/ - } - } else if (quadsDone == 0 || first[1] == _UT(']')) { - /* Single leading or trailing ":" */ - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - - if (setZipper) { - zipperEver = 1; - } - } - break; - - case _UT('.'): - if ((quadsDone + zipperEver > 6) /* NOTE */ - || (!zipperEver && (quadsDone < 6)) - || letterAmong - || (digitCount == 0) - || (digitCount == 4)) { - /* Invalid octet before */ - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } else if ((digitCount > 1) - && (digitHistory[0] == 0)) { - /* Leading zero */ - URI_FUNC(StopSyntax)(state, first - digitCount, memory); - return NULL; - } else if ((digitCount == 3) - && (100 * digitHistory[0] - + 10 * digitHistory[1] - + digitHistory[2] > 255)) { - /* Octet value too large */ - if (digitHistory[0] > 2) { - URI_FUNC(StopSyntax)(state, first - 3, memory); - } else if (digitHistory[1] > 5) { - URI_FUNC(StopSyntax)(state, first - 2, memory); - } else { - URI_FUNC(StopSyntax)(state, first - 1, memory); - } - return NULL; - } - - /* Copy first IPv4 octet */ - state->uri->hostData.ip6->data[16 - 4] = uriGetOctetValue(digitHistory, digitCount); - digitCount = 0; - - /* Switch over to IPv4 loop */ - ip4OctetsDone = 1; - walking = 0; - break; - - case _UT(']'): - /* Too little quads? */ - if (!zipperEver && !((quadsDone == 7) && (digitCount > 0))) { - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - - if (digitCount > 0) { - if (zipperEver) { - /* Too many quads? */ - if (quadsDone >= 7) { - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - uriWriteQuadToDoubleByte(digitHistory, digitCount, quadsAfterZipper + 2 * quadsAfterZipperCount); - quadsAfterZipperCount++; - } else { - uriWriteQuadToDoubleByte(digitHistory, digitCount, state->uri->hostData.ip6->data + 2 * quadsDone); - } - /* - quadsDone++; - digitCount = 0; - */ - } - - /* Copy missing quads to the end */ - memcpy(state->uri->hostData.ip6->data + 16 - 2 * quadsAfterZipperCount, - quadsAfterZipper, 2 * quadsAfterZipperCount); - - state->uri->hostText.afterLast = first; /* HOST END */ - return first + 1; /* Fine */ - - default: - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - first++; - - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; /* No ']' yet */ - } - } while (walking); - } - } +static const URI_CHAR * URI_FUNC(ParseIPv6address2)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + int zipperEver = 0; + int quadsDone = 0; + int digitCount = 0; + unsigned char digitHistory[4]; + int ip4OctetsDone = 0; + + unsigned char quadsAfterZipper[14]; + int quadsAfterZipperCount = 0; + + for (;;) { + if (first >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + /* Inside IPv4 part? */ + if (ip4OctetsDone > 0) { + /* Eat rest of IPv4 address */ + for (;;) { + switch (*first) { + case URI_SET_DIGIT: + if (digitCount == 4) { + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + digitHistory[digitCount++] = (unsigned char)(9 + *first - _UT('9')); + break; + + case _UT('.'): + if ((ip4OctetsDone == 4) /* NOTE! */ + || (digitCount == 0) || (digitCount == 4)) { + /* Invalid digit or octet count */ + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } else if ((digitCount > 1) && (digitHistory[0] == 0)) { + /* Leading zero */ + URI_FUNC(StopSyntax)(state, first - digitCount, memory); + return NULL; + } else if ((digitCount == 3) + && (100 * digitHistory[0] + 10 * digitHistory[1] + + digitHistory[2] + > 255)) { + /* Octet value too large */ + if (digitHistory[0] > 2) { + URI_FUNC(StopSyntax)(state, first - 3, memory); + } else if (digitHistory[1] > 5) { + URI_FUNC(StopSyntax)(state, first - 2, memory); + } else { + URI_FUNC(StopSyntax)(state, first - 1, memory); + } + return NULL; + } + + /* Copy IPv4 octet */ + state->uri->hostData.ip6->data[16 - 4 + ip4OctetsDone] = + uriGetOctetValue(digitHistory, digitCount); + digitCount = 0; + ip4OctetsDone++; + break; + + case _UT(']'): + if ((ip4OctetsDone != 3) /* NOTE! */ + || (digitCount == 0) || (digitCount == 4)) { + /* Invalid digit or octet count */ + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } else if ((digitCount > 1) && (digitHistory[0] == 0)) { + /* Leading zero */ + URI_FUNC(StopSyntax)(state, first - digitCount, memory); + return NULL; + } else if ((digitCount == 3) + && (100 * digitHistory[0] + 10 * digitHistory[1] + + digitHistory[2] + > 255)) { + /* Octet value too large */ + if (digitHistory[0] > 2) { + URI_FUNC(StopSyntax)(state, first - 3, memory); + } else if (digitHistory[1] > 5) { + URI_FUNC(StopSyntax)(state, first - 2, memory); + } else { + URI_FUNC(StopSyntax)(state, first - 1, memory); + } + return NULL; + } + + state->uri->hostText.afterLast = first; /* HOST END */ + + /* Copy missing quads right before IPv4 */ + memcpy(state->uri->hostData.ip6->data + 16 - 4 + - 2 * quadsAfterZipperCount, + quadsAfterZipper, 2 * quadsAfterZipperCount); + + /* Copy last IPv4 octet */ + state->uri->hostData.ip6->data[16 - 4 + 3] = + uriGetOctetValue(digitHistory, digitCount); + + return first + 1; + + default: + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + first++; + + if (first >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + } + } else { + /* Eat while no dot in sight */ + int letterAmong = 0; + int walking = 1; + do { + switch (*first) { + case URI_SET_HEX_LETTER_LOWER: + letterAmong = 1; + if (digitCount == 4) { + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + digitHistory[digitCount] = (unsigned char)(15 + *first - _UT('f')); + digitCount++; + break; + + case URI_SET_HEX_LETTER_UPPER: + letterAmong = 1; + if (digitCount == 4) { + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + digitHistory[digitCount] = (unsigned char)(15 + *first - _UT('F')); + digitCount++; + break; + + case URI_SET_DIGIT: + if (digitCount == 4) { + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + digitHistory[digitCount] = (unsigned char)(9 + *first - _UT('9')); + digitCount++; + break; + + case _UT(':'): { + int setZipper = 0; + + if (digitCount > 0) { + if (zipperEver) { + uriWriteQuadToDoubleByte(digitHistory, digitCount, + quadsAfterZipper + + 2 * quadsAfterZipperCount); + quadsAfterZipperCount++; + } else { + uriWriteQuadToDoubleByte(digitHistory, digitCount, + state->uri->hostData.ip6->data + + 2 * quadsDone); + } + quadsDone++; + digitCount = 0; + } + letterAmong = 0; + + /* Too many quads? */ + if (quadsDone >= 8 - zipperEver) { + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + + /* "::"? */ + if (afterLast - first < 2) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + if (first[1] == _UT(':')) { + const int resetOffset = 2 * (quadsDone + (digitCount > 0)); + + first++; + if (zipperEver) { + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; /* "::.+::" */ + } + + /* Zero everything after zipper */ + memset(state->uri->hostData.ip6->data + resetOffset, 0, + 16 - resetOffset); + setZipper = 1; + + /* ":::+"? */ + if (afterLast - first < 2) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; /* No ']' yet */ + } + if (first[1] == _UT(':')) { + URI_FUNC(StopSyntax)(state, first + 1, memory); + return NULL; /* ":::+ "*/ + } + } else if (quadsDone == 0 || first[1] == _UT(']')) { + /* Single leading or trailing ":" */ + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + + if (setZipper) { + zipperEver = 1; + } + } break; + + case _UT('.'): + if ((quadsDone + zipperEver > 6) /* NOTE */ + || (!zipperEver && (quadsDone < 6)) || letterAmong + || (digitCount == 0) || (digitCount == 4)) { + /* Invalid octet before */ + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } else if ((digitCount > 1) && (digitHistory[0] == 0)) { + /* Leading zero */ + URI_FUNC(StopSyntax)(state, first - digitCount, memory); + return NULL; + } else if ((digitCount == 3) + && (100 * digitHistory[0] + 10 * digitHistory[1] + + digitHistory[2] + > 255)) { + /* Octet value too large */ + if (digitHistory[0] > 2) { + URI_FUNC(StopSyntax)(state, first - 3, memory); + } else if (digitHistory[1] > 5) { + URI_FUNC(StopSyntax)(state, first - 2, memory); + } else { + URI_FUNC(StopSyntax)(state, first - 1, memory); + } + return NULL; + } + + /* Copy first IPv4 octet */ + state->uri->hostData.ip6->data[16 - 4] = + uriGetOctetValue(digitHistory, digitCount); + digitCount = 0; + + /* Switch over to IPv4 loop */ + ip4OctetsDone = 1; + walking = 0; + break; + + case _UT(']'): + /* Too little quads? */ + if (!zipperEver && !((quadsDone == 7) && (digitCount > 0))) { + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + + if (digitCount > 0) { + if (zipperEver) { + /* Too many quads? */ + if (quadsDone >= 7) { + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + uriWriteQuadToDoubleByte(digitHistory, digitCount, + quadsAfterZipper + + 2 * quadsAfterZipperCount); + quadsAfterZipperCount++; + } else { + uriWriteQuadToDoubleByte(digitHistory, digitCount, + state->uri->hostData.ip6->data + + 2 * quadsDone); + } + /* + quadsDone++; + digitCount = 0; + */ + } + + /* Copy missing quads to the end */ + memcpy(state->uri->hostData.ip6->data + 16 + - 2 * quadsAfterZipperCount, + quadsAfterZipper, 2 * quadsAfterZipperCount); + + state->uri->hostText.afterLast = first; /* HOST END */ + return first + 1; /* Fine */ + + default: + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + first++; + + if (first >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; /* No ']' yet */ + } + } while (walking); + } + } } - - /* * [mustBeSegmentNzNc]->[pctEncoded][mustBeSegmentNzNc] * [mustBeSegmentNzNc]->[subDelims][mustBeSegmentNzNc] @@ -888,346 +969,342 @@ static const URI_CHAR * URI_FUNC(ParseIPv6address2)( * [mustBeSegmentNzNc]->[segment][zeroMoreSlashSegs][uriTail] * [mustBeSegmentNzNc]-><@>[mustBeSegmentNzNc] */ -static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first, memory)) { /* SEGMENT BOTH */ - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - state->uri->scheme.first = NULL; /* Not a scheme, reset */ - return afterLast; - } - - switch (*first) { - case _UT('%'): - { - const URI_CHAR * const afterPctEncoded - = URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); - if (afterPctEncoded == NULL) { - return NULL; - } - return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast, memory); - } - - case _UT('@'): - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('*'): - case _UT(','): - case _UT(';'): - case _UT('\''): - case _UT('+'): - case _UT('='): - case _UT('-'): - case _UT('.'): - case _UT('_'): - case _UT('~'): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast, memory); - - case _UT('/'): - { - const URI_CHAR * afterZeroMoreSlashSegs; - const URI_CHAR * afterSegment; - if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first, memory)) { /* SEGMENT BOTH */ - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - state->uri->scheme.first = NULL; /* Not a scheme, reset */ - afterSegment = URI_FUNC(ParseSegment)(state, first + 1, afterLast, memory); - if (afterSegment == NULL) { - return NULL; - } - if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment, memory)) { /* SEGMENT BOTH */ - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - afterZeroMoreSlashSegs - = URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast, memory); - if (afterZeroMoreSlashSegs == NULL) { - return NULL; - } - return URI_FUNC(ParseUriTail)(state, afterZeroMoreSlashSegs, afterLast, memory); - } - - default: - if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first, memory)) { /* SEGMENT BOTH */ - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - state->uri->scheme.first = NULL; /* Not a scheme, reset */ - return URI_FUNC(ParseUriTail)(state, first, afterLast, memory); - } +static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first, + memory)) { /* SEGMENT BOTH */ + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + state->uri->scheme.first = NULL; /* Not a scheme, reset */ + return afterLast; + } + + switch (*first) { + case _UT('%'): { + const URI_CHAR * const afterPctEncoded = + URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); + if (afterPctEncoded == NULL) { + return NULL; + } + return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast, + memory); + } + + case _UT('@'): + case _UT('!'): + case _UT('$'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('*'): + case _UT(','): + case _UT(';'): + case _UT('\''): + case _UT('+'): + case _UT('='): + case _UT('-'): + case _UT('.'): + case _UT('_'): + case _UT('~'): + case URI_SET_DIGIT: + case URI_SET_ALPHA: + return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast, memory); + + case _UT('/'): { + const URI_CHAR * afterZeroMoreSlashSegs; + const URI_CHAR * afterSegment; + if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first, + memory)) { /* SEGMENT BOTH */ + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + state->uri->scheme.first = NULL; /* Not a scheme, reset */ + afterSegment = URI_FUNC(ParseSegment)(state, first + 1, afterLast, memory); + if (afterSegment == NULL) { + return NULL; + } + if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment, + memory)) { /* SEGMENT BOTH */ + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + afterZeroMoreSlashSegs = + URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast, memory); + if (afterZeroMoreSlashSegs == NULL) { + return NULL; + } + return URI_FUNC(ParseUriTail)(state, afterZeroMoreSlashSegs, afterLast, memory); + } + + default: + if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first, + memory)) { /* SEGMENT BOTH */ + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + state->uri->scheme.first = NULL; /* Not a scheme, reset */ + return URI_FUNC(ParseUriTail)(state, first, afterLast, memory); + } } - - /* * [ownHost]-><[>[ipLit2][authorityTwo] * [ownHost]->[ownHost2] // can take */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseOwnHost)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - state->uri->hostText.afterLast = afterLast; /* HOST END */ - return afterLast; - } - - switch (*first) { - case _UT('['): - { - const URI_CHAR * const afterIpLit2 - = URI_FUNC(ParseIpLit2)(state, first + 1, afterLast, memory); - if (afterIpLit2 == NULL) { - return NULL; - } - state->uri->hostText.first = first + 1; /* HOST BEGIN */ - return URI_FUNC(ParseAuthorityTwo)(state, afterIpLit2, afterLast); - } - - default: - return URI_FUNC(ParseOwnHost2)(state, first, afterLast, memory); - } +static URI_INLINE const URI_CHAR * URI_FUNC(ParseOwnHost)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + state->uri->hostText.afterLast = afterLast; /* HOST END */ + return afterLast; + } + + switch (*first) { + case _UT('['): { + const URI_CHAR * const afterIpLit2 = + URI_FUNC(ParseIpLit2)(state, first + 1, afterLast, memory); + if (afterIpLit2 == NULL) { + return NULL; + } + state->uri->hostText.first = first + 1; /* HOST BEGIN */ + return URI_FUNC(ParseAuthorityTwo)(state, afterIpLit2, afterLast); + } + + default: + return URI_FUNC(ParseOwnHost2)(state, first, afterLast, memory); + } } - - -static URI_INLINE UriBool URI_FUNC(OnExitOwnHost2)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - UriMemoryManager * memory) { - state->uri->hostText.afterLast = first; /* HOST END */ - - /* Valid IPv4 or just a regname? */ - state->uri->hostData.ip4 = memory->malloc(memory, 1 * sizeof(UriIp4)); /* Freed when stopping on parse error */ - if (state->uri->hostData.ip4 == NULL) { - return URI_FALSE; /* Raises malloc error */ - } - if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data, - state->uri->hostText.first, state->uri->hostText.afterLast)) { - /* Not IPv4 */ - memory->free(memory, state->uri->hostData.ip4); - state->uri->hostData.ip4 = NULL; - } - return URI_TRUE; /* Success */ +static URI_INLINE UriBool URI_FUNC(OnExitOwnHost2)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + UriMemoryManager * memory) { + state->uri->hostText.afterLast = first; /* HOST END */ + + /* Valid IPv4 or just a regname? */ + state->uri->hostData.ip4 = memory->malloc( + memory, 1 * sizeof(UriIp4)); /* Freed when stopping on parse error */ + if (state->uri->hostData.ip4 == NULL) { + return URI_FALSE; /* Raises malloc error */ + } + if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data, + state->uri->hostText.first, + state->uri->hostText.afterLast)) { + /* Not IPv4 */ + memory->free(memory, state->uri->hostData.ip4); + state->uri->hostData.ip4 = NULL; + } + return URI_TRUE; /* Success */ } - - /* * [ownHost2]->[authorityTwo] // can take * [ownHost2]->[pctSubUnres][ownHost2] */ -static const URI_CHAR * URI_FUNC(ParseOwnHost2)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - if (!URI_FUNC(OnExitOwnHost2)(state, first, memory)) { - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return afterLast; - } - - switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(';'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - { - const URI_CHAR * const afterPctSubUnres - = URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory); - if (afterPctSubUnres == NULL) { - return NULL; - } - return URI_FUNC(ParseOwnHost2)(state, afterPctSubUnres, afterLast, memory); - } - - default: - if (!URI_FUNC(OnExitOwnHost2)(state, first, memory)) { - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return URI_FUNC(ParseAuthorityTwo)(state, first, afterLast); - } +static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + if (!URI_FUNC(OnExitOwnHost2)(state, first, memory)) { + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return afterLast; + } + + switch (*first) { + case _UT('!'): + case _UT('$'): + case _UT('%'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('-'): + case _UT('*'): + case _UT(','): + case _UT('.'): + case _UT(';'): + case _UT('\''): + case _UT('_'): + case _UT('~'): + case _UT('+'): + case _UT('='): + case URI_SET_DIGIT: + case URI_SET_ALPHA: { + const URI_CHAR * const afterPctSubUnres = + URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory); + if (afterPctSubUnres == NULL) { + return NULL; + } + return URI_FUNC(ParseOwnHost2)(state, afterPctSubUnres, afterLast, memory); + } + + default: + if (!URI_FUNC(OnExitOwnHost2)(state, first, memory)) { + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return URI_FUNC(ParseAuthorityTwo)(state, first, afterLast); + } } - - -static URI_INLINE UriBool URI_FUNC(OnExitOwnHostUserInfo)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - UriMemoryManager * memory) { - state->uri->hostText.first = state->uri->userInfo.first; /* Host instead of userInfo, update */ - state->uri->userInfo.first = NULL; /* Not a userInfo, reset */ - state->uri->hostText.afterLast = first; /* HOST END */ - - /* Valid IPv4 or just a regname? */ - state->uri->hostData.ip4 = memory->malloc(memory, 1 * sizeof(UriIp4)); /* Freed when stopping on parse error */ - if (state->uri->hostData.ip4 == NULL) { - return URI_FALSE; /* Raises malloc error */ - } - if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data, - state->uri->hostText.first, state->uri->hostText.afterLast)) { - /* Not IPv4 */ - memory->free(memory, state->uri->hostData.ip4); - state->uri->hostData.ip4 = NULL; - } - return URI_TRUE; /* Success */ +static URI_INLINE UriBool URI_FUNC(OnExitOwnHostUserInfo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + UriMemoryManager * memory) { + state->uri->hostText.first = + state->uri->userInfo.first; /* Host instead of userInfo, update */ + state->uri->userInfo.first = NULL; /* Not a userInfo, reset */ + state->uri->hostText.afterLast = first; /* HOST END */ + + /* Valid IPv4 or just a regname? */ + state->uri->hostData.ip4 = memory->malloc( + memory, 1 * sizeof(UriIp4)); /* Freed when stopping on parse error */ + if (state->uri->hostData.ip4 == NULL) { + return URI_FALSE; /* Raises malloc error */ + } + if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data, + state->uri->hostText.first, + state->uri->hostText.afterLast)) { + /* Not IPv4 */ + memory->free(memory, state->uri->hostData.ip4); + state->uri->hostData.ip4 = NULL; + } + return URI_TRUE; /* Success */ } - - /* * [ownHostUserInfo]->[ownHostUserInfoNz] * [ownHostUserInfo]-> */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseOwnHostUserInfo)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first, memory)) { - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return afterLast; - } - - switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast, memory); - - default: - if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first, memory)) { - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return first; - } +static URI_INLINE const URI_CHAR * +URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + if (first >= afterLast) { + if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first, memory)) { + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return afterLast; + } + + switch (*first) { + case _UT('!'): + case _UT('$'): + case _UT('%'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('-'): + case _UT('*'): + case _UT(','): + case _UT('.'): + case _UT(':'): + case _UT(';'): + case _UT('@'): + case _UT('\''): + case _UT('_'): + case _UT('~'): + case _UT('+'): + case _UT('='): + case URI_SET_DIGIT: + case URI_SET_ALPHA: + return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast, memory); + + default: + if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first, memory)) { + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return first; + } } - - /* * [ownHostUserInfoNz]->[pctSubUnres][ownHostUserInfo] * [ownHostUserInfoNz]-><:>[ownPortUserInfo] * [ownHostUserInfoNz]-><@>[ownHost] */ -static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(';'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - { - const URI_CHAR * const afterPctSubUnres - = URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory); - if (afterPctSubUnres == NULL) { - return NULL; - } - return URI_FUNC(ParseOwnHostUserInfo)(state, afterPctSubUnres, afterLast, memory); - } - - case _UT(':'): - state->uri->hostText.afterLast = first; /* HOST END */ - state->uri->portText.first = first + 1; /* PORT BEGIN */ - return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast, memory); - - case _UT('@'): - state->uri->userInfo.afterLast = first; /* USERINFO END */ - state->uri->hostText.first = first + 1; /* HOST BEGIN */ - return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast, memory); - - default: - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } +static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + switch (*first) { + case _UT('!'): + case _UT('$'): + case _UT('%'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('-'): + case _UT('*'): + case _UT(','): + case _UT('.'): + case _UT(';'): + case _UT('\''): + case _UT('_'): + case _UT('~'): + case _UT('+'): + case _UT('='): + case URI_SET_DIGIT: + case URI_SET_ALPHA: { + const URI_CHAR * const afterPctSubUnres = + URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory); + if (afterPctSubUnres == NULL) { + return NULL; + } + return URI_FUNC(ParseOwnHostUserInfo)(state, afterPctSubUnres, afterLast, memory); + } + + case _UT(':'): + state->uri->hostText.afterLast = first; /* HOST END */ + state->uri->portText.first = first + 1; /* PORT BEGIN */ + return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast, memory); + + case _UT('@'): + state->uri->userInfo.afterLast = first; /* USERINFO END */ + state->uri->hostText.first = first + 1; /* HOST BEGIN */ + return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast, memory); + + default: + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } } - - -static URI_INLINE UriBool URI_FUNC(OnExitOwnPortUserInfo)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - UriMemoryManager * memory) { - state->uri->hostText.first = state->uri->userInfo.first; /* Host instead of userInfo, update */ - state->uri->userInfo.first = NULL; /* Not a userInfo, reset */ - state->uri->portText.afterLast = first; /* PORT END */ - - /* Valid IPv4 or just a regname? */ - state->uri->hostData.ip4 = memory->malloc(memory, 1 * sizeof(UriIp4)); /* Freed when stopping on parse error */ - if (state->uri->hostData.ip4 == NULL) { - return URI_FALSE; /* Raises malloc error */ - } - if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data, - state->uri->hostText.first, state->uri->hostText.afterLast)) { - /* Not IPv4 */ - memory->free(memory, state->uri->hostData.ip4); - state->uri->hostData.ip4 = NULL; - } - return URI_TRUE; /* Success */ +static URI_INLINE UriBool URI_FUNC(OnExitOwnPortUserInfo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + UriMemoryManager * memory) { + state->uri->hostText.first = + state->uri->userInfo.first; /* Host instead of userInfo, update */ + state->uri->userInfo.first = NULL; /* Not a userInfo, reset */ + state->uri->portText.afterLast = first; /* PORT END */ + + /* Valid IPv4 or just a regname? */ + state->uri->hostData.ip4 = memory->malloc( + memory, 1 * sizeof(UriIp4)); /* Freed when stopping on parse error */ + if (state->uri->hostData.ip4 == NULL) { + return URI_FALSE; /* Raises malloc error */ + } + if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data, + state->uri->hostText.first, + state->uri->hostText.afterLast)) { + /* Not IPv4 */ + memory->free(memory, state->uri->hostData.ip4); + state->uri->hostData.ip4 = NULL; + } + return URI_TRUE; /* Success */ } - - /* * [ownPortUserInfo]->[ALPHA][ownUserInfo] * [ownPortUserInfo]->[DIGIT][ownPortUserInfo] @@ -1241,281 +1318,268 @@ static URI_INLINE UriBool URI_FUNC(OnExitOwnPortUserInfo)( * [ownPortUserInfo]-><@>[ownHost] * [ownPortUserInfo]-> */ -static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - if (!URI_FUNC(OnExitOwnPortUserInfo)(state, first, memory)) { - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return afterLast; - } - - switch (*first) { - /* begin sub-delims */ - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('\''): - case _UT('('): - case _UT(')'): - case _UT('*'): - case _UT('+'): - case _UT(','): - case _UT(';'): - case _UT('='): - /* end sub-delims */ - /* begin unreserved (except alpha and digit) */ - case _UT('-'): - case _UT('.'): - case _UT('_'): - case _UT('~'): - /* end unreserved (except alpha and digit) */ - case _UT(':'): - case URI_SET_ALPHA: - state->uri->hostText.afterLast = NULL; /* Not a host, reset */ - state->uri->portText.first = NULL; /* Not a port, reset */ - return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast, memory); - - case URI_SET_DIGIT: - return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast, memory); - - case _UT('%'): - state->uri->portText.first = NULL; /* Not a port, reset */ - { - const URI_CHAR * const afterPct - = URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); - if (afterPct == NULL) { - return NULL; - } - return URI_FUNC(ParseOwnUserInfo)(state, afterPct, afterLast, memory); - } - - case _UT('@'): - state->uri->hostText.afterLast = NULL; /* Not a host, reset */ - state->uri->portText.first = NULL; /* Not a port, reset */ - state->uri->userInfo.afterLast = first; /* USERINFO END */ - state->uri->hostText.first = first + 1; /* HOST BEGIN */ - return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast, memory); - - default: - if (!URI_FUNC(OnExitOwnPortUserInfo)(state, first, memory)) { - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return first; - } +static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + if (!URI_FUNC(OnExitOwnPortUserInfo)(state, first, memory)) { + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return afterLast; + } + + switch (*first) { + /* begin sub-delims */ + case _UT('!'): + case _UT('$'): + case _UT('&'): + case _UT('\''): + case _UT('('): + case _UT(')'): + case _UT('*'): + case _UT('+'): + case _UT(','): + case _UT(';'): + case _UT('='): + /* end sub-delims */ + /* begin unreserved (except alpha and digit) */ + case _UT('-'): + case _UT('.'): + case _UT('_'): + case _UT('~'): + /* end unreserved (except alpha and digit) */ + case _UT(':'): + case URI_SET_ALPHA: + state->uri->hostText.afterLast = NULL; /* Not a host, reset */ + state->uri->portText.first = NULL; /* Not a port, reset */ + return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast, memory); + + case URI_SET_DIGIT: + return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast, memory); + + case _UT('%'): + state->uri->portText.first = NULL; /* Not a port, reset */ + const URI_CHAR * const afterPct = + URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); + if (afterPct == NULL) { + return NULL; + } + return URI_FUNC(ParseOwnUserInfo)(state, afterPct, afterLast, memory); + + case _UT('@'): + state->uri->hostText.afterLast = NULL; /* Not a host, reset */ + state->uri->portText.first = NULL; /* Not a port, reset */ + state->uri->userInfo.afterLast = first; /* USERINFO END */ + state->uri->hostText.first = first + 1; /* HOST BEGIN */ + return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast, memory); + + default: + if (!URI_FUNC(OnExitOwnPortUserInfo)(state, first, memory)) { + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return first; + } } - - /* * [ownUserInfo]->[pctSubUnres][ownUserInfo] * [ownUserInfo]-><:>[ownUserInfo] * [ownUserInfo]-><@>[ownHost] */ -static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(';'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - { - const URI_CHAR * const afterPctSubUnres - = URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory); - if (afterPctSubUnres == NULL) { - return NULL; - } - return URI_FUNC(ParseOwnUserInfo)(state, afterPctSubUnres, afterLast, memory); - } - - case _UT(':'): - return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast, memory); - - case _UT('@'): - /* SURE */ - state->uri->userInfo.afterLast = first; /* USERINFO END */ - state->uri->hostText.first = first + 1; /* HOST BEGIN */ - return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast, memory); - - default: - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } +static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + switch (*first) { + case _UT('!'): + case _UT('$'): + case _UT('%'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('-'): + case _UT('*'): + case _UT(','): + case _UT('.'): + case _UT(';'): + case _UT('\''): + case _UT('_'): + case _UT('~'): + case _UT('+'): + case _UT('='): + case URI_SET_DIGIT: + case URI_SET_ALPHA: { + const URI_CHAR * const afterPctSubUnres = + URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory); + if (afterPctSubUnres == NULL) { + return NULL; + } + return URI_FUNC(ParseOwnUserInfo)(state, afterPctSubUnres, afterLast, memory); + } + + case _UT(':'): + return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast, memory); + + case _UT('@'): + /* SURE */ + state->uri->userInfo.afterLast = first; /* USERINFO END */ + state->uri->hostText.first = first + 1; /* HOST BEGIN */ + return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast, memory); + + default: + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } } - - static URI_INLINE void URI_FUNC(OnExitPartHelperTwo)(URI_TYPE(ParserState) * state) { - state->uri->absolutePath = URI_TRUE; + state->uri->absolutePath = URI_TRUE; } - - /* * [partHelperTwo]->[pathAbsNoLeadSlash] // can take * [partHelperTwo]->[authority][pathAbsEmpty] */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParsePartHelperTwo)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - URI_FUNC(OnExitPartHelperTwo)(state); - return afterLast; - } - - switch (*first) { - case _UT('/'): - { - const URI_CHAR * const afterAuthority - = URI_FUNC(ParseAuthority)(state, first + 1, afterLast, memory); - const URI_CHAR * afterPathAbsEmpty; - if (afterAuthority == NULL) { - return NULL; - } - afterPathAbsEmpty = URI_FUNC(ParsePathAbsEmpty)(state, afterAuthority, afterLast, memory); - - URI_FUNC(FixEmptyTrailSegment)(state->uri, memory); - - return afterPathAbsEmpty; - } - - default: - URI_FUNC(OnExitPartHelperTwo)(state); - return URI_FUNC(ParsePathAbsNoLeadSlash)(state, first, afterLast, memory); - } +static URI_INLINE const URI_CHAR * +URI_FUNC(ParsePartHelperTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + if (first >= afterLast) { + URI_FUNC(OnExitPartHelperTwo)(state); + return afterLast; + } + + switch (*first) { + case _UT('/'): { + const URI_CHAR * const afterAuthority = + URI_FUNC(ParseAuthority)(state, first + 1, afterLast, memory); + const URI_CHAR * afterPathAbsEmpty; + if (afterAuthority == NULL) { + return NULL; + } + afterPathAbsEmpty = + URI_FUNC(ParsePathAbsEmpty)(state, afterAuthority, afterLast, memory); + + URI_FUNC(FixEmptyTrailSegment)(state->uri, memory); + + return afterPathAbsEmpty; + } + + default: + URI_FUNC(OnExitPartHelperTwo)(state); + return URI_FUNC(ParsePathAbsNoLeadSlash)(state, first, afterLast, memory); + } } - - /* * [pathAbsEmpty]->[segment][pathAbsEmpty] * [pathAbsEmpty]-> */ -static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('/'): - { - const URI_CHAR * const afterSegment - = URI_FUNC(ParseSegment)(state, first + 1, afterLast, memory); - if (afterSegment == NULL) { - return NULL; - } - if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment, memory)) { /* SEGMENT BOTH */ - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return URI_FUNC(ParsePathAbsEmpty)(state, afterSegment, afterLast, memory); - } - - default: - return first; - } +static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('/'): { + const URI_CHAR * const afterSegment = + URI_FUNC(ParseSegment)(state, first + 1, afterLast, memory); + if (afterSegment == NULL) { + return NULL; + } + if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment, + memory)) { /* SEGMENT BOTH */ + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return URI_FUNC(ParsePathAbsEmpty)(state, afterSegment, afterLast, memory); + } + + default: + return first; + } } - - /* * [pathAbsNoLeadSlash]->[segmentNz][zeroMoreSlashSegs] * [pathAbsNoLeadSlash]-> */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParsePathAbsNoLeadSlash)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - { - const URI_CHAR * const afterSegmentNz - = URI_FUNC(ParseSegmentNz)(state, first, afterLast, memory); - if (afterSegmentNz == NULL) { - return NULL; - } - if (!URI_FUNC(PushPathSegment)(state, first, afterSegmentNz, memory)) { /* SEGMENT BOTH */ - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegmentNz, afterLast, memory); - } - - default: - return first; - } +static URI_INLINE const URI_CHAR * +URI_FUNC(ParsePathAbsNoLeadSlash)(URI_TYPE(ParserState) * state, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('!'): + case _UT('$'): + case _UT('%'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('-'): + case _UT('*'): + case _UT(','): + case _UT('.'): + case _UT(':'): + case _UT(';'): + case _UT('@'): + case _UT('\''): + case _UT('_'): + case _UT('~'): + case _UT('+'): + case _UT('='): + case URI_SET_DIGIT: + case URI_SET_ALPHA: { + const URI_CHAR * const afterSegmentNz = + URI_FUNC(ParseSegmentNz)(state, first, afterLast, memory); + if (afterSegmentNz == NULL) { + return NULL; + } + if (!URI_FUNC(PushPathSegment)(state, first, afterSegmentNz, + memory)) { /* SEGMENT BOTH */ + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegmentNz, afterLast, memory); + } + + default: + return first; + } } - - /* * [pathRootless]->[segmentNz][zeroMoreSlashSegs] */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParsePathRootless)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - const URI_CHAR * const afterSegmentNz - = URI_FUNC(ParseSegmentNz)(state, first, afterLast, memory); - if (afterSegmentNz == NULL) { - return NULL; - } else { - if (!URI_FUNC(PushPathSegment)(state, first, afterSegmentNz, memory)) { /* SEGMENT BOTH */ - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - } - return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegmentNz, afterLast, memory); +static URI_INLINE const URI_CHAR * +URI_FUNC(ParsePathRootless)(URI_TYPE(ParserState) * state, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + const URI_CHAR * const afterSegmentNz = + URI_FUNC(ParseSegmentNz)(state, first, afterLast, memory); + if (afterSegmentNz == NULL) { + return NULL; + } else { + if (!URI_FUNC(PushPathSegment)(state, first, afterSegmentNz, + memory)) { /* SEGMENT BOTH */ + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + } + return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegmentNz, afterLast, memory); } - - /* * [pchar]->[pctEncoded] * [pchar]->[subDelims] @@ -1524,166 +1588,161 @@ static URI_INLINE const URI_CHAR * URI_FUNC(ParsePathRootless)( * [pchar]-><@> */ static const URI_CHAR * URI_FUNC(ParsePchar)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - switch (*first) { - case _UT('%'): - return URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); - - case _UT(':'): - case _UT('@'): - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('*'): - case _UT(','): - case _UT(';'): - case _UT('\''): - case _UT('+'): - case _UT('='): - case _UT('-'): - case _UT('.'): - case _UT('_'): - case _UT('~'): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - return first + 1; - - default: - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + switch (*first) { + case _UT('%'): + return URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); + + case _UT(':'): + case _UT('@'): + case _UT('!'): + case _UT('$'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('*'): + case _UT(','): + case _UT(';'): + case _UT('\''): + case _UT('+'): + case _UT('='): + case _UT('-'): + case _UT('.'): + case _UT('_'): + case _UT('~'): + case URI_SET_DIGIT: + case URI_SET_ALPHA: + return first + 1; + + default: + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } } - - /* * [pctEncoded]-><%>[HEXDIG][HEXDIG] */ -static const URI_CHAR * URI_FUNC(ParsePctEncoded)( - URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - /* - First character has already been - checked before entering this rule. - - switch (*first) { - case _UT('%'): - */ - if (afterLast - first < 2) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - switch (first[1]) { - case URI_SET_HEXDIG: - if (afterLast - first < 3) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - switch (first[2]) { - case URI_SET_HEXDIG: - return first + 3; - - default: - URI_FUNC(StopSyntax)(state, first + 2, memory); - return NULL; - } - - default: - URI_FUNC(StopSyntax)(state, first + 1, memory); - return NULL; - } - - /* - default: - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } - */ +static const URI_CHAR * URI_FUNC(ParsePctEncoded)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + /* + First character has already been + checked before entering this rule. + + switch (*first) { + case _UT('%'): + */ + if (afterLast - first < 2) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + switch (first[1]) { + case URI_SET_HEXDIG: + if (afterLast - first < 3) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + switch (first[2]) { + case URI_SET_HEXDIG: + return first + 3; + + default: + URI_FUNC(StopSyntax)(state, first + 2, memory); + return NULL; + } + + default: + URI_FUNC(StopSyntax)(state, first + 1, memory); + return NULL; + } + + /* + default: + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } + */ } - - /* * [pctSubUnres]->[pctEncoded] * [pctSubUnres]->[subDelims] * [pctSubUnres]->[unreserved] */ -static const URI_CHAR * URI_FUNC(ParsePctSubUnres)( - URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } - - switch (*first) { - case _UT('%'): - return URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); - - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('*'): - case _UT(','): - case _UT(';'): - case _UT('\''): - case _UT('+'): - case _UT('='): - case _UT('-'): - case _UT('.'): - case _UT('_'): - case _UT('~'): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - return first + 1; - - default: - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; - } +static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + URI_FUNC(StopSyntax)(state, afterLast, memory); + return NULL; + } + + switch (*first) { + case _UT('%'): + return URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); + + case _UT('!'): + case _UT('$'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('*'): + case _UT(','): + case _UT(';'): + case _UT('\''): + case _UT('+'): + case _UT('='): + case _UT('-'): + case _UT('.'): + case _UT('_'): + case _UT('~'): + case URI_SET_DIGIT: + case URI_SET_ALPHA: + return first + 1; + + default: + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; + } } - - /* * [port]->[DIGIT][port] * [port]-> */ -static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case URI_SET_DIGIT: - return URI_FUNC(ParsePort)(state, first + 1, afterLast); - - default: - return first; - } +static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case URI_SET_DIGIT: + return URI_FUNC(ParsePort)(state, first + 1, afterLast); + + default: + return first; + } } - - /* * [queryFrag]->[pchar][queryFrag] * [queryFrag]->[queryFrag] @@ -1691,130 +1750,122 @@ static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, const * [queryFrag]-> */ static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - { - const URI_CHAR * const afterPchar - = URI_FUNC(ParsePchar)(state, first, afterLast, memory); - if (afterPchar == NULL) { - return NULL; - } - return URI_FUNC(ParseQueryFrag)(state, afterPchar, afterLast, memory); - } - - case _UT('/'): - case _UT('?'): - return URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast, memory); - - default: - return first; - } + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('!'): + case _UT('$'): + case _UT('%'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('-'): + case _UT('*'): + case _UT(','): + case _UT('.'): + case _UT(':'): + case _UT(';'): + case _UT('@'): + case _UT('\''): + case _UT('_'): + case _UT('~'): + case _UT('+'): + case _UT('='): + case URI_SET_DIGIT: + case URI_SET_ALPHA: { + const URI_CHAR * const afterPchar = + URI_FUNC(ParsePchar)(state, first, afterLast, memory); + if (afterPchar == NULL) { + return NULL; + } + return URI_FUNC(ParseQueryFrag)(state, afterPchar, afterLast, memory); + } + + case _UT('/'): + case _UT('?'): + return URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast, memory); + + default: + return first; + } } - - /* * [segment]->[pchar][segment] * [segment]-> */ static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: - { - const URI_CHAR * const afterPchar - = URI_FUNC(ParsePchar)(state, first, afterLast, memory); - if (afterPchar == NULL) { - return NULL; - } - return URI_FUNC(ParseSegment)(state, afterPchar, afterLast, memory); - } - - default: - return first; - } + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('!'): + case _UT('$'): + case _UT('%'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('-'): + case _UT('*'): + case _UT(','): + case _UT('.'): + case _UT(':'): + case _UT(';'): + case _UT('@'): + case _UT('\''): + case _UT('_'): + case _UT('~'): + case _UT('+'): + case _UT('='): + case URI_SET_DIGIT: + case URI_SET_ALPHA: { + const URI_CHAR * const afterPchar = + URI_FUNC(ParsePchar)(state, first, afterLast, memory); + if (afterPchar == NULL) { + return NULL; + } + return URI_FUNC(ParseSegment)(state, afterPchar, afterLast, memory); + } + + default: + return first; + } } - - /* * [segmentNz]->[pchar][segment] */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseSegmentNz)( - URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - const URI_CHAR * const afterPchar - = URI_FUNC(ParsePchar)(state, first, afterLast, memory); - if (afterPchar == NULL) { - return NULL; - } - return URI_FUNC(ParseSegment)(state, afterPchar, afterLast, memory); +static URI_INLINE const URI_CHAR * URI_FUNC(ParseSegmentNz)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + const URI_CHAR * const afterPchar = + URI_FUNC(ParsePchar)(state, first, afterLast, memory); + if (afterPchar == NULL) { + return NULL; + } + return URI_FUNC(ParseSegment)(state, afterPchar, afterLast, memory); } - - static URI_INLINE UriBool URI_FUNC(OnExitSegmentNzNcOrScheme2)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - UriMemoryManager * memory) { - if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first, memory)) { /* SEGMENT BOTH */ - return URI_FALSE; /* Raises malloc error*/ - } - state->uri->scheme.first = NULL; /* Not a scheme, reset */ - return URI_TRUE; /* Success */ + URI_TYPE(ParserState) * state, const URI_CHAR * first, UriMemoryManager * memory) { + if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first, + memory)) { /* SEGMENT BOTH */ + return URI_FALSE; /* Raises malloc error*/ + } + state->uri->scheme.first = NULL; /* Not a scheme, reset */ + return URI_TRUE; /* Success */ } - - /* * [segmentNzNcOrScheme2]->[ALPHA][segmentNzNcOrScheme2] * [segmentNzNcOrScheme2]->[DIGIT][segmentNzNcOrScheme2] @@ -1839,97 +1890,96 @@ static URI_INLINE UriBool URI_FUNC(OnExitSegmentNzNcOrScheme2)( * [segmentNzNcOrScheme2]-><'>[mustBeSegmentNzNc] * [segmentNzNcOrScheme2]-><->[segmentNzNcOrScheme2] */ -static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - if (!URI_FUNC(OnExitSegmentNzNcOrScheme2)(state, first, memory)) { - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return afterLast; - } - - switch (*first) { - case _UT('.'): - case _UT('+'): - case _UT('-'): - case URI_SET_ALPHA: - case URI_SET_DIGIT: - return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast, memory); - - case _UT('%'): - { - const URI_CHAR * const afterPctEncoded - = URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); - if (afterPctEncoded == NULL) { - return NULL; - } - return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast, memory); - } - - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('*'): - case _UT(','): - case _UT(';'): - case _UT('@'): - case _UT('_'): - case _UT('~'): - case _UT('='): - case _UT('\''): - return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast, memory); - - case _UT('/'): - { - const URI_CHAR * afterZeroMoreSlashSegs; - const URI_CHAR * const afterSegment - = URI_FUNC(ParseSegment)(state, first + 1, afterLast, memory); - if (afterSegment == NULL) { - return NULL; - } - if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first, memory)) { /* SEGMENT BOTH */ - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - state->uri->scheme.first = NULL; /* Not a scheme, reset */ - if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment, memory)) { /* SEGMENT BOTH */ - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - afterZeroMoreSlashSegs - = URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast, memory); - if (afterZeroMoreSlashSegs == NULL) { - return NULL; - } - return URI_FUNC(ParseUriTail)(state, afterZeroMoreSlashSegs, afterLast, memory); - } - - case _UT(':'): - { - const URI_CHAR * const afterHierPart - = URI_FUNC(ParseHierPart)(state, first + 1, afterLast, memory); - state->uri->scheme.afterLast = first; /* SCHEME END */ - if (afterHierPart == NULL) { - return NULL; - } - return URI_FUNC(ParseUriTail)(state, afterHierPart, afterLast, memory); - } - - default: - if (!URI_FUNC(OnExitSegmentNzNcOrScheme2)(state, first, memory)) { - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return URI_FUNC(ParseUriTail)(state, first, afterLast, memory); - } +static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + if (!URI_FUNC(OnExitSegmentNzNcOrScheme2)(state, first, memory)) { + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return afterLast; + } + + switch (*first) { + case _UT('.'): + case _UT('+'): + case _UT('-'): + case URI_SET_ALPHA: + case URI_SET_DIGIT: + return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast, memory); + + case _UT('%'): { + const URI_CHAR * const afterPctEncoded = + URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); + if (afterPctEncoded == NULL) { + return NULL; + } + return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast, + memory); + } + + case _UT('!'): + case _UT('$'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('*'): + case _UT(','): + case _UT(';'): + case _UT('@'): + case _UT('_'): + case _UT('~'): + case _UT('='): + case _UT('\''): + return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast, memory); + + case _UT('/'): { + const URI_CHAR * afterZeroMoreSlashSegs; + const URI_CHAR * const afterSegment = + URI_FUNC(ParseSegment)(state, first + 1, afterLast, memory); + if (afterSegment == NULL) { + return NULL; + } + if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first, + memory)) { /* SEGMENT BOTH */ + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + state->uri->scheme.first = NULL; /* Not a scheme, reset */ + if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment, + memory)) { /* SEGMENT BOTH */ + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + afterZeroMoreSlashSegs = + URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast, memory); + if (afterZeroMoreSlashSegs == NULL) { + return NULL; + } + return URI_FUNC(ParseUriTail)(state, afterZeroMoreSlashSegs, afterLast, memory); + } + + case _UT(':'): { + const URI_CHAR * const afterHierPart = + URI_FUNC(ParseHierPart)(state, first + 1, afterLast, memory); + state->uri->scheme.afterLast = first; /* SCHEME END */ + if (afterHierPart == NULL) { + return NULL; + } + return URI_FUNC(ParseUriTail)(state, afterHierPart, afterLast, memory); + } + + default: + if (!URI_FUNC(OnExitSegmentNzNcOrScheme2)(state, first, memory)) { + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return URI_FUNC(ParseUriTail)(state, first, afterLast, memory); + } } - - /* * [uriReference]->[ALPHA][segmentNzNcOrScheme2] * [uriReference]->[DIGIT][mustBeSegmentNzNc] @@ -1943,452 +1993,418 @@ static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)( * [uriReference]-><~>[mustBeSegmentNzNc] * [uriReference]-><->[mustBeSegmentNzNc] */ -static const URI_CHAR * URI_FUNC(ParseUriReference)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case URI_SET_ALPHA: - state->uri->scheme.first = first; /* SCHEME BEGIN */ - return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast, memory); - - case URI_SET_DIGIT: - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('*'): - case _UT(','): - case _UT(';'): - case _UT('\''): - case _UT('+'): - case _UT('='): - case _UT('.'): - case _UT('_'): - case _UT('~'): - case _UT('-'): - case _UT('@'): - state->uri->scheme.first = first; /* SEGMENT BEGIN, ABUSE SCHEME POINTER */ - return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast, memory); - - case _UT('%'): - { - const URI_CHAR * const afterPctEncoded - = URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); - if (afterPctEncoded == NULL) { - return NULL; - } - state->uri->scheme.first = first; /* SEGMENT BEGIN, ABUSE SCHEME POINTER */ - return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast, memory); - } - - case _UT('/'): - { - const URI_CHAR * const afterPartHelperTwo - = URI_FUNC(ParsePartHelperTwo)(state, first + 1, afterLast, memory); - if (afterPartHelperTwo == NULL) { - return NULL; - } - return URI_FUNC(ParseUriTail)(state, afterPartHelperTwo, afterLast, memory); - } - - default: - return URI_FUNC(ParseUriTail)(state, first, afterLast, memory); - } +static const URI_CHAR * URI_FUNC(ParseUriReference)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case URI_SET_ALPHA: + state->uri->scheme.first = first; /* SCHEME BEGIN */ + return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast, memory); + + case URI_SET_DIGIT: + case _UT('!'): + case _UT('$'): + case _UT('&'): + case _UT('('): + case _UT(')'): + case _UT('*'): + case _UT(','): + case _UT(';'): + case _UT('\''): + case _UT('+'): + case _UT('='): + case _UT('.'): + case _UT('_'): + case _UT('~'): + case _UT('-'): + case _UT('@'): + state->uri->scheme.first = first; /* SEGMENT BEGIN, ABUSE SCHEME POINTER */ + return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast, memory); + + case _UT('%'): { + const URI_CHAR * const afterPctEncoded = + URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); + if (afterPctEncoded == NULL) { + return NULL; + } + state->uri->scheme.first = first; /* SEGMENT BEGIN, ABUSE SCHEME POINTER */ + return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast, + memory); + } + + case _UT('/'): { + const URI_CHAR * const afterPartHelperTwo = + URI_FUNC(ParsePartHelperTwo)(state, first + 1, afterLast, memory); + if (afterPartHelperTwo == NULL) { + return NULL; + } + return URI_FUNC(ParseUriTail)(state, afterPartHelperTwo, afterLast, memory); + } + + default: + return URI_FUNC(ParseUriTail)(state, first, afterLast, memory); + } } - - /* * [uriTail]-><#>[queryFrag] * [uriTail]->[queryFrag][uriTailTwo] * [uriTail]-> */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseUriTail)( - URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('#'): - { - const URI_CHAR * const afterQueryFrag = URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast, memory); - if (afterQueryFrag == NULL) { - return NULL; - } - state->uri->fragment.first = first + 1; /* FRAGMENT BEGIN */ - state->uri->fragment.afterLast = afterQueryFrag; /* FRAGMENT END */ - return afterQueryFrag; - } - - case _UT('?'): - { - const URI_CHAR * const afterQueryFrag - = URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast, memory); - if (afterQueryFrag == NULL) { - return NULL; - } - state->uri->query.first = first + 1; /* QUERY BEGIN */ - state->uri->query.afterLast = afterQueryFrag; /* QUERY END */ - return URI_FUNC(ParseUriTailTwo)(state, afterQueryFrag, afterLast, memory); - } - - default: - return first; - } +static URI_INLINE const URI_CHAR * URI_FUNC(ParseUriTail)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('#'): { + const URI_CHAR * const afterQueryFrag = + URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast, memory); + if (afterQueryFrag == NULL) { + return NULL; + } + state->uri->fragment.first = first + 1; /* FRAGMENT BEGIN */ + state->uri->fragment.afterLast = afterQueryFrag; /* FRAGMENT END */ + return afterQueryFrag; + } + + case _UT('?'): { + const URI_CHAR * const afterQueryFrag = + URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast, memory); + if (afterQueryFrag == NULL) { + return NULL; + } + state->uri->query.first = first + 1; /* QUERY BEGIN */ + state->uri->query.afterLast = afterQueryFrag; /* QUERY END */ + return URI_FUNC(ParseUriTailTwo)(state, afterQueryFrag, afterLast, memory); + } + + default: + return first; + } } - - /* * [uriTailTwo]-><#>[queryFrag] * [uriTailTwo]-> */ -static URI_INLINE const URI_CHAR * URI_FUNC(ParseUriTailTwo)( - URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('#'): - { - const URI_CHAR * const afterQueryFrag = URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast, memory); - if (afterQueryFrag == NULL) { - return NULL; - } - state->uri->fragment.first = first + 1; /* FRAGMENT BEGIN */ - state->uri->fragment.afterLast = afterQueryFrag; /* FRAGMENT END */ - return afterQueryFrag; - } - - default: - return first; - } +static URI_INLINE const URI_CHAR * +URI_FUNC(ParseUriTailTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('#'): { + const URI_CHAR * const afterQueryFrag = + URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast, memory); + if (afterQueryFrag == NULL) { + return NULL; + } + state->uri->fragment.first = first + 1; /* FRAGMENT BEGIN */ + state->uri->fragment.afterLast = afterQueryFrag; /* FRAGMENT END */ + return afterQueryFrag; + } + + default: + return first; + } } - - /* * [zeroMoreSlashSegs]->[segment][zeroMoreSlashSegs] * [zeroMoreSlashSegs]-> */ -static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - return afterLast; - } - - switch (*first) { - case _UT('/'): - { - const URI_CHAR * const afterSegment - = URI_FUNC(ParseSegment)(state, first + 1, afterLast, memory); - if (afterSegment == NULL) { - return NULL; - } - if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment, memory)) { /* SEGMENT BOTH */ - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast, memory); - } - - default: - return first; - } +static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if (first >= afterLast) { + return afterLast; + } + + switch (*first) { + case _UT('/'): { + const URI_CHAR * const afterSegment = + URI_FUNC(ParseSegment)(state, first + 1, afterLast, memory); + if (afterSegment == NULL) { + return NULL; + } + if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment, + memory)) { /* SEGMENT BOTH */ + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast, memory); + } + + default: + return first; + } } - - -static URI_INLINE void URI_FUNC(ResetParserStateExceptUri)(URI_TYPE(ParserState) * state) { - URI_TYPE(Uri) * const uriBackup = state->uri; - memset(state, 0, sizeof(URI_TYPE(ParserState))); - state->uri = uriBackup; +static URI_INLINE void URI_FUNC(ResetParserStateExceptUri)(URI_TYPE(ParserState) + * state) { + URI_TYPE(Uri) * const uriBackup = state->uri; + memset(state, 0, sizeof(URI_TYPE(ParserState))); + state->uri = uriBackup; } - - -static URI_INLINE UriBool URI_FUNC(PushPathSegment)( - URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - URI_TYPE(PathSegment) * segment = memory->calloc(memory, 1, sizeof(URI_TYPE(PathSegment))); - if (segment == NULL) { - return URI_FALSE; /* Raises malloc error */ - } - if (first == afterLast) { - segment->text.first = URI_FUNC(SafeToPointTo); - segment->text.afterLast = URI_FUNC(SafeToPointTo); - } else { - segment->text.first = first; - segment->text.afterLast = afterLast; - } - - /* First segment ever? */ - if (state->uri->pathHead == NULL) { - /* First segment ever, set head and tail */ - state->uri->pathHead = segment; - state->uri->pathTail = segment; - } else { - /* Append, update tail */ - state->uri->pathTail->next = segment; - state->uri->pathTail = segment; - } - - return URI_TRUE; /* Success */ +static URI_INLINE UriBool URI_FUNC(PushPathSegment)(URI_TYPE(ParserState) * state, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + URI_TYPE(PathSegment) * segment = + memory->calloc(memory, 1, sizeof(URI_TYPE(PathSegment))); + if (segment == NULL) { + return URI_FALSE; /* Raises malloc error */ + } + if (first == afterLast) { + segment->text.first = URI_FUNC(SafeToPointTo); + segment->text.afterLast = URI_FUNC(SafeToPointTo); + } else { + segment->text.first = first; + segment->text.afterLast = afterLast; + } + + /* First segment ever? */ + if (state->uri->pathHead == NULL) { + /* First segment ever, set head and tail */ + state->uri->pathHead = segment; + state->uri->pathTail = segment; + } else { + /* Append, update tail */ + state->uri->pathTail->next = segment; + state->uri->pathTail = segment; + } + + return URI_TRUE; /* Success */ } - - -int URI_FUNC(ParseUriEx)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast) { - return URI_FUNC(ParseUriExMm)(state, first, afterLast, NULL); +int URI_FUNC(ParseUriEx)(URI_TYPE(ParserState) * state, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(ParseUriExMm)(state, first, afterLast, NULL); } - - -static int URI_FUNC(ParseUriExMm)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - const URI_CHAR * afterUriReference; - URI_TYPE(Uri) * uri; - - /* Check params */ - if ((state == NULL) || (first == NULL) || (afterLast == NULL)) { - return URI_ERROR_NULL; - } - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - uri = state->uri; - - /* Init parser */ - URI_FUNC(ResetParserStateExceptUri)(state); - URI_FUNC(ResetUri)(uri); - - /* Parse */ - afterUriReference = URI_FUNC(ParseUriReference)(state, first, afterLast, memory); - if (afterUriReference == NULL) { - /* Waterproof errorPos <= afterLast */ - if (state->errorPos && (state->errorPos > afterLast)) { - state->errorPos = afterLast; - } - return state->errorCode; - } - if (afterUriReference != afterLast) { - if (afterUriReference < afterLast) { - URI_FUNC(StopSyntax)(state, afterUriReference, memory); - } else { - URI_FUNC(StopSyntax)(state, afterLast, memory); - } - return state->errorCode; - } - return URI_SUCCESS; +static int URI_FUNC(ParseUriExMm)(URI_TYPE(ParserState) * state, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + const URI_CHAR * afterUriReference; + URI_TYPE(Uri) * uri; + + /* Check params */ + if ((state == NULL) || (first == NULL) || (afterLast == NULL)) { + return URI_ERROR_NULL; + } + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + uri = state->uri; + + /* Init parser */ + URI_FUNC(ResetParserStateExceptUri)(state); + URI_FUNC(ResetUri)(uri); + + /* Parse */ + afterUriReference = URI_FUNC(ParseUriReference)(state, first, afterLast, memory); + if (afterUriReference == NULL) { + /* Waterproof errorPos <= afterLast */ + if (state->errorPos && (state->errorPos > afterLast)) { + state->errorPos = afterLast; + } + return state->errorCode; + } + if (afterUriReference != afterLast) { + if (afterUriReference < afterLast) { + URI_FUNC(StopSyntax)(state, afterUriReference, memory); + } else { + URI_FUNC(StopSyntax)(state, afterLast, memory); + } + return state->errorCode; + } + return URI_SUCCESS; } - - int URI_FUNC(ParseUri)(URI_TYPE(ParserState) * state, const URI_CHAR * text) { - if ((state == NULL) || (text == NULL)) { - return URI_ERROR_NULL; - } - return URI_FUNC(ParseUriEx)(state, text, text + URI_STRLEN(text)); + if ((state == NULL) || (text == NULL)) { + return URI_ERROR_NULL; + } + return URI_FUNC(ParseUriEx)(state, text, text + URI_STRLEN(text)); } - - int URI_FUNC(ParseSingleUri)(URI_TYPE(Uri) * uri, const URI_CHAR * text, - const URI_CHAR ** errorPos) { - return URI_FUNC(ParseSingleUriEx)(uri, text, NULL, errorPos); + const URI_CHAR ** errorPos) { + return URI_FUNC(ParseSingleUriEx)(uri, text, NULL, errorPos); } - - -int URI_FUNC(ParseSingleUriEx)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, const URI_CHAR * afterLast, - const URI_CHAR ** errorPos) { +int URI_FUNC(ParseSingleUriEx)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, const URI_CHAR ** errorPos) { if ((afterLast == NULL) && (first != NULL)) { - afterLast = first + URI_STRLEN(first); - } - return URI_FUNC(ParseSingleUriExMm)(uri, first, afterLast, errorPos, NULL); + afterLast = first + URI_STRLEN(first); + } + return URI_FUNC(ParseSingleUriExMm)(uri, first, afterLast, errorPos, NULL); } +int URI_FUNC(ParseSingleUriExMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, const URI_CHAR ** errorPos, + UriMemoryManager * memory) { + URI_TYPE(ParserState) state; + int res; + /* Check params */ + if ((uri == NULL) || (first == NULL) || (afterLast == NULL)) { + return URI_ERROR_NULL; + } + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ -int URI_FUNC(ParseSingleUriExMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, const URI_CHAR * afterLast, - const URI_CHAR ** errorPos, UriMemoryManager * memory) { - URI_TYPE(ParserState) state; - int res; + state.uri = uri; - /* Check params */ - if ((uri == NULL) || (first == NULL) || (afterLast == NULL)) { - return URI_ERROR_NULL; - } - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + res = URI_FUNC(ParseUriExMm)(&state, first, afterLast, memory); - state.uri = uri; + if (res != URI_SUCCESS) { + if (errorPos != NULL) { + *errorPos = state.errorPos; + } + URI_FUNC(FreeUriMembersMm)(uri, memory); + } - res = URI_FUNC(ParseUriExMm)(&state, first, afterLast, memory); - - if (res != URI_SUCCESS) { - if (errorPos != NULL) { - *errorPos = state.errorPos; - } - URI_FUNC(FreeUriMembersMm)(uri, memory); - } - - return res; + return res; } - - void URI_FUNC(FreeUriMembers)(URI_TYPE(Uri) * uri) { - URI_FUNC(FreeUriMembersMm)(uri, NULL); + URI_FUNC(FreeUriMembersMm)(uri, NULL); } - - int URI_FUNC(FreeUriMembersMm)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { - if (uri == NULL) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - if (uri->owner) { - /* Scheme */ - if (uri->scheme.first != NULL) { - if (uri->scheme.first != uri->scheme.afterLast) { - memory->free(memory, (URI_CHAR *)uri->scheme.first); - } - uri->scheme.first = NULL; - uri->scheme.afterLast = NULL; - } - - /* User info */ - if (uri->userInfo.first != NULL) { - if (uri->userInfo.first != uri->userInfo.afterLast) { - memory->free(memory, (URI_CHAR *)uri->userInfo.first); - } - uri->userInfo.first = NULL; - uri->userInfo.afterLast = NULL; - } - - /* Host data - IPvFuture (may affect host text) */ - if (uri->hostData.ipFuture.first != NULL) { - /* NOTE: .hostData.ipFuture holds the very same range pointers - * as .hostText; we must not free memory twice. */ - uri->hostText.first = NULL; - uri->hostText.afterLast = NULL; - - if (uri->hostData.ipFuture.first != uri->hostData.ipFuture.afterLast) { - memory->free(memory, (URI_CHAR *)uri->hostData.ipFuture.first); - } - uri->hostData.ipFuture.first = NULL; - uri->hostData.ipFuture.afterLast = NULL; - } - - /* Host text (after IPvFuture, see above) */ - if (uri->hostText.first != NULL) { - if (uri->hostText.first != uri->hostText.afterLast) { - memory->free(memory, (URI_CHAR *)uri->hostText.first); - } - uri->hostText.first = NULL; - uri->hostText.afterLast = NULL; - } - } - - /* Host data - IPv4 */ - if (uri->hostData.ip4 != NULL) { - memory->free(memory, uri->hostData.ip4); - uri->hostData.ip4 = NULL; - } - - /* Host data - IPv6 */ - if (uri->hostData.ip6 != NULL) { - memory->free(memory, uri->hostData.ip6); - uri->hostData.ip6 = NULL; - } - - /* Port text */ - if (uri->owner && (uri->portText.first != NULL)) { - if (uri->portText.first != uri->portText.afterLast) { - memory->free(memory, (URI_CHAR *)uri->portText.first); - } - uri->portText.first = NULL; - uri->portText.afterLast = NULL; - } - - /* Path */ - URI_FUNC(FreeUriPath)(uri, memory); - - if (uri->owner) { - /* Query */ - if (uri->query.first != NULL) { - if (uri->query.first != uri->query.afterLast) { - memory->free(memory, (URI_CHAR *)uri->query.first); - } - uri->query.first = NULL; - uri->query.afterLast = NULL; - } - - /* Fragment */ - if (uri->fragment.first != NULL) { - if (uri->fragment.first != uri->fragment.afterLast) { - memory->free(memory, (URI_CHAR *)uri->fragment.first); - } - uri->fragment.first = NULL; - uri->fragment.afterLast = NULL; - } - } - - return URI_SUCCESS; + if (uri == NULL) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if (uri->owner) { + /* Scheme */ + if (uri->scheme.first != NULL) { + if (uri->scheme.first != uri->scheme.afterLast) { + memory->free(memory, (URI_CHAR *)uri->scheme.first); + } + uri->scheme.first = NULL; + uri->scheme.afterLast = NULL; + } + + /* User info */ + if (uri->userInfo.first != NULL) { + if (uri->userInfo.first != uri->userInfo.afterLast) { + memory->free(memory, (URI_CHAR *)uri->userInfo.first); + } + uri->userInfo.first = NULL; + uri->userInfo.afterLast = NULL; + } + + /* Host data - IPvFuture (may affect host text) */ + if (uri->hostData.ipFuture.first != NULL) { + /* NOTE: .hostData.ipFuture holds the very same range pointers + * as .hostText; we must not free memory twice. */ + uri->hostText.first = NULL; + uri->hostText.afterLast = NULL; + + if (uri->hostData.ipFuture.first != uri->hostData.ipFuture.afterLast) { + memory->free(memory, (URI_CHAR *)uri->hostData.ipFuture.first); + } + uri->hostData.ipFuture.first = NULL; + uri->hostData.ipFuture.afterLast = NULL; + } + + /* Host text (after IPvFuture, see above) */ + if (uri->hostText.first != NULL) { + if (uri->hostText.first != uri->hostText.afterLast) { + memory->free(memory, (URI_CHAR *)uri->hostText.first); + } + uri->hostText.first = NULL; + uri->hostText.afterLast = NULL; + } + } + + /* Host data - IPv4 */ + if (uri->hostData.ip4 != NULL) { + memory->free(memory, uri->hostData.ip4); + uri->hostData.ip4 = NULL; + } + + /* Host data - IPv6 */ + if (uri->hostData.ip6 != NULL) { + memory->free(memory, uri->hostData.ip6); + uri->hostData.ip6 = NULL; + } + + /* Port text */ + if (uri->owner && (uri->portText.first != NULL)) { + if (uri->portText.first != uri->portText.afterLast) { + memory->free(memory, (URI_CHAR *)uri->portText.first); + } + uri->portText.first = NULL; + uri->portText.afterLast = NULL; + } + + /* Path */ + URI_FUNC(FreeUriPath)(uri, memory); + + if (uri->owner) { + /* Query */ + if (uri->query.first != NULL) { + if (uri->query.first != uri->query.afterLast) { + memory->free(memory, (URI_CHAR *)uri->query.first); + } + uri->query.first = NULL; + uri->query.afterLast = NULL; + } + + /* Fragment */ + if (uri->fragment.first != NULL) { + if (uri->fragment.first != uri->fragment.afterLast) { + memory->free(memory, (URI_CHAR *)uri->fragment.first); + } + uri->fragment.first = NULL; + uri->fragment.afterLast = NULL; + } + } + + return URI_SUCCESS; } - - UriBool URI_FUNC(_TESTING_ONLY_ParseIpSix)(const URI_CHAR * text) { - UriMemoryManager * const memory = &defaultMemoryManager; - URI_TYPE(Uri) uri; - URI_TYPE(ParserState) parser; - const URI_CHAR * const afterIpSix = text + URI_STRLEN(text); - const URI_CHAR * res; - - URI_FUNC(ResetUri)(&uri); - parser.uri = &uri; - URI_FUNC(ResetParserStateExceptUri)(&parser); - parser.uri->hostData.ip6 = memory->malloc(memory, 1 * sizeof(UriIp6)); - res = URI_FUNC(ParseIPv6address2)(&parser, text, afterIpSix, memory); - URI_FUNC(FreeUriMembersMm)(&uri, memory); - return res == afterIpSix ? URI_TRUE : URI_FALSE; + UriMemoryManager * const memory = &defaultMemoryManager; + URI_TYPE(Uri) uri; + URI_TYPE(ParserState) parser; + const URI_CHAR * const afterIpSix = text + URI_STRLEN(text); + const URI_CHAR * res; + + URI_FUNC(ResetUri)(&uri); + parser.uri = &uri; + URI_FUNC(ResetParserStateExceptUri)(&parser); + parser.uri->hostData.ip6 = memory->malloc(memory, 1 * sizeof(UriIp6)); + res = URI_FUNC(ParseIPv6address2)(&parser, text, afterIpSix, memory); + URI_FUNC(FreeUriMembersMm)(&uri, memory); + return res == afterIpSix ? URI_TRUE : URI_FALSE; } - - UriBool URI_FUNC(_TESTING_ONLY_ParseIpFour)(const URI_CHAR * text) { - unsigned char octets[4]; - int res = URI_FUNC(ParseIpFourAddress)(octets, text, text + URI_STRLEN(text)); - return (res == URI_SUCCESS) ? URI_TRUE : URI_FALSE; + unsigned char octets[4]; + int res = URI_FUNC(ParseIpFourAddress)(octets, text, text + URI_STRLEN(text)); + return (res == URI_SUCCESS) ? URI_TRUE : URI_FALSE; } - - -#undef URI_SET_DIGIT -#undef URI_SET_HEX_LETTER_UPPER -#undef URI_SET_HEX_LETTER_LOWER -#undef URI_SET_HEXDIG -#undef URI_SET_ALPHA - - +# undef URI_SET_DIGIT +# undef URI_SET_HEX_LETTER_UPPER +# undef URI_SET_HEX_LETTER_LOWER +# undef URI_SET_HEXDIG +# undef URI_SET_ALPHA #endif diff --git a/ext/uri/uriparser/src/UriParseBase.c b/ext/uri/uriparser/src/UriParseBase.c index 3a4aa08f6b760..0ee66d4427e3b 100644 --- a/ext/uri/uriparser/src/UriParseBase.c +++ b/ext/uri/uriparser/src/UriParseBase.c @@ -38,53 +38,48 @@ */ #ifndef URI_DOXYGEN -# include "UriParseBase.h" +# include "UriParseBase.h" #endif +void uriWriteQuadToDoubleByte(const unsigned char * hexDigits, int digitCount, + unsigned char * output) { + switch (digitCount) { + case 1: + /* 0x___? -> \x00 \x0? */ + output[0] = 0; + output[1] = hexDigits[0]; + break; + case 2: + /* 0x__?? -> \0xx \x?? */ + output[0] = 0; + output[1] = 16 * hexDigits[0] + hexDigits[1]; + break; -void uriWriteQuadToDoubleByte(const unsigned char * hexDigits, int digitCount, unsigned char * output) { - switch (digitCount) { - case 1: - /* 0x___? -> \x00 \x0? */ - output[0] = 0; - output[1] = hexDigits[0]; - break; + case 3: + /* 0x_??? -> \0x? \x?? */ + output[0] = hexDigits[0]; + output[1] = 16 * hexDigits[1] + hexDigits[2]; + break; - case 2: - /* 0x__?? -> \0xx \x?? */ - output[0] = 0; - output[1] = 16 * hexDigits[0] + hexDigits[1]; - break; - - case 3: - /* 0x_??? -> \0x? \x?? */ - output[0] = hexDigits[0]; - output[1] = 16 * hexDigits[1] + hexDigits[2]; - break; - - case 4: - /* 0x???? -> \0?? \x?? */ - output[0] = 16 * hexDigits[0] + hexDigits[1]; - output[1] = 16 * hexDigits[2] + hexDigits[3]; - break; - - } + case 4: + /* 0x???? -> \0?? \x?? */ + output[0] = 16 * hexDigits[0] + hexDigits[1]; + output[1] = 16 * hexDigits[2] + hexDigits[3]; + break; + } } - - unsigned char uriGetOctetValue(const unsigned char * digits, int digitCount) { - switch (digitCount) { - case 1: - return digits[0]; - - case 2: - return 10 * digits[0] + digits[1]; + switch (digitCount) { + case 1: + return digits[0]; - case 3: - default: - return 100 * digits[0] + 10 * digits[1] + digits[2]; + case 2: + return 10 * digits[0] + digits[1]; - } + case 3: + default: + return 100 * digits[0] + 10 * digits[1] + digits[2]; + } } diff --git a/ext/uri/uriparser/src/UriParseBase.h b/ext/uri/uriparser/src/UriParseBase.h index 6d7379de1c0a9..cdb2c6f778790 100644 --- a/ext/uri/uriparser/src/UriParseBase.h +++ b/ext/uri/uriparser/src/UriParseBase.h @@ -38,18 +38,12 @@ */ #ifndef URI_PARSE_BASE_H -#define URI_PARSE_BASE_H 1 - - - -#include - +# define URI_PARSE_BASE_H 1 +# include void uriWriteQuadToDoubleByte(const unsigned char * hexDigits, int digitCount, - unsigned char * output); + unsigned char * output); unsigned char uriGetOctetValue(const unsigned char * digits, int digitCount); - - #endif /* URI_PARSE_BASE_H */ diff --git a/ext/uri/uriparser/src/UriQuery.c b/ext/uri/uriparser/src/UriQuery.c index bbc15488773c6..801a237a3a1b2 100644 --- a/ext/uri/uriparser/src/UriQuery.c +++ b/ext/uri/uriparser/src/UriQuery.c @@ -41,465 +41,427 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriQuery.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriQuery.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriQuery.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriQuery.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -# include "UriMemory.h" -#endif - - - -#include -#include /* size_t */ - - +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +# endif + +# include +# include /* size_t */ static int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest, - const URI_TYPE(QueryList) * queryList, - int maxChars, int * charsWritten, int * charsRequired, - UriBool spaceToPlus, UriBool normalizeBreaks); - -static UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) ** prevNext, - int * itemCount, const URI_CHAR * keyFirst, const URI_CHAR * keyAfter, - const URI_CHAR * valueFirst, const URI_CHAR * valueAfter, - UriBool plusToSpace, UriBreakConversion breakConversion, - UriMemoryManager * memory); - + const URI_TYPE(QueryList) * queryList, + int maxChars, int * charsWritten, + int * charsRequired, UriBool spaceToPlus, + UriBool normalizeBreaks); +static UriBool URI_FUNC(AppendQueryItem)( + URI_TYPE(QueryList) * *prevNext, int * itemCount, const URI_CHAR * keyFirst, + const URI_CHAR * keyAfter, const URI_CHAR * valueFirst, const URI_CHAR * valueAfter, + UriBool plusToSpace, UriBreakConversion breakConversion, UriMemoryManager * memory); int URI_FUNC(ComposeQueryCharsRequired)(const URI_TYPE(QueryList) * queryList, - int * charsRequired) { - const UriBool spaceToPlus = URI_TRUE; - const UriBool normalizeBreaks = URI_TRUE; + int * charsRequired) { + const UriBool spaceToPlus = URI_TRUE; + const UriBool normalizeBreaks = URI_TRUE; - return URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, charsRequired, - spaceToPlus, normalizeBreaks); + return URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, charsRequired, spaceToPlus, + normalizeBreaks); } - - int URI_FUNC(ComposeQueryCharsRequiredEx)(const URI_TYPE(QueryList) * queryList, - int * charsRequired, UriBool spaceToPlus, UriBool normalizeBreaks) { - if ((queryList == NULL) || (charsRequired == NULL)) { - return URI_ERROR_NULL; - } - - return URI_FUNC(ComposeQueryEngine)(NULL, queryList, 0, NULL, - charsRequired, spaceToPlus, normalizeBreaks); + int * charsRequired, UriBool spaceToPlus, + UriBool normalizeBreaks) { + if ((queryList == NULL) || (charsRequired == NULL)) { + return URI_ERROR_NULL; + } + + return URI_FUNC(ComposeQueryEngine)(NULL, queryList, 0, NULL, charsRequired, + spaceToPlus, normalizeBreaks); } +int URI_FUNC(ComposeQuery)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList, + int maxChars, int * charsWritten) { + const UriBool spaceToPlus = URI_TRUE; + const UriBool normalizeBreaks = URI_TRUE; - -int URI_FUNC(ComposeQuery)(URI_CHAR * dest, - const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten) { - const UriBool spaceToPlus = URI_TRUE; - const UriBool normalizeBreaks = URI_TRUE; - - return URI_FUNC(ComposeQueryEx)(dest, queryList, maxChars, charsWritten, - spaceToPlus, normalizeBreaks); + return URI_FUNC(ComposeQueryEx)(dest, queryList, maxChars, charsWritten, spaceToPlus, + normalizeBreaks); } +int URI_FUNC(ComposeQueryEx)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList, + int maxChars, int * charsWritten, UriBool spaceToPlus, + UriBool normalizeBreaks) { + if ((dest == NULL) || (queryList == NULL)) { + return URI_ERROR_NULL; + } + if (maxChars < 1) { + return URI_ERROR_OUTPUT_TOO_LARGE; + } -int URI_FUNC(ComposeQueryEx)(URI_CHAR * dest, - const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten, - UriBool spaceToPlus, UriBool normalizeBreaks) { - if ((dest == NULL) || (queryList == NULL)) { - return URI_ERROR_NULL; - } - - if (maxChars < 1) { - return URI_ERROR_OUTPUT_TOO_LARGE; - } - - return URI_FUNC(ComposeQueryEngine)(dest, queryList, maxChars, - charsWritten, NULL, spaceToPlus, normalizeBreaks); + return URI_FUNC(ComposeQueryEngine)(dest, queryList, maxChars, charsWritten, NULL, + spaceToPlus, normalizeBreaks); } - - int URI_FUNC(ComposeQueryMalloc)(URI_CHAR ** dest, - const URI_TYPE(QueryList) * queryList) { - const UriBool spaceToPlus = URI_TRUE; - const UriBool normalizeBreaks = URI_TRUE; + const URI_TYPE(QueryList) * queryList) { + const UriBool spaceToPlus = URI_TRUE; + const UriBool normalizeBreaks = URI_TRUE; - return URI_FUNC(ComposeQueryMallocEx)(dest, queryList, - spaceToPlus, normalizeBreaks); + return URI_FUNC(ComposeQueryMallocEx)(dest, queryList, spaceToPlus, normalizeBreaks); } - - int URI_FUNC(ComposeQueryMallocEx)(URI_CHAR ** dest, - const URI_TYPE(QueryList) * queryList, - UriBool spaceToPlus, UriBool normalizeBreaks) { - return URI_FUNC(ComposeQueryMallocExMm)(dest, queryList, spaceToPlus, - normalizeBreaks, NULL); + const URI_TYPE(QueryList) * queryList, + UriBool spaceToPlus, UriBool normalizeBreaks) { + return URI_FUNC(ComposeQueryMallocExMm)(dest, queryList, spaceToPlus, normalizeBreaks, + NULL); } - - int URI_FUNC(ComposeQueryMallocExMm)(URI_CHAR ** dest, - const URI_TYPE(QueryList) * queryList, - UriBool spaceToPlus, UriBool normalizeBreaks, - UriMemoryManager * memory) { - int charsRequired; - int res; - URI_CHAR * queryString; - - if (dest == NULL) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - /* Calculate space */ - res = URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, &charsRequired, - spaceToPlus, normalizeBreaks); - if (res != URI_SUCCESS) { - return res; - } - if (charsRequired == INT_MAX) { - return URI_ERROR_MALLOC; - } - charsRequired++; - - /* Allocate space */ - queryString = memory->calloc(memory, charsRequired, sizeof(URI_CHAR)); - if (queryString == NULL) { - return URI_ERROR_MALLOC; - } - - /* Put query in */ - res = URI_FUNC(ComposeQueryEx)(queryString, queryList, charsRequired, - NULL, spaceToPlus, normalizeBreaks); - if (res != URI_SUCCESS) { - memory->free(memory, queryString); - return res; - } - - *dest = queryString; - return URI_SUCCESS; + const URI_TYPE(QueryList) * queryList, + UriBool spaceToPlus, UriBool normalizeBreaks, + UriMemoryManager * memory) { + int charsRequired; + int res; + URI_CHAR * queryString; + + if (dest == NULL) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + /* Calculate space */ + res = URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, &charsRequired, spaceToPlus, + normalizeBreaks); + if (res != URI_SUCCESS) { + return res; + } + if (charsRequired == INT_MAX) { + return URI_ERROR_MALLOC; + } + charsRequired++; + + /* Allocate space */ + queryString = memory->calloc(memory, charsRequired, sizeof(URI_CHAR)); + if (queryString == NULL) { + return URI_ERROR_MALLOC; + } + + /* Put query in */ + res = URI_FUNC(ComposeQueryEx)(queryString, queryList, charsRequired, NULL, + spaceToPlus, normalizeBreaks); + if (res != URI_SUCCESS) { + memory->free(memory, queryString); + return res; + } + + *dest = queryString; + return URI_SUCCESS; } - - -int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest, - const URI_TYPE(QueryList) * queryList, - int maxChars, int * charsWritten, int * charsRequired, - UriBool spaceToPlus, UriBool normalizeBreaks) { - UriBool firstItem = URI_TRUE; - int ampersandLen = 0; /* increased to 1 from second item on */ - URI_CHAR * write = dest; - - /* Subtract terminator */ - if (dest == NULL) { - *charsRequired = 0; - } else { - maxChars--; - } - - while (queryList != NULL) { - const URI_CHAR * const key = queryList->key; - const URI_CHAR * const value = queryList->value; - const int worstCase = (normalizeBreaks == URI_TRUE ? 6 : 3); - const size_t keyLen = (key == NULL) ? 0 : URI_STRLEN(key); - int keyRequiredChars; - const size_t valueLen = (value == NULL) ? 0 : URI_STRLEN(value); - int valueRequiredChars; - - if ((keyLen >= (size_t)INT_MAX / worstCase) || (valueLen >= (size_t)INT_MAX / worstCase)) { - return URI_ERROR_OUTPUT_TOO_LARGE; - } - keyRequiredChars = worstCase * (int)keyLen; - valueRequiredChars = worstCase * (int)valueLen; - - if (dest == NULL) { - (*charsRequired) += ampersandLen + keyRequiredChars + ((value == NULL) - ? 0 - : 1 + valueRequiredChars); - - if (firstItem == URI_TRUE) { - ampersandLen = 1; - firstItem = URI_FALSE; - } - } else { - if ((write - dest) + ampersandLen + keyRequiredChars > maxChars) { - return URI_ERROR_OUTPUT_TOO_LARGE; - } - - /* Copy key */ - if (firstItem == URI_TRUE) { - ampersandLen = 1; - firstItem = URI_FALSE; - } else { - write[0] = _UT('&'); - write++; - } - write = URI_FUNC(EscapeEx)(key, key + keyLen, - write, spaceToPlus, normalizeBreaks); - - if (value != NULL) { - if ((write - dest) + 1 + valueRequiredChars > maxChars) { - return URI_ERROR_OUTPUT_TOO_LARGE; - } - - /* Copy value */ - write[0] = _UT('='); - write++; - write = URI_FUNC(EscapeEx)(value, value + valueLen, - write, spaceToPlus, normalizeBreaks); - } - } - - queryList = queryList->next; - } - - if (dest != NULL) { - write[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = (int)(write - dest) + 1; /* .. for terminator */ - } - } - - return URI_SUCCESS; +int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList, + int maxChars, int * charsWritten, int * charsRequired, + UriBool spaceToPlus, UriBool normalizeBreaks) { + UriBool firstItem = URI_TRUE; + int ampersandLen = 0; /* increased to 1 from second item on */ + URI_CHAR * write = dest; + + /* Subtract terminator */ + if (dest == NULL) { + *charsRequired = 0; + } else { + maxChars--; + } + + while (queryList != NULL) { + const URI_CHAR * const key = queryList->key; + const URI_CHAR * const value = queryList->value; + const int worstCase = (normalizeBreaks == URI_TRUE ? 6 : 3); + const size_t keyLen = (key == NULL) ? 0 : URI_STRLEN(key); + int keyRequiredChars; + const size_t valueLen = (value == NULL) ? 0 : URI_STRLEN(value); + int valueRequiredChars; + + if ((keyLen >= (size_t)INT_MAX / worstCase) + || (valueLen >= (size_t)INT_MAX / worstCase)) { + return URI_ERROR_OUTPUT_TOO_LARGE; + } + keyRequiredChars = worstCase * (int)keyLen; + valueRequiredChars = worstCase * (int)valueLen; + + if (dest == NULL) { + (*charsRequired) += ampersandLen + keyRequiredChars + + ((value == NULL) ? 0 : 1 + valueRequiredChars); + + if (firstItem == URI_TRUE) { + ampersandLen = 1; + firstItem = URI_FALSE; + } + } else { + if ((write - dest) + ampersandLen + keyRequiredChars > maxChars) { + return URI_ERROR_OUTPUT_TOO_LARGE; + } + + /* Copy key */ + if (firstItem == URI_TRUE) { + ampersandLen = 1; + firstItem = URI_FALSE; + } else { + write[0] = _UT('&'); + write++; + } + write = URI_FUNC(EscapeEx)(key, key + keyLen, write, spaceToPlus, + normalizeBreaks); + + if (value != NULL) { + if ((write - dest) + 1 + valueRequiredChars > maxChars) { + return URI_ERROR_OUTPUT_TOO_LARGE; + } + + /* Copy value */ + write[0] = _UT('='); + write++; + write = URI_FUNC(EscapeEx)(value, value + valueLen, write, spaceToPlus, + normalizeBreaks); + } + } + + queryList = queryList->next; + } + + if (dest != NULL) { + write[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = (int)(write - dest) + 1; /* .. for terminator */ + } + } + + return URI_SUCCESS; } - - -UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) ** prevNext, - int * itemCount, const URI_CHAR * keyFirst, const URI_CHAR * keyAfter, - const URI_CHAR * valueFirst, const URI_CHAR * valueAfter, - UriBool plusToSpace, UriBreakConversion breakConversion, - UriMemoryManager * memory) { - const int keyLen = (int)(keyAfter - keyFirst); - const int valueLen = (int)(valueAfter - valueFirst); - URI_CHAR * key; - URI_CHAR * value; - - if ((prevNext == NULL) || (itemCount == NULL) - || (keyFirst == NULL) || (keyAfter == NULL) - || (keyFirst > keyAfter) || (valueFirst > valueAfter) - || ((keyFirst == keyAfter) - && (valueFirst == NULL) && (valueAfter == NULL))) { - return URI_TRUE; - } - - /* Append new empty item */ - *prevNext = memory->malloc(memory, 1 * sizeof(URI_TYPE(QueryList))); - if (*prevNext == NULL) { - return URI_FALSE; /* Raises malloc error */ - } - (*prevNext)->next = NULL; - - - /* Fill key */ - key = memory->malloc(memory, (keyLen + 1) * sizeof(URI_CHAR)); - if (key == NULL) { - memory->free(memory, *prevNext); - *prevNext = NULL; - return URI_FALSE; /* Raises malloc error */ - } - - key[keyLen] = _UT('\0'); - if (keyLen > 0) { - /* Copy 1:1 */ - memcpy(key, keyFirst, keyLen * sizeof(URI_CHAR)); - - /* Unescape */ - URI_FUNC(UnescapeInPlaceEx)(key, plusToSpace, breakConversion); - } - (*prevNext)->key = key; - - - /* Fill value */ - if (valueFirst != NULL) { - value = memory->malloc(memory, (valueLen + 1) * sizeof(URI_CHAR)); - if (value == NULL) { - memory->free(memory, key); - memory->free(memory, *prevNext); - *prevNext = NULL; - return URI_FALSE; /* Raises malloc error */ - } - - value[valueLen] = _UT('\0'); - if (valueLen > 0) { - /* Copy 1:1 */ - memcpy(value, valueFirst, valueLen * sizeof(URI_CHAR)); - - /* Unescape */ - URI_FUNC(UnescapeInPlaceEx)(value, plusToSpace, breakConversion); - } - (*prevNext)->value = value; - } else { - value = NULL; - } - (*prevNext)->value = value; - - (*itemCount)++; - return URI_TRUE; +UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) * *prevNext, int * itemCount, + const URI_CHAR * keyFirst, const URI_CHAR * keyAfter, + const URI_CHAR * valueFirst, + const URI_CHAR * valueAfter, UriBool plusToSpace, + UriBreakConversion breakConversion, + UriMemoryManager * memory) { + const int keyLen = (int)(keyAfter - keyFirst); + const int valueLen = (int)(valueAfter - valueFirst); + URI_CHAR * key; + URI_CHAR * value; + + if ((prevNext == NULL) || (itemCount == NULL) || (keyFirst == NULL) + || (keyAfter == NULL) || (keyFirst > keyAfter) || (valueFirst > valueAfter) + || ((keyFirst == keyAfter) && (valueFirst == NULL) && (valueAfter == NULL))) { + return URI_TRUE; + } + + /* Append new empty item */ + *prevNext = memory->malloc(memory, 1 * sizeof(URI_TYPE(QueryList))); + if (*prevNext == NULL) { + return URI_FALSE; /* Raises malloc error */ + } + (*prevNext)->next = NULL; + + /* Fill key */ + key = memory->malloc(memory, (keyLen + 1) * sizeof(URI_CHAR)); + if (key == NULL) { + memory->free(memory, *prevNext); + *prevNext = NULL; + return URI_FALSE; /* Raises malloc error */ + } + + key[keyLen] = _UT('\0'); + if (keyLen > 0) { + /* Copy 1:1 */ + memcpy(key, keyFirst, keyLen * sizeof(URI_CHAR)); + + /* Unescape */ + URI_FUNC(UnescapeInPlaceEx)(key, plusToSpace, breakConversion); + } + (*prevNext)->key = key; + + /* Fill value */ + if (valueFirst != NULL) { + value = memory->malloc(memory, (valueLen + 1) * sizeof(URI_CHAR)); + if (value == NULL) { + memory->free(memory, key); + memory->free(memory, *prevNext); + *prevNext = NULL; + return URI_FALSE; /* Raises malloc error */ + } + + value[valueLen] = _UT('\0'); + if (valueLen > 0) { + /* Copy 1:1 */ + memcpy(value, valueFirst, valueLen * sizeof(URI_CHAR)); + + /* Unescape */ + URI_FUNC(UnescapeInPlaceEx)(value, plusToSpace, breakConversion); + } + (*prevNext)->value = value; + } else { + value = NULL; + } + (*prevNext)->value = value; + + (*itemCount)++; + return URI_TRUE; } - - void URI_FUNC(FreeQueryList)(URI_TYPE(QueryList) * queryList) { - URI_FUNC(FreeQueryListMm)(queryList, NULL); + URI_FUNC(FreeQueryListMm)(queryList, NULL); } - - int URI_FUNC(FreeQueryListMm)(URI_TYPE(QueryList) * queryList, - UriMemoryManager * memory) { - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - while (queryList != NULL) { - URI_TYPE(QueryList) * nextBackup = queryList->next; - memory->free(memory, (URI_CHAR *)queryList->key); /* const cast */ - memory->free(memory, (URI_CHAR *)queryList->value); /* const cast */ - memory->free(memory, queryList); - queryList = nextBackup; - } - return URI_SUCCESS; + UriMemoryManager * memory) { + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + while (queryList != NULL) { + URI_TYPE(QueryList) * nextBackup = queryList->next; + memory->free(memory, (URI_CHAR *)queryList->key); /* const cast */ + memory->free(memory, (URI_CHAR *)queryList->value); /* const cast */ + memory->free(memory, queryList); + queryList = nextBackup; + } + return URI_SUCCESS; } +int URI_FUNC(DissectQueryMalloc)(URI_TYPE(QueryList) * *dest, int * itemCount, + const URI_CHAR * first, const URI_CHAR * afterLast) { + const UriBool plusToSpace = URI_TRUE; + const UriBreakConversion breakConversion = URI_BR_DONT_TOUCH; - -int URI_FUNC(DissectQueryMalloc)(URI_TYPE(QueryList) ** dest, int * itemCount, - const URI_CHAR * first, const URI_CHAR * afterLast) { - const UriBool plusToSpace = URI_TRUE; - const UriBreakConversion breakConversion = URI_BR_DONT_TOUCH; - - return URI_FUNC(DissectQueryMallocEx)(dest, itemCount, first, afterLast, - plusToSpace, breakConversion); + return URI_FUNC(DissectQueryMallocEx)(dest, itemCount, first, afterLast, plusToSpace, + breakConversion); } - - -int URI_FUNC(DissectQueryMallocEx)(URI_TYPE(QueryList) ** dest, int * itemCount, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriBool plusToSpace, UriBreakConversion breakConversion) { - return URI_FUNC(DissectQueryMallocExMm)(dest, itemCount, first, afterLast, - plusToSpace, breakConversion, NULL); +int URI_FUNC(DissectQueryMallocEx)(URI_TYPE(QueryList) * *dest, int * itemCount, + const URI_CHAR * first, const URI_CHAR * afterLast, + UriBool plusToSpace, + UriBreakConversion breakConversion) { + return URI_FUNC(DissectQueryMallocExMm)(dest, itemCount, first, afterLast, + plusToSpace, breakConversion, NULL); } - - -int URI_FUNC(DissectQueryMallocExMm)(URI_TYPE(QueryList) ** dest, int * itemCount, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriBool plusToSpace, UriBreakConversion breakConversion, - UriMemoryManager * memory) { - const URI_CHAR * walk = first; - const URI_CHAR * keyFirst = first; - const URI_CHAR * keyAfter = NULL; - const URI_CHAR * valueFirst = NULL; - const URI_CHAR * valueAfter = NULL; - URI_TYPE(QueryList) ** prevNext = dest; - int nullCounter; - int * itemsAppended = (itemCount == NULL) ? &nullCounter : itemCount; - - if ((dest == NULL) || (first == NULL) || (afterLast == NULL)) { - return URI_ERROR_NULL; - } - - if (first > afterLast) { - return URI_ERROR_RANGE_INVALID; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - *dest = NULL; - *itemsAppended = 0; - - /* Parse query string */ - for (; walk < afterLast; walk++) { - switch (*walk) { - case _UT('&'): - if (valueFirst != NULL) { - valueAfter = walk; - } else { - keyAfter = walk; - } - - if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended, - keyFirst, keyAfter, valueFirst, valueAfter, - plusToSpace, breakConversion, memory) - == URI_FALSE) { - /* Free list we built */ - *itemsAppended = 0; - URI_FUNC(FreeQueryListMm)(*dest, memory); - return URI_ERROR_MALLOC; - } - - /* Make future items children of the current */ - if ((prevNext != NULL) && (*prevNext != NULL)) { - prevNext = &((*prevNext)->next); - } - - if (walk + 1 < afterLast) { - keyFirst = walk + 1; - } else { - keyFirst = NULL; - } - keyAfter = NULL; - valueFirst = NULL; - valueAfter = NULL; - break; - - case _UT('='): - /* NOTE: WE treat the first '=' as a separator, */ - /* all following go into the value part */ - if (keyAfter == NULL) { - keyAfter = walk; - if (walk + 1 <= afterLast) { - valueFirst = walk + 1; - valueAfter = walk + 1; - } - } - break; - - default: - break; - } - } - - if (valueFirst != NULL) { - /* Must be key/value pair */ - valueAfter = walk; - } else { - /* Must be key only */ - keyAfter = walk; - } - - if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended, keyFirst, keyAfter, - valueFirst, valueAfter, plusToSpace, breakConversion, memory) - == URI_FALSE) { - /* Free list we built */ - *itemsAppended = 0; - URI_FUNC(FreeQueryListMm)(*dest, memory); - return URI_ERROR_MALLOC; - } - - return URI_SUCCESS; +int URI_FUNC(DissectQueryMallocExMm)(URI_TYPE(QueryList) * *dest, int * itemCount, + const URI_CHAR * first, const URI_CHAR * afterLast, + UriBool plusToSpace, + UriBreakConversion breakConversion, + UriMemoryManager * memory) { + const URI_CHAR * walk = first; + const URI_CHAR * keyFirst = first; + const URI_CHAR * keyAfter = NULL; + const URI_CHAR * valueFirst = NULL; + const URI_CHAR * valueAfter = NULL; + URI_TYPE(QueryList) ** prevNext = dest; + int nullCounter; + int * itemsAppended = (itemCount == NULL) ? &nullCounter : itemCount; + + if ((dest == NULL) || (first == NULL) || (afterLast == NULL)) { + return URI_ERROR_NULL; + } + + if (first > afterLast) { + return URI_ERROR_RANGE_INVALID; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + *dest = NULL; + *itemsAppended = 0; + + /* Parse query string */ + for (; walk < afterLast; walk++) { + switch (*walk) { + case _UT('&'): + if (valueFirst != NULL) { + valueAfter = walk; + } else { + keyAfter = walk; + } + + if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended, keyFirst, keyAfter, + valueFirst, valueAfter, plusToSpace, + breakConversion, memory) + == URI_FALSE) { + /* Free list we built */ + *itemsAppended = 0; + URI_FUNC(FreeQueryListMm)(*dest, memory); + return URI_ERROR_MALLOC; + } + + /* Make future items children of the current */ + if ((prevNext != NULL) && (*prevNext != NULL)) { + prevNext = &((*prevNext)->next); + } + + if (walk + 1 < afterLast) { + keyFirst = walk + 1; + } else { + keyFirst = NULL; + } + keyAfter = NULL; + valueFirst = NULL; + valueAfter = NULL; + break; + + case _UT('='): + /* NOTE: WE treat the first '=' as a separator, */ + /* all following go into the value part */ + if (keyAfter == NULL) { + keyAfter = walk; + if (walk + 1 <= afterLast) { + valueFirst = walk + 1; + valueAfter = walk + 1; + } + } + break; + + default: + break; + } + } + + if (valueFirst != NULL) { + /* Must be key/value pair */ + valueAfter = walk; + } else { + /* Must be key only */ + keyAfter = walk; + } + + if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended, keyFirst, keyAfter, valueFirst, + valueAfter, plusToSpace, breakConversion, memory) + == URI_FALSE) { + /* Free list we built */ + *itemsAppended = 0; + URI_FUNC(FreeQueryListMm)(*dest, memory); + return URI_ERROR_MALLOC; + } + + return URI_SUCCESS; } - - #endif diff --git a/ext/uri/uriparser/src/UriRecompose.c b/ext/uri/uriparser/src/UriRecompose.c index 1567efc81dcbf..61ffee77248f1 100644 --- a/ext/uri/uriparser/src/UriRecompose.c +++ b/ext/uri/uriparser/src/UriRecompose.c @@ -41,537 +41,564 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriRecompose.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriRecompose.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriRecompose.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriRecompose.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -#endif - - +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# endif static int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(Uri) * uri, - int maxChars, int * charsWritten, int * charsRequired); - + int maxChars, int * charsWritten, + int * charsRequired); - -int URI_FUNC(ToStringCharsRequired)(const URI_TYPE(Uri) * uri, - int * charsRequired) { - const int MAX_CHARS = ((unsigned int)-1) >> 1; - return URI_FUNC(ToStringEngine)(NULL, uri, MAX_CHARS, NULL, charsRequired); +int URI_FUNC(ToStringCharsRequired)(const URI_TYPE(Uri) * uri, int * charsRequired) { + const int MAX_CHARS = ((unsigned int)-1) >> 1; + return URI_FUNC(ToStringEngine)(NULL, uri, MAX_CHARS, NULL, charsRequired); } - - -int URI_FUNC(ToString)(URI_CHAR * dest, const URI_TYPE(Uri) * uri, - int maxChars, int * charsWritten) { - return URI_FUNC(ToStringEngine)(dest, uri, maxChars, charsWritten, NULL); +int URI_FUNC(ToString)(URI_CHAR * dest, const URI_TYPE(Uri) * uri, int maxChars, + int * charsWritten) { + return URI_FUNC(ToStringEngine)(dest, uri, maxChars, charsWritten, NULL); } - - -static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, - const URI_TYPE(Uri) * uri, int maxChars, int * charsWritten, - int * charsRequired) { - int written = 0; - if ((uri == NULL) || ((dest == NULL) && (charsRequired == NULL))) { - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_NULL; - } - - if (maxChars < 1) { - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - maxChars--; /* So we don't have to subtract 1 for '\0' all the time */ - - /* [01/19] result = "" */ - if (dest != NULL) { - dest[0] = _UT('\0'); - } else { - (*charsRequired) = 0; - } - /* [02/19] if defined(scheme) then */ - if (uri->scheme.first != NULL) { - /* [03/19] append scheme to result; */ - const int charsToWrite - = (int)(uri->scheme.afterLast - uri->scheme.first); - if (dest != NULL) { - if (written + charsToWrite <= maxChars) { - memcpy(dest + written, uri->scheme.first, - charsToWrite * sizeof(URI_CHAR)); - written += charsToWrite; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += charsToWrite; - } - /* [04/19] append ":" to result; */ - if (dest != NULL) { - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT(":"), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 1; - } - /* [05/19] endif; */ - } - /* [06/19] if defined(authority) then */ - if (URI_FUNC(HasHost)(uri)) { - /* [07/19] append "//" to result; */ - if (dest != NULL) { - if (written + 2 <= maxChars) { - memcpy(dest + written, _UT("//"), - 2 * sizeof(URI_CHAR)); - written += 2; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 2; - } - /* [08/19] append authority to result; */ - /* UserInfo */ - if (uri->userInfo.first != NULL) { - const int charsToWrite = (int)(uri->userInfo.afterLast - uri->userInfo.first); - if (dest != NULL) { - if (written + charsToWrite <= maxChars) { - memcpy(dest + written, uri->userInfo.first, - charsToWrite * sizeof(URI_CHAR)); - written += charsToWrite; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT("@"), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += charsToWrite + 1; - } - } - - /* Host */ - if (uri->hostData.ip4 != NULL) { - /* IPv4 */ - int i = 0; - for (; i < 4; i++) { - const unsigned char value = uri->hostData.ip4->data[i]; - const int charsToWrite = (value > 99) ? 3 : ((value > 9) ? 2 : 1); - if (dest != NULL) { - if (written + charsToWrite <= maxChars) { - URI_CHAR text[4]; - if (value > 99) { - text[0] = _UT('0') + (value / 100); - text[1] = _UT('0') + ((value % 100) / 10); - text[2] = _UT('0') + (value % 10); - } else if (value > 9) { - text[0] = _UT('0') + (value / 10); - text[1] = _UT('0') + (value % 10); - } else { - text[0] = _UT('0') + value; - } - text[charsToWrite] = _UT('\0'); - memcpy(dest + written, text, charsToWrite * sizeof(URI_CHAR)); - written += charsToWrite; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - if (i < 3) { - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT("."), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } - } else { - (*charsRequired) += charsToWrite + ((i == 3) ? 0 : 1); - } - } - } else if (uri->hostData.ip6 != NULL) { - /* IPv6 */ - int i = 0; - if (dest != NULL) { - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT("["), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 1; - } - - for (; i < 16; i++) { - const unsigned char value = uri->hostData.ip6->data[i]; - if (dest != NULL) { - if (written + 2 <= maxChars) { - URI_CHAR text[3]; - text[0] = URI_FUNC(HexToLetterEx)(value / 16, URI_FALSE); - text[1] = URI_FUNC(HexToLetterEx)(value % 16, URI_FALSE); - text[2] = _UT('\0'); - memcpy(dest + written, text, 2 * sizeof(URI_CHAR)); - written += 2; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 2; - } - if (((i & 1) == 1) && (i < 15)) { - if (dest != NULL) { - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT(":"), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 1; - } - } - } - - if (dest != NULL) { - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT("]"), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 1; - } - } else if (uri->hostData.ipFuture.first != NULL) { - /* IPvFuture */ - const int charsToWrite = (int)(uri->hostData.ipFuture.afterLast - - uri->hostData.ipFuture.first); - if (dest != NULL) { - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT("["), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - - if (written + charsToWrite <= maxChars) { - memcpy(dest + written, uri->hostData.ipFuture.first, charsToWrite * sizeof(URI_CHAR)); - written += charsToWrite; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT("]"), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 1 + charsToWrite + 1; - } - } else if (uri->hostText.first != NULL) { - /* Regname */ - const int charsToWrite = (int)(uri->hostText.afterLast - uri->hostText.first); - if (dest != NULL) { - if (written + charsToWrite <= maxChars) { - memcpy(dest + written, uri->hostText.first, - charsToWrite * sizeof(URI_CHAR)); - written += charsToWrite; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += charsToWrite; - } - } - - /* Port */ - if (uri->portText.first != NULL) { - const int charsToWrite = (int)(uri->portText.afterLast - uri->portText.first); - if (dest != NULL) { - /* Leading ':' */ - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT(":"), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - - /* Port number */ - if (written + charsToWrite <= maxChars) { - memcpy(dest + written, uri->portText.first, - charsToWrite * sizeof(URI_CHAR)); - written += charsToWrite; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 1 + charsToWrite; - } - } - /* [09/19] endif; */ - } - /* [10/19] append path to result; */ - /* Slash needed here? */ - if (uri->absolutePath || ((uri->pathHead != NULL) - && URI_FUNC(HasHost)(uri))) { - if (dest != NULL) { - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT("/"), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 1; - } - } - - if (uri->pathHead != NULL) { - URI_TYPE(PathSegment) * walker = uri->pathHead; - do { - const int charsToWrite = (int)(walker->text.afterLast - walker->text.first); - if (dest != NULL) { - if (written + charsToWrite <= maxChars) { - memcpy(dest + written, walker->text.first, - charsToWrite * sizeof(URI_CHAR)); - written += charsToWrite; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += charsToWrite; - } - - /* Not last segment -> append slash */ - if (walker->next != NULL) { - if (dest != NULL) { - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT("/"), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 1; - } - } - - walker = walker->next; - } while (walker != NULL); - } - /* [11/19] if defined(query) then */ - if (uri->query.first != NULL) { - /* [12/19] append "?" to result; */ - if (dest != NULL) { - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT("?"), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 1; - } - /* [13/19] append query to result; */ - { - const int charsToWrite - = (int)(uri->query.afterLast - uri->query.first); - if (dest != NULL) { - if (written + charsToWrite <= maxChars) { - memcpy(dest + written, uri->query.first, - charsToWrite * sizeof(URI_CHAR)); - written += charsToWrite; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += charsToWrite; - } - } - /* [14/19] endif; */ - } - /* [15/19] if defined(fragment) then */ - if (uri->fragment.first != NULL) { - /* [16/19] append "#" to result; */ - if (dest != NULL) { - if (written + 1 <= maxChars) { - memcpy(dest + written, _UT("#"), - 1 * sizeof(URI_CHAR)); - written += 1; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += 1; - } - /* [17/19] append fragment to result; */ - { - const int charsToWrite - = (int)(uri->fragment.afterLast - uri->fragment.first); - if (dest != NULL) { - if (written + charsToWrite <= maxChars) { - memcpy(dest + written, uri->fragment.first, - charsToWrite * sizeof(URI_CHAR)); - written += charsToWrite; - } else { - dest[0] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = 0; - } - return URI_ERROR_TOSTRING_TOO_LONG; - } - } else { - (*charsRequired) += charsToWrite; - } - } - /* [18/19] endif; */ - } - /* [19/19] return result; */ - if (dest != NULL) { - dest[written++] = _UT('\0'); - if (charsWritten != NULL) { - *charsWritten = written; - } - } - return URI_SUCCESS; +static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(Uri) * uri, + int maxChars, int * charsWritten, + int * charsRequired) { + int written = 0; + if ((uri == NULL) || ((dest == NULL) && (charsRequired == NULL))) { + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_NULL; + } + + if (maxChars < 1) { + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + maxChars--; /* So we don't have to subtract 1 for '\0' all the time */ + + /* NOTE: The curly brackets here force deeper indent (and that's all) */ + { + { + { + /* clang-format off */ + /* [01/19] result = "" */ + /* clang-format on */ + if (dest != NULL) { + dest[0] = _UT('\0'); + } else { + (*charsRequired) = 0; + } + /* clang-format off */ + /* [02/19] if defined(scheme) then */ + /* clang-format on */ + if (uri->scheme.first != NULL) { + /* clang-format off */ + /* [03/19] append scheme to result; */ + /* clang-format on */ + const int charsToWrite = + (int)(uri->scheme.afterLast - uri->scheme.first); + if (dest != NULL) { + if (written + charsToWrite <= maxChars) { + memcpy(dest + written, uri->scheme.first, + charsToWrite * sizeof(URI_CHAR)); + written += charsToWrite; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += charsToWrite; + } + /* clang-format off */ + /* [04/19] append ":" to result; */ + /* clang-format on */ + if (dest != NULL) { + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT(":"), 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 1; + } + /* clang-format off */ + /* [05/19] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [06/19] if defined(authority) then */ + /* clang-format on */ + if (URI_FUNC(HasHost)(uri)) { + /* clang-format off */ + /* [07/19] append "//" to result; */ + /* clang-format on */ + if (dest != NULL) { + if (written + 2 <= maxChars) { + memcpy(dest + written, _UT("//"), 2 * sizeof(URI_CHAR)); + written += 2; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 2; + } + /* clang-format off */ + /* [08/19] append authority to result; */ + /* clang-format on */ + /* UserInfo */ + if (uri->userInfo.first != NULL) { + const int charsToWrite = + (int)(uri->userInfo.afterLast - uri->userInfo.first); + if (dest != NULL) { + if (written + charsToWrite <= maxChars) { + memcpy(dest + written, uri->userInfo.first, + charsToWrite * sizeof(URI_CHAR)); + written += charsToWrite; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT("@"), 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += charsToWrite + 1; + } + } + + /* Host */ + if (uri->hostData.ip4 != NULL) { + /* IPv4 */ + int i = 0; + for (; i < 4; i++) { + const unsigned char value = uri->hostData.ip4->data[i]; + const int charsToWrite = + (value > 99) ? 3 : ((value > 9) ? 2 : 1); + if (dest != NULL) { + if (written + charsToWrite <= maxChars) { + URI_CHAR text[4]; + if (value > 99) { + text[0] = _UT('0') + (value / 100); + text[1] = _UT('0') + ((value % 100) / 10); + text[2] = _UT('0') + (value % 10); + } else if (value > 9) { + text[0] = _UT('0') + (value / 10); + text[1] = _UT('0') + (value % 10); + } else { + text[0] = _UT('0') + value; + } + text[charsToWrite] = _UT('\0'); + memcpy(dest + written, text, + charsToWrite * sizeof(URI_CHAR)); + written += charsToWrite; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + if (i < 3) { + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT("."), + 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } + } else { + (*charsRequired) += charsToWrite + ((i == 3) ? 0 : 1); + } + } + } else if (uri->hostData.ip6 != NULL) { + /* IPv6 */ + int i = 0; + if (dest != NULL) { + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT("["), 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 1; + } + + for (; i < 16; i++) { + const unsigned char value = uri->hostData.ip6->data[i]; + if (dest != NULL) { + if (written + 2 <= maxChars) { + URI_CHAR text[3]; + text[0] = + URI_FUNC(HexToLetterEx)(value / 16, URI_FALSE); + text[1] = + URI_FUNC(HexToLetterEx)(value % 16, URI_FALSE); + text[2] = _UT('\0'); + memcpy(dest + written, text, 2 * sizeof(URI_CHAR)); + written += 2; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 2; + } + if (((i & 1) == 1) && (i < 15)) { + if (dest != NULL) { + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT(":"), + 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 1; + } + } + } + + if (dest != NULL) { + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT("]"), 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 1; + } + } else if (uri->hostData.ipFuture.first != NULL) { + /* IPvFuture */ + const int charsToWrite = (int)(uri->hostData.ipFuture.afterLast + - uri->hostData.ipFuture.first); + if (dest != NULL) { + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT("["), 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= maxChars) { + memcpy(dest + written, uri->hostData.ipFuture.first, + charsToWrite * sizeof(URI_CHAR)); + written += charsToWrite; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT("]"), 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 1 + charsToWrite + 1; + } + } else if (uri->hostText.first != NULL) { + /* Regname */ + const int charsToWrite = + (int)(uri->hostText.afterLast - uri->hostText.first); + if (dest != NULL) { + if (written + charsToWrite <= maxChars) { + memcpy(dest + written, uri->hostText.first, + charsToWrite * sizeof(URI_CHAR)); + written += charsToWrite; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += charsToWrite; + } + } + + /* Port */ + if (uri->portText.first != NULL) { + const int charsToWrite = + (int)(uri->portText.afterLast - uri->portText.first); + if (dest != NULL) { + /* Leading ':' */ + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT(":"), 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + + /* Port number */ + if (written + charsToWrite <= maxChars) { + memcpy(dest + written, uri->portText.first, + charsToWrite * sizeof(URI_CHAR)); + written += charsToWrite; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 1 + charsToWrite; + } + } + /* clang-format off */ + /* [09/19] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [10/19] append path to result; */ + /* clang-format on */ + /* Slash needed here? */ + if (uri->absolutePath + || ((uri->pathHead != NULL) && URI_FUNC(HasHost)(uri))) { + if (dest != NULL) { + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT("/"), 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 1; + } + } + + if (uri->pathHead != NULL) { + URI_TYPE(PathSegment) * walker = uri->pathHead; + do { + const int charsToWrite = + (int)(walker->text.afterLast - walker->text.first); + if (dest != NULL) { + if (written + charsToWrite <= maxChars) { + memcpy(dest + written, walker->text.first, + charsToWrite * sizeof(URI_CHAR)); + written += charsToWrite; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += charsToWrite; + } + + /* Not last segment -> append slash */ + if (walker->next != NULL) { + if (dest != NULL) { + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT("/"), + 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 1; + } + } + + walker = walker->next; + } while (walker != NULL); + } + /* clang-format off */ + /* [11/19] if defined(query) then */ + /* clang-format on */ + if (uri->query.first != NULL) { + /* clang-format off */ + /* [12/19] append "?" to result; */ + /* clang-format on */ + if (dest != NULL) { + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT("?"), 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 1; + } + /* clang-format off */ + /* [13/19] append query to result; */ + /* clang-format on */ + const int charsToWrite = + (int)(uri->query.afterLast - uri->query.first); + if (dest != NULL) { + if (written + charsToWrite <= maxChars) { + memcpy(dest + written, uri->query.first, + charsToWrite * sizeof(URI_CHAR)); + written += charsToWrite; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += charsToWrite; + } + /* clang-format off */ + /* [14/19] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [15/19] if defined(fragment) then */ + /* clang-format on */ + if (uri->fragment.first != NULL) { + /* clang-format off */ + /* [16/19] append "#" to result; */ + /* clang-format on */ + if (dest != NULL) { + if (written + 1 <= maxChars) { + memcpy(dest + written, _UT("#"), 1 * sizeof(URI_CHAR)); + written += 1; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += 1; + } + /* clang-format off */ + /* [17/19] append fragment to result; */ + /* clang-format on */ + const int charsToWrite = + (int)(uri->fragment.afterLast - uri->fragment.first); + if (dest != NULL) { + if (written + charsToWrite <= maxChars) { + memcpy(dest + written, uri->fragment.first, + charsToWrite * sizeof(URI_CHAR)); + written += charsToWrite; + } else { + dest[0] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = 0; + } + return URI_ERROR_TOSTRING_TOO_LONG; + } + } else { + (*charsRequired) += charsToWrite; + } + /* clang-format off */ + /* [18/19] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [19/19] return result; */ + /* clang-format on */ + if (dest != NULL) { + dest[written++] = _UT('\0'); + if (charsWritten != NULL) { + *charsWritten = written; + } + } + return URI_SUCCESS; + } + } + } } - - #endif diff --git a/ext/uri/uriparser/src/UriResolve.c b/ext/uri/uriparser/src/UriResolve.c index 8e47e6af8c6f9..302665d21cd66 100644 --- a/ext/uri/uriparser/src/UriResolve.c +++ b/ext/uri/uriparser/src/UriResolve.c @@ -41,289 +41,358 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriResolve.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriResolve.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriResolve.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriResolve.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -# include "UriMemory.h" -#endif - - +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +# endif /* Appends a relative URI to an absolute. The last path segment of * the absolute URI is replaced. */ static URI_INLINE UriBool URI_FUNC(MergePath)(URI_TYPE(Uri) * absWork, - const URI_TYPE(Uri) * relAppend, UriMemoryManager * memory) { - URI_TYPE(PathSegment) * sourceWalker; - URI_TYPE(PathSegment) * destPrev; - if (relAppend->pathHead == NULL) { - return URI_TRUE; - } - - /* Replace last segment ("" if trailing slash) with first of append chain */ - if (absWork->pathHead == NULL) { - URI_TYPE(PathSegment) * const dup = memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); - if (dup == NULL) { - return URI_FALSE; /* Raises malloc error */ - } - dup->next = NULL; - absWork->pathHead = dup; - absWork->pathTail = dup; - } - absWork->pathTail->text.first = relAppend->pathHead->text.first; - absWork->pathTail->text.afterLast = relAppend->pathHead->text.afterLast; - - /* Append all the others */ - sourceWalker = relAppend->pathHead->next; - if (sourceWalker == NULL) { - return URI_TRUE; - } - destPrev = absWork->pathTail; - - for (;;) { - URI_TYPE(PathSegment) * const dup = memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); - if (dup == NULL) { - destPrev->next = NULL; - absWork->pathTail = destPrev; - return URI_FALSE; /* Raises malloc error */ - } - dup->text = sourceWalker->text; - destPrev->next = dup; - - if (sourceWalker->next == NULL) { - absWork->pathTail = dup; - absWork->pathTail->next = NULL; - break; - } - destPrev = dup; - sourceWalker = sourceWalker->next; - } - - return URI_TRUE; + const URI_TYPE(Uri) * relAppend, + UriMemoryManager * memory) { + URI_TYPE(PathSegment) * sourceWalker; + URI_TYPE(PathSegment) * destPrev; + if (relAppend->pathHead == NULL) { + return URI_TRUE; + } + + /* Replace last segment ("" if trailing slash) with first of append chain */ + if (absWork->pathHead == NULL) { + URI_TYPE(PathSegment) * const dup = + memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); + if (dup == NULL) { + return URI_FALSE; /* Raises malloc error */ + } + dup->next = NULL; + absWork->pathHead = dup; + absWork->pathTail = dup; + } + absWork->pathTail->text.first = relAppend->pathHead->text.first; + absWork->pathTail->text.afterLast = relAppend->pathHead->text.afterLast; + + /* Append all the others */ + sourceWalker = relAppend->pathHead->next; + if (sourceWalker == NULL) { + return URI_TRUE; + } + destPrev = absWork->pathTail; + + for (;;) { + URI_TYPE(PathSegment) * const dup = + memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); + if (dup == NULL) { + destPrev->next = NULL; + absWork->pathTail = destPrev; + return URI_FALSE; /* Raises malloc error */ + } + dup->text = sourceWalker->text; + destPrev->next = dup; + + if (sourceWalker->next == NULL) { + absWork->pathTail = dup; + absWork->pathTail->next = NULL; + break; + } + destPrev = dup; + sourceWalker = sourceWalker->next; + } + + return URI_TRUE; } - static int URI_FUNC(ResolveAbsolutePathFlag)(URI_TYPE(Uri) * absWork, - UriMemoryManager * memory) { - if (absWork == NULL) { - return URI_ERROR_NULL; - } - - if (URI_FUNC(HasHost)(absWork) && absWork->absolutePath) { - /* Empty segment needed, instead? */ - if (absWork->pathHead == NULL) { - URI_TYPE(PathSegment) * const segment = memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); - if (segment == NULL) { - return URI_ERROR_MALLOC; - } - segment->text.first = URI_FUNC(SafeToPointTo); - segment->text.afterLast = URI_FUNC(SafeToPointTo); - segment->next = NULL; - - absWork->pathHead = segment; - absWork->pathTail = segment; - } - - absWork->absolutePath = URI_FALSE; - } - - return URI_SUCCESS; + UriMemoryManager * memory) { + if (absWork == NULL) { + return URI_ERROR_NULL; + } + + if (URI_FUNC(HasHost)(absWork) && absWork->absolutePath) { + /* Empty segment needed, instead? */ + if (absWork->pathHead == NULL) { + URI_TYPE(PathSegment) * const segment = + memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); + if (segment == NULL) { + return URI_ERROR_MALLOC; + } + segment->text.first = URI_FUNC(SafeToPointTo); + segment->text.afterLast = URI_FUNC(SafeToPointTo); + segment->next = NULL; + + absWork->pathHead = segment; + absWork->pathTail = segment; + } + + absWork->absolutePath = URI_FALSE; + } + + return URI_SUCCESS; } - static int URI_FUNC(AddBaseUriImpl)(URI_TYPE(Uri) * absDest, - const URI_TYPE(Uri) * relSource, - const URI_TYPE(Uri) * absBase, - UriResolutionOptions options, UriMemoryManager * memory) { - UriBool relSourceHasScheme; - - if (absDest == NULL) { - return URI_ERROR_NULL; - } - URI_FUNC(ResetUri)(absDest); - - if ((relSource == NULL) || (absBase == NULL)) { - return URI_ERROR_NULL; - } - - /* absBase absolute? */ - if (absBase->scheme.first == NULL) { - return URI_ERROR_ADDBASE_REL_BASE; - } - - /* [00/32] -- A non-strict parser may ignore a scheme in the reference */ - /* [00/32] -- if it is identical to the base URI's scheme. */ - /* [00/32] if ((not strict) and (R.scheme == Base.scheme)) then */ - relSourceHasScheme = (relSource->scheme.first != NULL) ? URI_TRUE : URI_FALSE; - if ((options & URI_RESOLVE_IDENTICAL_SCHEME_COMPAT) - && (absBase->scheme.first != NULL) - && (relSource->scheme.first != NULL) - && (0 == URI_FUNC(CompareRange)(&(absBase->scheme), &(relSource->scheme)))) { - /* [00/32] undefine(R.scheme); */ - relSourceHasScheme = URI_FALSE; - /* [00/32] endif; */ - } - - /* [01/32] if defined(R.scheme) then */ - if (relSourceHasScheme) { - /* [02/32] T.scheme = R.scheme; */ - absDest->scheme = relSource->scheme; - /* [03/32] T.authority = R.authority; */ - if (!URI_FUNC(CopyAuthority)(absDest, relSource, memory)) { - return URI_ERROR_MALLOC; - } - /* [04/32] T.path = remove_dot_segments(R.path); */ - if (!URI_FUNC(CopyPath)(absDest, relSource, memory)) { - return URI_ERROR_MALLOC; - } - if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest, memory)) { - return URI_ERROR_MALLOC; - } - /* [05/32] T.query = R.query; */ - absDest->query = relSource->query; - /* [06/32] else */ - } else { - /* [07/32] if defined(R.authority) then */ - if (URI_FUNC(HasHost)(relSource)) { - /* [08/32] T.authority = R.authority; */ - if (!URI_FUNC(CopyAuthority)(absDest, relSource, memory)) { - return URI_ERROR_MALLOC; - } - /* [09/32] T.path = remove_dot_segments(R.path); */ - if (!URI_FUNC(CopyPath)(absDest, relSource, memory)) { - return URI_ERROR_MALLOC; - } - if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest, memory)) { - return URI_ERROR_MALLOC; - } - /* [10/32] T.query = R.query; */ - absDest->query = relSource->query; - /* [11/32] else */ - } else { - /* [28/32] T.authority = Base.authority; */ - if (!URI_FUNC(CopyAuthority)(absDest, absBase, memory)) { - return URI_ERROR_MALLOC; - } - /* [12/32] if (R.path == "") then */ - if (relSource->pathHead == NULL && !relSource->absolutePath) { - /* [13/32] T.path = Base.path; */ - if (!URI_FUNC(CopyPath)(absDest, absBase, memory)) { - return URI_ERROR_MALLOC; - } - /* [14/32] if defined(R.query) then */ - if (relSource->query.first != NULL) { - /* [15/32] T.query = R.query; */ - absDest->query = relSource->query; - /* [16/32] else */ - } else { - /* [17/32] T.query = Base.query; */ - absDest->query = absBase->query; - /* [18/32] endif; */ - } - /* [19/32] else */ - } else { - /* [20/32] if (R.path starts-with "/") then */ - if (relSource->absolutePath) { - int res; - /* [21/32] T.path = remove_dot_segments(R.path); */ - if (!URI_FUNC(CopyPath)(absDest, relSource, memory)) { - return URI_ERROR_MALLOC; - } - res = URI_FUNC(ResolveAbsolutePathFlag)(absDest, memory); - if (res != URI_SUCCESS) { - return res; - } - if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest, memory)) { - return URI_ERROR_MALLOC; - } - /* [22/32] else */ - } else { - /* [23/32] T.path = merge(Base.path, R.path); */ - if (!URI_FUNC(CopyPath)(absDest, absBase, memory)) { - return URI_ERROR_MALLOC; - } - if (!URI_FUNC(MergePath)(absDest, relSource, memory)) { - return URI_ERROR_MALLOC; - } - /* [24/32] T.path = remove_dot_segments(T.path); */ - if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest, memory)) { - return URI_ERROR_MALLOC; - } - - if (!URI_FUNC(FixAmbiguity)(absDest, memory)) { - return URI_ERROR_MALLOC; - } - /* [25/32] endif; */ - } - /* [26/32] T.query = R.query; */ - absDest->query = relSource->query; - /* [27/32] endif; */ - } - URI_FUNC(FixEmptyTrailSegment)(absDest, memory); - /* [29/32] endif; */ - } - /* [30/32] T.scheme = Base.scheme; */ - absDest->scheme = absBase->scheme; - /* [31/32] endif; */ - } - /* [32/32] T.fragment = R.fragment; */ - absDest->fragment = relSource->fragment; - - return URI_SUCCESS; - + const URI_TYPE(Uri) * relSource, + const URI_TYPE(Uri) * absBase, + UriResolutionOptions options, + UriMemoryManager * memory) { + UriBool relSourceHasScheme; + + if (absDest == NULL) { + return URI_ERROR_NULL; + } + URI_FUNC(ResetUri)(absDest); + + if ((relSource == NULL) || (absBase == NULL)) { + return URI_ERROR_NULL; + } + + /* absBase absolute? */ + if (absBase->scheme.first == NULL) { + return URI_ERROR_ADDBASE_REL_BASE; + } + + /* NOTE: The curly brackets here force deeper indent (and that's all) */ + { + { + { + /* clang-format off */ + /* [00/32] -- A non-strict parser may ignore a scheme in the reference */ + /* [00/32] -- if it is identical to the base URI's scheme. */ + /* [00/32] if ((not strict) and (R.scheme == Base.scheme)) then */ + /* clang-format on */ + relSourceHasScheme = + (relSource->scheme.first != NULL) ? URI_TRUE : URI_FALSE; + if ((options & URI_RESOLVE_IDENTICAL_SCHEME_COMPAT) + && (absBase->scheme.first != NULL) + && (relSource->scheme.first != NULL) + && (0 + == URI_FUNC(CompareRange)(&(absBase->scheme), + &(relSource->scheme)))) { + /* clang-format off */ + /* [00/32] undefine(R.scheme); */ + /* clang-format on */ + relSourceHasScheme = URI_FALSE; + /* clang-format off */ + /* [00/32] endif; */ + /* clang-format on */ + } + + /* clang-format off */ + /* [01/32] if defined(R.scheme) then */ + /* clang-format on */ + if (relSourceHasScheme) { + /* clang-format off */ + /* [02/32] T.scheme = R.scheme; */ + /* clang-format on */ + absDest->scheme = relSource->scheme; + /* clang-format off */ + /* [03/32] T.authority = R.authority; */ + /* clang-format on */ + if (!URI_FUNC(CopyAuthority)(absDest, relSource, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [04/32] T.path = remove_dot_segments(R.path); */ + /* clang-format on */ + if (!URI_FUNC(CopyPath)(absDest, relSource, memory)) { + return URI_ERROR_MALLOC; + } + if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [05/32] T.query = R.query; */ + /* clang-format on */ + absDest->query = relSource->query; + /* clang-format off */ + /* [06/32] else */ + /* clang-format on */ + } else { + /* clang-format off */ + /* [07/32] if defined(R.authority) then */ + /* clang-format on */ + if (URI_FUNC(HasHost)(relSource)) { + /* clang-format off */ + /* [08/32] T.authority = R.authority; */ + /* clang-format on */ + if (!URI_FUNC(CopyAuthority)(absDest, relSource, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [09/32] T.path = remove_dot_segments(R.path); */ + /* clang-format on */ + if (!URI_FUNC(CopyPath)(absDest, relSource, memory)) { + return URI_ERROR_MALLOC; + } + if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [10/32] T.query = R.query; */ + /* clang-format on */ + absDest->query = relSource->query; + /* clang-format off */ + /* [11/32] else */ + /* clang-format on */ + } else { + /* clang-format off */ + /* [28/32] T.authority = Base.authority; */ + /* clang-format on */ + if (!URI_FUNC(CopyAuthority)(absDest, absBase, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [12/32] if (R.path == "") then */ + /* clang-format on */ + if (relSource->pathHead == NULL && !relSource->absolutePath) { + /* clang-format off */ + /* [13/32] T.path = Base.path; */ + /* clang-format on */ + if (!URI_FUNC(CopyPath)(absDest, absBase, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [14/32] if defined(R.query) then */ + /* clang-format on */ + if (relSource->query.first != NULL) { + /* clang-format off */ + /* [15/32] T.query = R.query; */ + /* clang-format on */ + absDest->query = relSource->query; + /* clang-format off */ + /* [16/32] else */ + /* clang-format on */ + } else { + /* clang-format off */ + /* [17/32] T.query = Base.query; */ + /* clang-format on */ + absDest->query = absBase->query; + /* clang-format off */ + /* [18/32] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [19/32] else */ + /* clang-format on */ + } else { + /* clang-format off */ + /* [20/32] if (R.path starts-with "/") then */ + /* clang-format on */ + if (relSource->absolutePath) { + int res; + /* clang-format off */ + /* [21/32] T.path = remove_dot_segments(R.path); */ + /* clang-format on */ + if (!URI_FUNC(CopyPath)(absDest, relSource, memory)) { + return URI_ERROR_MALLOC; + } + res = URI_FUNC(ResolveAbsolutePathFlag)(absDest, memory); + if (res != URI_SUCCESS) { + return res; + } + if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest, + memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [22/32] else */ + /* clang-format on */ + } else { + /* clang-format off */ + /* [23/32] T.path = merge(Base.path, R.path); */ + /* clang-format on */ + if (!URI_FUNC(CopyPath)(absDest, absBase, memory)) { + return URI_ERROR_MALLOC; + } + if (!URI_FUNC(MergePath)(absDest, relSource, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [24/32] T.path = remove_dot_segments(T.path); */ + /* clang-format on */ + if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest, + memory)) { + return URI_ERROR_MALLOC; + } + + if (!URI_FUNC(FixAmbiguity)(absDest, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [25/32] endif; */ + } + /* clang-format off */ + /* [26/32] T.query = R.query; */ + /* clang-format on */ + absDest->query = relSource->query; + /* clang-format off */ + /* [27/32] endif; */ + /* clang-format on */ + } + URI_FUNC(FixEmptyTrailSegment)(absDest, memory); + /* clang-format off */ + /* [29/32] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [30/32] T.scheme = Base.scheme; */ + /* clang-format on */ + absDest->scheme = absBase->scheme; + /* clang-format off */ + /* [31/32] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [32/32] T.fragment = R.fragment; */ + /* clang-format on */ + absDest->fragment = relSource->fragment; + } + } + } + return URI_SUCCESS; } - - -int URI_FUNC(AddBaseUri)(URI_TYPE(Uri) * absDest, - const URI_TYPE(Uri) * relSource, const URI_TYPE(Uri) * absBase) { - const UriResolutionOptions options = URI_RESOLVE_STRICTLY; - return URI_FUNC(AddBaseUriEx)(absDest, relSource, absBase, options); +int URI_FUNC(AddBaseUri)(URI_TYPE(Uri) * absDest, const URI_TYPE(Uri) * relSource, + const URI_TYPE(Uri) * absBase) { + const UriResolutionOptions options = URI_RESOLVE_STRICTLY; + return URI_FUNC(AddBaseUriEx)(absDest, relSource, absBase, options); } - - -int URI_FUNC(AddBaseUriEx)(URI_TYPE(Uri) * absDest, - const URI_TYPE(Uri) * relSource, const URI_TYPE(Uri) * absBase, - UriResolutionOptions options) { - return URI_FUNC(AddBaseUriExMm)(absDest, relSource, absBase, options, NULL); +int URI_FUNC(AddBaseUriEx)(URI_TYPE(Uri) * absDest, const URI_TYPE(Uri) * relSource, + const URI_TYPE(Uri) * absBase, UriResolutionOptions options) { + return URI_FUNC(AddBaseUriExMm)(absDest, relSource, absBase, options, NULL); } +int URI_FUNC(AddBaseUriExMm)(URI_TYPE(Uri) * absDest, const URI_TYPE(Uri) * relSource, + const URI_TYPE(Uri) * absBase, UriResolutionOptions options, + UriMemoryManager * memory) { + int res; + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ -int URI_FUNC(AddBaseUriExMm)(URI_TYPE(Uri) * absDest, - const URI_TYPE(Uri) * relSource, const URI_TYPE(Uri) * absBase, - UriResolutionOptions options, UriMemoryManager * memory) { - int res; - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - res = URI_FUNC(AddBaseUriImpl)(absDest, relSource, absBase, options, memory); - if ((res != URI_SUCCESS) && (absDest != NULL)) { - URI_FUNC(FreeUriMembersMm)(absDest, memory); - } - return res; + res = URI_FUNC(AddBaseUriImpl)(absDest, relSource, absBase, options, memory); + if ((res != URI_SUCCESS) && (absDest != NULL)) { + URI_FUNC(FreeUriMembersMm)(absDest, memory); + } + return res; } - - #endif diff --git a/ext/uri/uriparser/src/UriSetFragment.c b/ext/uri/uriparser/src/UriSetFragment.c index f2327c1afa4d0..b9c5c53b04202 100644 --- a/ext/uri/uriparser/src/UriSetFragment.c +++ b/ext/uri/uriparser/src/UriSetFragment.c @@ -40,267 +40,234 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetFragment.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetFragment.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetFragment.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetFragment.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -# include "UriMemory.h" -#endif - - - -#include - - - -#define URI_SET_DIGIT \ - _UT('0'): \ - case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - - - -#define URI_SET_HEX_LETTER_UPPER \ - _UT('A'): \ - case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - - - -#define URI_SET_HEX_LETTER_LOWER \ - _UT('a'): \ - case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - - - -#define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - - - -#define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - - - -#define URI_SET_SUB_DELIMS \ - _UT('!'): \ - case _UT('$'): \ - case _UT('&'): \ - case _UT('\''): \ - case _UT('('): \ - case _UT(')'): \ - case _UT('*'): \ - case _UT('+'): \ - case _UT(','): \ - case _UT(';'): \ - case _UT('=') - - - -#define URI_SET_UNRESERVED \ - URI_SET_ALPHA: \ - case URI_SET_DIGIT: \ - case _UT('-'): \ - case _UT('.'): \ - case _UT('_'): \ - case _UT('~') - - - -UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, const URI_CHAR * afterLast) { - if ((first == NULL) || (afterLast == NULL)) { - return URI_FALSE; - } - - /* The related part of the grammar in RFC 3986 reads: - * - * fragment = *( pchar / "/" / "?" ) - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - */ - while (first < afterLast) { - switch (first[0]) { - case URI_SET_UNRESERVED: - break; - - /* pct-encoded */ - case _UT('%'): - if (afterLast - first < 3) { - return URI_FALSE; - } - switch (first[1]) { - case URI_SET_HEXDIG: - break; - default: - return URI_FALSE; - } - switch (first[2]) { - case URI_SET_HEXDIG: - break; - default: - return URI_FALSE; - } - first += 2; - break; - - case URI_SET_SUB_DELIMS: - break; - - /* ":" / "@" and "/" / "?" */ - case _UT(':'): - case _UT('@'): - case _UT('/'): - case _UT('?'): - break; - - default: - return URI_FALSE; - } - - first++; - } - return URI_TRUE; +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +# endif + +# include + +# define URI_SET_DIGIT \ + _UT('0') : case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + +# define URI_SET_HEX_LETTER_UPPER \ + _UT('A') : case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + +# define URI_SET_HEX_LETTER_LOWER \ + _UT('a') : case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + +# define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + +# define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + +# define URI_SET_SUB_DELIMS \ + _UT('!') : case _UT('$'): \ + case _UT('&'): \ + case _UT('\''): \ + case _UT('('): \ + case _UT(')'): \ + case _UT('*'): \ + case _UT('+'): \ + case _UT(','): \ + case _UT(';'): \ + case _UT('=') + +# define URI_SET_UNRESERVED \ + URI_SET_ALPHA: \ + case URI_SET_DIGIT: \ + case _UT('-'): \ + case _UT('.'): \ + case _UT('_'): \ + case _UT('~') + +UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, + const URI_CHAR * afterLast) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* The related part of the grammar in RFC 3986 reads: + * + * fragment = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_UNRESERVED: + break; + + /* pct-encoded */ + case _UT('%'): + if (afterLast - first < 3) { + return URI_FALSE; + } + switch (first[1]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + switch (first[2]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + first += 2; + break; + + case URI_SET_SUB_DELIMS: + break; + + /* ":" / "@" and "/" / "?" */ + case _UT(':'): + case _UT('@'): + case _UT('/'): + case _UT('?'): + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; } - - -int URI_FUNC(SetFragmentMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - /* Input validation (before making any changes) */ - if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - if ((first != NULL) && (URI_FUNC(IsWellFormedFragment)(first, afterLast) == URI_FALSE)) { - return URI_ERROR_SYNTAX; - } - - /* Clear old value */ - if ((uri->owner == URI_TRUE) && (uri->fragment.first != uri->fragment.afterLast)) { - memory->free(memory, (URI_CHAR *)uri->fragment.first); - } - uri->fragment.first = NULL; - uri->fragment.afterLast = NULL; - - /* Already done? */ - if (first == NULL) { - return URI_SUCCESS; - } - - assert(first != NULL); - - /* Ensure owned */ - if (uri->owner == URI_FALSE) { - const int res = URI_FUNC(MakeOwnerMm)(uri, memory); - if (res != URI_SUCCESS) { - return res; - } - } - - assert(uri->owner == URI_TRUE); - - /* Apply new value */ - { - URI_TYPE(TextRange) sourceRange; - sourceRange.first = first; - sourceRange.afterLast = afterLast; - - if (URI_FUNC(CopyRangeAsNeeded)(&uri->fragment, &sourceRange, memory) == URI_FALSE) { - return URI_ERROR_MALLOC; - } - } - - return URI_SUCCESS; +int URI_FUNC(SetFragmentMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if ((first != NULL) + && (URI_FUNC(IsWellFormedFragment)(first, afterLast) == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + if ((uri->owner == URI_TRUE) && (uri->fragment.first != uri->fragment.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->fragment.first); + } + uri->fragment.first = NULL; + uri->fragment.afterLast = NULL; + + /* Already done? */ + if (first == NULL) { + return URI_SUCCESS; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->fragment, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + + return URI_SUCCESS; } - - -int URI_FUNC(SetFragment)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(SetFragmentMm)(uri, first, afterLast, NULL); +int URI_FUNC(SetFragment)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetFragmentMm)(uri, first, afterLast, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriSetHostAuto.c b/ext/uri/uriparser/src/UriSetHostAuto.c index 66ade6a5365bb..df015880b33f6 100644 --- a/ext/uri/uriparser/src/UriSetHostAuto.c +++ b/ext/uri/uriparser/src/UriSetHostAuto.c @@ -40,100 +40,85 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetHostAuto.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetHostAuto.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostAuto.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostAuto.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriSetHostBase.h" -# include "UriSetHostCommon.h" -# include "UriMemory.h" -#endif - - - -#include - - - -int URI_FUNC(SetHostAutoMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - if ((first == NULL) || (first >= afterLast)) { - return URI_FUNC(SetHostRegNameMm)(uri, first, afterLast, memory); - } - - /* Auto-detect type and then apply */ - { - UriHostType hostType; - - /* IPv6 or IPvFuture? */ - if (first[0] == _UT('[')) { - if ((afterLast - first < 2) || (afterLast[-1] != _UT(']'))) { - return URI_ERROR_SYNTAX; - } - - /* Drop the bracket wrap (for InternalSetHostMm call below) */ - first++; - afterLast--; - - if (first >= afterLast) { - return URI_ERROR_SYNTAX; - } - - switch (first[0]) { - case _UT('v'): - case _UT('V'): - hostType = URI_HOST_TYPE_IPFUTURE; - break; - default: - hostType = URI_HOST_TYPE_IP6; - break; - } - /* IPv4? */ - } else if (URI_FUNC(IsWellFormedHostIp4)(first, afterLast)) { - hostType = URI_HOST_TYPE_IP4; - } else { - /* RegName! */ - hostType = URI_HOST_TYPE_REGNAME; - } - - return URI_FUNC(InternalSetHostMm)(uri, hostType, first, afterLast, memory); - } +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +# include "UriMemory.h" +# endif + +# include + +int URI_FUNC(SetHostAutoMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if ((first == NULL) || (first >= afterLast)) { + return URI_FUNC(SetHostRegNameMm)(uri, first, afterLast, memory); + } + + /* Auto-detect type and then apply */ + UriHostType hostType; + + /* IPv6 or IPvFuture? */ + if (first[0] == _UT('[')) { + if ((afterLast - first < 2) || (afterLast[-1] != _UT(']'))) { + return URI_ERROR_SYNTAX; + } + + /* Drop the bracket wrap (for InternalSetHostMm call below) */ + first++; + afterLast--; + + if (first >= afterLast) { + return URI_ERROR_SYNTAX; + } + + switch (first[0]) { + case _UT('v'): + case _UT('V'): + hostType = URI_HOST_TYPE_IPFUTURE; + break; + default: + hostType = URI_HOST_TYPE_IP6; + break; + } + /* IPv4? */ + } else if (URI_FUNC(IsWellFormedHostIp4)(first, afterLast)) { + hostType = URI_HOST_TYPE_IP4; + } else { + /* RegName! */ + hostType = URI_HOST_TYPE_REGNAME; + } + + return URI_FUNC(InternalSetHostMm)(uri, hostType, first, afterLast, memory); } - - -int URI_FUNC(SetHostAuto)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(SetHostAutoMm)(uri, first, afterLast, NULL); +int URI_FUNC(SetHostAuto)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetHostAutoMm)(uri, first, afterLast, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriSetHostBase.h b/ext/uri/uriparser/src/UriSetHostBase.h index 79b643cc0c4b0..081db36f219de 100644 --- a/ext/uri/uriparser/src/UriSetHostBase.h +++ b/ext/uri/uriparser/src/UriSetHostBase.h @@ -37,9 +37,7 @@ */ #ifndef URI_SET_HOST_BASE_H -#define URI_SET_HOST_BASE_H 1 - - +# define URI_SET_HOST_BASE_H 1 typedef enum UriHostTypeEnum { URI_HOST_TYPE_IP4, @@ -48,6 +46,4 @@ typedef enum UriHostTypeEnum { URI_HOST_TYPE_REGNAME } UriHostType; - - #endif /* URI_SET_HOST_BASE_H */ diff --git a/ext/uri/uriparser/src/UriSetHostCommon.c b/ext/uri/uriparser/src/UriSetHostCommon.c index 343db849dfff2..bd8095aea3cfa 100644 --- a/ext/uri/uriparser/src/UriSetHostCommon.c +++ b/ext/uri/uriparser/src/UriSetHostCommon.c @@ -46,221 +46,203 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetHostCommon.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetHostCommon.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostCommon.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostCommon.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include -# include "UriCommon.h" -# include "UriMemory.h" -# include "UriSetHostBase.h" -# include "UriSetHostCommon.h" -#endif - - - -#include - - - -int URI_FUNC(InternalSetHostMm)(URI_TYPE(Uri) * uri, - UriHostType hostType, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - /* Superficial input validation (before making any changes) */ - if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - /* The RFC 3986 grammar reads: - * authority = [ userinfo "@" ] host [ ":" port ] - * So no user info or port without a host. */ - if (first == NULL) { - if (uri->userInfo.first != NULL) { - return URI_ERROR_SETHOST_USERINFO_SET; - } else if (uri->portText.first != NULL) { - return URI_ERROR_SETHOST_PORT_SET; - } - } - - /* Syntax-check the new value */ - if (first != NULL) { - switch (hostType) { - case URI_HOST_TYPE_IP4: - if (URI_FUNC(IsWellFormedHostIp4)(first, afterLast) == URI_FALSE) { - return URI_ERROR_SYNTAX; - } - break; - case URI_HOST_TYPE_IP6: - { - const int res = URI_FUNC(IsWellFormedHostIp6Mm)(first, afterLast, memory); - assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); - if (res != URI_SUCCESS) { - return res; - } - } - break; - case URI_HOST_TYPE_IPFUTURE: - { - const int res = URI_FUNC(IsWellFormedHostIpFutureMm)(first, afterLast, memory); - assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); - if (res != URI_SUCCESS) { - return res; - } - } - break; - case URI_HOST_TYPE_REGNAME: - if (URI_FUNC(IsWellFormedHostRegName)(first, afterLast) == URI_FALSE) { - return URI_ERROR_SYNTAX; - } - break; - default: - assert(0 && "Unsupported URI host type"); - } - } - - { - /* Clear old value */ - const UriBool hadHostBefore = URI_FUNC(HasHost)(uri); - if (uri->hostData.ipFuture.first != NULL) { - /* NOTE: .hostData.ipFuture holds the very same range pointers - * as .hostText; we must not free memory twice. */ - uri->hostText.first = NULL; - uri->hostText.afterLast = NULL; - - if ((uri->owner == URI_TRUE) && (uri->hostData.ipFuture.first != uri->hostData.ipFuture.afterLast)) { - memory->free(memory, (URI_CHAR *)uri->hostData.ipFuture.first); - } - uri->hostData.ipFuture.first = NULL; - uri->hostData.ipFuture.afterLast = NULL; - } else if (uri->hostText.first != NULL) { - if ((uri->owner == URI_TRUE) && (uri->hostText.first != uri->hostText.afterLast)) { - memory->free(memory, (URI_CHAR *)uri->hostText.first); - } - uri->hostText.first = NULL; - uri->hostText.afterLast = NULL; - } - - if (uri->hostData.ip4 != NULL) { - memory->free(memory, uri->hostData.ip4); - uri->hostData.ip4 = NULL; - } else if (uri->hostData.ip6 != NULL) { - memory->free(memory, uri->hostData.ip6); - uri->hostData.ip6 = NULL; - } - - /* Already done setting? */ - if (first == NULL) { - /* Yes, but disambiguate as needed */ - if (hadHostBefore == URI_TRUE) { - uri->absolutePath = URI_TRUE; - - { - const UriBool success = URI_FUNC(EnsureThatPathIsNotMistakenForHost)(uri, memory); - return (success == URI_TRUE) - ? URI_SUCCESS - : URI_ERROR_MALLOC; - } - } - - return URI_SUCCESS; - } - } - - assert(first != NULL); - - /* Ensure owned */ - if (uri->owner == URI_FALSE) { - const int res = URI_FUNC(MakeOwnerMm)(uri, memory); - if (res != URI_SUCCESS) { - return res; - } - } - - assert(uri->owner == URI_TRUE); - - /* Apply new value; NOTE that .hostText is set for all four host types */ - { - URI_TYPE(TextRange) sourceRange; - sourceRange.first = first; - sourceRange.afterLast = afterLast; - - if (URI_FUNC(CopyRangeAsNeeded)(&uri->hostText, &sourceRange, memory) == URI_FALSE) { - return URI_ERROR_MALLOC; - } - - uri->absolutePath = URI_FALSE; /* always URI_FALSE for URIs with host */ - - /* Fill .hostData as needed */ - switch (hostType) { - case URI_HOST_TYPE_IP4: - { - uri->hostData.ip4 = memory->malloc(memory, sizeof(UriIp4)); - if (uri->hostData.ip4 == NULL) { - return URI_ERROR_MALLOC; - } - - { - const int res = URI_FUNC(ParseIpFourAddress)(uri->hostData.ip4->data, first, afterLast); -#if defined(NDEBUG) - (void)res; /* i.e. mark as unused */ -#else - assert(res == URI_SUCCESS); /* because checked for well-formedness earlier */ -#endif - } - } - break; - case URI_HOST_TYPE_IP6: - { - uri->hostData.ip6 = memory->malloc(memory, sizeof(UriIp6)); - if (uri->hostData.ip6 == NULL) { - return URI_ERROR_MALLOC; - } - - { - const int res = URI_FUNC(ParseIpSixAddressMm)(uri->hostData.ip6, first, afterLast, memory); - assert((res == URI_SUCCESS) || (res == URI_ERROR_MALLOC)); /* because checked for well-formedness earlier */ - if (res != URI_SUCCESS) { - return res; - } - } - } - break; - case URI_HOST_TYPE_IPFUTURE: - uri->hostData.ipFuture.first = uri->hostText.first; - uri->hostData.ipFuture.afterLast = uri->hostText.afterLast; - break; - case URI_HOST_TYPE_REGNAME: - break; - default: - assert(0 && "Unsupported URI host type"); - } - } - - return URI_SUCCESS; +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include +# include "UriCommon.h" +# include "UriMemory.h" +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +# endif + +# include + +int URI_FUNC(InternalSetHostMm)(URI_TYPE(Uri) * uri, UriHostType hostType, + const URI_CHAR * first, const URI_CHAR * afterLast, + UriMemoryManager * memory) { + /* Superficial input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + /* The RFC 3986 grammar reads: + * authority = [ userinfo "@" ] host [ ":" port ] + * So no user info or port without a host. */ + if (first == NULL) { + if (uri->userInfo.first != NULL) { + return URI_ERROR_SETHOST_USERINFO_SET; + } else if (uri->portText.first != NULL) { + return URI_ERROR_SETHOST_PORT_SET; + } + } + + /* Syntax-check the new value */ + if (first != NULL) { + switch (hostType) { + case URI_HOST_TYPE_IP4: + if (URI_FUNC(IsWellFormedHostIp4)(first, afterLast) == URI_FALSE) { + return URI_ERROR_SYNTAX; + } + break; + case URI_HOST_TYPE_IP6: { + const int res = URI_FUNC(IsWellFormedHostIp6Mm)(first, afterLast, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) + || (res == URI_ERROR_MALLOC)); + if (res != URI_SUCCESS) { + return res; + } + } break; + case URI_HOST_TYPE_IPFUTURE: { + const int res = + URI_FUNC(IsWellFormedHostIpFutureMm)(first, afterLast, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) + || (res == URI_ERROR_MALLOC)); + if (res != URI_SUCCESS) { + return res; + } + } break; + case URI_HOST_TYPE_REGNAME: + if (URI_FUNC(IsWellFormedHostRegName)(first, afterLast) == URI_FALSE) { + return URI_ERROR_SYNTAX; + } + break; + default: + assert(0 && "Unsupported URI host type"); + } + } + + /* Clear old value */ + const UriBool hadHostBefore = URI_FUNC(HasHost)(uri); + if (uri->hostData.ipFuture.first != NULL) { + /* NOTE: .hostData.ipFuture holds the very same range pointers + * as .hostText; we must not free memory twice. */ + uri->hostText.first = NULL; + uri->hostText.afterLast = NULL; + + if ((uri->owner == URI_TRUE) + && (uri->hostData.ipFuture.first != uri->hostData.ipFuture.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->hostData.ipFuture.first); + } + uri->hostData.ipFuture.first = NULL; + uri->hostData.ipFuture.afterLast = NULL; + } else if (uri->hostText.first != NULL) { + if ((uri->owner == URI_TRUE) + && (uri->hostText.first != uri->hostText.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->hostText.first); + } + uri->hostText.first = NULL; + uri->hostText.afterLast = NULL; + } + + if (uri->hostData.ip4 != NULL) { + memory->free(memory, uri->hostData.ip4); + uri->hostData.ip4 = NULL; + } else if (uri->hostData.ip6 != NULL) { + memory->free(memory, uri->hostData.ip6); + uri->hostData.ip6 = NULL; + } + + /* Already done setting? */ + if (first == NULL) { + /* Yes, but disambiguate as needed */ + if (hadHostBefore == URI_TRUE) { + if (uri->pathHead != NULL) { + uri->absolutePath = URI_TRUE; + } + + const UriBool success = + URI_FUNC(EnsureThatPathIsNotMistakenForHost)(uri, memory); + return (success == URI_TRUE) ? URI_SUCCESS : URI_ERROR_MALLOC; + } + + return URI_SUCCESS; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value; NOTE that .hostText is set for all four host types */ + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->hostText, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + + uri->absolutePath = URI_FALSE; /* always URI_FALSE for URIs with host */ + + /* Fill .hostData as needed */ + switch (hostType) { + case URI_HOST_TYPE_IP4: { + uri->hostData.ip4 = memory->malloc(memory, sizeof(UriIp4)); + if (uri->hostData.ip4 == NULL) { + return URI_ERROR_MALLOC; + } + + const int res = + URI_FUNC(ParseIpFourAddress)(uri->hostData.ip4->data, first, afterLast); +# if defined(NDEBUG) + (void)res; /* i.e. mark as unused */ +# else + assert(res == URI_SUCCESS); /* because checked for well-formedness earlier */ +# endif + } break; + case URI_HOST_TYPE_IP6: { + uri->hostData.ip6 = memory->malloc(memory, sizeof(UriIp6)); + if (uri->hostData.ip6 == NULL) { + return URI_ERROR_MALLOC; + } + + const int res = + URI_FUNC(ParseIpSixAddressMm)(uri->hostData.ip6, first, afterLast, memory); + assert((res == URI_SUCCESS) + || (res == URI_ERROR_MALLOC)); /* because checked for + well-formedness earlier */ + if (res != URI_SUCCESS) { + return res; + } + } break; + case URI_HOST_TYPE_IPFUTURE: + uri->hostData.ipFuture.first = uri->hostText.first; + uri->hostData.ipFuture.afterLast = uri->hostText.afterLast; + break; + case URI_HOST_TYPE_REGNAME: + break; + default: + assert(0 && "Unsupported URI host type"); + } + + return URI_SUCCESS; } - - #endif diff --git a/ext/uri/uriparser/src/UriSetHostCommon.h b/ext/uri/uriparser/src/UriSetHostCommon.h index 1914d8480eb4a..6627767f2caf5 100644 --- a/ext/uri/uriparser/src/UriSetHostCommon.h +++ b/ext/uri/uriparser/src/UriSetHostCommon.h @@ -37,43 +37,38 @@ */ #if (defined(URI_PASS_ANSI) && !defined(URI_SET_HOST_COMMON_H_ANSI)) \ - || (defined(URI_PASS_UNICODE) && !defined(URI_SET_HOST_COMMON_H_UNICODE)) \ - || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) + || (defined(URI_PASS_UNICODE) && !defined(URI_SET_HOST_COMMON_H_UNICODE)) \ + || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* What encodings are enabled? */ -#include -#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +# include +# if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetHostCommon.h" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetHostCommon.h" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostCommon.h" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostCommon.h" +# undef URI_PASS_UNICODE +# endif /* Only one pass for each encoding */ -#elif (defined(URI_PASS_ANSI) && !defined(URI_SET_HOST_COMMON_H_ANSI) \ - && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \ - && !defined(URI_SET_HOST_COMMON_H_UNICODE) && defined(URI_ENABLE_UNICODE)) -# ifdef URI_PASS_ANSI -# define URI_SET_HOST_COMMON_H_ANSI 1 -# include -# else -# define URI_SET_HOST_COMMON_H_UNICODE 1 -# include -# endif +# elif (defined(URI_PASS_ANSI) && !defined(URI_SET_HOST_COMMON_H_ANSI) \ + && defined(URI_ENABLE_ANSI)) \ + || (defined(URI_PASS_UNICODE) && !defined(URI_SET_HOST_COMMON_H_UNICODE) \ + && defined(URI_ENABLE_UNICODE)) +# ifdef URI_PASS_ANSI +# define URI_SET_HOST_COMMON_H_ANSI 1 +# include +# else +# define URI_SET_HOST_COMMON_H_UNICODE 1 +# include +# endif +int URI_FUNC(InternalSetHostMm)(URI_TYPE(Uri) * uri, UriHostType hostType, + const URI_CHAR * first, const URI_CHAR * afterLast, + UriMemoryManager * memory); - -int URI_FUNC(InternalSetHostMm)(URI_TYPE(Uri) * uri, - UriHostType hostType, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); - - - -#endif +# endif #endif diff --git a/ext/uri/uriparser/src/UriSetHostIp4.c b/ext/uri/uriparser/src/UriSetHostIp4.c index e07e249c23d2a..e52880be4c421 100644 --- a/ext/uri/uriparser/src/UriSetHostIp4.c +++ b/ext/uri/uriparser/src/UriSetHostIp4.c @@ -40,66 +40,52 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetHostIp4.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetHostIp4.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostIp4.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostIp4.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include -# include "UriMemory.h" -# include "UriSetHostBase.h" -# include "UriSetHostCommon.h" -#endif - - - -UriBool URI_FUNC(IsWellFormedHostIp4)(const URI_CHAR * first, const URI_CHAR * afterLast) { - if ((first == NULL) || (afterLast == NULL)) { - return URI_FALSE; - } - - { - unsigned char octetOutput[4]; - return (URI_FUNC(ParseIpFourAddress)(octetOutput, first, afterLast) == URI_SUCCESS) - ? URI_TRUE - : URI_FALSE; - } +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include +# include "UriMemory.h" +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +# endif + +UriBool URI_FUNC(IsWellFormedHostIp4)(const URI_CHAR * first, + const URI_CHAR * afterLast) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + unsigned char octetOutput[4]; + return (URI_FUNC(ParseIpFourAddress)(octetOutput, first, afterLast) == URI_SUCCESS) + ? URI_TRUE + : URI_FALSE; } - - -int URI_FUNC(SetHostIp4Mm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_IP4, first, afterLast, memory); +int URI_FUNC(SetHostIp4Mm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_IP4, first, afterLast, memory); } - - -int URI_FUNC(SetHostIp4)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(SetHostIp4Mm)(uri, first, afterLast, NULL); +int URI_FUNC(SetHostIp4)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetHostIp4Mm)(uri, first, afterLast, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriSetHostIp6.c b/ext/uri/uriparser/src/UriSetHostIp6.c index f99365558ff0b..9637a4384b232 100644 --- a/ext/uri/uriparser/src/UriSetHostIp6.c +++ b/ext/uri/uriparser/src/UriSetHostIp6.c @@ -40,143 +40,119 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetHostIp6.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetHostIp6.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostIp6.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostIp6.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriMemory.h" -# include "UriSetHostBase.h" -# include "UriSetHostCommon.h" -#endif - - - -#include -#include /* for memcpy */ - - - -#define URI_MAX_IP6_LEN (8 * 4 + 7 * 1) /* i.e. 8 full quads plus 7 colon separators */ - - - -int URI_FUNC(ParseIpSixAddressMm)(UriIp6 * output, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - /* NOTE: output is allowed to be NULL */ - if ((first == NULL) || (afterLast == NULL)) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - /* Are we dealing with potential IPvFuture input? */ - if (first < afterLast) { - switch (first[0]) { - case _UT('v'): - case _UT('V'): - return URI_ERROR_SYNTAX; - default: - break; - } - } - - /* Are we dealing with IPv6 input? */ - { - /* Assemble "//[..]" input wrap for upcoming parse as a URI - * NOTE: If the input contains closing "]" on its own, the resulting - * string will not be valid URI syntax, and hence there is - * no risk of false positives from "bracket injection". */ - URI_CHAR candidate[3 + URI_MAX_IP6_LEN + 1 + 1] = _UT("//["); - const size_t inputLenChars = (afterLast - first); - - /* Detect overflow */ - if (inputLenChars > URI_MAX_IP6_LEN) { - return URI_ERROR_SYNTAX; - } - - memcpy(candidate + 3, first, inputLenChars * sizeof(URI_CHAR)); - memcpy(candidate + 3 + inputLenChars, _UT("]"), 2 * sizeof(URI_CHAR)); /* includes zero terminator */ - - /* Parse as an RFC 3986 URI */ - { - const size_t candidateLenChars = 3 + inputLenChars + 1; - URI_TYPE(Uri) uri; - const int res = URI_FUNC(ParseSingleUriExMm)(&uri, candidate, candidate + candidateLenChars, NULL, memory); - - assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); - - if (res == URI_SUCCESS) { - assert(uri.hostData.ip6 != NULL); - - if (output != NULL) { - memcpy(output->data, uri.hostData.ip6->data, sizeof(output->data)); - } - - URI_FUNC(FreeUriMembersMm)(&uri, memory); - } - - return res; - } - } +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriMemory.h" +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +# endif + +# include +# include /* for memcpy */ + +# define URI_MAX_IP6_LEN \ + (8 * 4 + 7 * 1) /* i.e. 8 full quads plus 7 colon separators \ + */ + +int URI_FUNC(ParseIpSixAddressMm)(UriIp6 * output, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + /* NOTE: output is allowed to be NULL */ + if ((first == NULL) || (afterLast == NULL)) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + /* Are we dealing with potential IPvFuture input? */ + if (first < afterLast) { + switch (first[0]) { + case _UT('v'): + case _UT('V'): + return URI_ERROR_SYNTAX; + default: + break; + } + } + + /* Are we dealing with IPv6 input? */ + /* Assemble "//[..]" input wrap for upcoming parse as a URI + * NOTE: If the input contains closing "]" on its own, the resulting + * string will not be valid URI syntax, and hence there is + * no risk of false positives from "bracket injection". */ + URI_CHAR candidate[3 + URI_MAX_IP6_LEN + 1 + 1] = _UT("//["); + const size_t inputLenChars = (afterLast - first); + + /* Detect overflow */ + if (inputLenChars > URI_MAX_IP6_LEN) { + return URI_ERROR_SYNTAX; + } + + memcpy(candidate + 3, first, inputLenChars * sizeof(URI_CHAR)); + memcpy(candidate + 3 + inputLenChars, _UT("]"), + 2 * sizeof(URI_CHAR)); /* includes zero terminator */ + + /* Parse as an RFC 3986 URI */ + const size_t candidateLenChars = 3 + inputLenChars + 1; + URI_TYPE(Uri) uri; + const int res = URI_FUNC(ParseSingleUriExMm)( + &uri, candidate, candidate + candidateLenChars, NULL, memory); + + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) + || (res == URI_ERROR_MALLOC)); + + if (res == URI_SUCCESS) { + assert(uri.hostData.ip6 != NULL); + + if (output != NULL) { + memcpy(output->data, uri.hostData.ip6->data, sizeof(output->data)); + } + + URI_FUNC(FreeUriMembersMm)(&uri, memory); + } + + return res; } - - -int URI_FUNC(ParseIpSixAddress)(UriIp6 * output, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(ParseIpSixAddressMm)(output, first, afterLast, NULL); +int URI_FUNC(ParseIpSixAddress)(UriIp6 * output, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(ParseIpSixAddressMm)(output, first, afterLast, NULL); } - - -int URI_FUNC(IsWellFormedHostIp6Mm)(const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { +int URI_FUNC(IsWellFormedHostIp6Mm)(const URI_CHAR * first, const URI_CHAR * afterLast, + UriMemoryManager * memory) { return URI_FUNC(ParseIpSixAddressMm)(NULL, first, afterLast, memory); } - - int URI_FUNC(IsWellFormedHostIp6)(const URI_CHAR * first, const URI_CHAR * afterLast) { - return URI_FUNC(IsWellFormedHostIp6Mm)(first, afterLast, NULL); + return URI_FUNC(IsWellFormedHostIp6Mm)(first, afterLast, NULL); } - - -int URI_FUNC(SetHostIp6Mm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_IP6, first, afterLast, memory); +int URI_FUNC(SetHostIp6Mm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_IP6, first, afterLast, memory); } - - -int URI_FUNC(SetHostIp6)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(SetHostIp6Mm)(uri, first, afterLast, NULL); +int URI_FUNC(SetHostIp6)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetHostIp6Mm)(uri, first, afterLast, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriSetHostIpFuture.c b/ext/uri/uriparser/src/UriSetHostIpFuture.c index aa84a82f4fe68..c5044c49c76fc 100644 --- a/ext/uri/uriparser/src/UriSetHostIpFuture.c +++ b/ext/uri/uriparser/src/UriSetHostIpFuture.c @@ -40,135 +40,118 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetHostIpFuture.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetHostIpFuture.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostIpFuture.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostIpFuture.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriMemory.h" -# include "UriSetHostBase.h" -# include "UriSetHostCommon.h" -#endif - - - -#include -#include /* for memcpy */ - - - -int URI_FUNC(IsWellFormedHostIpFutureMm)(const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { - if ((first == NULL) || (afterLast == NULL)) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - /* Are we dealing with potential IPv6 input? */ - if (first < afterLast) { - switch (first[0]) { - case _UT('v'): - case _UT('V'): - break; - default: - return URI_ERROR_SYNTAX; - } - } - - /* Are we dealing with IPvFuture input? */ - { - /* Assemble "//[..]" input wrap for upcoming parse as a URI - * NOTE: If the input contains closing "]" on its own, the resulting - * string will not be valid URI syntax, and hence there is - * no risk of false positives from "bracket injection". */ - const size_t inputLenChars = (afterLast - first); - const size_t MAX_SIZE_T = (size_t)-1; - - /* Detect overflow */ - if (MAX_SIZE_T - inputLenChars < 3 + 1 + 1) { - return URI_ERROR_MALLOC; - } - - { - const size_t candidateLenChars = 3 + inputLenChars + 1; - - /* Detect overflow */ - if (MAX_SIZE_T / sizeof(URI_CHAR) < candidateLenChars + 1) { - return URI_ERROR_MALLOC; - } - - { - URI_CHAR * const candidate = memory->malloc(memory, (candidateLenChars + 1) * sizeof(URI_CHAR)); - - if (candidate == NULL) { - return URI_ERROR_MALLOC; - } - - memcpy(candidate, _UT("//["), 3 * sizeof(URI_CHAR)); - memcpy(candidate + 3, first, inputLenChars * sizeof(URI_CHAR)); - memcpy(candidate + 3 + inputLenChars, _UT("]"), 2 * sizeof(URI_CHAR)); /* includes zero terminator */ - - /* Parse as an RFC 3986 URI */ - { - URI_TYPE(Uri) uri; - const int res = URI_FUNC(ParseSingleUriExMm)(&uri, candidate, candidate + candidateLenChars, NULL, memory); - - assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); - - if (res == URI_SUCCESS) { - assert(uri.hostData.ipFuture.first != NULL); - URI_FUNC(FreeUriMembersMm)(&uri, memory); - } - - memory->free(memory, candidate); - - return res; - } - } - } - } +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriMemory.h" +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +# endif + +# include +# include /* for memcpy */ + +int URI_FUNC(IsWellFormedHostIpFutureMm)(const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + /* Are we dealing with potential IPv6 input? */ + if (first < afterLast) { + switch (first[0]) { + case _UT('v'): + case _UT('V'): + break; + default: + return URI_ERROR_SYNTAX; + } + } + + /* Are we dealing with IPvFuture input? */ + /* Assemble "//[..]" input wrap for upcoming parse as a URI + * NOTE: If the input contains closing "]" on its own, the resulting + * string will not be valid URI syntax, and hence there is + * no risk of false positives from "bracket injection". */ + const size_t inputLenChars = (afterLast - first); + const size_t MAX_SIZE_T = (size_t)-1; + + /* Detect overflow */ + if (MAX_SIZE_T - inputLenChars < 3 + 1 + 1) { + return URI_ERROR_MALLOC; + } + + const size_t candidateLenChars = 3 + inputLenChars + 1; + + /* Detect overflow */ + if (MAX_SIZE_T / sizeof(URI_CHAR) < candidateLenChars + 1) { + return URI_ERROR_MALLOC; + } + + URI_CHAR * const candidate = + memory->malloc(memory, (candidateLenChars + 1) * sizeof(URI_CHAR)); + + if (candidate == NULL) { + return URI_ERROR_MALLOC; + } + + memcpy(candidate, _UT("//["), 3 * sizeof(URI_CHAR)); + memcpy(candidate + 3, first, inputLenChars * sizeof(URI_CHAR)); + memcpy(candidate + 3 + inputLenChars, _UT("]"), + 2 * sizeof(URI_CHAR)); /* includes zero terminator */ + + /* Parse as an RFC 3986 URI */ + URI_TYPE(Uri) uri; + const int res = URI_FUNC(ParseSingleUriExMm)( + &uri, candidate, candidate + candidateLenChars, NULL, memory); + + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) + || (res == URI_ERROR_MALLOC)); + + if (res == URI_SUCCESS) { + assert(uri.hostData.ipFuture.first != NULL); + URI_FUNC(FreeUriMembersMm)(&uri, memory); + } + + memory->free(memory, candidate); + + return res; } - - -int URI_FUNC(IsWellFormedHostIpFuture)(const URI_CHAR * first, const URI_CHAR * afterLast) { - return URI_FUNC(IsWellFormedHostIpFutureMm)(first, afterLast, NULL); +int URI_FUNC(IsWellFormedHostIpFuture)(const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(IsWellFormedHostIpFutureMm)(first, afterLast, NULL); } - - -int URI_FUNC(SetHostIpFutureMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_IPFUTURE, first, afterLast, memory); +int URI_FUNC(SetHostIpFutureMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_IPFUTURE, first, afterLast, + memory); } - - -int URI_FUNC(SetHostIpFuture)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(SetHostIpFutureMm)(uri, first, afterLast, NULL); +int URI_FUNC(SetHostIpFuture)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetHostIpFutureMm)(uri, first, afterLast, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriSetHostRegName.c b/ext/uri/uriparser/src/UriSetHostRegName.c index d09e2e98e3616..61694b248adc6 100644 --- a/ext/uri/uriparser/src/UriSetHostRegName.c +++ b/ext/uri/uriparser/src/UriSetHostRegName.c @@ -40,207 +40,178 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetHostRegName.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetHostRegName.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostRegName.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostRegName.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriMemory.h" -# include "UriSetHostBase.h" -# include "UriSetHostCommon.h" -#endif - - - -#define URI_SET_DIGIT \ - _UT('0'): \ - case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - - - -#define URI_SET_HEX_LETTER_UPPER \ - _UT('A'): \ - case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - - - -#define URI_SET_HEX_LETTER_LOWER \ - _UT('a'): \ - case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - - - -#define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - - - -#define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - - - -#define URI_SET_SUB_DELIMS \ - _UT('!'): \ - case _UT('$'): \ - case _UT('&'): \ - case _UT('\''): \ - case _UT('('): \ - case _UT(')'): \ - case _UT('*'): \ - case _UT('+'): \ - case _UT(','): \ - case _UT(';'): \ - case _UT('=') - - - -#define URI_SET_UNRESERVED \ - URI_SET_ALPHA: \ - case URI_SET_DIGIT: \ - case _UT('-'): \ - case _UT('.'): \ - case _UT('_'): \ - case _UT('~') - - - -UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, const URI_CHAR * afterLast) { - if ((first == NULL) || (afterLast == NULL)) { - return URI_FALSE; - } - - /* reg-name = *( unreserved / pct-encoded / sub-delims ) */ - while (first < afterLast) { - switch (first[0]) { - case URI_SET_UNRESERVED: - break; - - /* pct-encoded */ - case _UT('%'): - if (afterLast - first < 3) { - return URI_FALSE; - } - switch (first[1]) { - case URI_SET_HEXDIG: - break; - default: - return URI_FALSE; - } - switch (first[2]) { - case URI_SET_HEXDIG: - break; - default: - return URI_FALSE; - } - first += 2; - break; - - case URI_SET_SUB_DELIMS: - break; - - default: - return URI_FALSE; - } - - first++; - } - return URI_TRUE; +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriMemory.h" +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +# endif + +# define URI_SET_DIGIT \ + _UT('0') : case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + +# define URI_SET_HEX_LETTER_UPPER \ + _UT('A') : case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + +# define URI_SET_HEX_LETTER_LOWER \ + _UT('a') : case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + +# define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + +# define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + +# define URI_SET_SUB_DELIMS \ + _UT('!') : case _UT('$'): \ + case _UT('&'): \ + case _UT('\''): \ + case _UT('('): \ + case _UT(')'): \ + case _UT('*'): \ + case _UT('+'): \ + case _UT(','): \ + case _UT(';'): \ + case _UT('=') + +# define URI_SET_UNRESERVED \ + URI_SET_ALPHA: \ + case URI_SET_DIGIT: \ + case _UT('-'): \ + case _UT('.'): \ + case _UT('_'): \ + case _UT('~') + +UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, + const URI_CHAR * afterLast) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* reg-name = *( unreserved / pct-encoded / sub-delims ) */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_UNRESERVED: + break; + + /* pct-encoded */ + case _UT('%'): + if (afterLast - first < 3) { + return URI_FALSE; + } + switch (first[1]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + switch (first[2]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + first += 2; + break; + + case URI_SET_SUB_DELIMS: + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; } - - -int URI_FUNC(SetHostRegNameMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_REGNAME, first, afterLast, memory); +int URI_FUNC(SetHostRegNameMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_REGNAME, first, afterLast, + memory); } - - -int URI_FUNC(SetHostRegName)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(SetHostRegNameMm)(uri, first, afterLast, NULL); +int URI_FUNC(SetHostRegName)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetHostRegNameMm)(uri, first, afterLast, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriSetPath.c b/ext/uri/uriparser/src/UriSetPath.c index abd783b3c65ae..d9e8bec0aa802 100644 --- a/ext/uri/uriparser/src/UriSetPath.c +++ b/ext/uri/uriparser/src/UriSetPath.c @@ -40,456 +40,403 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetPath.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetPath.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetPath.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetPath.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -# include "UriMemory.h" -#endif - - - -#include - - - -#define URI_SET_DIGIT \ - _UT('0'): \ - case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - - - -#define URI_SET_HEX_LETTER_UPPER \ - _UT('A'): \ - case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - - - -#define URI_SET_HEX_LETTER_LOWER \ - _UT('a'): \ - case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - - - -#define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - - - -#define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - - - -#define URI_SET_SUB_DELIMS \ - _UT('!'): \ - case _UT('$'): \ - case _UT('&'): \ - case _UT('\''): \ - case _UT('('): \ - case _UT(')'): \ - case _UT('*'): \ - case _UT('+'): \ - case _UT(','): \ - case _UT(';'): \ - case _UT('=') - - - -#define URI_SET_UNRESERVED \ - URI_SET_ALPHA: \ - case URI_SET_DIGIT: \ - case _UT('-'): \ - case _UT('.'): \ - case _UT('_'): \ - case _UT('~') - - - -UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, const URI_CHAR * afterLast, UriBool hasHost) { - if ((first == NULL) || (afterLast == NULL)) { - return URI_FALSE; - } - - if ((hasHost == URI_TRUE) && ((first >= afterLast) || (first[0] != _UT('/')))) { - return URI_FALSE; - } - - /* The related part of the grammar in RFC 3986 (section 3.3) reads: - * - * path = path-abempty ; begins with "/" or is empty - * / path-absolute ; begins with "/" but not "//" - * / path-noscheme ; begins with a non-colon segment - * / path-rootless ; begins with a segment - * / path-empty ; zero characters - * - * path-abempty = *( "/" segment ) - * path-absolute = "/" [ segment-nz *( "/" segment ) ] - * path-noscheme = segment-nz-nc *( "/" segment ) - * path-rootless = segment-nz *( "/" segment ) - * path-empty = 0 - * - * segment = *pchar - * segment-nz = 1*pchar - * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) - * ; non-zero-length segment without any colon ":" - * - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * - * The check below simplifies this to .. - * - * path = *( unreserved / pct-encoded / sub-delims / ":" / "@" / "/" ) - * - * .. and leaves the rest to pre-return removal of ambiguity - * from cases like "path1:/path2" and "//path1/path2" inside SetPath. - */ - while (first < afterLast) { - switch (first[0]) { - case URI_SET_UNRESERVED: - break; - - /* pct-encoded */ - case _UT('%'): - if (afterLast - first < 3) { - return URI_FALSE; - } - switch (first[1]) { - case URI_SET_HEXDIG: - break; - default: - return URI_FALSE; - } - switch (first[2]) { - case URI_SET_HEXDIG: - break; - default: - return URI_FALSE; - } - first += 2; - break; - - case URI_SET_SUB_DELIMS: - break; - - /* ":" / "@" and "/" */ - case _UT(':'): - case _UT('@'): - case _UT('/'): - break; - - default: - return URI_FALSE; - } - - first++; - } - return URI_TRUE; +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +# endif + +# include + +# define URI_SET_DIGIT \ + _UT('0') : case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + +# define URI_SET_HEX_LETTER_UPPER \ + _UT('A') : case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + +# define URI_SET_HEX_LETTER_LOWER \ + _UT('a') : case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + +# define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + +# define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + +# define URI_SET_SUB_DELIMS \ + _UT('!') : case _UT('$'): \ + case _UT('&'): \ + case _UT('\''): \ + case _UT('('): \ + case _UT(')'): \ + case _UT('*'): \ + case _UT('+'): \ + case _UT(','): \ + case _UT(';'): \ + case _UT('=') + +# define URI_SET_UNRESERVED \ + URI_SET_ALPHA: \ + case URI_SET_DIGIT: \ + case _UT('-'): \ + case _UT('.'): \ + case _UT('_'): \ + case _UT('~') + +UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, const URI_CHAR * afterLast, + UriBool hasHost) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + if ((hasHost == URI_TRUE) && ((first >= afterLast) || (first[0] != _UT('/')))) { + return URI_FALSE; + } + + /* The related part of the grammar in RFC 3986 (section 3.3) reads: + * + * path = path-abempty ; begins with "/" or is empty + * / path-absolute ; begins with "/" but not "//" + * / path-noscheme ; begins with a non-colon segment + * / path-rootless ; begins with a segment + * / path-empty ; zero characters + * + * path-abempty = *( "/" segment ) + * path-absolute = "/" [ segment-nz *( "/" segment ) ] + * path-noscheme = segment-nz-nc *( "/" segment ) + * path-rootless = segment-nz *( "/" segment ) + * path-empty = 0 + * + * segment = *pchar + * segment-nz = 1*pchar + * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) + * ; non-zero-length segment without any colon ":" + * + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * + * The check below simplifies this to .. + * + * path = *( unreserved / pct-encoded / sub-delims / ":" / "@" / "/" ) + * + * .. and leaves the rest to pre-return removal of ambiguity + * from cases like "path1:/path2" and "//path1/path2" inside SetPath. + */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_UNRESERVED: + break; + + /* pct-encoded */ + case _UT('%'): + if (afterLast - first < 3) { + return URI_FALSE; + } + switch (first[1]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + switch (first[2]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + first += 2; + break; + + case URI_SET_SUB_DELIMS: + break; + + /* ":" / "@" and "/" */ + case _UT(':'): + case _UT('@'): + case _UT('/'): + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; } +static void URI_FUNC(DropEmptyFirstPathSegment)(URI_TYPE(Uri) * uri, + UriMemoryManager * memory) { + assert(uri != NULL); + assert(memory != NULL); + assert(uri->pathHead != NULL); + assert(uri->pathHead->text.first == uri->pathHead->text.afterLast); + URI_TYPE(PathSegment) * const originalHead = uri->pathHead; -static void URI_FUNC(DropEmptyFirstPathSegment)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { - assert(uri != NULL); - assert(memory != NULL); - assert(uri->pathHead != NULL); - assert(uri->pathHead->text.first == uri->pathHead->text.afterLast); - - { - URI_TYPE(PathSegment) * const originalHead = uri->pathHead; + uri->pathHead = uri->pathHead->next; - uri->pathHead = uri->pathHead->next; - - originalHead->text.first = NULL; - originalHead->text.afterLast = NULL; - memory->free(memory, originalHead); - } + originalHead->text.first = NULL; + originalHead->text.afterLast = NULL; + memory->free(memory, originalHead); } - - /* URIs without a host encode a leading slash in the path as .absolutePath == URI_TRUE. - * This function checks for a leading empty path segment (that would have the "visual effect" - * of a leading slash during stringification) and transforms it into .absolutePath == URI_TRUE - * instead, if present. */ -static void URI_FUNC(TransformEmptyLeadPathSegments)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { - assert(uri != NULL); - assert(memory != NULL); + * This function checks for a leading empty path segment (that would have the "visual + * effect" of a leading slash during stringification) and transforms it into .absolutePath + * == URI_TRUE instead, if present. */ +static void URI_FUNC(TransformEmptyLeadPathSegments)(URI_TYPE(Uri) * uri, + UriMemoryManager * memory) { + assert(uri != NULL); + assert(memory != NULL); - if ((URI_FUNC(HasHost)(uri) == URI_TRUE) - || (uri->pathHead == NULL) - || (uri->pathHead->text.first != uri->pathHead->text.afterLast)) { - return; /* i.e. nothing to do */ - } + if ((URI_FUNC(HasHost)(uri) == URI_TRUE) || (uri->pathHead == NULL) + || (uri->pathHead->text.first != uri->pathHead->text.afterLast)) { + return; /* i.e. nothing to do */ + } - assert(uri->absolutePath == URI_FALSE); + assert(uri->absolutePath == URI_FALSE); - URI_FUNC(DropEmptyFirstPathSegment)(uri, memory); + URI_FUNC(DropEmptyFirstPathSegment)(uri, memory); - uri->absolutePath = URI_TRUE; + uri->absolutePath = URI_TRUE; } - - -static int URI_FUNC(InternalSetPath)(URI_TYPE(Uri) * destUri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - assert(destUri != NULL); - assert(first != NULL); - assert(afterLast != NULL); - assert(memory != NULL); - assert(destUri->pathHead == NULL); /* set by SetPathMm right before */ - assert(destUri->pathTail == NULL); /* set by SetPathMm right before */ - assert(destUri->absolutePath == URI_FALSE); /* set by SetPathMm right before */ - - /* Skip the leading slash from target URIs with a host (so that we can - * transfer the path 1:1 further down) */ - if (URI_FUNC(HasHost)(destUri) == URI_TRUE) { - /* NOTE: This is because SetPathMm called IsWellFormedPath earlier: */ - assert((afterLast - first >= 1) && (first[0] == _UT('/'))); - first++; - } else if (first == afterLast) { - /* This avoids (1) all the expensive but unnecessary work below - * and also (2) mis-encoding as single empty path segment - * that would need (detection and) repair further down otherwise */ - return URI_SUCCESS; - } - - /* Assemble "///.." input wrap for upcoming parse as a URI */ - { - const size_t inputLenChars = (afterLast - first); - const size_t MAX_SIZE_T = (size_t)-1; - - /* Detect overflow */ - if (MAX_SIZE_T - inputLenChars < 3 + 1) { - return URI_ERROR_MALLOC; - } - - { - const size_t candidateLenChars = 3 + inputLenChars; - - /* Detect overflow */ - if (MAX_SIZE_T / sizeof(URI_CHAR) < candidateLenChars + 1) { - return URI_ERROR_MALLOC; - } - - { - URI_CHAR * const candidate = memory->malloc(memory, (candidateLenChars + 1) * sizeof(URI_CHAR)); - - if (candidate == NULL) { - return URI_ERROR_MALLOC; - } - - memcpy(candidate, _UT("///"), 3 * sizeof(URI_CHAR)); - memcpy(candidate + 3, first, inputLenChars * sizeof(URI_CHAR)); - candidate[3 + inputLenChars] = _UT('\0'); - - /* Parse as an RFC 3986 URI */ - { - URI_TYPE(Uri) tempUri; - const int res = URI_FUNC(ParseSingleUriExMm)(&tempUri, - candidate, - candidate + candidateLenChars, - NULL, - memory); - assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); - if (res != URI_SUCCESS) { - memory->free(memory, candidate); - return res; - } - - /* Nothing but path and host is supposed to be set by the parse, in particular not: */ - assert(tempUri.query.first == NULL); - assert(tempUri.fragment.first == NULL); - - /* Ensure that the strings in the path segments are all owned by `tempUri` - * because we want to (1) rip out and keep the full path list further down - * and (2) be able to free the parsed string (`candidate`) also. */ - { - const int res = URI_FUNC(MakeOwnerMm)(&tempUri, memory); - assert((res == URI_SUCCESS) || (res == URI_ERROR_MALLOC)); - if (res != URI_SUCCESS) { - URI_FUNC(FreeUriMembersMm)(&tempUri, memory); - memory->free(memory, candidate); - return res; - } - assert(tempUri.owner == URI_TRUE); - } - - /* Move path to destination URI */ - assert(tempUri.absolutePath == URI_FALSE); /* always URI_FALSE for URIs with host */ - destUri->pathHead = tempUri.pathHead; - destUri->pathTail = tempUri.pathTail; - destUri->absolutePath = URI_FALSE; - - tempUri.pathHead = NULL; - tempUri.pathTail = NULL; - - /* Free the rest of the temp URI */ - URI_FUNC(FreeUriMembersMm)(&tempUri, memory); - memory->free(memory, candidate); - - /* Restore use of .absolutePath as needed */ - URI_FUNC(TransformEmptyLeadPathSegments)(destUri, memory); - - /* Disambiguate as needed */ - { - const UriBool success = URI_FUNC(FixPathNoScheme)(destUri, memory); - if (success == URI_FALSE) { - return URI_ERROR_MALLOC; - } - } - { - const UriBool success = URI_FUNC(EnsureThatPathIsNotMistakenForHost)(destUri, memory); - if (success == URI_FALSE) { - return URI_ERROR_MALLOC; - } - } - } - } - } - } - - return URI_SUCCESS; +static int URI_FUNC(InternalSetPath)(URI_TYPE(Uri) * destUri, const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + assert(destUri != NULL); + assert(first != NULL); + assert(afterLast != NULL); + assert(memory != NULL); + assert(destUri->pathHead == NULL); /* set by SetPathMm right before */ + assert(destUri->pathTail == NULL); /* set by SetPathMm right before */ + assert(destUri->absolutePath == URI_FALSE); /* set by SetPathMm right before */ + + /* Skip the leading slash from target URIs with a host (so that we can + * transfer the path 1:1 further down) */ + if (URI_FUNC(HasHost)(destUri) == URI_TRUE) { + /* NOTE: This is because SetPathMm called IsWellFormedPath earlier: */ + assert((afterLast - first >= 1) && (first[0] == _UT('/'))); + first++; + } else if (first == afterLast) { + /* This avoids (1) all the expensive but unnecessary work below + * and also (2) mis-encoding as single empty path segment + * that would need (detection and) repair further down otherwise */ + return URI_SUCCESS; + } + + /* Assemble "///.." input wrap for upcoming parse as a URI */ + const size_t inputLenChars = (afterLast - first); + const size_t MAX_SIZE_T = (size_t)-1; + + /* Detect overflow */ + if (MAX_SIZE_T - inputLenChars < 3 + 1) { + return URI_ERROR_MALLOC; + } + + const size_t candidateLenChars = 3 + inputLenChars; + + /* Detect overflow */ + if (MAX_SIZE_T / sizeof(URI_CHAR) < candidateLenChars + 1) { + return URI_ERROR_MALLOC; + } + + URI_CHAR * const candidate = + memory->malloc(memory, (candidateLenChars + 1) * sizeof(URI_CHAR)); + + if (candidate == NULL) { + return URI_ERROR_MALLOC; + } + + memcpy(candidate, _UT("///"), 3 * sizeof(URI_CHAR)); + memcpy(candidate + 3, first, inputLenChars * sizeof(URI_CHAR)); + candidate[3 + inputLenChars] = _UT('\0'); + + /* Parse as an RFC 3986 URI */ + URI_TYPE(Uri) tempUri; + int res = URI_FUNC(ParseSingleUriExMm)(&tempUri, candidate, + candidate + candidateLenChars, NULL, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) + || (res == URI_ERROR_MALLOC)); + if (res != URI_SUCCESS) { + memory->free(memory, candidate); + return res; + } + + /* Nothing but path and host is supposed to be set by the parse, in + * particular not: */ + assert(tempUri.query.first == NULL); + assert(tempUri.fragment.first == NULL); + + /* Ensure that the strings in the path segments are all owned by + * `tempUri` because we want to (1) rip out and keep the full path + * list further down and (2) be able to free the parsed string + * (`candidate`) also. */ + res = URI_FUNC(MakeOwnerMm)(&tempUri, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_MALLOC)); + if (res != URI_SUCCESS) { + URI_FUNC(FreeUriMembersMm)(&tempUri, memory); + memory->free(memory, candidate); + return res; + } + assert(tempUri.owner == URI_TRUE); + + /* Move path to destination URI */ + assert(tempUri.absolutePath == URI_FALSE); /* always URI_FALSE for URIs with host */ + destUri->pathHead = tempUri.pathHead; + destUri->pathTail = tempUri.pathTail; + destUri->absolutePath = URI_FALSE; + + tempUri.pathHead = NULL; + tempUri.pathTail = NULL; + + /* Free the rest of the temp URI */ + URI_FUNC(FreeUriMembersMm)(&tempUri, memory); + memory->free(memory, candidate); + + /* Restore use of .absolutePath as needed */ + URI_FUNC(TransformEmptyLeadPathSegments)(destUri, memory); + + /* Disambiguate as needed */ + UriBool success = URI_FUNC(FixPathNoScheme)(destUri, memory); + if (success == URI_FALSE) { + return URI_ERROR_MALLOC; + } + + success = URI_FUNC(EnsureThatPathIsNotMistakenForHost)(destUri, memory); + if (success == URI_FALSE) { + return URI_ERROR_MALLOC; + } + + return URI_SUCCESS; } - - -int URI_FUNC(SetPathMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - /* Input validation (before making any changes) */ - if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - if ((first != NULL) && (URI_FUNC(IsWellFormedPath)(first, afterLast, URI_FUNC(HasHost)(uri)) == URI_FALSE)) { - return URI_ERROR_SYNTAX; - } - - /* Clear old value */ - { - const int res = URI_FUNC(FreeUriPath)(uri, memory); - if (res != URI_SUCCESS) { - return res; - } - uri->absolutePath = URI_FALSE; - } - - /* Already done? */ - if (first == NULL) { - return URI_SUCCESS; - } - - assert(first != NULL); - - /* Ensure owned */ - if (uri->owner == URI_FALSE) { - const int res = URI_FUNC(MakeOwnerMm)(uri, memory); - if (res != URI_SUCCESS) { - return res; - } - } - - assert(uri->owner == URI_TRUE); - - /* Apply new value */ - { - const int res = URI_FUNC(InternalSetPath)(uri, first, afterLast, memory); - assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); - return res; - } +int URI_FUNC(SetPathMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if ((first != NULL) + && (URI_FUNC(IsWellFormedPath)(first, afterLast, URI_FUNC(HasHost)(uri)) + == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + int res = URI_FUNC(FreeUriPath)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + uri->absolutePath = URI_FALSE; + + /* Already done? */ + if (first == NULL) { + return URI_SUCCESS; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + res = URI_FUNC(InternalSetPath)(uri, first, afterLast, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) + || (res == URI_ERROR_MALLOC)); + return res; } - - -int URI_FUNC(SetPath)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(SetPathMm)(uri, first, afterLast, NULL); +int URI_FUNC(SetPath)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetPathMm)(uri, first, afterLast, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriSetPort.c b/ext/uri/uriparser/src/UriSetPort.c index 3331b717e21e7..1c373013f66d0 100644 --- a/ext/uri/uriparser/src/UriSetPort.c +++ b/ext/uri/uriparser/src/UriSetPort.c @@ -40,140 +40,120 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetPort.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetPort.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetPort.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetPort.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -# include "UriMemory.h" -#endif - - - -#include - - - -#define URI_SET_DIGIT \ - _UT('0'): \ - case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - - +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +# endif + +# include + +# define URI_SET_DIGIT \ + _UT('0') : case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') UriBool URI_FUNC(IsWellFormedPort)(const URI_CHAR * first, const URI_CHAR * afterLast) { - if ((first == NULL) || (afterLast == NULL)) { - return URI_FALSE; - } - - /* NOTE: Grammar reads "port = *DIGIT" which includes the empty string. */ - while (first < afterLast) { - switch (first[0]) { - case URI_SET_DIGIT: - break; - default: - return URI_FALSE; - } - first++; - } - return URI_TRUE; + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* NOTE: Grammar reads "port = *DIGIT" which includes the empty string. */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_DIGIT: + break; + default: + return URI_FALSE; + } + first++; + } + return URI_TRUE; } - - -int URI_FUNC(SetPortTextMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - /* Input validation (before making any changes) */ - if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - /* The RFC 3986 grammar reads: - * authority = [ userinfo "@" ] host [ ":" port ] - * So no port without a host. */ - if ((first != NULL) && (URI_FUNC(HasHost)(uri) == URI_FALSE)) { - return URI_ERROR_SETPORT_HOST_NOT_SET; - } - - if ((first != NULL) && (URI_FUNC(IsWellFormedPort)(first, afterLast) == URI_FALSE)) { - return URI_ERROR_SYNTAX; - } - - /* Clear old value */ - if ((uri->owner == URI_TRUE) && (uri->portText.first != uri->portText.afterLast)) { - memory->free(memory, (URI_CHAR *)uri->portText.first); - } - uri->portText.first = NULL; - uri->portText.afterLast = NULL; - - /* Already done? */ - if (first == NULL) { - return URI_SUCCESS; - } - - assert(first != NULL); - - /* Ensure owned */ - if (uri->owner == URI_FALSE) { - const int res = URI_FUNC(MakeOwnerMm)(uri, memory); - if (res != URI_SUCCESS) { - return res; - } - } - - assert(uri->owner == URI_TRUE); - - /* Apply new value */ - { - URI_TYPE(TextRange) sourceRange; - sourceRange.first = first; - sourceRange.afterLast = afterLast; - - if (URI_FUNC(CopyRangeAsNeeded)(&uri->portText, &sourceRange, memory) == URI_FALSE) { - return URI_ERROR_MALLOC; - } - } - - return URI_SUCCESS; +int URI_FUNC(SetPortTextMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + /* The RFC 3986 grammar reads: + * authority = [ userinfo "@" ] host [ ":" port ] + * So no port without a host. */ + if ((first != NULL) && (URI_FUNC(HasHost)(uri) == URI_FALSE)) { + return URI_ERROR_SETPORT_HOST_NOT_SET; + } + + if ((first != NULL) && (URI_FUNC(IsWellFormedPort)(first, afterLast) == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + if ((uri->owner == URI_TRUE) && (uri->portText.first != uri->portText.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->portText.first); + } + uri->portText.first = NULL; + uri->portText.afterLast = NULL; + + /* Already done? */ + if (first == NULL) { + return URI_SUCCESS; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->portText, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + + return URI_SUCCESS; } - - -int URI_FUNC(SetPortText)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(SetPortTextMm)(uri, first, afterLast, NULL); +int URI_FUNC(SetPortText)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetPortTextMm)(uri, first, afterLast, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriSetQuery.c b/ext/uri/uriparser/src/UriSetQuery.c index 567ff6d1d8aa7..a189c14bb1ed9 100644 --- a/ext/uri/uriparser/src/UriSetQuery.c +++ b/ext/uri/uriparser/src/UriSetQuery.c @@ -40,267 +40,232 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetQuery.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetQuery.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetQuery.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetQuery.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -# include "UriMemory.h" -#endif - - - -#include - - - -#define URI_SET_DIGIT \ - _UT('0'): \ - case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - - - -#define URI_SET_HEX_LETTER_UPPER \ - _UT('A'): \ - case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - - - -#define URI_SET_HEX_LETTER_LOWER \ - _UT('a'): \ - case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - - - -#define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - - - -#define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - - - -#define URI_SET_SUB_DELIMS \ - _UT('!'): \ - case _UT('$'): \ - case _UT('&'): \ - case _UT('\''): \ - case _UT('('): \ - case _UT(')'): \ - case _UT('*'): \ - case _UT('+'): \ - case _UT(','): \ - case _UT(';'): \ - case _UT('=') - - - -#define URI_SET_UNRESERVED \ - URI_SET_ALPHA: \ - case URI_SET_DIGIT: \ - case _UT('-'): \ - case _UT('.'): \ - case _UT('_'): \ - case _UT('~') - - +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +# endif + +# include + +# define URI_SET_DIGIT \ + _UT('0') : case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + +# define URI_SET_HEX_LETTER_UPPER \ + _UT('A') : case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + +# define URI_SET_HEX_LETTER_LOWER \ + _UT('a') : case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + +# define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + +# define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + +# define URI_SET_SUB_DELIMS \ + _UT('!') : case _UT('$'): \ + case _UT('&'): \ + case _UT('\''): \ + case _UT('('): \ + case _UT(')'): \ + case _UT('*'): \ + case _UT('+'): \ + case _UT(','): \ + case _UT(';'): \ + case _UT('=') + +# define URI_SET_UNRESERVED \ + URI_SET_ALPHA: \ + case URI_SET_DIGIT: \ + case _UT('-'): \ + case _UT('.'): \ + case _UT('_'): \ + case _UT('~') UriBool URI_FUNC(IsWellFormedQuery)(const URI_CHAR * first, const URI_CHAR * afterLast) { - if ((first == NULL) || (afterLast == NULL)) { - return URI_FALSE; - } - - /* The related part of the grammar in RFC 3986 reads: - * - * query = *( pchar / "/" / "?" ) - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - */ - while (first < afterLast) { - switch (first[0]) { - case URI_SET_UNRESERVED: - break; - - /* pct-encoded */ - case _UT('%'): - if (afterLast - first < 3) { - return URI_FALSE; - } - switch (first[1]) { - case URI_SET_HEXDIG: - break; - default: - return URI_FALSE; - } - switch (first[2]) { - case URI_SET_HEXDIG: - break; - default: - return URI_FALSE; - } - first += 2; - break; - - case URI_SET_SUB_DELIMS: - break; - - /* ":" / "@" and "/" / "?" */ - case _UT(':'): - case _UT('@'): - case _UT('/'): - case _UT('?'): - break; - - default: - return URI_FALSE; - } - - first++; - } - return URI_TRUE; + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* The related part of the grammar in RFC 3986 reads: + * + * query = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_UNRESERVED: + break; + + /* pct-encoded */ + case _UT('%'): + if (afterLast - first < 3) { + return URI_FALSE; + } + switch (first[1]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + switch (first[2]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + first += 2; + break; + + case URI_SET_SUB_DELIMS: + break; + + /* ":" / "@" and "/" / "?" */ + case _UT(':'): + case _UT('@'): + case _UT('/'): + case _UT('?'): + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; } - - -int URI_FUNC(SetQueryMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - /* Input validation (before making any changes) */ - if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - if ((first != NULL) && (URI_FUNC(IsWellFormedQuery)(first, afterLast) == URI_FALSE)) { - return URI_ERROR_SYNTAX; - } - - /* Clear old value */ - if ((uri->owner == URI_TRUE) && (uri->query.first != uri->query.afterLast)) { - memory->free(memory, (URI_CHAR *)uri->query.first); - } - uri->query.first = NULL; - uri->query.afterLast = NULL; - - /* Already done? */ - if (first == NULL) { - return URI_SUCCESS; - } - - assert(first != NULL); - - /* Ensure owned */ - if (uri->owner == URI_FALSE) { - const int res = URI_FUNC(MakeOwnerMm)(uri, memory); - if (res != URI_SUCCESS) { - return res; - } - } - - assert(uri->owner == URI_TRUE); - - /* Apply new value */ - { - URI_TYPE(TextRange) sourceRange; - sourceRange.first = first; - sourceRange.afterLast = afterLast; - - if (URI_FUNC(CopyRangeAsNeeded)(&uri->query, &sourceRange, memory) == URI_FALSE) { - return URI_ERROR_MALLOC; - } - } - - return URI_SUCCESS; +int URI_FUNC(SetQueryMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if ((first != NULL) && (URI_FUNC(IsWellFormedQuery)(first, afterLast) == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + if ((uri->owner == URI_TRUE) && (uri->query.first != uri->query.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->query.first); + } + uri->query.first = NULL; + uri->query.afterLast = NULL; + + /* Already done? */ + if (first == NULL) { + return URI_SUCCESS; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->query, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + + return URI_SUCCESS; } - - -int URI_FUNC(SetQuery)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(SetQueryMm)(uri, first, afterLast, NULL); +int URI_FUNC(SetQuery)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetQueryMm)(uri, first, afterLast, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriSetScheme.c b/ext/uri/uriparser/src/UriSetScheme.c index 73a75b82350fd..9a21d45f26319 100644 --- a/ext/uri/uriparser/src/UriSetScheme.c +++ b/ext/uri/uriparser/src/UriSetScheme.c @@ -40,233 +40,202 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetScheme.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetScheme.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetScheme.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetScheme.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -# include "UriMemory.h" -#endif - - - -#include - - - -#define URI_SET_DIGIT \ - _UT('0'): \ - case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - - - -#define URI_SET_HEX_LETTER_UPPER \ - _UT('A'): \ - case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - - - -#define URI_SET_HEX_LETTER_LOWER \ - _UT('a'): \ - case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - - - -#define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - - - -#define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - - +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +# endif + +# include + +# define URI_SET_DIGIT \ + _UT('0') : case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + +# define URI_SET_HEX_LETTER_UPPER \ + _UT('A') : case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + +# define URI_SET_HEX_LETTER_LOWER \ + _UT('a') : case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + +# define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + +# define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') UriBool URI_FUNC(IsWellFormedScheme)(const URI_CHAR * first, const URI_CHAR * afterLast) { - if ((first == NULL) || (afterLast == NULL)) { - return URI_FALSE; - } - - /* The related part of the grammar in RFC 3986 reads: - * - * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - */ - if (first >= afterLast) { - return URI_FALSE; - } - - switch (first[0]) { - case URI_SET_ALPHA: - break; - - default: - return URI_FALSE; - } - - first++; - - while (first < afterLast) { - switch (first[0]) { - case URI_SET_ALPHA: - case URI_SET_DIGIT: - case _UT('+'): - case _UT('-'): - case _UT('.'): - break; - - default: - return URI_FALSE; - } - - first++; - } - return URI_TRUE; + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* The related part of the grammar in RFC 3986 reads: + * + * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + */ + if (first >= afterLast) { + return URI_FALSE; + } + + switch (first[0]) { + case URI_SET_ALPHA: + break; + + default: + return URI_FALSE; + } + + first++; + + while (first < afterLast) { + switch (first[0]) { + case URI_SET_ALPHA: + case URI_SET_DIGIT: + case _UT('+'): + case _UT('-'): + case _UT('.'): + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; } - - -int URI_FUNC(SetSchemeMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - /* Input validation (before making any changes) */ - if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - if ((first != NULL) && (URI_FUNC(IsWellFormedScheme)(first, afterLast) == URI_FALSE)) { - return URI_ERROR_SYNTAX; - } - - /* Clear old value */ - if ((uri->owner == URI_TRUE) && (uri->scheme.first != uri->scheme.afterLast)) { - memory->free(memory, (URI_CHAR *)uri->scheme.first); - } - uri->scheme.first = NULL; - uri->scheme.afterLast = NULL; - - /* Already done setting? */ - if (first == NULL) { - /* Yes, but disambiguate as needed */ - const UriBool success = URI_FUNC(FixPathNoScheme)(uri, memory); - return (success == URI_TRUE) - ? URI_SUCCESS - : URI_ERROR_MALLOC; - } - - assert(first != NULL); - - /* Ensure owned */ - if (uri->owner == URI_FALSE) { - const int res = URI_FUNC(MakeOwnerMm)(uri, memory); - if (res != URI_SUCCESS) { - return res; - } - } - - assert(uri->owner == URI_TRUE); - - /* Apply new value */ - { - URI_TYPE(TextRange) sourceRange; - sourceRange.first = first; - sourceRange.afterLast = afterLast; - - if (URI_FUNC(CopyRangeAsNeeded)(&uri->scheme, &sourceRange, memory) == URI_FALSE) { - return URI_ERROR_MALLOC; - } - } - - return URI_SUCCESS; +int URI_FUNC(SetSchemeMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if ((first != NULL) + && (URI_FUNC(IsWellFormedScheme)(first, afterLast) == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + if ((uri->owner == URI_TRUE) && (uri->scheme.first != uri->scheme.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->scheme.first); + } + uri->scheme.first = NULL; + uri->scheme.afterLast = NULL; + + /* Already done setting? */ + if (first == NULL) { + /* Yes, but disambiguate as needed */ + const UriBool success = URI_FUNC(FixPathNoScheme)(uri, memory); + return (success == URI_TRUE) ? URI_SUCCESS : URI_ERROR_MALLOC; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->scheme, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + + return URI_SUCCESS; } - - -int URI_FUNC(SetScheme)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(SetSchemeMm)(uri, first, afterLast, NULL); +int URI_FUNC(SetScheme)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetSchemeMm)(uri, first, afterLast, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriSetUserInfo.c b/ext/uri/uriparser/src/UriSetUserInfo.c index d30f984395a24..af1ec41a0763d 100644 --- a/ext/uri/uriparser/src/UriSetUserInfo.c +++ b/ext/uri/uriparser/src/UriSetUserInfo.c @@ -40,267 +40,234 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriSetUserInfo.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriSetUserInfo.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetUserInfo.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetUserInfo.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -# include "UriMemory.h" -#endif - - - -#include - - - -#define URI_SET_DIGIT \ - _UT('0'): \ - case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - - - -#define URI_SET_HEX_LETTER_UPPER \ - _UT('A'): \ - case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - - - -#define URI_SET_HEX_LETTER_LOWER \ - _UT('a'): \ - case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - - - -#define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - - - -#define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - - - -#define URI_SET_SUB_DELIMS \ - _UT('!'): \ - case _UT('$'): \ - case _UT('&'): \ - case _UT('\''): \ - case _UT('('): \ - case _UT(')'): \ - case _UT('*'): \ - case _UT('+'): \ - case _UT(','): \ - case _UT(';'): \ - case _UT('=') - - - -#define URI_SET_UNRESERVED \ - URI_SET_ALPHA: \ - case URI_SET_DIGIT: \ - case _UT('-'): \ - case _UT('.'): \ - case _UT('_'): \ - case _UT('~') - - - -UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, const URI_CHAR * afterLast) { - if ((first == NULL) || (afterLast == NULL)) { - return URI_FALSE; - } - - /* userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) */ - while (first < afterLast) { - switch (first[0]) { - case URI_SET_UNRESERVED: - break; - - /* pct-encoded */ - case _UT('%'): - if (afterLast - first < 3) { - return URI_FALSE; - } - switch (first[1]) { - case URI_SET_HEXDIG: - break; - default: - return URI_FALSE; - } - switch (first[2]) { - case URI_SET_HEXDIG: - break; - default: - return URI_FALSE; - } - first += 2; - break; - - case URI_SET_SUB_DELIMS: - break; - - /* ":" */ - case _UT(':'): - break; - - default: - return URI_FALSE; - } - - first++; - } - return URI_TRUE; +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +# endif + +# include + +# define URI_SET_DIGIT \ + _UT('0') : case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + +# define URI_SET_HEX_LETTER_UPPER \ + _UT('A') : case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + +# define URI_SET_HEX_LETTER_LOWER \ + _UT('a') : case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + +# define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + +# define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + +# define URI_SET_SUB_DELIMS \ + _UT('!') : case _UT('$'): \ + case _UT('&'): \ + case _UT('\''): \ + case _UT('('): \ + case _UT(')'): \ + case _UT('*'): \ + case _UT('+'): \ + case _UT(','): \ + case _UT(';'): \ + case _UT('=') + +# define URI_SET_UNRESERVED \ + URI_SET_ALPHA: \ + case URI_SET_DIGIT: \ + case _UT('-'): \ + case _UT('.'): \ + case _UT('_'): \ + case _UT('~') + +UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, + const URI_CHAR * afterLast) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_UNRESERVED: + break; + + /* pct-encoded */ + case _UT('%'): + if (afterLast - first < 3) { + return URI_FALSE; + } + switch (first[1]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + switch (first[2]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + first += 2; + break; + + case URI_SET_SUB_DELIMS: + break; + + /* ":" */ + case _UT(':'): + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; } - - -int URI_FUNC(SetUserInfoMm)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - /* Input validation (before making any changes) */ - if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { - return URI_ERROR_NULL; - } - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - /* The RFC 3986 grammar reads: - * authority = [ userinfo "@" ] host [ ":" port ] - * So no user info without a host. */ - if ((first != NULL) && (URI_FUNC(HasHost)(uri) == URI_FALSE)) { - return URI_ERROR_SETUSERINFO_HOST_NOT_SET; - } - - if ((first != NULL) && (URI_FUNC(IsWellFormedUserInfo)(first, afterLast) == URI_FALSE)) { - return URI_ERROR_SYNTAX; - } - - /* Clear old value */ - if ((uri->owner == URI_TRUE) && (uri->userInfo.first != uri->userInfo.afterLast)) { - memory->free(memory, (URI_CHAR *)uri->userInfo.first); - } - uri->userInfo.first = NULL; - uri->userInfo.afterLast = NULL; - - /* Already done? */ - if (first == NULL) { - return URI_SUCCESS; - } - - assert(first != NULL); - - /* Ensure owned */ - if (uri->owner == URI_FALSE) { - const int res = URI_FUNC(MakeOwnerMm)(uri, memory); - if (res != URI_SUCCESS) { - return res; - } - } - - assert(uri->owner == URI_TRUE); - - /* Apply new value */ - { - URI_TYPE(TextRange) sourceRange; - sourceRange.first = first; - sourceRange.afterLast = afterLast; - - if (URI_FUNC(CopyRangeAsNeeded)(&uri->userInfo, &sourceRange, memory) == URI_FALSE) { - return URI_ERROR_MALLOC; - } - } - - return URI_SUCCESS; +int URI_FUNC(SetUserInfoMm)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast, UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + /* The RFC 3986 grammar reads: + * authority = [ userinfo "@" ] host [ ":" port ] + * So no user info without a host. */ + if ((first != NULL) && (URI_FUNC(HasHost)(uri) == URI_FALSE)) { + return URI_ERROR_SETUSERINFO_HOST_NOT_SET; + } + + if ((first != NULL) + && (URI_FUNC(IsWellFormedUserInfo)(first, afterLast) == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + if ((uri->owner == URI_TRUE) && (uri->userInfo.first != uri->userInfo.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->userInfo.first); + } + uri->userInfo.first = NULL; + uri->userInfo.afterLast = NULL; + + /* Already done? */ + if (first == NULL) { + return URI_SUCCESS; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->userInfo, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + + return URI_SUCCESS; } - - -int URI_FUNC(SetUserInfo)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, - const URI_CHAR * afterLast) { - return URI_FUNC(SetUserInfoMm)(uri, first, afterLast, NULL); +int URI_FUNC(SetUserInfo)(URI_TYPE(Uri) * uri, const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetUserInfoMm)(uri, first, afterLast, NULL); } - - #endif diff --git a/ext/uri/uriparser/src/UriShorten.c b/ext/uri/uriparser/src/UriShorten.c index d2f893592d9ce..548b0b4157dd0 100644 --- a/ext/uri/uriparser/src/UriShorten.c +++ b/ext/uri/uriparser/src/UriShorten.c @@ -41,284 +41,386 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriShorten.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriShorten.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriShorten.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriShorten.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -# include "UriCommon.h" -# include "UriMemory.h" -#endif - - +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + +# ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +# endif static URI_INLINE UriBool URI_FUNC(AppendSegment)(URI_TYPE(Uri) * uri, - const URI_CHAR * first, const URI_CHAR * afterLast, - UriMemoryManager * memory) { - /* Create segment */ - URI_TYPE(PathSegment) * segment = memory->malloc(memory, 1 * sizeof(URI_TYPE(PathSegment))); - if (segment == NULL) { - return URI_FALSE; /* Raises malloc error */ - } - segment->next = NULL; - segment->text.first = first; - segment->text.afterLast = afterLast; - - /* Put into chain */ - if (uri->pathTail == NULL) { - uri->pathHead = segment; - } else { - uri->pathTail->next = segment; - } - uri->pathTail = segment; - - return URI_TRUE; + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + /* Create segment */ + URI_TYPE(PathSegment) * segment = + memory->malloc(memory, 1 * sizeof(URI_TYPE(PathSegment))); + if (segment == NULL) { + return URI_FALSE; /* Raises malloc error */ + } + segment->next = NULL; + segment->text.first = first; + segment->text.afterLast = afterLast; + + /* Put into chain */ + if (uri->pathTail == NULL) { + uri->pathHead = segment; + } else { + uri->pathTail->next = segment; + } + uri->pathTail = segment; + + return URI_TRUE; } - - static URI_INLINE UriBool URI_FUNC(EqualsAuthority)(const URI_TYPE(Uri) * first, - const URI_TYPE(Uri) * second) { - /* IPv4 */ - if (first->hostData.ip4 != NULL) { - return ((second->hostData.ip4 != NULL) - && !memcmp(first->hostData.ip4->data, - second->hostData.ip4->data, 4)) ? URI_TRUE : URI_FALSE; - } - - /* IPv6 */ - if (first->hostData.ip6 != NULL) { - return ((second->hostData.ip6 != NULL) - && !memcmp(first->hostData.ip6->data, - second->hostData.ip6->data, 16)) ? URI_TRUE : URI_FALSE; - } - - /* IPvFuture */ - if (first->hostData.ipFuture.first != NULL) { - return ((second->hostData.ipFuture.first != NULL) - && !URI_FUNC(CompareRange)(&first->hostData.ipFuture, - &second->hostData.ipFuture)) ? URI_TRUE : URI_FALSE; - } - - return !URI_FUNC(CompareRange)(&first->hostText, &second->hostText) - ? URI_TRUE : URI_FALSE; + const URI_TYPE(Uri) * second) { + /* IPv4 */ + if (first->hostData.ip4 != NULL) { + return ((second->hostData.ip4 != NULL) + && !memcmp(first->hostData.ip4->data, second->hostData.ip4->data, 4)) + ? URI_TRUE + : URI_FALSE; + } + + /* IPv6 */ + if (first->hostData.ip6 != NULL) { + return ((second->hostData.ip6 != NULL) + && !memcmp(first->hostData.ip6->data, second->hostData.ip6->data, 16)) + ? URI_TRUE + : URI_FALSE; + } + + /* IPvFuture */ + if (first->hostData.ipFuture.first != NULL) { + return ((second->hostData.ipFuture.first != NULL) + && !URI_FUNC(CompareRange)(&first->hostData.ipFuture, + &second->hostData.ipFuture)) + ? URI_TRUE + : URI_FALSE; + } + + return !URI_FUNC(CompareRange)(&first->hostText, &second->hostText) ? URI_TRUE + : URI_FALSE; } - - static int URI_FUNC(RemoveBaseUriImpl)(URI_TYPE(Uri) * dest, - const URI_TYPE(Uri) * absSource, - const URI_TYPE(Uri) * absBase, - UriBool domainRootMode, UriMemoryManager * memory) { - if (dest == NULL) { - return URI_ERROR_NULL; - } - URI_FUNC(ResetUri)(dest); - - if ((absSource == NULL) || (absBase == NULL)) { - return URI_ERROR_NULL; - } - - /* absBase absolute? */ - if (absBase->scheme.first == NULL) { - return URI_ERROR_REMOVEBASE_REL_BASE; - } - - /* absSource absolute? */ - if (absSource->scheme.first == NULL) { - return URI_ERROR_REMOVEBASE_REL_SOURCE; - } - - /* [01/50] if (A.scheme != Base.scheme) then */ - if (URI_FUNC(CompareRange)(&absSource->scheme, &absBase->scheme)) { - /* [02/50] T.scheme = A.scheme; */ - dest->scheme = absSource->scheme; - /* [03/50] T.authority = A.authority; */ - if (!URI_FUNC(CopyAuthority)(dest, absSource, memory)) { - return URI_ERROR_MALLOC; - } - /* [04/50] T.path = A.path; */ - if (!URI_FUNC(CopyPath)(dest, absSource, memory)) { - return URI_ERROR_MALLOC; - } - /* [05/50] else */ - } else { - /* [06/50] undef(T.scheme); */ - /* NOOP */ - /* [07/50] if (A.authority != Base.authority) then */ - if (!URI_FUNC(EqualsAuthority)(absSource, absBase)) { - /* [08/50] T.authority = A.authority; */ - if (!URI_FUNC(CopyAuthority)(dest, absSource, memory)) { - return URI_ERROR_MALLOC; - } - /* [09/50] T.path = A.path; */ - if (!URI_FUNC(CopyPath)(dest, absSource, memory)) { - return URI_ERROR_MALLOC; - } - /* [10/50] else */ - } else { - /* [11/50] if domainRootMode then */ - if (domainRootMode == URI_TRUE) { - /* [12/50] undef(T.authority); */ - /* NOOP */ - /* [13/50] if (first(A.path) == "") then */ - /* GROUPED */ - /* [14/50] T.path = "/." + A.path; */ - /* GROUPED */ - /* [15/50] else */ - /* GROUPED */ - /* [16/50] T.path = A.path; */ - /* GROUPED */ - /* [17/50] endif; */ - if (!URI_FUNC(CopyPath)(dest, absSource, memory)) { - return URI_ERROR_MALLOC; - } - dest->absolutePath = URI_TRUE; - - if (!URI_FUNC(FixAmbiguity)(dest, memory)) { - return URI_ERROR_MALLOC; - } - /* [18/50] else */ - } else { - const URI_TYPE(PathSegment) * sourceSeg = absSource->pathHead; - const URI_TYPE(PathSegment) * baseSeg = absBase->pathHead; - /* [19/50] bool pathNaked = true; */ - UriBool pathNaked = URI_TRUE; - /* [20/50] undef(last(Base.path)); */ - /* NOOP */ - /* [21/50] T.path = ""; */ - dest->absolutePath = URI_FALSE; - /* [22/50] while (first(A.path) == first(Base.path)) do */ - while ((sourceSeg != NULL) && (baseSeg != NULL) - && !URI_FUNC(CompareRange)(&sourceSeg->text, &baseSeg->text) - && !((sourceSeg->text.first == sourceSeg->text.afterLast) - && ((sourceSeg->next == NULL) != (baseSeg->next == NULL)))) { - /* [23/50] A.path++; */ - sourceSeg = sourceSeg->next; - /* [24/50] Base.path++; */ - baseSeg = baseSeg->next; - /* [25/50] endwhile; */ - } - /* [26/50] while defined(first(Base.path)) do */ - while ((baseSeg != NULL) && (baseSeg->next != NULL)) { - /* [27/50] Base.path++; */ - baseSeg = baseSeg->next; - /* [28/50] T.path += "../"; */ - if (!URI_FUNC(AppendSegment)(dest, URI_FUNC(ConstParent), - URI_FUNC(ConstParent) + 2, memory)) { - return URI_ERROR_MALLOC; - } - /* [29/50] pathNaked = false; */ - pathNaked = URI_FALSE; - /* [30/50] endwhile; */ - } - /* [31/50] while defined(first(A.path)) do */ - while (sourceSeg != NULL) { - /* [32/50] if pathNaked then */ - if (pathNaked == URI_TRUE) { - /* [33/50] if (first(A.path) contains ":") then */ - UriBool containsColon = URI_FALSE; - const URI_CHAR * ch = sourceSeg->text.first; - for (; ch < sourceSeg->text.afterLast; ch++) { - if (*ch == _UT(':')) { - containsColon = URI_TRUE; - break; - } - } - - if (containsColon) { - /* [34/50] T.path += "./"; */ - if (!URI_FUNC(AppendSegment)(dest, URI_FUNC(ConstPwd), - URI_FUNC(ConstPwd) + 1, memory)) { - return URI_ERROR_MALLOC; - } - /* [35/50] elseif (first(A.path) == "") then */ - } else if (sourceSeg->text.first == sourceSeg->text.afterLast) { - /* [36/50] T.path += "/."; */ - if (!URI_FUNC(AppendSegment)(dest, URI_FUNC(ConstPwd), - URI_FUNC(ConstPwd) + 1, memory)) { - return URI_ERROR_MALLOC; - } - /* [37/50] endif; */ - } - /* [38/50] endif; */ - } - /* [39/50] T.path += first(A.path); */ - if (!URI_FUNC(AppendSegment)(dest, sourceSeg->text.first, - sourceSeg->text.afterLast, memory)) { - return URI_ERROR_MALLOC; - } - /* [40/50] pathNaked = false; */ - pathNaked = URI_FALSE; - /* [41/50] A.path++; */ - sourceSeg = sourceSeg->next; - /* [42/50] if defined(first(A.path)) then */ - /* NOOP */ - /* [43/50] T.path += + "/"; */ - /* NOOP */ - /* [44/50] endif; */ - /* NOOP */ - /* [45/50] endwhile; */ - } - /* [46/50] endif; */ - } - /* [47/50] endif; */ - } - /* [48/50] endif; */ - } - /* [49/50] T.query = A.query; */ - dest->query = absSource->query; - /* [50/50] T.fragment = A.fragment; */ - dest->fragment = absSource->fragment; - - return URI_SUCCESS; + const URI_TYPE(Uri) * absSource, + const URI_TYPE(Uri) * absBase, + UriBool domainRootMode, + UriMemoryManager * memory) { + if (dest == NULL) { + return URI_ERROR_NULL; + } + URI_FUNC(ResetUri)(dest); + + if ((absSource == NULL) || (absBase == NULL)) { + return URI_ERROR_NULL; + } + + /* absBase absolute? */ + if (absBase->scheme.first == NULL) { + return URI_ERROR_REMOVEBASE_REL_BASE; + } + + /* absSource absolute? */ + if (absSource->scheme.first == NULL) { + return URI_ERROR_REMOVEBASE_REL_SOURCE; + } + + /* NOTE: The curly brackets here force deeper indent (and that's all) */ + { + { + { + /* clang-format off */ + /* [01/50] if (A.scheme != Base.scheme) then */ + /* clang-format on */ + if (URI_FUNC(CompareRange)(&absSource->scheme, &absBase->scheme)) { + /* clang-format off */ + /* [02/50] T.scheme = A.scheme; */ + /* clang-format on */ + dest->scheme = absSource->scheme; + /* clang-format off */ + /* [03/50] T.authority = A.authority; */ + /* clang-format on */ + if (!URI_FUNC(CopyAuthority)(dest, absSource, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [04/50] T.path = A.path; */ + /* clang-format on */ + if (!URI_FUNC(CopyPath)(dest, absSource, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [05/50] else */ + /* clang-format on */ + } else { + /* clang-format off */ + /* [06/50] undef(T.scheme); */ + /* clang-format on */ + /* NOOP */ + /* clang-format off */ + /* [07/50] if (A.authority != Base.authority) then */ + /* clang-format on */ + if (!URI_FUNC(EqualsAuthority)(absSource, absBase)) { + /* clang-format off */ + /* [08/50] T.authority = A.authority; */ + /* clang-format on */ + if (!URI_FUNC(CopyAuthority)(dest, absSource, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [09/50] T.path = A.path; */ + /* clang-format on */ + if (!URI_FUNC(CopyPath)(dest, absSource, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [10/50] else */ + /* clang-format on */ + } else { + /* clang-format off */ + /* [11/50] if domainRootMode then */ + /* clang-format on */ + if (domainRootMode == URI_TRUE) { + /* clang-format off */ + /* [12/50] undef(T.authority); */ + /* clang-format on */ + /* NOOP */ + /* clang-format off */ + /* [13/50] if (first(A.path) == "") then */ + /* clang-format on */ + /* GROUPED */ + /* clang-format off */ + /* [14/50] T.path = "/." + A.path; */ + /* clang-format on */ + /* GROUPED */ + /* clang-format off */ + /* [15/50] else */ + /* clang-format on */ + /* GROUPED */ + /* clang-format off */ + /* [16/50] T.path = A.path; */ + /* clang-format on */ + /* GROUPED */ + /* clang-format off */ + /* [17/50] endif; */ + /* clang-format on */ + if (!URI_FUNC(CopyPath)(dest, absSource, memory)) { + return URI_ERROR_MALLOC; + } + dest->absolutePath = URI_TRUE; + + if (!URI_FUNC(FixAmbiguity)(dest, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [18/50] else */ + /* clang-format on */ + } else { + const URI_TYPE(PathSegment) * sourceSeg = absSource->pathHead; + const URI_TYPE(PathSegment) * baseSeg = absBase->pathHead; + /* clang-format off */ + /* [19/50] bool pathNaked = true; */ + /* clang-format on */ + UriBool pathNaked = URI_TRUE; + /* clang-format off */ + /* [20/50] undef(last(Base.path)); */ + /* clang-format on */ + /* NOOP */ + /* clang-format off */ + /* [21/50] T.path = ""; */ + /* clang-format on */ + dest->absolutePath = URI_FALSE; + /* clang-format off */ + /* [22/50] while (first(A.path) == first(Base.path)) do */ + /* clang-format on */ + while ( + (sourceSeg != NULL) && (baseSeg != NULL) + && !URI_FUNC(CompareRange)(&sourceSeg->text, + &baseSeg->text) + && !((sourceSeg->text.first == sourceSeg->text.afterLast) + && ((sourceSeg->next == NULL) + != (baseSeg->next == NULL)))) { + /* clang-format off */ + /* [23/50] A.path++; */ + /* clang-format on */ + sourceSeg = sourceSeg->next; + /* clang-format off */ + /* [24/50] Base.path++; */ + /* clang-format on */ + baseSeg = baseSeg->next; + /* clang-format off */ + /* [25/50] endwhile; */ + /* clang-format on */ + } + /* clang-format off */ + /* [26/50] while defined(first(Base.path)) do */ + /* clang-format on */ + while ((baseSeg != NULL) && (baseSeg->next != NULL)) { + /* clang-format off */ + /* [27/50] Base.path++; */ + /* clang-format on */ + baseSeg = baseSeg->next; + /* clang-format off */ + /* [28/50] T.path += "../"; */ + /* clang-format on */ + if (!URI_FUNC(AppendSegment)(dest, URI_FUNC(ConstParent), + URI_FUNC(ConstParent) + 2, + memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [29/50] pathNaked = false; */ + /* clang-format on */ + pathNaked = URI_FALSE; + /* clang-format off */ + /* [30/50] endwhile; */ + /* clang-format on */ + } + /* clang-format off */ + /* [31/50] while defined(first(A.path)) do */ + /* clang-format on */ + while (sourceSeg != NULL) { + /* clang-format off */ + /* [32/50] if pathNaked then */ + /* clang-format on */ + if (pathNaked == URI_TRUE) { + /* clang-format off */ + /* [33/50] if (first(A.path) contains ":") then */ + /* clang-format on */ + UriBool containsColon = URI_FALSE; + const URI_CHAR * ch = sourceSeg->text.first; + for (; ch < sourceSeg->text.afterLast; ch++) { + if (*ch == _UT(':')) { + containsColon = URI_TRUE; + break; + } + } + + if (containsColon) { + /* clang-format off */ + /* [34/50] T.path += "./"; */ + /* clang-format on */ + if (!URI_FUNC(AppendSegment)( + dest, URI_FUNC(ConstPwd), + URI_FUNC(ConstPwd) + 1, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [35/50] elseif (first(A.path) == "") then */ + /* clang-format on */ + } else if (sourceSeg->text.first + == sourceSeg->text.afterLast) { + /* clang-format off */ + /* [36/50] T.path += "/."; */ + /* clang-format on */ + if (!URI_FUNC(AppendSegment)( + dest, URI_FUNC(ConstPwd), + URI_FUNC(ConstPwd) + 1, memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [37/50] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [38/50] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [39/50] T.path += first(A.path); */ + /* clang-format on */ + if (!URI_FUNC(AppendSegment)(dest, sourceSeg->text.first, + sourceSeg->text.afterLast, + memory)) { + return URI_ERROR_MALLOC; + } + /* clang-format off */ + /* [40/50] pathNaked = false; */ + /* clang-format on */ + pathNaked = URI_FALSE; + /* clang-format off */ + /* [41/50] A.path++; */ + /* clang-format on */ + sourceSeg = sourceSeg->next; + /* clang-format off */ + /* [42/50] if defined(first(A.path)) then */ + /* clang-format on */ + /* NOOP */ + /* clang-format off */ + /* [43/50] T.path += + "/"; */ + /* clang-format on */ + /* NOOP */ + /* clang-format off */ + /* [44/50] endif; */ + /* clang-format on */ + /* NOOP */ + /* clang-format off */ + /* [45/50] endwhile; */ + /* clang-format on */ + } + /* clang-format off */ + /* [46/50] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [47/50] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [48/50] endif; */ + /* clang-format on */ + } + /* clang-format off */ + /* [49/50] T.query = A.query; */ + /* clang-format on */ + dest->query = absSource->query; + /* clang-format off */ + /* [50/50] T.fragment = A.fragment; */ + /* clang-format on */ + dest->fragment = absSource->fragment; + } + } + } + return URI_SUCCESS; } - - -int URI_FUNC(RemoveBaseUri)(URI_TYPE(Uri) * dest, - const URI_TYPE(Uri) * absSource, - const URI_TYPE(Uri) * absBase, - UriBool domainRootMode) { - return URI_FUNC(RemoveBaseUriMm)(dest, absSource, absBase, - domainRootMode, NULL); +int URI_FUNC(RemoveBaseUri)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * absSource, + const URI_TYPE(Uri) * absBase, UriBool domainRootMode) { + return URI_FUNC(RemoveBaseUriMm)(dest, absSource, absBase, domainRootMode, NULL); } +int URI_FUNC(RemoveBaseUriMm)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * absSource, + const URI_TYPE(Uri) * absBase, UriBool domainRootMode, + UriMemoryManager * memory) { + int res; + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ -int URI_FUNC(RemoveBaseUriMm)(URI_TYPE(Uri) * dest, - const URI_TYPE(Uri) * absSource, - const URI_TYPE(Uri) * absBase, - UriBool domainRootMode, UriMemoryManager * memory) { - int res; - - URI_CHECK_MEMORY_MANAGER(memory); /* may return */ - - res = URI_FUNC(RemoveBaseUriImpl)(dest, absSource, - absBase, domainRootMode, memory); - if ((res != URI_SUCCESS) && (dest != NULL)) { - URI_FUNC(FreeUriMembersMm)(dest, memory); - } - return res; + res = URI_FUNC(RemoveBaseUriImpl)(dest, absSource, absBase, domainRootMode, memory); + if ((res != URI_SUCCESS) && (dest != NULL)) { + URI_FUNC(FreeUriMembersMm)(dest, memory); + } + return res; } - - #endif diff --git a/ext/uri/uriparser/src/UriVersion.c b/ext/uri/uriparser/src/UriVersion.c index 81295ca942b31..5c9a3e3f4b0af 100644 --- a/ext/uri/uriparser/src/UriVersion.c +++ b/ext/uri/uriparser/src/UriVersion.c @@ -46,42 +46,36 @@ #include #if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) /* Include SELF twice */ -# ifdef URI_ENABLE_ANSI -# define URI_PASS_ANSI 1 -# include "UriVersion.c" -# undef URI_PASS_ANSI -# endif -# ifdef URI_ENABLE_UNICODE -# define URI_PASS_UNICODE 1 -# include "UriVersion.c" -# undef URI_PASS_UNICODE -# endif +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriVersion.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriVersion.c" +# undef URI_PASS_UNICODE +# endif #else -# ifdef URI_PASS_ANSI -# include -# else -# include -# include -# endif - - - -#ifndef URI_DOXYGEN -# include -#endif - +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif +# ifndef URI_DOXYGEN +# include +# endif const URI_CHAR * URI_FUNC(BaseRuntimeVersion)(void) { -#if defined(URI_PASS_ANSI) - return URI_VER_ANSI; -#elif defined(URI_PASS_UNICODE) - return URI_VER_UNICODE; -#else -# error Either URI_PASS_ANSI or URI_PASS_UNICODE must be defined -#endif +# if defined(URI_PASS_ANSI) + return URI_VER_ANSI; +# elif defined(URI_PASS_UNICODE) + return URI_VER_UNICODE; +# else +# error Either URI_PASS_ANSI or URI_PASS_UNICODE must be defined +# endif } - - #endif From 88285c3333fbd2d6ebb1ede98c9d8ee6e0ef88fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 11 Nov 2025 17:05:13 +0100 Subject: [PATCH 018/252] ext/uri: Fix the distinction between an empty and a missing query/fragment for WHATWG URLs (#20208) --- .../whatwg/modification/fragment_success_hashmark.phpt | 2 +- .../whatwg/modification/query_success_question_mark.phpt | 2 +- ext/uri/uri_parser_whatwg.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/uri/tests/whatwg/modification/fragment_success_hashmark.phpt b/ext/uri/tests/whatwg/modification/fragment_success_hashmark.phpt index 12ce6d6e6b64d..4639d993df04d 100644 --- a/ext/uri/tests/whatwg/modification/fragment_success_hashmark.phpt +++ b/ext/uri/tests/whatwg/modification/fragment_success_hashmark.phpt @@ -15,5 +15,5 @@ var_dump($url2->toAsciiString()); ?> --EXPECT-- NULL -NULL +string(0) "" string(21) "https://example.com/#" diff --git a/ext/uri/tests/whatwg/modification/query_success_question_mark.phpt b/ext/uri/tests/whatwg/modification/query_success_question_mark.phpt index 18a1593a9819a..3e1bf5ab9dce5 100644 --- a/ext/uri/tests/whatwg/modification/query_success_question_mark.phpt +++ b/ext/uri/tests/whatwg/modification/query_success_question_mark.phpt @@ -15,5 +15,5 @@ var_dump($url2->toAsciiString()); ?> --EXPECT-- NULL -NULL +string(0) "" string(21) "https://example.com/?" diff --git a/ext/uri/uri_parser_whatwg.c b/ext/uri/uri_parser_whatwg.c index ef9bf0e020c34..2e9ffad22d463 100644 --- a/ext/uri/uri_parser_whatwg.c +++ b/ext/uri/uri_parser_whatwg.c @@ -431,7 +431,7 @@ static zend_result php_uri_parser_whatwg_path_read(void *uri, php_uri_component_ { const lxb_url_t *lexbor_uri = uri; - if (lexbor_uri->path.str.length) { + if (lexbor_uri->path.str.length > 0) { ZVAL_STRINGL(retval, (const char *) lexbor_uri->path.str.data, lexbor_uri->path.str.length); } else { ZVAL_EMPTY_STRING(retval); @@ -460,7 +460,7 @@ static zend_result php_uri_parser_whatwg_query_read(void *uri, php_uri_component { const lxb_url_t *lexbor_uri = uri; - if (lexbor_uri->query.length) { + if (lexbor_uri->query.data != NULL) { ZVAL_STRINGL(retval, (const char *) lexbor_uri->query.data, lexbor_uri->query.length); } else { ZVAL_NULL(retval); @@ -489,7 +489,7 @@ static zend_result php_uri_parser_whatwg_fragment_read(void *uri, php_uri_compon { const lxb_url_t *lexbor_uri = uri; - if (lexbor_uri->fragment.length) { + if (lexbor_uri->fragment.data != NULL) { ZVAL_STRINGL(retval, (const char *) lexbor_uri->fragment.data, lexbor_uri->fragment.length); } else { ZVAL_NULL(retval); From 27a2caa2329a0a5d8ce6f029e27fa900b3ba7b59 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 11 Nov 2025 18:11:54 +0000 Subject: [PATCH 019/252] zend_attributes: add const qualifiers (#20448) --- Zend/zend_attributes.c | 18 +++++++++--------- Zend/zend_attributes.h | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index b69e192701e48..cba95810ba496 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -38,7 +38,7 @@ static zend_object_handlers attributes_object_handlers_sensitive_parameter_value static HashTable internal_attributes; -uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope) +uint32_t zend_attribute_attribute_get_flags(const zend_attribute *attr, zend_class_entry *scope) { // TODO: More proper signature validation: Too many args, incorrect arg names. if (attr->argc > 0) { @@ -265,7 +265,7 @@ ZEND_METHOD(NoDiscard, __construct) } } -static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) +static zend_attribute *get_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset) { if (attributes) { zend_attribute *attr; @@ -280,7 +280,7 @@ static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, return NULL; } -static zend_attribute *get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset) +static zend_attribute *get_attribute_str(const HashTable *attributes, const char *str, size_t len, uint32_t offset) { if (attributes) { zend_attribute *attr; @@ -295,27 +295,27 @@ static zend_attribute *get_attribute_str(HashTable *attributes, const char *str, return NULL; } -ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname) +ZEND_API zend_attribute *zend_get_attribute(const HashTable *attributes, const zend_string *lcname) { return get_attribute(attributes, lcname, 0); } -ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len) +ZEND_API zend_attribute *zend_get_attribute_str(const HashTable *attributes, const char *str, size_t len) { return get_attribute_str(attributes, str, len, 0); } -ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) +ZEND_API zend_attribute *zend_get_parameter_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset) { return get_attribute(attributes, lcname, offset + 1); } -ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset) +ZEND_API zend_attribute *zend_get_parameter_attribute_str(const HashTable *attributes, const char *str, size_t len, uint32_t offset) { return get_attribute_str(attributes, str, len, offset + 1); } -ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope) +ZEND_API zend_result zend_get_attribute_value(zval *ret, const zend_attribute *attr, uint32_t i, zend_class_entry *scope) { if (i >= attr->argc) { return FAILURE; @@ -447,7 +447,7 @@ ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags) return smart_str_extract(&str); } -ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr) +ZEND_API bool zend_is_attribute_repeated(const HashTable *attributes, const zend_attribute *attr) { zend_attribute *other; diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 10227c2d1e8ef..f8b61ac9d1666 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -77,17 +77,17 @@ typedef struct _zend_internal_attribute { zend_string* (*validator)(zend_attribute *attr, uint32_t target, zend_class_entry *scope); } zend_internal_attribute; -ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname); -ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len); +ZEND_API zend_attribute *zend_get_attribute(const HashTable *attributes, const zend_string *lcname); +ZEND_API zend_attribute *zend_get_attribute_str(const HashTable *attributes, const char *str, size_t len); -ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset); -ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset); +ZEND_API zend_attribute *zend_get_parameter_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset); +ZEND_API zend_attribute *zend_get_parameter_attribute_str(const HashTable *attributes, const char *str, size_t len, uint32_t offset); -ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope); +ZEND_API zend_result zend_get_attribute_value(zval *ret, const zend_attribute *attr, uint32_t i, zend_class_entry *scope); ZEND_API zend_result zend_get_attribute_object(zval *out, zend_class_entry *attribute_ce, zend_attribute *attribute_data, zend_class_entry *scope, zend_string *filename); ZEND_API zend_string *zend_get_attribute_target_names(uint32_t targets); -ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr); +ZEND_API bool zend_is_attribute_repeated(const HashTable *attributes, const zend_attribute *attr); ZEND_API zend_internal_attribute *zend_mark_internal_attribute(zend_class_entry *ce); ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce, uint32_t flags); @@ -97,7 +97,7 @@ ZEND_API zend_attribute *zend_add_attribute( HashTable **attributes, zend_string *name, uint32_t argc, uint32_t flags, uint32_t offset, uint32_t lineno); -uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope); +uint32_t zend_attribute_attribute_get_flags(const zend_attribute *attr, zend_class_entry *scope); END_EXTERN_C() @@ -119,13 +119,13 @@ static zend_always_inline zend_attribute *zend_add_parameter_attribute(zend_func return zend_add_attribute(&func->common.attributes, name, argc, flags, offset + 1, 0); } -static zend_always_inline zend_attribute *zend_add_property_attribute(zend_class_entry *ce, zend_property_info *info, zend_string *name, uint32_t argc) +static zend_always_inline zend_attribute *zend_add_property_attribute(const zend_class_entry *ce, zend_property_info *info, zend_string *name, uint32_t argc) { uint32_t flags = ce->type != ZEND_USER_CLASS ? ZEND_ATTRIBUTE_PERSISTENT : 0; return zend_add_attribute(&info->attributes, name, argc, flags, 0, 0); } -static zend_always_inline zend_attribute *zend_add_class_constant_attribute(zend_class_entry *ce, zend_class_constant *c, zend_string *name, uint32_t argc) +static zend_always_inline zend_attribute *zend_add_class_constant_attribute(const zend_class_entry *ce, zend_class_constant *c, zend_string *name, uint32_t argc) { uint32_t flags = ce->type != ZEND_USER_CLASS ? ZEND_ATTRIBUTE_PERSISTENT : 0; return zend_add_attribute(&c->attributes, name, argc, flags, 0, 0); From 3cc36b0b5e76da23846452c04f3883a998a02836 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:47:15 +0100 Subject: [PATCH 020/252] Fix GH-20439: xml_set_default_handler() does not properly handle special characters in attributes when passing data to callback (#20453) We would need to escape the attributes, but there's no builtin method that we can call in libxml2 to do so in a way consistent with the attribute escape rules and expat. In fact, expat just repeats the input, while we reconstruct it. To fix the issue, and fix consistency with expat, we repeat the input as well. This works by seeking to the start and end of the tag and passing it to the default handler. This is fine for the parser because the parser used in ext/xml is always in non-progressive mode, so we have access to the entire input buffer. --- NEWS | 4 ++ ext/xml/compat.c | 106 ++++++++--------------------------- ext/xml/tests/gh20439_1.phpt | 24 ++++++++ ext/xml/tests/gh20439_2.phpt | 22 ++++++++ 4 files changed, 74 insertions(+), 82 deletions(-) create mode 100644 ext/xml/tests/gh20439_1.phpt create mode 100644 ext/xml/tests/gh20439_2.phpt diff --git a/NEWS b/NEWS index 28263cc2c0d24..7d0fc8ce6309d 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,10 @@ PHP NEWS - Tidy: . Fixed bug GH-20374 (PHP with tidy and custom-tags). (ndossche) +- XML: + . Fixed bug GH-20439 (xml_set_default_handler() does not properly handle + special characters in attributes when passing data to callback). (ndossche) + 20 Nov 2025, PHP 8.3.28 - Core: diff --git a/ext/xml/compat.c b/ext/xml/compat.c index 3de77d0723e5f..25add45f0340a 100644 --- a/ext/xml/compat.c +++ b/ext/xml/compat.c @@ -45,6 +45,28 @@ _qualify_namespace(XML_Parser parser, const xmlChar *name, const xmlChar *URI, x } } +static void start_element_emit_default(XML_Parser parser) +{ + if (parser->h_default) { + /* Grammar does not allow embedded '<' and '>' in elements, so we can seek to the start and end positions. + * Since the parser in the current mode mode is non-progressive, it contains the entire input. */ + const xmlChar *cur = parser->parser->input->cur; + const xmlChar *end = cur; + for (const xmlChar *base = parser->parser->input->base; cur > base && *cur != '<'; cur--); + if (*end == '/') { + /* BC: Keep split between start & end element. + * TODO: In the future this could be aligned with expat and only emit a start event, or vice versa. + * See gh20439_2.phpt */ + xmlChar *tmp = BAD_CAST estrndup((const char *) cur, end - cur + 1); + tmp[end - cur] = '>'; + parser->h_default(parser->user, tmp, end - cur + 1); + efree(tmp); + } else { + parser->h_default(parser->user, cur, end - cur + 1); + } + } +} + static void _start_element_handler(void *user, const xmlChar *name, const xmlChar **attributes) { @@ -52,29 +74,7 @@ _start_element_handler(void *user, const xmlChar *name, const xmlChar **attribut xmlChar *qualified_name = NULL; if (parser->h_start_element == NULL) { - if (parser->h_default) { - int attno = 0; - - qualified_name = xmlStrncatNew((xmlChar *)"<", name, xmlStrlen(name)); - if (attributes) { - while (attributes[attno] != NULL) { - int att_len; - char *att_string, *att_name, *att_value; - - att_name = (char *)attributes[attno++]; - att_value = (char *)attributes[attno++]; - - att_len = spprintf(&att_string, 0, " %s=\"%s\"", att_name, att_value); - - qualified_name = xmlStrncat(qualified_name, (xmlChar *)att_string, att_len); - efree(att_string); - } - - } - qualified_name = xmlStrncat(qualified_name, (xmlChar *)">", 1); - parser->h_default(parser->user, (const XML_Char *) qualified_name, xmlStrlen(qualified_name)); - xmlFree(qualified_name); - } + start_element_emit_default(parser); return; } @@ -104,65 +104,7 @@ _start_element_handler_ns(void *user, const xmlChar *name, const xmlChar *prefix } if (parser->h_start_element == NULL) { - if (parser->h_default) { - - if (prefix) { - qualified_name = xmlStrncatNew((xmlChar *)"<", prefix, xmlStrlen(prefix)); - qualified_name = xmlStrncat(qualified_name, (xmlChar *)":", 1); - qualified_name = xmlStrncat(qualified_name, name, xmlStrlen(name)); - } else { - qualified_name = xmlStrncatNew((xmlChar *)"<", name, xmlStrlen(name)); - } - - if (namespaces) { - int i, j; - for (i = 0,j = 0;j < nb_namespaces;j++) { - int ns_len; - char *ns_string, *ns_prefix, *ns_url; - - ns_prefix = (char *) namespaces[i++]; - ns_url = (char *) namespaces[i++]; - - if (ns_prefix) { - ns_len = spprintf(&ns_string, 0, " xmlns:%s=\"%s\"", ns_prefix, ns_url); - } else { - ns_len = spprintf(&ns_string, 0, " xmlns=\"%s\"", ns_url); - } - qualified_name = xmlStrncat(qualified_name, (xmlChar *)ns_string, ns_len); - - efree(ns_string); - } - } - - if (attributes) { - for (i = 0; i < nb_attributes; i += 1) { - int att_len; - char *att_string, *att_name, *att_value, *att_prefix, *att_valueend; - - att_name = (char *) attributes[y++]; - att_prefix = (char *)attributes[y++]; - y++; - att_value = (char *)attributes[y++]; - att_valueend = (char *)attributes[y++]; - - if (att_prefix) { - att_len = spprintf(&att_string, 0, " %s:%s=\"", att_prefix, att_name); - } else { - att_len = spprintf(&att_string, 0, " %s=\"", att_name); - } - - qualified_name = xmlStrncat(qualified_name, (xmlChar *)att_string, att_len); - qualified_name = xmlStrncat(qualified_name, (xmlChar *)att_value, att_valueend - att_value); - qualified_name = xmlStrncat(qualified_name, (xmlChar *)"\"", 1); - - efree(att_string); - } - - } - qualified_name = xmlStrncat(qualified_name, (xmlChar *)">", 1); - parser->h_default(parser->user, (const XML_Char *) qualified_name, xmlStrlen(qualified_name)); - xmlFree(qualified_name); - } + start_element_emit_default(parser); return; } _qualify_namespace(parser, name, URI, &qualified_name); diff --git a/ext/xml/tests/gh20439_1.phpt b/ext/xml/tests/gh20439_1.phpt new file mode 100644 index 0000000000000..cda6803e9d078 --- /dev/null +++ b/ext/xml/tests/gh20439_1.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-20439 (xml_set_default_handler() does not properly handle special characters in attributes when passing data to callback) +--EXTENSIONS-- +xml +--FILE-- +"; +$inputs = str_split($input); + +// Test chunked parser wrt non-progressive parser +foreach ($inputs as $input) { + xml_parse($x, $input, false); +} +xml_parse($x, "", true); + +?> +--EXPECT-- +string(12) "" +string(71) "" +string(6) "" diff --git a/ext/xml/tests/gh20439_2.phpt b/ext/xml/tests/gh20439_2.phpt new file mode 100644 index 0000000000000..dce4f5976a140 --- /dev/null +++ b/ext/xml/tests/gh20439_2.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-20439 (xml_set_default_handler() does not properly handle special characters in attributes when passing data to callback) - closing solidus variant +--EXTENSIONS-- +xml +--SKIPIF-- + +--FILE-- +"; +xml_parse($x, $input, true); + +?> +--EXPECT-- +string(29) "" +string(10) "" From c95a944c1b72dff922061aeee6da36f38aaa04e5 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 01:15:27 +0100 Subject: [PATCH 021/252] Zend: Factor out is_sensitive check --- Zend/zend_builtin_functions.c | 45 +++++++++++++---------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index d0733fbd8c8b5..9d83a4917d974 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1715,6 +1715,18 @@ ZEND_FUNCTION(get_defined_constants) } /* }}} */ +static bool backtrace_is_arg_sensitive(const zend_execute_data *call, uint32_t offset) +{ + zend_attribute *attribute = zend_get_parameter_attribute_str( + call->func->common.attributes, + "sensitiveparameter", + sizeof("sensitiveparameter") - 1, + offset + ); + + return attribute != NULL; +} + static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /* {{{ */ { uint32_t num_args = ZEND_CALL_NUM_ARGS(call); @@ -1738,14 +1750,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) / zend_string *arg_name = call->func->op_array.vars[i]; zval original_arg; zval *arg = zend_hash_find_ex_ind(call->symbol_table, arg_name, 1); - zend_attribute *attribute = zend_get_parameter_attribute_str( - call->func->common.attributes, - "sensitiveparameter", - sizeof("sensitiveparameter") - 1, - i - ); - - bool is_sensitive = attribute != NULL; + bool is_sensitive = backtrace_is_arg_sensitive(call, i); if (arg) { ZVAL_DEREF(arg); @@ -1770,13 +1775,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) / } else { while (i < first_extra_arg) { zval original_arg; - zend_attribute *attribute = zend_get_parameter_attribute_str( - call->func->common.attributes, - "sensitiveparameter", - sizeof("sensitiveparameter") - 1, - i - ); - bool is_sensitive = attribute != NULL; + bool is_sensitive = backtrace_is_arg_sensitive(call, i); if (EXPECTED(Z_TYPE_INFO_P(p) != IS_UNDEF)) { zval *arg = p; @@ -1809,13 +1808,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) / bool is_sensitive = 0; if (i < call->func->common.num_args || call->func->common.fn_flags & ZEND_ACC_VARIADIC) { - zend_attribute *attribute = zend_get_parameter_attribute_str( - call->func->common.attributes, - "sensitiveparameter", - sizeof("sensitiveparameter") - 1, - MIN(i, call->func->common.num_args) - ); - is_sensitive = attribute != NULL; + is_sensitive = backtrace_is_arg_sensitive(call, MIN(i, call->func->common.num_args)); } if (EXPECTED(Z_TYPE_INFO_P(p) != IS_UNDEF)) { @@ -1852,13 +1845,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) / ZEND_ASSERT(call->func->common.fn_flags & ZEND_ACC_VARIADIC); - zend_attribute *attribute = zend_get_parameter_attribute_str( - call->func->common.attributes, - "sensitiveparameter", - sizeof("sensitiveparameter") - 1, - call->func->common.num_args - ); - bool is_sensitive = attribute != NULL; + bool is_sensitive = backtrace_is_arg_sensitive(call, call->func->common.num_args); SEPARATE_ARRAY(arg_array); ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(call->extra_named_params, name, arg) { From f15c7121fb956e84ace73eaef2df9ae442d4c3c1 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 01:18:22 +0100 Subject: [PATCH 022/252] Zend: Use object_init_with_constructor() for SensitiveParameterValue --- Zend/zend_builtin_functions.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 9d83a4917d974..a1b8a2a161e75 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1761,8 +1761,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) / if (is_sensitive) { zval redacted_arg; - object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value); - zend_call_known_function(Z_OBJCE_P(&redacted_arg)->constructor, Z_OBJ_P(&redacted_arg), Z_OBJCE_P(&redacted_arg), NULL, 1, &original_arg, NULL); + object_init_with_constructor(&redacted_arg, zend_ce_sensitive_parameter_value, 1, &original_arg, NULL); ZEND_HASH_FILL_SET(&redacted_arg); } else { Z_TRY_ADDREF_P(&original_arg); @@ -1787,8 +1786,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) / if (is_sensitive) { zval redacted_arg; - object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value); - zend_call_known_function(Z_OBJCE_P(&redacted_arg)->constructor, Z_OBJ_P(&redacted_arg), Z_OBJCE_P(&redacted_arg), NULL, 1, &original_arg, NULL); + object_init_with_constructor(&redacted_arg, zend_ce_sensitive_parameter_value, 1, &original_arg, NULL); ZEND_HASH_FILL_SET(&redacted_arg); } else { Z_TRY_ADDREF_P(&original_arg); @@ -1821,8 +1819,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) / if (is_sensitive) { zval redacted_arg; - object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value); - zend_call_known_function(Z_OBJCE_P(&redacted_arg)->constructor, Z_OBJ_P(&redacted_arg), Z_OBJCE_P(&redacted_arg), NULL, 1, &original_arg, NULL); + object_init_with_constructor(&redacted_arg, zend_ce_sensitive_parameter_value, 1, &original_arg, NULL); ZEND_HASH_FILL_SET(&redacted_arg); } else { Z_TRY_ADDREF_P(&original_arg); @@ -1852,8 +1849,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) / ZVAL_DEREF(arg); if (is_sensitive) { zval redacted_arg; - object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value); - zend_call_method_with_1_params(Z_OBJ_P(&redacted_arg), zend_ce_sensitive_parameter_value, &zend_ce_sensitive_parameter_value->constructor, "__construct", NULL, arg); + object_init_with_constructor(&redacted_arg, zend_ce_sensitive_parameter_value, 1, arg, NULL); zend_hash_add_new(Z_ARRVAL_P(arg_array), name, &redacted_arg); } else { Z_TRY_ADDREF_P(arg); From c145c2018eadf833eaadee69253dec9c3a285ab6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 23:03:10 +0100 Subject: [PATCH 023/252] xml: Fix unused variable warning --- ext/xml/compat.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/xml/compat.c b/ext/xml/compat.c index a70cb35e3717f..3232d5e9a859c 100644 --- a/ext/xml/compat.c +++ b/ext/xml/compat.c @@ -60,7 +60,6 @@ static void start_element_handler(void *user, const xmlChar *name, const xmlChar **attributes) { XML_Parser parser = (XML_Parser) user; - xmlChar *qualified_name = NULL; if (parser->h_start_element == NULL) { start_element_emit_default(parser); From 2711864e82e58f5825c80238dd29d041f1a84485 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 11 Nov 2025 15:22:25 -0800 Subject: [PATCH 024/252] Revert "Deprecate returning non-string values from a user output handler (#18932)" (#20455) This partially reverts commit d8577d9bfbb89bc2924b0bcb60a8e7fb5fd5267a, and reverts parts of 07f1cfd9b01ff0f3720c1a5580b9e263eec5fce1. --- UPGRADING | 8 +- main/output.c | 21 +-- tests/output/ob_start_basic_002.phpt | 9 +- .../exception_handler.phpt | 147 ----------------- .../exception_handler_nested.phpt | 143 ---------------- .../handler_false_removed.phpt | 23 --- .../handler_is_stringable_removed.phpt | 30 ---- .../handler_non_stringable_removed.phpt | 32 ---- .../handler_true_removed.phpt | 23 --- .../handler_zero_removed.phpt | 23 --- .../multiple_handlers.phpt | 106 ------------ .../exception_handler.phpt | 153 ------------------ .../exception_handler_nested.phpt | 149 ----------------- .../handler_false_removed.phpt | 26 --- .../handler_is_stringable_removed.phpt | 33 ---- .../handler_non_stringable_removed.phpt | 35 ---- .../handler_true_removed.phpt | 26 --- .../handler_zero_removed.phpt | 26 --- .../multiple_handlers.phpt | 115 ------------- 19 files changed, 5 insertions(+), 1123 deletions(-) delete mode 100644 tests/output/ob_start_callback_bad_return/exception_handler.phpt delete mode 100644 tests/output/ob_start_callback_bad_return/exception_handler_nested.phpt delete mode 100644 tests/output/ob_start_callback_bad_return/handler_false_removed.phpt delete mode 100644 tests/output/ob_start_callback_bad_return/handler_is_stringable_removed.phpt delete mode 100644 tests/output/ob_start_callback_bad_return/handler_non_stringable_removed.phpt delete mode 100644 tests/output/ob_start_callback_bad_return/handler_true_removed.phpt delete mode 100644 tests/output/ob_start_callback_bad_return/handler_zero_removed.phpt delete mode 100644 tests/output/ob_start_callback_bad_return/multiple_handlers.phpt delete mode 100644 tests/output/ob_start_callback_output_and_bad_return/exception_handler.phpt delete mode 100644 tests/output/ob_start_callback_output_and_bad_return/exception_handler_nested.phpt delete mode 100644 tests/output/ob_start_callback_output_and_bad_return/handler_false_removed.phpt delete mode 100644 tests/output/ob_start_callback_output_and_bad_return/handler_is_stringable_removed.phpt delete mode 100644 tests/output/ob_start_callback_output_and_bad_return/handler_non_stringable_removed.phpt delete mode 100644 tests/output/ob_start_callback_output_and_bad_return/handler_true_removed.phpt delete mode 100644 tests/output/ob_start_callback_output_and_bad_return/handler_zero_removed.phpt delete mode 100644 tests/output/ob_start_callback_output_and_bad_return/multiple_handlers.phpt diff --git a/UPGRADING b/UPGRADING index b18b6825224b0..f95ca587594a5 100644 --- a/UPGRADING +++ b/UPGRADING @@ -361,16 +361,10 @@ PHP 8.5 UPGRADE NOTES ======================================== - Core: - . Returning a non-string from a user output handler is deprecated. The - deprecation warning will bypass the handler with the bad return to ensure - it is visible; if there are nested output handlers the next one will still - be used. - RFC: https://wiki.php.net/rfc/deprecations_php_8_4 . Trying to produce output (e.g. with `echo`) within a user output handler is deprecated. The deprecation warning will bypass the handler producing the output to ensure it is visible; if there are nested output handlers the next - one will still be used. If a user output handler returns a non-string and - produces output, the warning about producing an output is emitted first. + one will still be used. RFC: https://wiki.php.net/rfc/deprecations_php_8_4 . Non-canonical cast names (boolean), (integer), (double), and (binary) have been deprecated, use (bool), (int), (float), and (string) respectively. diff --git a/main/output.c b/main/output.c index c75b09e86c18b..653b457e9b641 100644 --- a/main/output.c +++ b/main/output.c @@ -956,7 +956,6 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl if (handler->flags & PHP_OUTPUT_HANDLER_USER) { zval ob_args[2]; zval retval; - ZVAL_UNDEF(&retval); /* ob_data */ ZVAL_STRINGL(&ob_args[0], handler->buffer.data, handler->buffer.used); @@ -969,17 +968,10 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl handler->func.user->fci.retval = &retval; if (SUCCESS == zend_call_function(&handler->func.user->fci, &handler->func.user->fcc) && Z_TYPE(retval) != IS_UNDEF) { - if (Z_TYPE(retval) != IS_STRING || handler->flags & PHP_OUTPUT_HANDLER_PRODUCED_OUTPUT) { + if (handler->flags & PHP_OUTPUT_HANDLER_PRODUCED_OUTPUT) { // Make sure that we don't get lost in the current output buffer // by disabling it handler->flags |= PHP_OUTPUT_HANDLER_DISABLED; - // Make sure we keep a reference to the handler name in - // case - // * The handler produced output *and* returned a non-string - // * The first deprecation message causes the handler to - // be removed - zend_string *handler_name = handler->name; - zend_string_addref(handler_name); if (handler->flags & PHP_OUTPUT_HANDLER_PRODUCED_OUTPUT) { // The handler might not always produce output handler->flags &= ~PHP_OUTPUT_HANDLER_PRODUCED_OUTPUT; @@ -987,18 +979,9 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl NULL, E_DEPRECATED, "Producing output from user output handler %s is deprecated", - ZSTR_VAL(handler_name) - ); - } - if (Z_TYPE(retval) != IS_STRING) { - php_error_docref( - NULL, - E_DEPRECATED, - "Returning a non-string result from user output handler %s is deprecated", - ZSTR_VAL(handler_name) + ZSTR_VAL(handler->name) ); } - zend_string_release(handler_name); // Check if the handler is still in the list of handlers to // determine if the PHP_OUTPUT_HANDLER_DISABLED flag can diff --git a/tests/output/ob_start_basic_002.phpt b/tests/output/ob_start_basic_002.phpt index e9af2b5e1904c..700dab5d3c381 100644 --- a/tests/output/ob_start_basic_002.phpt +++ b/tests/output/ob_start_basic_002.phpt @@ -35,24 +35,19 @@ foreach ($callbacks as $callback) { } ?> ---EXPECTF-- +--EXPECT-- --> Use callback 'return_empty_string': --> Use callback 'return_false': - -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_false is deprecated in %s on line %d My output. --> Use callback 'return_null': -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_null is deprecated in %s on line %d - --> Use callback 'return_string': I stole your output. --> Use callback 'return_zero': - -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_zero is deprecated in %s on line %d 0 + diff --git a/tests/output/ob_start_callback_bad_return/exception_handler.phpt b/tests/output/ob_start_callback_bad_return/exception_handler.phpt deleted file mode 100644 index eef3fccc77ec0..0000000000000 --- a/tests/output/ob_start_callback_bad_return/exception_handler.phpt +++ /dev/null @@ -1,147 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation converted to exception [bad return] ---FILE-- -val; - } -} - -$log = []; - -set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) { - throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); -}); - -function return_null($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return null; -} - -function return_false($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return false; -} - -function return_true($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return true; -} - -function return_zero($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return 0; -} - -function return_non_stringable($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return new NotStringable($string); -} - -function return_stringable($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return new IsStringable($string); -} - -$cases = [ - 'return_null', - 'return_false', - 'return_true', - 'return_zero', - 'return_non_stringable', - 'return_stringable', -]; -foreach ($cases as $case) { - $log = []; - echo "\n\nTesting: $case\n"; - ob_start($case); - echo "Inside of $case\n"; - try { - ob_end_flush(); - } catch (\ErrorException $e) { - echo $e . "\n"; - } - echo "\nEnd of $case, log was:\n"; - echo implode("\n", $log); -} - -?> ---EXPECTF-- -Testing: return_null -ErrorException: ob_end_flush(): Returning a non-string result from user output handler return_null is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, %d) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_null, log was: -return_null: <<>> - -Testing: return_false -Inside of return_false -ErrorException: ob_end_flush(): Returning a non-string result from user output handler return_false is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, %d) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_false, log was: -return_false: <<>> - -Testing: return_true -ErrorException: ob_end_flush(): Returning a non-string result from user output handler return_true is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, %d) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_true, log was: -return_true: <<>> - -Testing: return_zero -0ErrorException: ob_end_flush(): Returning a non-string result from user output handler return_zero is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, %d) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_zero, log was: -return_zero: <<>> - -Testing: return_non_stringable -ErrorException: ob_end_flush(): Returning a non-string result from user output handler return_non_stringable is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, 69) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_non_stringable, log was: -return_non_stringable: <<>> - -Testing: return_stringable -ErrorException: ob_end_flush(): Returning a non-string result from user output handler return_stringable is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, 69) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_stringable, log was: -return_stringable: <<>> diff --git a/tests/output/ob_start_callback_bad_return/exception_handler_nested.phpt b/tests/output/ob_start_callback_bad_return/exception_handler_nested.phpt deleted file mode 100644 index 64d7f805687b2..0000000000000 --- a/tests/output/ob_start_callback_bad_return/exception_handler_nested.phpt +++ /dev/null @@ -1,143 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with nested deprecation converted to exception [bad return] ---FILE-- -val; - } -} - -$log = []; - -set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) { - throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); -}); - -function return_null($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return null; -} - -function return_false($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return false; -} - -function return_true($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return true; -} - -function return_zero($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return 0; -} - -function return_non_stringable($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return new NotStringable($string); -} - -function return_stringable($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return new IsStringable($string); -} - -ob_start('return_null'); -ob_start('return_false'); -ob_start('return_true'); -ob_start('return_zero'); -ob_start('return_non_stringable'); -ob_start('return_stringable'); - -echo "In all of them\n\n"; -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_stringable handler\n\n"; - -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_non_stringable handler\n\n"; - -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_zero handler\n\n"; - -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_true handler\n\n"; - -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_false handler\n\n"; - -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_null handler\n\n"; - -echo "All handlers are over\n\n"; -echo implode("\n", $log); - -?> ---EXPECT-- -ob_end_flush(): Returning a non-string result from user output handler return_null is deprecated -Ended return_null handler - -All handlers are over - -return_stringable: <<>> -return_non_stringable: <<>> -return_zero: <<>> -return_true: <<<0ob_end_flush(): Returning a non-string result from user output handler return_zero is deprecated -Ended return_zero handler - ->>> -return_false: <<>> -return_null: <<>> diff --git a/tests/output/ob_start_callback_bad_return/handler_false_removed.phpt b/tests/output/ob_start_callback_bad_return/handler_false_removed.phpt deleted file mode 100644 index 32702a58fcc14..0000000000000 --- a/tests/output/ob_start_callback_bad_return/handler_false_removed.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation when OOM triggers handler removal (handler returns false) ---INI-- -memory_limit=2M ---FILE-- - ---EXPECTF-- -Deprecated: main(): Returning a non-string result from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/tests/output/ob_start_callback_bad_return/handler_is_stringable_removed.phpt b/tests/output/ob_start_callback_bad_return/handler_is_stringable_removed.phpt deleted file mode 100644 index 0d87358da1b9d..0000000000000 --- a/tests/output/ob_start_callback_bad_return/handler_is_stringable_removed.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation when OOM triggers handler removal (handler returns stringable object) ---INI-- -memory_limit=2M ---FILE-- -val; - } -} - -ob_start(function() { - // We are out of memory, now trigger a deprecation - return new IsStringable(""); -}); - -$a = []; -// trigger OOM in a resize operation -while (1) { - $a[] = 1; -} - -?> ---EXPECTF-- -Deprecated: main(): Returning a non-string result from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/tests/output/ob_start_callback_bad_return/handler_non_stringable_removed.phpt b/tests/output/ob_start_callback_bad_return/handler_non_stringable_removed.phpt deleted file mode 100644 index 65d8ccfbcba61..0000000000000 --- a/tests/output/ob_start_callback_bad_return/handler_non_stringable_removed.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation when OOM triggers handler removal (handler returns non-stringable object) ---INI-- -memory_limit=2M ---FILE-- - ---EXPECTF-- -Deprecated: main(): Returning a non-string result from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d - -Fatal error: Uncaught Error: Object of class NotStringable could not be converted to string in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d diff --git a/tests/output/ob_start_callback_bad_return/handler_true_removed.phpt b/tests/output/ob_start_callback_bad_return/handler_true_removed.phpt deleted file mode 100644 index 5ad19826c4ac7..0000000000000 --- a/tests/output/ob_start_callback_bad_return/handler_true_removed.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation when OOM triggers handler removal (handler returns true) ---INI-- -memory_limit=2M ---FILE-- - ---EXPECTF-- -Deprecated: main(): Returning a non-string result from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/tests/output/ob_start_callback_bad_return/handler_zero_removed.phpt b/tests/output/ob_start_callback_bad_return/handler_zero_removed.phpt deleted file mode 100644 index 1bc7279c71d35..0000000000000 --- a/tests/output/ob_start_callback_bad_return/handler_zero_removed.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation when OOM triggers handler removal (handler returns zero) ---INI-- -memory_limit=2M ---FILE-- - ---EXPECTF-- -Deprecated: main(): Returning a non-string result from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/tests/output/ob_start_callback_bad_return/multiple_handlers.phpt b/tests/output/ob_start_callback_bad_return/multiple_handlers.phpt deleted file mode 100644 index 100abe7d79d18..0000000000000 --- a/tests/output/ob_start_callback_bad_return/multiple_handlers.phpt +++ /dev/null @@ -1,106 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with multiple nested handlers with bad return values ---FILE-- ->>"; - return $string; -} - -function return_empty_string($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return ""; -} - -function return_false($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return false; -} - -function return_true($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return true; -} - -function return_null($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return null; -} - -function return_string($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return "I stole your output."; -} - -function return_zero($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - return 0; -} - -ob_start('return_given_string'); -ob_start('return_empty_string'); -ob_start('return_false'); -ob_start('return_true'); -ob_start('return_null'); -ob_start('return_string'); -ob_start('return_zero'); - -echo "Testing..."; - -ob_end_flush(); -ob_end_flush(); -ob_end_flush(); -ob_end_flush(); -ob_end_flush(); -ob_end_flush(); -ob_end_flush(); - -echo "\n\nLog:\n"; -echo implode("\n", $log); -?> ---EXPECTF-- -Deprecated: ob_end_flush(): Producing output from user output handler return_given_string is deprecated in %s on line %d3 - -Deprecated: ob_end_flush(): Producing output from user output handler return_empty_string is deprecated in %s on line %d2 - - -Log: -return_zero: <<>> -return_string: <<< -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_zero is deprecated in %s on line %d -0>>> -return_null: <<< -Deprecated: ob_end_flush(): Producing output from user output handler return_string is deprecated in %s on line %d -I stole your output.>>> -return_true: <<< -Deprecated: ob_end_flush(): Producing output from user output handler return_null is deprecated in %s on line %d - -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_null is deprecated in %s on line %d ->>> -return_false: <<< -Deprecated: ob_end_flush(): Producing output from user output handler return_true is deprecated in %s on line %d - -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_true is deprecated in %s on line %d ->>> -return_empty_string: <<< -Deprecated: ob_end_flush(): Producing output from user output handler return_false is deprecated in %s on line %d - -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_false is deprecated in %s on line %d - -Deprecated: ob_end_flush(): Producing output from user output handler return_true is deprecated in %s on line %d - -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_true is deprecated in %s on line %d ->>> -return_given_string: <<< -Deprecated: ob_end_flush(): Producing output from user output handler return_empty_string is deprecated in %s on line %d2 ->>> diff --git a/tests/output/ob_start_callback_output_and_bad_return/exception_handler.phpt b/tests/output/ob_start_callback_output_and_bad_return/exception_handler.phpt deleted file mode 100644 index 2b018c792a52c..0000000000000 --- a/tests/output/ob_start_callback_output_and_bad_return/exception_handler.phpt +++ /dev/null @@ -1,153 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation converted to exception [bad return + produce output] ---FILE-- -val; - } -} - -$log = []; - -set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) { - throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); -}); - -function return_null($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return null; -} - -function return_false($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return false; -} - -function return_true($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return true; -} - -function return_zero($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return 0; -} - -function return_non_stringable($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return new NotStringable($string); -} - -function return_stringable($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return new IsStringable($string); -} - -$cases = [ - 'return_null', - 'return_false', - 'return_true', - 'return_zero', - 'return_non_stringable', - 'return_stringable', -]; -foreach ($cases as $case) { - $log = []; - echo "\n\nTesting: $case\n"; - ob_start($case); - echo "Inside of $case\n"; - try { - ob_end_flush(); - } catch (\ErrorException $e) { - echo $e . "\n"; - } - echo "\nEnd of $case, log was:\n"; - echo implode("\n", $log); -} - -?> ---EXPECTF-- -Testing: return_null -ErrorException: ob_end_flush(): Producing output from user output handler return_null is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, %d) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_null, log was: -return_null: <<>> - -Testing: return_false -Inside of return_false -return_falseErrorException: ob_end_flush(): Producing output from user output handler return_false is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, %d) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_false, log was: -return_false: <<>> - -Testing: return_true -ErrorException: ob_end_flush(): Producing output from user output handler return_true is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, %d) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_true, log was: -return_true: <<>> - -Testing: return_zero -0ErrorException: ob_end_flush(): Producing output from user output handler return_zero is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, %d) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_zero, log was: -return_zero: <<>> - -Testing: return_non_stringable -ErrorException: ob_end_flush(): Producing output from user output handler return_non_stringable is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, %d) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_non_stringable, log was: -return_non_stringable: <<>> - -Testing: return_stringable -ErrorException: ob_end_flush(): Producing output from user output handler return_stringable is deprecated in %s:%d -Stack trace: -#0 [internal function]: {closure:%s:%d}(8192, 'ob_end_flush():...', %s, %d) -#1 %s(%d): ob_end_flush() -#2 {main} - -End of return_stringable, log was: -return_stringable: <<>> diff --git a/tests/output/ob_start_callback_output_and_bad_return/exception_handler_nested.phpt b/tests/output/ob_start_callback_output_and_bad_return/exception_handler_nested.phpt deleted file mode 100644 index 7eb060acc2133..0000000000000 --- a/tests/output/ob_start_callback_output_and_bad_return/exception_handler_nested.phpt +++ /dev/null @@ -1,149 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with nested deprecation converted to exception [bad return + produce output] ---FILE-- -val; - } -} - -$log = []; - -set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) { - throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); -}); - -function return_null($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return null; -} - -function return_false($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return false; -} - -function return_true($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return true; -} - -function return_zero($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return 0; -} - -function return_non_stringable($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return new NotStringable($string); -} - -function return_stringable($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return new IsStringable($string); -} - -ob_start('return_null'); -ob_start('return_false'); -ob_start('return_true'); -ob_start('return_zero'); -ob_start('return_non_stringable'); -ob_start('return_stringable'); - -echo "In all of them\n\n"; -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_stringable handler\n\n"; - -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_non_stringable handler\n\n"; - -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_zero handler\n\n"; - -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_true handler\n\n"; - -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_false handler\n\n"; - -try { - ob_end_flush(); -} catch (\ErrorException $e) { - echo $e->getMessage() . "\n"; -} -echo "Ended return_null handler\n\n"; - -echo "All handlers are over\n\n"; -echo implode("\n", $log); - -?> ---EXPECT-- -ob_end_flush(): Producing output from user output handler return_null is deprecated -Ended return_null handler - -All handlers are over - -return_stringable: <<>> -return_non_stringable: <<>> -return_zero: <<>> -return_true: <<<0ob_end_flush(): Producing output from user output handler return_zero is deprecated -Ended return_zero handler - ->>> -return_false: <<>> -return_null: <<>> diff --git a/tests/output/ob_start_callback_output_and_bad_return/handler_false_removed.phpt b/tests/output/ob_start_callback_output_and_bad_return/handler_false_removed.phpt deleted file mode 100644 index 78c736b80cd14..0000000000000 --- a/tests/output/ob_start_callback_output_and_bad_return/handler_false_removed.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation when OOM triggers handler removal (handler returns false + produces output) ---INI-- -memory_limit=2M ---FILE-- - ---EXPECTF-- -Deprecated: main(): Producing output from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Deprecated: main(): Returning a non-string result from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/tests/output/ob_start_callback_output_and_bad_return/handler_is_stringable_removed.phpt b/tests/output/ob_start_callback_output_and_bad_return/handler_is_stringable_removed.phpt deleted file mode 100644 index 9da82bc147e19..0000000000000 --- a/tests/output/ob_start_callback_output_and_bad_return/handler_is_stringable_removed.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation when OOM triggers handler removal (handler returns stringable object + produces output) ---INI-- -memory_limit=2M ---FILE-- -val; - } -} - -ob_start(function() { - // We are out of memory, now trigger a deprecation - echo "IN HANDLER\n"; - return new IsStringable(""); -}); - -$a = []; -// trigger OOM in a resize operation -while (1) { - $a[] = 1; -} - -?> ---EXPECTF-- -Deprecated: main(): Producing output from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Deprecated: main(): Returning a non-string result from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/tests/output/ob_start_callback_output_and_bad_return/handler_non_stringable_removed.phpt b/tests/output/ob_start_callback_output_and_bad_return/handler_non_stringable_removed.phpt deleted file mode 100644 index 476acaee9c3a3..0000000000000 --- a/tests/output/ob_start_callback_output_and_bad_return/handler_non_stringable_removed.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation when OOM triggers handler removal (handler returns non-stringable object + produces output) ---INI-- -memory_limit=2M ---FILE-- - ---EXPECTF-- -Deprecated: main(): Producing output from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Deprecated: main(): Returning a non-string result from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d - -Fatal error: Uncaught Error: Object of class NotStringable could not be converted to string in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d diff --git a/tests/output/ob_start_callback_output_and_bad_return/handler_true_removed.phpt b/tests/output/ob_start_callback_output_and_bad_return/handler_true_removed.phpt deleted file mode 100644 index 2b0218341c9b8..0000000000000 --- a/tests/output/ob_start_callback_output_and_bad_return/handler_true_removed.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation when OOM triggers handler removal (handler returns true + produces output) ---INI-- -memory_limit=2M ---FILE-- - ---EXPECTF-- -Deprecated: main(): Producing output from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Deprecated: main(): Returning a non-string result from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/tests/output/ob_start_callback_output_and_bad_return/handler_zero_removed.phpt b/tests/output/ob_start_callback_output_and_bad_return/handler_zero_removed.phpt deleted file mode 100644 index 8681a846a3648..0000000000000 --- a/tests/output/ob_start_callback_output_and_bad_return/handler_zero_removed.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with deprecation when OOM triggers handler removal (handler returns zero + produces output) ---INI-- -memory_limit=2M ---FILE-- - ---EXPECTF-- -Deprecated: main(): Producing output from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Deprecated: main(): Returning a non-string result from user output handler {closure:%s:%d} is deprecated in %s on line %d - -Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/tests/output/ob_start_callback_output_and_bad_return/multiple_handlers.phpt b/tests/output/ob_start_callback_output_and_bad_return/multiple_handlers.phpt deleted file mode 100644 index 94d5d34c03830..0000000000000 --- a/tests/output/ob_start_callback_output_and_bad_return/multiple_handlers.phpt +++ /dev/null @@ -1,115 +0,0 @@ ---TEST-- -ob_start(): Check behaviour with multiple nested handlers with bad return values and output ---FILE-- ->>"; - echo __FUNCTION__; - return $string; -} - -function return_empty_string($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return ""; -} - -function return_false($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return false; -} - -function return_true($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return true; -} - -function return_null($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return null; -} - -function return_string($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return "I stole your output."; -} - -function return_zero($string) { - global $log; - $log[] = __FUNCTION__ . ": <<<" . $string . ">>>"; - echo __FUNCTION__; - return 0; -} - -ob_start('return_given_string'); -ob_start('return_empty_string'); -ob_start('return_false'); -ob_start('return_true'); -ob_start('return_null'); -ob_start('return_string'); -ob_start('return_zero'); - -echo "Testing..."; - -ob_end_flush(); -ob_end_flush(); -ob_end_flush(); -ob_end_flush(); -ob_end_flush(); -ob_end_flush(); -ob_end_flush(); - -echo "\n\nLog:\n"; -echo implode("\n", $log); -?> ---EXPECTF-- -Deprecated: ob_end_flush(): Producing output from user output handler return_given_string is deprecated in %s on line %d0 - -Deprecated: ob_end_flush(): Producing output from user output handler return_empty_string is deprecated in %s on line %d9 - - -Log: -return_zero: <<>> -return_string: <<< -Deprecated: ob_end_flush(): Producing output from user output handler return_zero is deprecated in %s on line %d - -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_zero is deprecated in %s on line %d -0>>> -return_null: <<< -Deprecated: ob_end_flush(): Producing output from user output handler return_string is deprecated in %s on line %d5 -I stole your output.>>> -return_true: <<< -Deprecated: ob_end_flush(): Producing output from user output handler return_null is deprecated in %s on line %d - -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_null is deprecated in %s on line %d ->>> -return_false: <<< -Deprecated: ob_end_flush(): Producing output from user output handler return_true is deprecated in %s on line %d - -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_true is deprecated in %s on line %d ->>> -return_empty_string: <<< -Deprecated: ob_end_flush(): Producing output from user output handler return_false is deprecated in %s on line %d - -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_false is deprecated in %s on line %d - -Deprecated: ob_end_flush(): Producing output from user output handler return_true is deprecated in %s on line %d - -Deprecated: ob_end_flush(): Returning a non-string result from user output handler return_true is deprecated in %s on line %d -return_false>>> -return_given_string: <<< -Deprecated: ob_end_flush(): Producing output from user output handler return_empty_string is deprecated in %s on line %d9 ->>> From b620d9d22850c6dc40e54c5f0a9bd3db4f46dc1f Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 11 Nov 2025 16:13:56 +0100 Subject: [PATCH 025/252] Correctly handle extra named args for magic call in debug_backtrace_get_args() Closes GH-20454 --- Zend/tests/function_arguments/gh20435_2.phpt | 38 ++++++++++++++++++++ Zend/zend_builtin_functions.c | 8 +++-- 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 Zend/tests/function_arguments/gh20435_2.phpt diff --git a/Zend/tests/function_arguments/gh20435_2.phpt b/Zend/tests/function_arguments/gh20435_2.phpt new file mode 100644 index 0000000000000..a58c4d6be1ca5 --- /dev/null +++ b/Zend/tests/function_arguments/gh20435_2.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-20435: ZEND_CALL_HAS_EXTRA_NAMED_PARAMS & magic methods in debug_backtrace_get_args() +--FILE-- +__toString(), "\n\n"; + } + public function __call($name, $args) { + echo (new Exception())->__toString(), "\n\n"; + } + public function __invoke(...$args) { + echo (new Exception())->__toString(), "\n"; + } +} + +$c = new C(); +$c->foo(bar: 'bar'); +C::foo(bar: 'bar'); +$c(bar: 'bar'); + +?> +--EXPECTF-- +Exception in %s:%d +Stack trace: +#0 %s(%d): C->__call('foo', Array) +#1 {main} + +Exception in %s:%d +Stack trace: +#0 %s(%d): C::__callStatic('foo', Array) +#1 {main} + +Exception in %s:%d +Stack trace: +#0 %s(%d): C->__invoke(bar: 'bar') +#1 {main} diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 344983a6e2878..374d4b3734eaa 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1680,12 +1680,14 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) / ZVAL_EMPTY_ARRAY(arg_array); } - if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + if ((ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) + /* __call and __callStatic are non-variadic, potentially with + * HAS_EXTRA_NAMED_PARAMS set. Don't add extra args, as they're already + * contained in the 2nd param. */ + && (call->func->common.fn_flags & ZEND_ACC_VARIADIC)) { zend_string *name; zval *arg; - ZEND_ASSERT(call->func->common.fn_flags & ZEND_ACC_VARIADIC); - zend_attribute *attribute = zend_get_parameter_attribute_str( call->func->common.attributes, "sensitiveparameter", From 86b0a2a2b0bb706d88a4a54a1cbab39f0c47e819 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 11 Nov 2025 18:22:27 -0800 Subject: [PATCH 026/252] NEWS: update for PHP 8.5.0RC5, fix next release Copy the NEWS entries from the RC5 tag with the changes included there. One of those entries was already listed in the section for the next release from PHP-8.5; remove it from that section. Also update the header at the top for what release is next - the next release from the PHP-8.5 branch is going to be PHP 8.5.1. [skip ci] --- NEWS | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 77324a9fe779f..402ae83ae64a1 100644 --- a/NEWS +++ b/NEWS @@ -1,13 +1,11 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.5.0 +?? ??? ????, PHP 8.5.1 - Core: . Sync all boost.context files with release 1.86.0. (mvorisek) . Fixed bug GH-20435 (SensitiveParameter doesn't work for named argument passing to variadic parameter). (ndossche) - . Fixed bug GH-17951 (memory_limit is not always limited by max_memory_limit). - (manuelm) - DOM: . Fix memory leak when edge case is hit when registering xpath callback. @@ -28,6 +26,22 @@ PHP NEWS . Fixed bug GH-20439 (xml_set_default_handler() does not properly handle special characters in attributes when passing data to callback). (ndossche) +13 Nov 2025, PHP 8.5.0RC5 + +- Core: + . Fixed bug GH-17951 (memory_limit is not always limited by max_memory_limit). + (manuelm) + . Address bug GH-20384 (Confirm if ob_gzhandler is impacted by ob_start + handler changes) by reverting GH-18932 (Deprecate returning non-string + values from a user output handler). (DanielEScherzer) + +- URI: + . Fixed bug GH-20431 (Uri\Rfc3986\Uri::setHost(null) turns empty path into /) + by updating to a newer uriparser snapshot. (timwolla) + . Fixed the distinction between an empty and a missing query/fragment + when using Uri\WhatWg\Url::getQuery() and Uri\WhatWg\Url::getFragment(). + (kocsismate) + 06 Nov 2025, PHP 8.5.0RC4 - Core: From 414e7db68ae32324a30a85c24bd6a6d13c6b03f3 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 12 Nov 2025 16:01:39 +0000 Subject: [PATCH 027/252] sapi/phpdbg: use 'h' ZPP specifier instead of'H' The stubs say array so in debug mode we get a ZPP violation assertion and even by fixing the stubs the behaviour is not identical due to missing indirect handling. This indicates using objects was never done, thus use the correct ZPP specifier --- sapi/phpdbg/phpdbg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index f27d87de84187..4d284a1ac23df 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -491,7 +491,7 @@ PHP_FUNCTION(phpdbg_get_executable) HashTable *files = &PHPDBG_G(file_sources); HashTable files_tmp; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|h", &options) == FAILURE) { RETURN_THROWS(); } @@ -585,7 +585,7 @@ PHP_FUNCTION(phpdbg_end_oplog) bool by_function = 0; bool by_opcode = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|h", &options) == FAILURE) { RETURN_THROWS(); } From b9562adff6597aa43ed91967c679e4a4ebac95f6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Wed, 12 Nov 2025 17:20:41 +0100 Subject: [PATCH 028/252] standard: Simplify array_chunk() (#20423) --- ext/standard/array.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 0389eb1840a96..7bedbdfe59fdc 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -7037,8 +7037,7 @@ PHP_FUNCTION(array_chunk) if (size > num_in) { if (num_in == 0) { - RETVAL_EMPTY_ARRAY(); - return; + RETURN_EMPTY_ARRAY(); } size = num_in; } @@ -7046,12 +7045,11 @@ PHP_FUNCTION(array_chunk) array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1)); zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); - ZVAL_UNDEF(&chunk); - ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) { /* If new chunk, create and initialize it. */ - if (Z_TYPE(chunk) == IS_UNDEF) { + if (current == 0) { array_init_size(&chunk, (uint32_t)size); + add_next_index_zval(return_value, &chunk); } /* Add entry to the chunk, preserving keys if necessary. */ @@ -7066,19 +7064,10 @@ PHP_FUNCTION(array_chunk) } zval_add_ref(entry); - /* If reached the chunk size, add it to the result array, and reset the - * pointer. */ if (++current == size) { - add_next_index_zval(return_value, &chunk); - ZVAL_UNDEF(&chunk); current = 0; } } ZEND_HASH_FOREACH_END(); - - /* Add the final chunk if there is one. */ - if (Z_TYPE(chunk) != IS_UNDEF) { - add_next_index_zval(return_value, &chunk); - } } /* }}} */ From 7db647dd9943ee59069847e953c933dd7b4de3b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Wed, 12 Nov 2025 18:10:21 +0100 Subject: [PATCH 029/252] Verify bundled PCRE2 in CI Closes GH-20354 --- .../actions/verify-generated-files/action.yml | 3 +- .github/scripts/download-bundled/pcre2.sh | 32 ++++++++++++++ .github/scripts/test-directory-unchanged.sh | 13 ++++++ .github/workflows/verify-bundled-files.yml | 43 +++++++++++++++++++ ext/pcre/pcre2lib/pcre2_chartables.c | 12 ++---- 5 files changed, 92 insertions(+), 11 deletions(-) create mode 100755 .github/scripts/download-bundled/pcre2.sh create mode 100755 .github/scripts/test-directory-unchanged.sh create mode 100644 .github/workflows/verify-bundled-files.yml diff --git a/.github/actions/verify-generated-files/action.yml b/.github/actions/verify-generated-files/action.yml index 13f0cf9f128bb..79c49dbfcfffb 100644 --- a/.github/actions/verify-generated-files/action.yml +++ b/.github/actions/verify-generated-files/action.yml @@ -13,5 +13,4 @@ runs: ext/tokenizer/tokenizer_data_gen.php build/gen_stub.php -f --generate-optimizer-info --verify ext/phar/makestub.php - # Use the -a flag for a bug in git 2.46.0, which doesn't consider changed -diff files. - git add . -N && git diff -a --exit-code + .github/scripts/test-directory-unchanged.sh . diff --git a/.github/scripts/download-bundled/pcre2.sh b/.github/scripts/download-bundled/pcre2.sh new file mode 100755 index 0000000000000..b43554206c929 --- /dev/null +++ b/.github/scripts/download-bundled/pcre2.sh @@ -0,0 +1,32 @@ +#!/bin/sh +set -ex +cd "$(dirname "$0")/../../.." + +revision=refs/tags/pcre2-10.44 + +git clone --depth 1 --recurse-submodules --revision="$revision" https://github.com/PCRE2Project/pcre2.git /tmp/php-src-bundled/pcre2 + +rm -rf ext/pcre/pcre2lib +cp -R /tmp/php-src-bundled/pcre2/src ext/pcre/pcre2lib + +cd ext/pcre/pcre2lib + +# remove unneeded files +rm config.h.generic +rm pcre2.h.in +rm pcre2_dftables.c +rm pcre2_fuzzsupport.c +rm pcre2_jit_test.c +rm pcre2demo.c +rm pcre2grep.c +rm pcre2posix.c +rm pcre2posix.h +rm pcre2posix_test.c +rm pcre2test.c + +# move renamed files +mv pcre2.h.generic pcre2.h +mv pcre2_chartables.c.dist pcre2_chartables.c + +# add extra files +git restore config.h # based on config.h.generic but with many changes diff --git a/.github/scripts/test-directory-unchanged.sh b/.github/scripts/test-directory-unchanged.sh new file mode 100755 index 0000000000000..0ce7fd4cc4afd --- /dev/null +++ b/.github/scripts/test-directory-unchanged.sh @@ -0,0 +1,13 @@ +#!/bin/sh +set -ex + +cd "$(dirname "$0")/../../$1" + +# notify git about untracked (except ignored) files +git add -N . + +# display overview of changed files +git status . + +# display diff of working directory vs HEAD commit and set exit code +git diff -a --exit-code HEAD . diff --git a/.github/workflows/verify-bundled-files.yml b/.github/workflows/verify-bundled-files.yml new file mode 100644 index 0000000000000..e15fcb36a0e7a --- /dev/null +++ b/.github/workflows/verify-bundled-files.yml @@ -0,0 +1,43 @@ +name: Verify Bundled Files + +on: + push: + paths: &paths + - '.github/scripts/download-bundled/pcre2.sh' + - 'ext/pcre/pcre2lib/**' + pull_request: + paths: *paths + schedule: + - cron: "0 1 * * *" + workflow_dispatch: ~ + +permissions: + contents: read + +jobs: + VERIFY_BUNDLED_FILES: + name: Verify Bundled Files + runs-on: ubuntu-24.04 + steps: + - name: git checkout + uses: actions/checkout@v5 + + - name: Detect changed files + uses: dorny/paths-filter@v3 + id: changes + with: + base: master + filters: | + pcre2: + - '.github/scripts/download-bundled/pcre2.sh' + - 'ext/pcre/pcre2lib/**' + + - name: PCRE2 + if: ${{ !cancelled() && (steps.changes.outputs.pcre2 == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} + run: | + echo "::group::Download" + .github/scripts/download-bundled/pcre2.sh + echo "::endgroup::" + echo "::group::Verify files" + .github/scripts/test-directory-unchanged.sh ext/pcre/pcre2lib + echo "::endgroup::" diff --git a/ext/pcre/pcre2lib/pcre2_chartables.c b/ext/pcre/pcre2lib/pcre2_chartables.c index 861914d1ac3ac..7362c3f2345ab 100644 --- a/ext/pcre/pcre2lib/pcre2_chartables.c +++ b/ext/pcre/pcre2lib/pcre2_chartables.c @@ -5,7 +5,8 @@ /* This file was automatically written by the pcre2_dftables auxiliary program. It contains character tables that are used when no external tables are passed to PCRE2 by the application that calls it. The tables -are used only for characters whose code values are less than 256. */ +are used only for characters whose code values are less than 256, and +only relevant if not in UCP mode. */ /* This set of tables was written in the C locale. */ @@ -18,13 +19,6 @@ PCRE2 is configured with --enable-rebuild-chartables. However, you can run pcre2_dftables manually with the -L option to build tables using the LC_ALL locale. */ -/* The following #include is present because without it gcc 4.x may remove -the array definition from the final binary if PCRE2 is built into a static -library and dead code stripping is activated. This leads to link errors. -Pulling in the header ensures that the array gets flagged as "someone -outside this compilation unit might reference this" and so it will always -be supplied to the linker. */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -163,7 +157,7 @@ graph, print, punct, and cntrl. Other classes are built from combinations. */ 0x02 letter 0x04 lower case letter 0x08 decimal digit - 0x10 alphanumeric or '_' + 0x10 word (alphanumeric or '_') */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ From 3ad5799f2534593bfd274411314cf7c6b90e188e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:30:58 +0100 Subject: [PATCH 030/252] Fix GH-20395: \Dom\ParentNode::querySelector and \Dom\ParentNode::querySelectorAll requires elements in $selectors to be lowercase (#20409) The selector needs to be compared in a lowercase manner. This also almost completely obsoletes the interned string optimization, so get rid of that for simplicity sake. While there is still theoretical benefit, it is only 1-2% in my random tests, not worth it anymore. --- NEWS | 2 + .../lexbor/selectors-adapted/selectors.c | 96 ++++++++++--------- .../lexbor/selectors-adapted/selectors.h | 1 - .../tests/modern/css_selectors/gh20395.phpt | 33 +++++++ .../css_selectors/pseudo_classes_links.phpt | 11 +++ 5 files changed, 96 insertions(+), 47 deletions(-) create mode 100644 ext/dom/tests/modern/css_selectors/gh20395.phpt diff --git a/NEWS b/NEWS index bf7335a597c49..806659094a9a1 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,8 @@ PHP NEWS - DOM: . Fix memory leak when edge case is hit when registering xpath callback. (ndossche) + . Fixed bug GH-20395 (querySelector and querySelectorAll requires elements + in $selectors to be lowercase). (ndossche) - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string diff --git a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c index 4e094f632ef79..7a494017a2d6d 100644 --- a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c +++ b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c @@ -35,7 +35,22 @@ static void dom_lxb_str_wrapper_release(dom_lxb_str_wrapper *wrapper) } } -static zend_always_inline bool lxb_selectors_adapted_cmp_local_name_literal(const xmlNode *node, const char *name) +static bool lxb_selectors_str_cmp_loright(const char *lhs, const char *rhs) +{ + while (true) { + if (*rhs != zend_tolower_ascii(*lhs)) { + return false; + } + if (!*lhs) { + return true; + } + ++rhs; + ++lhs; + } +} + +/* `name` is lowercase */ +static zend_always_inline bool lxb_selectors_cmp_html_name_lit(const xmlNode *node, const char *name) { return strcmp((const char *) node->name, name) == 0; } @@ -48,14 +63,15 @@ static zend_always_inline bool lxb_selectors_adapted_cmp_ns(const xmlNode *a, co static zend_always_inline bool lxb_selectors_adapted_cmp_local_name_id(const xmlNode *node, const lxb_selectors_adapted_id *id) { - uintptr_t ptr = (uintptr_t) node->name; - if (id->interned && (ptr & (ZEND_MM_ALIGNMENT - 1)) != 0) { - /* It cannot be a heap-allocated string because the pointer is not properly aligned for a heap allocation. - * Therefore, it must be interned into the dictionary pool. */ - return node->name == id->name; + ZEND_ASSERT(node->doc != NULL); + if (php_dom_ns_is_html_and_document_is_html(node)) { + /* From https://html.spec.whatwg.org/#case-sensitivity-of-selectors: + * The element name must be compared case sensitively _after_ converting the selector to lowercase. + * E.g. selector "DIV" must match element "div" but not "Div". */ + return lxb_selectors_str_cmp_loright((const char *) id->name, (const char *) node->name); + } else { + return strcmp((const char *) node->name, (const char *) id->name) == 0; } - - return strcmp((const char *) node->name, (const char *) id->name) == 0; } static zend_always_inline const xmlAttr *lxb_selectors_adapted_attr(const xmlNode *node, const lxb_char_t *name) @@ -64,9 +80,8 @@ static zend_always_inline const xmlAttr *lxb_selectors_adapted_attr(const xmlNod ZEND_ASSERT(node->doc != NULL); if (php_dom_ns_is_html_and_document_is_html(node)) { /* No need to handle DTD entities as we're in HTML. */ - size_t name_bound = strlen((const char *) name) + 1; for (const xmlAttr *cur = node->properties; cur != NULL; cur = cur->next) { - if (lexbor_str_data_nlocmp_right(cur->name, name, name_bound)) { + if (lxb_selectors_str_cmp_loright((const char *) name, (const char *) cur->name)) { attr = cur; break; } @@ -154,18 +169,7 @@ static bool lxb_selectors_is_lowercased_html_attrib_name(const lxb_css_selector_ static void lxb_selectors_adapted_set_entry_id_ex(lxb_selectors_entry_t *entry, const lxb_css_selector_t *selector, const xmlNode *node) { entry->id.attr_case_insensitive = lxb_selectors_is_lowercased_html_attrib_name(selector); - - if (node->doc != NULL && node->doc->dict != NULL) { - const xmlChar *interned = xmlDictExists(node->doc->dict, selector->name.data, selector->name.length); - if (interned != NULL) { - entry->id.name = interned; - entry->id.interned = true; - return; - } - } - entry->id.name = selector->name.data; - entry->id.interned = false; } static zend_always_inline void lxb_selectors_adapted_set_entry_id(lxb_selectors_entry_t *entry, const lxb_css_selector_t *selector, const xmlNode *node) @@ -1686,8 +1690,8 @@ lxb_selectors_pseudo_class(const lxb_css_selector_t *selector, case LXB_CSS_SELECTOR_PSEUDO_CLASS_ANY_LINK: /* https://drafts.csswg.org/selectors/#the-any-link-pseudo */ if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) - && (lxb_selectors_adapted_cmp_local_name_literal(node, "a") - || lxb_selectors_adapted_cmp_local_name_literal(node, "area"))) + && (lxb_selectors_cmp_html_name_lit(node, "a") + || lxb_selectors_cmp_html_name_lit(node, "area"))) { return lxb_selectors_adapted_has_attr(node, "href"); } @@ -1705,7 +1709,7 @@ lxb_selectors_pseudo_class(const lxb_css_selector_t *selector, if (!php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token)) { return false; } - if (lxb_selectors_adapted_cmp_local_name_literal(node, "input")) { + if (lxb_selectors_cmp_html_name_lit(node, "input")) { const xmlAttr *dom_attr = lxb_selectors_adapted_attr(node, (const lxb_char_t *) "type"); if (dom_attr == NULL) { return false; @@ -1729,7 +1733,7 @@ lxb_selectors_pseudo_class(const lxb_css_selector_t *selector, return res; } - else if(lxb_selectors_adapted_cmp_local_name_literal(node, "option")) { + else if(lxb_selectors_cmp_html_name_lit(node, "option")) { return lxb_selectors_adapted_has_attr(node, "selected"); } @@ -1802,8 +1806,8 @@ lxb_selectors_pseudo_class(const lxb_css_selector_t *selector, case LXB_CSS_SELECTOR_PSEUDO_CLASS_LINK: /* https://html.spec.whatwg.org/multipage/semantics-other.html#selector-link */ if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) - && (lxb_selectors_adapted_cmp_local_name_literal(node, "a") - || lxb_selectors_adapted_cmp_local_name_literal(node, "area"))) + && (lxb_selectors_cmp_html_name_lit(node, "a") + || lxb_selectors_cmp_html_name_lit(node, "area"))) { return lxb_selectors_adapted_has_attr(node, "href"); } @@ -1823,9 +1827,9 @@ lxb_selectors_pseudo_class(const lxb_css_selector_t *selector, case LXB_CSS_SELECTOR_PSEUDO_CLASS_OPTIONAL: if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) - && (lxb_selectors_adapted_cmp_local_name_literal(node, "input") - || lxb_selectors_adapted_cmp_local_name_literal(node, "select") - || lxb_selectors_adapted_cmp_local_name_literal(node, "textarea"))) + && (lxb_selectors_cmp_html_name_lit(node, "input") + || lxb_selectors_cmp_html_name_lit(node, "select") + || lxb_selectors_cmp_html_name_lit(node, "textarea"))) { return !lxb_selectors_adapted_has_attr(node, "required"); } @@ -1840,8 +1844,8 @@ lxb_selectors_pseudo_class(const lxb_css_selector_t *selector, case LXB_CSS_SELECTOR_PSEUDO_CLASS_PLACEHOLDER_SHOWN: if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) - && (lxb_selectors_adapted_cmp_local_name_literal(node, "input") - || lxb_selectors_adapted_cmp_local_name_literal(node, "textarea"))) + && (lxb_selectors_cmp_html_name_lit(node, "input") + || lxb_selectors_cmp_html_name_lit(node, "textarea"))) { return lxb_selectors_adapted_has_attr(node, "placeholder"); } @@ -1856,9 +1860,9 @@ lxb_selectors_pseudo_class(const lxb_css_selector_t *selector, case LXB_CSS_SELECTOR_PSEUDO_CLASS_REQUIRED: if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) - && (lxb_selectors_adapted_cmp_local_name_literal(node, "input") - || lxb_selectors_adapted_cmp_local_name_literal(node, "select") - || lxb_selectors_adapted_cmp_local_name_literal(node, "textarea"))) + && (lxb_selectors_cmp_html_name_lit(node, "input") + || lxb_selectors_cmp_html_name_lit(node, "select") + || lxb_selectors_cmp_html_name_lit(node, "textarea"))) { return lxb_selectors_adapted_has_attr(node, "required"); } @@ -2104,24 +2108,24 @@ lxb_selectors_pseudo_class_disabled(const xmlNode *node) } if (lxb_selectors_adapted_has_attr(node, "disabled") - && (lxb_selectors_adapted_cmp_local_name_literal(node, "button") - || lxb_selectors_adapted_cmp_local_name_literal(node, "input") - || lxb_selectors_adapted_cmp_local_name_literal(node, "select") - || lxb_selectors_adapted_cmp_local_name_literal(node, "textarea") - || lxb_selectors_adapted_cmp_local_name_literal(node, "optgroup") - || lxb_selectors_adapted_cmp_local_name_literal(node, "fieldset"))) + && (lxb_selectors_cmp_html_name_lit(node, "button") + || lxb_selectors_cmp_html_name_lit(node, "input") + || lxb_selectors_cmp_html_name_lit(node, "select") + || lxb_selectors_cmp_html_name_lit(node, "textarea") + || lxb_selectors_cmp_html_name_lit(node, "optgroup") + || lxb_selectors_cmp_html_name_lit(node, "fieldset"))) { return true; } - if (lxb_selectors_adapted_cmp_local_name_literal(node, "fieldset")) { + if (lxb_selectors_cmp_html_name_lit(node, "fieldset")) { const xmlNode *fieldset = node; node = node->parent; while (node != NULL && CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) { /* node is a disabled fieldset that is an ancestor of fieldset */ if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) - && lxb_selectors_adapted_cmp_local_name_literal(node, "fieldset") + && lxb_selectors_cmp_html_name_lit(node, "fieldset") && lxb_selectors_adapted_has_attr(node, "disabled")) { /* Search first legend child and figure out if fieldset is a descendent from that. */ @@ -2129,7 +2133,7 @@ lxb_selectors_pseudo_class_disabled(const xmlNode *node) do { if (search_current->type == XML_ELEMENT_NODE && php_dom_ns_is_fast(search_current, php_dom_ns_is_html_magic_token) - && lxb_selectors_adapted_cmp_local_name_literal(search_current, "legend")) { + && lxb_selectors_cmp_html_name_lit(search_current, "legend")) { /* search_current is a legend element. */ const xmlNode *inner_search_current = fieldset; @@ -2235,8 +2239,8 @@ static bool lxb_selectors_pseudo_class_read_write(const xmlNode *node) { if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token)) { - if (lxb_selectors_adapted_cmp_local_name_literal(node, "input") - || lxb_selectors_adapted_cmp_local_name_literal(node, "textarea")) { + if (lxb_selectors_cmp_html_name_lit(node, "input") + || lxb_selectors_cmp_html_name_lit(node, "textarea")) { return !lxb_selectors_adapted_has_attr(node, "readonly") && !lxb_selectors_adapted_has_attr(node, "disabled"); } else { const xmlAttr *attr = lxb_selectors_adapted_attr(node, (const lxb_char_t *) "contenteditable"); diff --git a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.h b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.h index c0f76cce3d5cc..b64a9e49ee262 100644 --- a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.h +++ b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.h @@ -77,7 +77,6 @@ typedef lxb_selectors_entry_t * typedef struct { const xmlChar *name; - bool interned; bool attr_case_insensitive; } lxb_selectors_adapted_id; diff --git a/ext/dom/tests/modern/css_selectors/gh20395.phpt b/ext/dom/tests/modern/css_selectors/gh20395.phpt new file mode 100644 index 0000000000000..af04cb1c27a42 --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/gh20395.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-20395 (\Dom\ParentNode::querySelector and \Dom\ParentNode::querySelectorAll requires elements in $selectors to be lowercase) +--EXTENSIONS-- +dom +--CREDITS-- +DeveloperRob +--FILE-- +'; +$dom = Dom\HtmlDocument::createFromString($html); +var_dump(is_null($dom->querySelector('html'))); +var_dump(is_null($dom->querySelector('Html'))); +var_dump(is_null($dom->querySelector('HTML'))); + +$dom->body->appendChild($dom->createElement('div')); +$dom->body->appendChild($dom->createElementNS('http://www.w3.org/1999/xhtml', 'Div')); + +foreach ($dom->querySelectorAll('div') as $div) { + var_dump($div->localName); +} + +foreach ($dom->querySelectorAll('Div') as $div) { + var_dump($div->localName); +} + +?> +--EXPECT-- +bool(false) +bool(false) +bool(false) +string(3) "div" +string(3) "div" diff --git a/ext/dom/tests/modern/css_selectors/pseudo_classes_links.phpt b/ext/dom/tests/modern/css_selectors/pseudo_classes_links.phpt index 8a688286b6806..7afcb6e3cfbde 100644 --- a/ext/dom/tests/modern/css_selectors/pseudo_classes_links.phpt +++ b/ext/dom/tests/modern/css_selectors/pseudo_classes_links.phpt @@ -11,6 +11,7 @@ $dom = DOM\XMLDocument::createFromString(<< Link Link + Not actually a link Link XML); @@ -18,6 +19,7 @@ XML); test_helper($dom, ':any-link'); test_helper($dom, ':link'); test_helper($dom, 'a:not(:any-link)'); +test_helper($dom, ':not(:any-link)'); ?> --EXPECT-- @@ -29,3 +31,12 @@ test_helper($dom, 'a:not(:any-link)'); Link --- Selector: a:not(:any-link) --- Link +--- Selector: :not(:any-link) --- + + Link + Link + Not actually a link + Link + +Link +Not actually a link From 4743dd673e3c894e93c8344e79b8e80704400742 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 12 Nov 2025 19:33:38 +0100 Subject: [PATCH 031/252] ext/session: Remove unused source files and headers on Windows (#20463) These are only useful on Unix-like environments when libmm is enabled via configuration option. --- ext/session/config.w32 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/session/config.w32 b/ext/session/config.w32 index 825bc8b61d291..40f9f78a313ed 100644 --- a/ext/session/config.w32 +++ b/ext/session/config.w32 @@ -3,10 +3,10 @@ ARG_ENABLE("session", "session support", "yes"); if (PHP_SESSION == "yes") { - EXTENSION("session", "mod_user_class.c session.c mod_files.c mod_mm.c mod_user.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); + EXTENSION("session", "mod_user_class.c session.c mod_files.c mod_user.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); ADD_EXTENSION_DEP('session', 'date'); // https://bugs.php.net/53141 ADD_EXTENSION_DEP('session', 'spl', true); AC_DEFINE("HAVE_PHP_SESSION", 1, "Define to 1 if the PHP extension 'session' is available."); - PHP_INSTALL_HEADERS("ext/session", "mod_mm.h php_session.h mod_files.h mod_user.h"); + PHP_INSTALL_HEADERS("ext/session", "php_session.h mod_files.h mod_user.h"); } From db8d35e7ebe9d3ea94b8058f29b1eb4b9a7e0f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 12 Nov 2025 21:28:56 +0100 Subject: [PATCH 032/252] tree-wide: Replace `ZEND_WRONG_PARAM_COUNT()` by its definition (#20066) * tree-wide: Replace `WRONG_PARAM_COUNT` by `ZEND_WRONG_PARAM_COUNT()` This is a direct alias. * tree-wide: Replace `ZEND_WRONG_PARAM_COUNT()` by its definition This macro was hiding control flow (the return statement) and thus was particularly unhygienic. --- UPGRADING.INTERNALS | 3 +++ Zend/zend_API.h | 2 -- ext/ldap/ldap.c | 3 ++- ext/spl/spl_directory.c | 3 ++- ext/standard/file.c | 3 ++- ext/standard/string.c | 3 ++- 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 88df8f6edb2f5..028779705a736 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -45,6 +45,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES __callStatic zend_function* instead of a CE and a boolean argument. . The zend_set_hash_symbol() API has been removed. . Added zend_hash_str_lookup(). + . The WRONG_PARAM_COUNT and ZEND_WRONG_PARAM_COUNT() macros have been + removed. Call zend_wrong_param_count(); followed by RETURN_THROWS(); + instead. ======================== 2. Build system changes diff --git a/Zend/zend_API.h b/Zend/zend_API.h index e6d5a024cf61b..1bb47a2e8703d 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -523,9 +523,7 @@ ZEND_API const char *zend_get_type_by_const(int type); #define getThis() (hasThis() ? ZEND_THIS : NULL) #define ZEND_IS_METHOD_CALL() (EX(func)->common.scope != NULL) -#define WRONG_PARAM_COUNT ZEND_WRONG_PARAM_COUNT() #define ZEND_NUM_ARGS() EX_NUM_ARGS() -#define ZEND_WRONG_PARAM_COUNT() { zend_wrong_param_count(); return; } #ifndef ZEND_WIN32 #define DLEXPORT diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index 366903b2c2ab1..e9acd18990664 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -980,7 +980,8 @@ PHP_FUNCTION(ldap_connect) #ifdef HAVE_ORALDAP if (ZEND_NUM_ARGS() == 3 || ZEND_NUM_ARGS() == 4) { - WRONG_PARAM_COUNT; + zend_wrong_param_count(); + RETURN_THROWS(); } if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!lssl", &host, &hostlen, &port, &wallet, &walletlen, &walletpasswd, &walletpasswdlen, &authmode) != SUCCESS) { diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index c67a6ec4b2824..5ef7540f822d1 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -2501,7 +2501,8 @@ PHP_METHOD(SplFileObject, fscanf) int result = php_sscanf_internal(ZSTR_VAL(intern->u.file.current_line), ZSTR_VAL(format_str), (int)num_varargs, varargs, 0, return_value); if (SCAN_ERROR_WRONG_PARAM_COUNT == result) { - WRONG_PARAM_COUNT; + zend_wrong_param_count(); + RETURN_THROWS(); } } /* }}} */ diff --git a/ext/standard/file.c b/ext/standard/file.c index ab6ed4fbadd2d..fdeabd1872d20 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -961,7 +961,8 @@ PHP_FUNCTION(fscanf) efree(buf); if (SCAN_ERROR_WRONG_PARAM_COUNT == result) { - WRONG_PARAM_COUNT; + zend_wrong_param_count(); + RETURN_THROWS(); } } /* }}} */ diff --git a/ext/standard/string.c b/ext/standard/string.c index ddf4221bf6edb..029c7a29bb478 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -5873,7 +5873,8 @@ PHP_FUNCTION(sscanf) result = php_sscanf_internal(str, format, num_args, args, 0, return_value); if (SCAN_ERROR_WRONG_PARAM_COUNT == result) { - WRONG_PARAM_COUNT; + zend_wrong_param_count(); + RETURN_THROWS(); } } /* }}} */ From 8ad59157561fc2db26f437049cfd3342f36e6ff7 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Wed, 12 Nov 2025 22:07:11 +0100 Subject: [PATCH 033/252] dom: Fix missing NUL byte check on C14NFile() Closes GH-20466. --- NEWS | 3 +++ ext/dom/node.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 7d0fc8ce6309d..21b31fb93efc3 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,9 @@ PHP NEWS . Fix crashes when trying to instantiate uninstantiable classes via date static constructors. (ndossche) +- DOM: + . Fix missing NUL byte check on C14NFile(). (ndossche) + - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) diff --git a/ext/dom/node.c b/ext/dom/node.c index 588665830bf2e..d25c921e0ebeb 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -1857,7 +1857,7 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), - "s|bba!a!", &file, &file_len, &exclusive, + "p|bba!a!", &file, &file_len, &exclusive, &with_comments, &xpath_array, &ns_prefixes) == FAILURE) { RETURN_THROWS(); } From 31bd92d2dc152e93de8474ebc217999330537ea1 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 13 Nov 2025 01:34:45 +0000 Subject: [PATCH 034/252] ext/standard/array.c: refactor sort functions (#20462) Consolidate userland functions to rely on a single implementation Split get_comparison functions into a reverse and a normal one Use bool type when applicable --- ext/standard/array.c | 315 ++++++++++++++++--------------------------- 1 file changed, 119 insertions(+), 196 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 7bedbdfe59fdc..54b26a3dadfd5 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -214,7 +214,7 @@ static zend_always_inline int php_array_key_compare_string_unstable_i(Bucket *f, } /* }}} */ -static int php_array_key_compare_string_natural_general(Bucket *f, Bucket *s, int fold_case) /* {{{ */ +static int php_array_key_compare_string_natural_general(Bucket *f, Bucket *s, bool fold_case) /* {{{ */ { const char *s1, *s2; size_t l1, l2; @@ -241,25 +241,25 @@ static int php_array_key_compare_string_natural_general(Bucket *f, Bucket *s, in static int php_array_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */ { - RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(a, b, 1)); + RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(a, b, true)); } /* }}} */ static int php_array_reverse_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */ { - RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(b, a, 1)); + RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(b, a, true)); } /* }}} */ static int php_array_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */ { - RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(a, b, 0)); + RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(a, b, false)); } /* }}} */ static int php_array_reverse_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */ { - RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(b, a, 0)); + RETURN_STABLE_SORT(a, b, php_array_key_compare_string_natural_general(b, a, false)); } /* }}} */ @@ -327,7 +327,7 @@ static zend_always_inline int php_array_data_compare_string_unstable_i(Bucket *f } /* }}} */ -static int php_array_natural_general_compare(Bucket *f, Bucket *s, int fold_case) /* {{{ */ +static int php_array_natural_general_compare(Bucket *f, Bucket *s, bool fold_case) /* {{{ */ { zend_string *tmp_str1, *tmp_str2; zend_string *str1 = zval_get_tmp_string(&f->val, &tmp_str1); @@ -343,13 +343,13 @@ static int php_array_natural_general_compare(Bucket *f, Bucket *s, int fold_case static zend_always_inline int php_array_natural_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */ { - return php_array_natural_general_compare(a, b, 0); + return php_array_natural_general_compare(a, b, false); } /* }}} */ static zend_always_inline int php_array_natural_case_compare_unstable_i(Bucket *a, Bucket *b) /* {{{ */ { - return php_array_natural_general_compare(a, b, 1); + return php_array_natural_general_compare(a, b, true); } /* }}} */ @@ -372,135 +372,127 @@ DEFINE_SORT_VARIANTS(data_compare_string_locale); DEFINE_SORT_VARIANTS(natural_compare); DEFINE_SORT_VARIANTS(natural_case_compare); -static bucket_compare_func_t php_get_key_compare_func(zend_long sort_type, int reverse) /* {{{ */ +static bucket_compare_func_t php_get_key_compare_func(zend_long sort_type) { switch (sort_type & ~PHP_SORT_FLAG_CASE) { case PHP_SORT_NUMERIC: - if (reverse) { - return php_array_reverse_key_compare_numeric; - } else { - return php_array_key_compare_numeric; - } - break; + return php_array_key_compare_numeric; case PHP_SORT_STRING: if (sort_type & PHP_SORT_FLAG_CASE) { - if (reverse) { - return php_array_reverse_key_compare_string_case; - } else { - return php_array_key_compare_string_case; - } + return php_array_key_compare_string_case; } else { - if (reverse) { - return php_array_reverse_key_compare_string; - } else { - return php_array_key_compare_string; - } + return php_array_key_compare_string; } - break; case PHP_SORT_NATURAL: if (sort_type & PHP_SORT_FLAG_CASE) { - if (reverse) { - return php_array_reverse_key_compare_string_natural_case; - } else { - return php_array_key_compare_string_natural_case; - } + return php_array_key_compare_string_natural_case; } else { - if (reverse) { - return php_array_reverse_key_compare_string_natural; - } else { - return php_array_key_compare_string_natural; - } + return php_array_key_compare_string_natural; } - break; case PHP_SORT_LOCALE_STRING: - if (reverse) { - return php_array_reverse_key_compare_string_locale; - } else { - return php_array_key_compare_string_locale; - } - break; + return php_array_key_compare_string_locale; case PHP_SORT_REGULAR: default: - if (reverse) { - return php_array_reverse_key_compare; - } else { - return php_array_key_compare; - } - break; + return php_array_key_compare; } return NULL; } -/* }}} */ -static bucket_compare_func_t php_get_data_compare_func(zend_long sort_type, int reverse) /* {{{ */ +static bucket_compare_func_t php_get_key_reverse_compare_func(zend_long sort_type) { switch (sort_type & ~PHP_SORT_FLAG_CASE) { case PHP_SORT_NUMERIC: - if (reverse) { - return php_array_reverse_data_compare_numeric; - } else { - return php_array_data_compare_numeric; - } - break; + return php_array_reverse_key_compare_numeric; case PHP_SORT_STRING: if (sort_type & PHP_SORT_FLAG_CASE) { - if (reverse) { - return php_array_reverse_data_compare_string_case; - } else { - return php_array_data_compare_string_case; - } + return php_array_reverse_key_compare_string_case; } else { - if (reverse) { - return php_array_reverse_data_compare_string; - } else { - return php_array_data_compare_string; - } + return php_array_reverse_key_compare_string; } - break; case PHP_SORT_NATURAL: if (sort_type & PHP_SORT_FLAG_CASE) { - if (reverse) { - return php_array_reverse_natural_case_compare; - } else { - return php_array_natural_case_compare; - } + return php_array_reverse_key_compare_string_natural_case; } else { - if (reverse) { - return php_array_reverse_natural_compare; - } else { - return php_array_natural_compare; - } + return php_array_reverse_key_compare_string_natural; } - break; case PHP_SORT_LOCALE_STRING: - if (reverse) { - return php_array_reverse_data_compare_string_locale; + return php_array_reverse_key_compare_string_locale; + + case PHP_SORT_REGULAR: + default: + return php_array_reverse_key_compare; + } + return NULL; +} + +static bucket_compare_func_t php_get_data_compare_func(zend_long sort_type) /* {{{ */ +{ + switch (sort_type & ~PHP_SORT_FLAG_CASE) { + case PHP_SORT_NUMERIC: + return php_array_data_compare_numeric; + + case PHP_SORT_STRING: + if (sort_type & PHP_SORT_FLAG_CASE) { + return php_array_data_compare_string_case; } else { - return php_array_data_compare_string_locale; + return php_array_data_compare_string; } - break; + + case PHP_SORT_NATURAL: + if (sort_type & PHP_SORT_FLAG_CASE) { + return php_array_natural_case_compare; + } else { + return php_array_natural_compare; + } + + case PHP_SORT_LOCALE_STRING: + return php_array_data_compare_string_locale; case PHP_SORT_REGULAR: default: - if (reverse) { - return php_array_reverse_data_compare; + return php_array_data_compare; + } + return NULL; +} + +static bucket_compare_func_t php_get_data_reverse_compare_func(zend_long sort_type) /* {{{ */ +{ + switch (sort_type & ~PHP_SORT_FLAG_CASE) { + case PHP_SORT_NUMERIC: + return php_array_reverse_data_compare_numeric; + + case PHP_SORT_STRING: + if (sort_type & PHP_SORT_FLAG_CASE) { + return php_array_reverse_data_compare_string_case; } else { - return php_array_data_compare; + return php_array_reverse_data_compare_string; } - break; + + case PHP_SORT_NATURAL: + if (sort_type & PHP_SORT_FLAG_CASE) { + return php_array_reverse_natural_case_compare; + } else { + return php_array_reverse_natural_compare; + } + + case PHP_SORT_LOCALE_STRING: + return php_array_reverse_data_compare_string_locale; + + case PHP_SORT_REGULAR: + default: + return php_array_reverse_data_compare; } return NULL; } -/* }}} */ -static bucket_compare_func_t php_get_data_compare_func_unstable(zend_long sort_type, int reverse) /* {{{ */ +static bucket_compare_func_t php_get_data_compare_func_unstable(zend_long sort_type, bool reverse) /* {{{ */ { switch (sort_type & ~PHP_SORT_FLAG_CASE) { case PHP_SORT_NUMERIC: @@ -564,48 +556,6 @@ static bucket_compare_func_t php_get_data_compare_func_unstable(zend_long sort_t } /* }}} */ -/* {{{ Sort an array by key value in reverse order */ -PHP_FUNCTION(krsort) -{ - zval *array; - zend_long sort_type = PHP_SORT_REGULAR; - bucket_compare_func_t cmp; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY_EX(array, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(sort_type) - ZEND_PARSE_PARAMETERS_END(); - - cmp = php_get_key_compare_func(sort_type, 1); - - zend_hash_sort(Z_ARRVAL_P(array), cmp, 0); - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ Sort an array by key */ -PHP_FUNCTION(ksort) -{ - zval *array; - zend_long sort_type = PHP_SORT_REGULAR; - bucket_compare_func_t cmp; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY_EX(array, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(sort_type) - ZEND_PARSE_PARAMETERS_END(); - - cmp = php_get_key_compare_func(sort_type, 0); - - zend_hash_sort(Z_ARRVAL_P(array), cmp, 0); - - RETURN_TRUE; -} -/* }}} */ - PHPAPI zend_long php_count_recursive(HashTable *ht) /* {{{ */ { zend_long cnt = 0; @@ -702,119 +652,92 @@ PHP_FUNCTION(count) } /* }}} */ -static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case) /* {{{ */ +static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t sort_fn) { - zval *array; + HashTable *array; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY_EX(array, 0, 1) + Z_PARAM_ARRAY_HT_EX(array, 0, 1) ZEND_PARSE_PARAMETERS_END(); - if (fold_case) { - zend_array_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0); - } else { - zend_array_sort(Z_ARRVAL_P(array), php_array_natural_compare, 0); - } + zend_array_sort(array, sort_fn, false); RETURN_TRUE; } -/* }}} */ /* {{{ Sort an array using natural sort */ PHP_FUNCTION(natsort) { - php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_natural_compare); } /* }}} */ /* {{{ Sort an array using case-insensitive natural sort */ PHP_FUNCTION(natcasesort) { - php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_natural_case_compare); } /* }}} */ -/* {{{ Sort an array and maintain index association */ -PHP_FUNCTION(asort) -{ - zval *array; +typedef bucket_compare_func_t(*get_compare_function)(zend_long); + +static zend_always_inline void php_sort(INTERNAL_FUNCTION_PARAMETERS, get_compare_function get_cmp, bool renumber) { + HashTable *array; zend_long sort_type = PHP_SORT_REGULAR; bucket_compare_func_t cmp; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY_EX(array, 0, 1) + Z_PARAM_ARRAY_HT_EX(array, 0, 1) Z_PARAM_OPTIONAL Z_PARAM_LONG(sort_type) ZEND_PARSE_PARAMETERS_END(); - cmp = php_get_data_compare_func(sort_type, 0); + cmp = get_cmp(sort_type); - zend_array_sort(Z_ARRVAL_P(array), cmp, 0); + zend_array_sort(array, cmp, renumber); RETURN_TRUE; } + +/* {{{ Sort an array and maintain index association */ +PHP_FUNCTION(asort) +{ + php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_compare_func, false); +} /* }}} */ /* {{{ Sort an array in reverse order and maintain index association */ PHP_FUNCTION(arsort) { - zval *array; - zend_long sort_type = PHP_SORT_REGULAR; - bucket_compare_func_t cmp; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY_EX(array, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(sort_type) - ZEND_PARSE_PARAMETERS_END(); - - cmp = php_get_data_compare_func(sort_type, 1); - - zend_array_sort(Z_ARRVAL_P(array), cmp, 0); - - RETURN_TRUE; + php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_reverse_compare_func, false); } /* }}} */ /* {{{ Sort an array */ PHP_FUNCTION(sort) { - zval *array; - zend_long sort_type = PHP_SORT_REGULAR; - bucket_compare_func_t cmp; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY_EX(array, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(sort_type) - ZEND_PARSE_PARAMETERS_END(); - - cmp = php_get_data_compare_func(sort_type, 0); - - zend_array_sort(Z_ARRVAL_P(array), cmp, 1); - - RETURN_TRUE; + php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_compare_func, true); } /* }}} */ /* {{{ Sort an array in reverse order */ PHP_FUNCTION(rsort) { - zval *array; - zend_long sort_type = PHP_SORT_REGULAR; - bucket_compare_func_t cmp; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY_EX(array, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(sort_type) - ZEND_PARSE_PARAMETERS_END(); - - cmp = php_get_data_compare_func(sort_type, 1); + php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_data_reverse_compare_func, true); +} +/* }}} */ - zend_array_sort(Z_ARRVAL_P(array), cmp, 1); +/* {{{ Sort an array by key value in reverse order */ +PHP_FUNCTION(krsort) +{ + php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_key_reverse_compare_func, false); +} +/* }}} */ - RETURN_TRUE; +/* {{{ Sort an array by key */ +PHP_FUNCTION(ksort) +{ + php_sort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_get_key_compare_func, false); } /* }}} */ @@ -912,14 +835,14 @@ static void php_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compar /* {{{ Sort an array by values using a user-defined comparison function */ PHP_FUNCTION(usort) { - php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1); + php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, true); } /* }}} */ /* {{{ Sort an array with a user-defined comparison function and maintain index association */ PHP_FUNCTION(uasort) { - php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 0); + php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, false); } /* }}} */ @@ -986,7 +909,7 @@ static int php_array_user_key_compare(Bucket *a, Bucket *b) /* {{{ */ /* {{{ Sort an array by keys using a user-defined comparison function */ PHP_FUNCTION(uksort) { - php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, 0); + php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, false); } /* }}} */ @@ -5014,7 +4937,7 @@ PHP_FUNCTION(array_unique) return; } - cmp = php_get_data_compare_func_unstable(sort_type, 0); + cmp = php_get_data_compare_func_unstable(sort_type, false); bool in_place = zend_may_modify_arg_in_place(array); if (in_place) { From 94831808af4bf469230e857d2a6cac007f8e6e31 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 13 Nov 2025 08:31:02 +0100 Subject: [PATCH 035/252] xml: Drop unused version function (#20467) --- ext/xml/compat.c | 5 ----- ext/xml/expat_compat.h | 1 - 2 files changed, 6 deletions(-) diff --git a/ext/xml/compat.c b/ext/xml/compat.c index 6bdb2695c7d5f..450362ef7efe4 100644 --- a/ext/xml/compat.c +++ b/ext/xml/compat.c @@ -652,11 +652,6 @@ XML_GetCurrentByteCount(XML_Parser parser) return (int) XML_GetCurrentByteIndex(parser); } -PHP_XML_API const XML_Char *XML_ExpatVersion(void) -{ - return (const XML_Char *) "1.0"; -} - PHP_XML_API void XML_ParserFree(XML_Parser parser) { diff --git a/ext/xml/expat_compat.h b/ext/xml/expat_compat.h index 94ca8aeb4c351..1cb6f5d140e5b 100644 --- a/ext/xml/expat_compat.h +++ b/ext/xml/expat_compat.h @@ -148,7 +148,6 @@ PHP_XML_API int XML_GetCurrentLineNumber(XML_Parser); PHP_XML_API int XML_GetCurrentColumnNumber(XML_Parser); PHP_XML_API long XML_GetCurrentByteIndex(XML_Parser); PHP_XML_API int XML_GetCurrentByteCount(XML_Parser); -PHP_XML_API const XML_Char *XML_ExpatVersion(void); PHP_XML_API void XML_ParserFree(XML_Parser); #elif defined(HAVE_LIBEXPAT) From 9cd367362da5442861f30d3b41e967d641b90cbd Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 13 Nov 2025 23:33:50 +0100 Subject: [PATCH 036/252] [ci skip] UPGRADING.INTERNALS entry for 94831808 --- UPGRADING.INTERNALS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 028779705a736..1650e6b109000 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -57,6 +57,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES 3. Module changes ======================== +- ext/xml: + . Removed the XML_ExpatVersion() libxml compatibility wrapper, + as it was unused. + ======================== 4. OpCode changes ======================== From 11070509aeff04623a4825b3bbcc55b86a785042 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 14 Nov 2025 18:23:13 +0100 Subject: [PATCH 037/252] xml: Drop unused XML_GetCurrentByteCount() compatibility wrapper (#20476) --- UPGRADING.INTERNALS | 2 ++ ext/xml/compat.c | 8 -------- ext/xml/expat_compat.h | 1 - 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 1650e6b109000..33632968368c7 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -60,6 +60,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES - ext/xml: . Removed the XML_ExpatVersion() libxml compatibility wrapper, as it was unused. + . Removed the XML_GetCurrentByteCount() libxml compatibility wrapper, + as it was unused and could return the wrong result. ======================== 4. OpCode changes diff --git a/ext/xml/compat.c b/ext/xml/compat.c index 450362ef7efe4..72a0720bfcb62 100644 --- a/ext/xml/compat.c +++ b/ext/xml/compat.c @@ -644,14 +644,6 @@ XML_GetCurrentByteIndex(XML_Parser parser) (parser->parser->input->cur - parser->parser->input->base); } -PHP_XML_API int -XML_GetCurrentByteCount(XML_Parser parser) -{ - /* TODO: this is identical to ByteIndex; it should probably - * be different */ - return (int) XML_GetCurrentByteIndex(parser); -} - PHP_XML_API void XML_ParserFree(XML_Parser parser) { diff --git a/ext/xml/expat_compat.h b/ext/xml/expat_compat.h index 1cb6f5d140e5b..a7faffbac504f 100644 --- a/ext/xml/expat_compat.h +++ b/ext/xml/expat_compat.h @@ -147,7 +147,6 @@ PHP_XML_API const XML_Char *XML_ErrorString(int); PHP_XML_API int XML_GetCurrentLineNumber(XML_Parser); PHP_XML_API int XML_GetCurrentColumnNumber(XML_Parser); PHP_XML_API long XML_GetCurrentByteIndex(XML_Parser); -PHP_XML_API int XML_GetCurrentByteCount(XML_Parser); PHP_XML_API void XML_ParserFree(XML_Parser); #elif defined(HAVE_LIBEXPAT) From 0375060d16bfa21b0d3ce4868bf67114278dda60 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 15 Nov 2025 01:02:11 +0100 Subject: [PATCH 038/252] xml: Don't make copies of the object, remove self-reference (#20471) --- ext/xml/xml.c | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/ext/xml/xml.c b/ext/xml/xml.c index 813718bb8a229..0b46aa6b2fe2f 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -64,11 +64,7 @@ typedef struct { XML_Parser parser; XML_Char *target_encoding; - /* Reference to the object itself, for convenience. - * It is not owned, do not release it. */ - zval index; - - zend_object *object; + zend_object *object; /* object with handlers */ zend_fcall_info_cache startElementHandler; zend_fcall_info_cache endElementHandler; zend_fcall_info_cache characterDataHandler; @@ -641,7 +637,7 @@ void xml_startElementHandler(void *userData, const XML_Char *name, const XML_Cha if (ZEND_FCC_INITIALIZED(parser->startElementHandler)) { zval args[3]; - ZVAL_COPY(&args[0], &parser->index); + ZVAL_OBJ(&args[0], &parser->std); ZVAL_STR(&args[1], xml_stripped_tag(tag_name, parser->toffset)); array_init(&args[2]); @@ -660,7 +656,6 @@ void xml_startElementHandler(void *userData, const XML_Char *name, const XML_Cha } zend_call_known_fcc(&parser->startElementHandler, /* retval */ NULL, /* param_count */ 3, args, /* named_params */ NULL); - zval_ptr_dtor(&args[0]); zval_ptr_dtor_str(&args[1]); zval_ptr_dtor(&args[2]); } @@ -742,11 +737,10 @@ void xml_endElementHandler(void *userData, const XML_Char *name) if (ZEND_FCC_INITIALIZED(parser->endElementHandler)) { zval args[2]; - ZVAL_COPY(&args[0], &parser->index); + ZVAL_OBJ(&args[0], &parser->std); ZVAL_STR(&args[1], xml_stripped_tag(tag_name, parser->toffset)); zend_call_known_fcc(&parser->endElementHandler, /* retval */ NULL, /* param_count */ 2, args, /* named_params */ NULL); - zval_ptr_dtor(&args[0]); zval_ptr_dtor_str(&args[1]); } @@ -803,11 +797,10 @@ void xml_characterDataHandler(void *userData, const XML_Char *s, int len) if (ZEND_FCC_INITIALIZED(parser->characterDataHandler)) { zval args[2]; - ZVAL_COPY(&args[0], &parser->index); + ZVAL_OBJ(&args[0], &parser->std); xml_xmlchar_zval(s, len, parser->target_encoding, &args[1]); zend_call_known_fcc(&parser->characterDataHandler, /* retval */ NULL, /* param_count */ 2, args, /* named_params */ NULL); - zval_ptr_dtor(&args[0]); zval_ptr_dtor_str(&args[1]); } @@ -911,12 +904,11 @@ void xml_processingInstructionHandler(void *userData, const XML_Char *target, co zval args[3]; - ZVAL_COPY(&args[0], &parser->index); + ZVAL_OBJ(&args[0], &parser->std); xml_xmlchar_zval(target, 0, parser->target_encoding, &args[1]); xml_xmlchar_zval(data, 0, parser->target_encoding, &args[2]); zend_call_known_fcc(&parser->processingInstructionHandler, /* retval */ NULL, /* param_count */ 3, args, /* named_params */ NULL); - zval_ptr_dtor(&args[0]); zval_ptr_dtor_str(&args[1]); zval_ptr_dtor_str(&args[2]); } @@ -933,11 +925,10 @@ void xml_defaultHandler(void *userData, const XML_Char *s, int len) zval args[2]; - ZVAL_COPY(&args[0], &parser->index); + ZVAL_OBJ(&args[0], &parser->std); xml_xmlchar_zval(s, len, parser->target_encoding, &args[1]); zend_call_known_fcc(&parser->defaultHandler, /* retval */ NULL, /* param_count */ 2, args, /* named_params */ NULL); - zval_ptr_dtor(&args[0]); zval_ptr_dtor_str(&args[1]); } /* }}} */ @@ -955,7 +946,7 @@ void xml_unparsedEntityDeclHandler(void *userData, zval args[6]; - ZVAL_COPY(&args[0], &parser->index); + ZVAL_OBJ(&args[0], &parser->std); xml_xmlchar_zval(entityName, 0, parser->target_encoding, &args[1]); xml_xmlchar_zval(base, 0, parser->target_encoding, &args[2]); xml_xmlchar_zval(systemId, 0, parser->target_encoding, &args[3]); @@ -963,7 +954,6 @@ void xml_unparsedEntityDeclHandler(void *userData, xml_xmlchar_zval(notationName, 0, parser->target_encoding, &args[5]); zend_call_known_fcc(&parser->unparsedEntityDeclHandler, /* retval */ NULL, /* param_count */ 6, args, /* named_params */ NULL); - zval_ptr_dtor(&args[0]); zval_ptr_dtor_str(&args[1]); zval_ptr_dtor_str(&args[2]); zval_ptr_dtor_str(&args[3]); @@ -984,14 +974,13 @@ void xml_notationDeclHandler(void *userData, const XML_Char *notationName, zval args[5]; - ZVAL_COPY(&args[0], &parser->index); + ZVAL_OBJ(&args[0], &parser->std); xml_xmlchar_zval(notationName, 0, parser->target_encoding, &args[1]); xml_xmlchar_zval(base, 0, parser->target_encoding, &args[2]); xml_xmlchar_zval(systemId, 0, parser->target_encoding, &args[3]); xml_xmlchar_zval(publicId, 0, parser->target_encoding, &args[4]); zend_call_known_fcc(&parser->notationDeclHandler, /* retval */ NULL, /* param_count */ 5, args, /* named_params */ NULL); - zval_ptr_dtor(&args[0]); zval_ptr_dtor_str(&args[1]); zval_ptr_dtor_str(&args[2]); zval_ptr_dtor_str(&args[3]); @@ -1013,14 +1002,13 @@ int xml_externalEntityRefHandler(XML_Parser userData, const XML_Char *openEntity zval args[5]; zval retval; - ZVAL_COPY(&args[0], &parser->index); + ZVAL_OBJ(&args[0], &parser->std); xml_xmlchar_zval(openEntityNames, 0, parser->target_encoding, &args[1]); xml_xmlchar_zval(base, 0, parser->target_encoding, &args[2]); xml_xmlchar_zval(systemId, 0, parser->target_encoding, &args[3]); xml_xmlchar_zval(publicId, 0, parser->target_encoding, &args[4]); zend_call_known_fcc(&parser->externalEntityRefHandler, /* retval */ &retval, /* param_count */ 5, args, /* named_params */ NULL); - zval_ptr_dtor(&args[0]); zval_ptr_dtor_str(&args[1]); zval_ptr_dtor_str(&args[2]); zval_ptr_dtor_str(&args[3]); @@ -1048,12 +1036,11 @@ void xml_startNamespaceDeclHandler(void *userData,const XML_Char *prefix, const zval args[3]; - ZVAL_COPY(&args[0], &parser->index); + ZVAL_OBJ(&args[0], &parser->std); xml_xmlchar_zval(prefix, 0, parser->target_encoding, &args[1]); xml_xmlchar_zval(uri, 0, parser->target_encoding, &args[2]); zend_call_known_fcc(&parser->startNamespaceDeclHandler, /* retval */ NULL, /* param_count */ 3, args, /* named_params */ NULL); - zval_ptr_dtor(&args[0]); zval_ptr_dtor_str(&args[1]); zval_ptr_dtor_str(&args[2]); } @@ -1070,11 +1057,10 @@ void xml_endNamespaceDeclHandler(void *userData, const XML_Char *prefix) zval args[2]; - ZVAL_COPY(&args[0], &parser->index); + ZVAL_OBJ(&args[0], &parser->std); xml_xmlchar_zval(prefix, 0, parser->target_encoding, &args[1]); zend_call_known_fcc(&parser->endNamespaceDeclHandler, /* retval */ NULL, /* param_count */ 2, args, /* named_params */ NULL); - zval_ptr_dtor(&args[0]); zval_ptr_dtor_str(&args[1]); } /* }}} */ @@ -1133,7 +1119,6 @@ static void php_xml_parser_create_impl(INTERNAL_FUNCTION_PARAMETERS, int ns_supp parser->parsehuge = false; /* It's the default for BC & DoS protection */ XML_SetUserData(parser->parser, parser); - ZVAL_COPY_VALUE(&parser->index, return_value); } /* }}} */ From 48b19a8eded16782e64718ea4058af99f2a8d7a3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 13 Nov 2025 22:41:23 +0100 Subject: [PATCH 039/252] xml: Use safe_emalloc() correctly Fortunately, libxml won't allow _at this point in time_ to have more than INT_MAX/5 attributes, so this doesn't cause issues right now. However, if this limit is ever raised then it can cause an integer overflow which will cause a heap overflow. So future-proof this code by properly using safe_emalloc(). Closes GH-20472. --- ext/xml/compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/xml/compat.c b/ext/xml/compat.c index 25add45f0340a..df241e5b684fc 100644 --- a/ext/xml/compat.c +++ b/ext/xml/compat.c @@ -111,7 +111,7 @@ _start_element_handler_ns(void *user, const xmlChar *name, const xmlChar *prefix if (attributes != NULL) { xmlChar *qualified_name_attr = NULL; - attrs = safe_emalloc((nb_attributes * 2) + 1, sizeof(int *), 0); + attrs = safe_emalloc(nb_attributes, 2 * sizeof(int *), sizeof(int *)); for (i = 0; i < nb_attributes; i += 1) { From 7263cb61e05d0227061db6e9c34f443d3276d9d9 Mon Sep 17 00:00:00 2001 From: Theodore Brown Date: Sat, 15 Nov 2025 05:48:34 -0600 Subject: [PATCH 040/252] [skip ci] Fix destructuring typo in NEWS and UPGRADING (#20488) --- NEWS | 2 +- UPGRADING | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 26b6a59cb5d33..494915f3d9234 100644 --- a/NEWS +++ b/NEWS @@ -289,7 +289,7 @@ PHP NEWS 11 Sep 2025, PHP 8.5.0beta3 - Core: - . Destructing non-array values (other than NULL) using [] or list() now + . Destructuring non-array values (other than NULL) using [] or list() now emits a warning. (Girgias) . Fixed bug GH-19637 (Incorrect Closure scope for FCC in constant expression). (timwolla) diff --git a/UPGRADING b/UPGRADING index f95ca587594a5..94b272c69c70f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -52,7 +52,7 @@ PHP 8.5 UPGRADE NOTES . The disable_classes INI setting has been removed as it causes various engine assumptions to be broken. RFC: https://wiki.php.net/rfc/deprecations_php_8_5#remove_disable_classes_ini_setting - . Destructing non-array values (other than NULL) using [] or list() now + . Destructuring non-array values (other than NULL) using [] or list() now emits a warning. RFC: https://wiki.php.net/rfc/warnings-php-8-5#destructuring_non-array_values . A warning is now emitted when casting floats (or strings that look like From 2f9d86b67787592c2532939df4f91957cd3df7c4 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 13 Nov 2025 23:06:21 +0100 Subject: [PATCH 041/252] phar: Fix broken return value of fflush() for phar file entries The flush functions always return EOF, even in the success path. The success path should return 0 to indicate success. Closes GH-20474. --- NEWS | 1 + ext/phar/phar.c | 2 +- ext/phar/tar.c | 2 +- .../fflush_phar_file_report_success.phpt | 25 +++++++++++++++++++ ext/phar/zip.c | 2 +- 5 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 ext/phar/tests/fflush_phar_file_report_success.phpt diff --git a/NEWS b/NEWS index 52cfb5ee63857..a7b5e126e3a41 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,7 @@ PHP NEWS - Phar: . Fixed bug GH-20442 (Phar does not respect case-insensitiveness of __halt_compiler() when reading stub). (ndossche, TimWolla) + . Fix broken return value of fflush() for phar file entries. (ndossche) - PHPDBG: . Fixed ZPP type violation in phpdbg_get_executable() and phpdbg_end_oplog(). diff --git a/ext/phar/phar.c b/ext/phar/phar.c index aa9a8821d8e8c..a63a8dd8eb6d6 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -3225,7 +3225,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int conv return EOF; } - return EOF; + return 0; cleanup: if (shared_cfp != NULL) { diff --git a/ext/phar/tar.c b/ext/phar/tar.c index e675b86943217..3bbaad5968260 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -1378,6 +1378,6 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int php_stream_close(newfile); } } - return EOF; + return 0; } /* }}} */ diff --git a/ext/phar/tests/fflush_phar_file_report_success.phpt b/ext/phar/tests/fflush_phar_file_report_success.phpt new file mode 100644 index 0000000000000..615f4e58e5678 --- /dev/null +++ b/ext/phar/tests/fflush_phar_file_report_success.phpt @@ -0,0 +1,25 @@ +--TEST-- +fflush() on phar file should report success +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +--FILE-- +addFromString('test', 'contents'); +unset($phar); + +$f = fopen('phar://' . __DIR__.'/fflush_phar_file_report_success.phar/test', 'w'); +var_dump(fflush($f)); +var_dump(fclose($f)); + +?> +--CLEAN-- + +--EXPECT-- +bool(true) +bool(true) diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 63c56108ed985..b5133063e4460 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -1542,6 +1542,6 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, zend_long len, int if (closeoldfile) { php_stream_close(oldfile); } - return EOF; + return 0; } /* }}} */ From fd5c14e682a12a681ebeae66208cd3e09c8952d1 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 15 Nov 2025 13:57:47 +0100 Subject: [PATCH 042/252] Revert "ext/phar: Voidify flush function as it always returns EOL" This reverts commit 2513258a2b4e83a5c864cc1152914414d6565375. --- ext/phar/phar.c | 41 ++++++++++++++++++------------------ ext/phar/phar_internal.h | 8 +++---- ext/phar/stream.c | 5 +++-- ext/phar/tar.c | 45 ++++++++++++++++++++-------------------- ext/phar/zip.c | 29 +++++++++++++------------- 5 files changed, 66 insertions(+), 62 deletions(-) diff --git a/ext/phar/phar.c b/ext/phar/phar.c index d6c402eefaee3..c01ffc9b6e0d0 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -2496,8 +2496,8 @@ zend_string *phar_create_default_stub(const char *index_php, const char *web_ind } /* }}} */ -void phar_flush(phar_archive_data *phar, char **error) { - phar_flush_ex(phar, NULL, false, error); +int phar_flush(phar_archive_data *phar, char **error) { + return phar_flush_ex(phar, NULL, false, error); } /** @@ -2505,7 +2505,7 @@ void phar_flush(phar_archive_data *phar, char **error) { * * if user_stub is NULL the default or existing stub should be used */ -void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_default_stub, char **error) /* {{{ */ +int phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_default_stub, char **error) /* {{{ */ { static const char halt_stub[] = "__HALT_COMPILER();"; @@ -2533,7 +2533,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa if (error) { spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname); } - return; + return EOF; } if (error) { @@ -2541,23 +2541,21 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa } if (!zend_hash_num_elements(&phar->manifest) && !user_stub) { - return; + return EOF; } zend_hash_clean(&phar->virtual_dirs); if (phar->is_zip) { - phar_zip_flush(phar, user_stub, is_default_stub, error); - return; + return phar_zip_flush(phar, user_stub, is_default_stub, error); } if (phar->is_tar) { - phar_tar_flush(phar, user_stub, is_default_stub, error); - return; + return phar_tar_flush(phar, user_stub, is_default_stub, error); } if (PHAR_G(readonly)) { - return; + return EOF; } if (phar->fp && !phar->is_brandnew) { @@ -2576,7 +2574,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa if (must_close_old_file) { php_stream_close(oldfile); } - return; + return EOF; } if (user_stub) { @@ -2590,7 +2588,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa if (error) { spprintf(error, 0, "illegal stub for phar \"%s\" (__HALT_COMPILER(); is missing)", phar->fname); } - return; + return EOF; } size_t len = pos - ZSTR_VAL(user_stub) + strlen(halt_stub); @@ -2608,7 +2606,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa if (error) { spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname); } - return; + return EOF; } phar->halt_offset = len + end_sequence_len; } else { @@ -2638,7 +2636,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa if (new_stub) { zend_string_free(new_stub); } - return; + return EOF; } if (new_stub) { zend_string_free(new_stub); @@ -2734,7 +2732,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa if (error) { spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname); } - return; + return EOF; } newcrc32 = php_crc32_bulk_init(); php_crc32_stream_bulk_update(&newcrc32, file, entry->uncompressed_filesize); @@ -2760,7 +2758,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname); } } - return; + return EOF; } /* create new file that holds the compressed versions */ @@ -3086,7 +3084,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa php_stream_close(oldfile); } php_stream_close(newfile); - return; + return EOF; } php_stream_write(newfile, digest, digest_len); @@ -3138,7 +3136,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa if (error) { spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname); } - return; + return EOF; } if (phar->flags & PHAR_FILE_COMPRESSED_GZ) { @@ -3154,7 +3152,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa if (error) { spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname); } - return; + return EOF; } php_stream_filter_append(&phar->fp->writefilters, filter); @@ -3184,9 +3182,10 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa if (error) { spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname); } + return EOF; } - return; + return EOF; cleanup: if (shared_cfp != NULL) { @@ -3198,6 +3197,8 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa entry->header_offset = 0; } } ZEND_HASH_FOREACH_END(); + + return EOF; } /* }}} */ diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 2f9ae7c1c84ea..d8e319eeb6e76 100644 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -447,12 +447,12 @@ zend_result phar_copy_on_write(phar_archive_data **pphar); bool phar_is_tar(char *buf, char *fname); zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, char *alias, size_t alias_len, phar_archive_data** pphar, uint32_t compression, char **error); zend_result phar_open_or_create_tar(char *fname, size_t fname_len, char *alias, size_t alias_len, int is_data, uint32_t options, phar_archive_data** pphar, char **error); -void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_default_stub, char **error); +int phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_default_stub, char **error); /* zip functions in zip.c */ int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alias, size_t alias_len, phar_archive_data** pphar, char **error); int phar_open_or_create_zip(char *fname, size_t fname_len, char *alias, size_t alias_len, int is_data, uint32_t options, phar_archive_data** pphar, char **error); -void phar_zip_flush(phar_archive_data *archive, zend_string *user_stub, bool is_default_stub, char **error); +int phar_zip_flush(phar_archive_data *archive, zend_string *user_stub, bool is_default_stub, char **error); #ifdef PHAR_MAIN extern const php_stream_wrapper php_stream_phar_wrapper; @@ -468,8 +468,8 @@ phar_entry_info *phar_get_entry_info(phar_archive_data *phar, char *path, size_t phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, size_t path_len, char dir, char **error, int security); phar_entry_data *phar_get_or_create_entry_data(char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, int security); zend_result phar_get_entry_data(phar_entry_data **ret, char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, int security); -void phar_flush_ex(phar_archive_data *archive, zend_string *user_stub, bool is_default_stub, char **error); -void phar_flush(phar_archive_data *archive, char **error); +int phar_flush_ex(phar_archive_data *archive, zend_string *user_stub, bool is_default_stub, char **error); +int phar_flush(phar_archive_data *archive, char **error); zend_result phar_detect_phar_fname_ext(const char *filename, size_t filename_len, const char **ext_str, size_t *ext_len, int executable, int for_create, int is_complete); zend_result phar_split_fname(const char *filename, size_t filename_len, char **arch, size_t *arch_len, char **entry, size_t *entry_len, int executable, int for_create); diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 41f1ed9c0ddee..9a7fd3763e321 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -471,16 +471,17 @@ static ssize_t phar_stream_write(php_stream *stream, const char *buf, size_t cou static int phar_stream_flush(php_stream *stream) /* {{{ */ { char *error; + int ret; phar_entry_data *data = (phar_entry_data *) stream->abstract; if (data->internal_file->is_modified) { data->internal_file->timestamp = time(0); - phar_flush(data->phar, &error); + ret = phar_flush(data->phar, &error); if (error) { php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS, "%s", error); efree(error); } - return EOF; + return ret; } else { return EOF; } diff --git a/ext/phar/tar.c b/ext/phar/tar.c index 63a9cdbf88fd1..d53d3a7107e87 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -959,7 +959,7 @@ static int phar_tar_setupmetadata(zval *zv, void *argument) /* {{{ */ } /* }}} */ -void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_default_stub, char **error) /* {{{ */ +int phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_default_stub, char **error) /* {{{ */ { static const char newstub[] = "fname); } - return; + return EOF; } if (phar->is_data) { @@ -1001,7 +1001,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (entry.fp == NULL) { efree(entry.filename); spprintf(error, 0, "phar error: unable to create temporary file"); - return; + return -1; } if (phar->alias_len != php_stream_write(entry.fp, phar->alias, phar->alias_len)) { if (error) { @@ -1009,7 +1009,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def } php_stream_close(entry.fp); efree(entry.filename); - return; + return EOF; } entry.uncompressed_filesize = phar->alias_len; @@ -1029,7 +1029,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (error) { spprintf(error, 0, "illegal stub for tar-based phar \"%s\"", phar->fname); } - return; + return EOF; } size_t len = pos - ZSTR_VAL(user_stub) + strlen(halt_stub); @@ -1039,7 +1039,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def entry.fp = php_stream_fopen_tmpfile(); if (entry.fp == NULL) { spprintf(error, 0, "phar error: unable to create temporary file"); - return; + return EOF; } entry.uncompressed_filesize = len + end_sequence_len; @@ -1051,7 +1051,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def spprintf(error, 0, "unable to create stub from string in new tar-based phar \"%s\"", phar->fname); } php_stream_close(entry.fp); - return; + return EOF; } entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1); @@ -1062,14 +1062,14 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def entry.fp = php_stream_fopen_tmpfile(); if (entry.fp == NULL) { spprintf(error, 0, "phar error: unable to create temporary file"); - return; + return EOF; } if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) { php_stream_close(entry.fp); if (error) { spprintf(error, 0, "unable to %s stub in%star-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname); } - return; + return EOF; } entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1; @@ -1084,7 +1084,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (error) { spprintf(error, 0, "unable to create stub in tar-based phar \"%s\"", phar->fname); } - return; + return EOF; } } else { php_stream_close(entry.fp); @@ -1112,7 +1112,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (must_close_old_file) { php_stream_close(oldfile); } - return; + return EOF; } pass.old = oldfile; @@ -1128,7 +1128,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (must_close_old_file) { php_stream_close(oldfile); } - return; + return EOF; } } else { phar_entry_info newentry = {0}; @@ -1144,7 +1144,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (must_close_old_file) { php_stream_close(oldfile); } - return; + return EOF; } if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(&phar->metadata_tracker, mentry, error)) { @@ -1152,7 +1152,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (must_close_old_file) { php_stream_close(oldfile); } - return; + return EOF; } } } @@ -1166,7 +1166,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def /* on error in the hash iterator above, error is set */ php_stream_close(newfile); - return; + return EOF; } zend_hash_apply_with_argument(&phar->manifest, phar_tar_writeheaders, (void *) &pass); @@ -1195,7 +1195,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def } php_stream_close(newfile); - return; + return EOF; } entry.filename = ".phar/signature.bin"; @@ -1209,7 +1209,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def php_stream_close(oldfile); } php_stream_close(newfile); - return; + return EOF; } #ifdef WORDS_BIGENDIAN # define PHAR_SET_32(destination, source) do { \ @@ -1235,7 +1235,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def php_stream_close(oldfile); } php_stream_close(newfile); - return; + return EOF; } efree(signature); @@ -1249,7 +1249,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def } /* error is set by writeheaders */ php_stream_close(newfile); - return; + return EOF; } } /* signature */ @@ -1265,7 +1265,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def /* on error in the hash iterator above, error is set */ if (error && *error) { php_stream_close(newfile); - return; + return EOF; } if (phar->fp && pass.free_fp) { @@ -1292,7 +1292,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (error) { spprintf(error, 0, "unable to open new phar \"%s\" for writing", phar->fname); } - return; + return EOF; } if (phar->flags & PHAR_FILE_COMPRESSED_GZ) { @@ -1316,7 +1316,7 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (error) { spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname); } - return; + return EOF; } php_stream_filter_append(&phar->fp->writefilters, filter); @@ -1343,5 +1343,6 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def php_stream_close(newfile); } } + return EOF; } /* }}} */ diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 026fe3935c5e7..5bb0e6986bbd2 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -1236,7 +1236,7 @@ static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pas } /* }}} */ -void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_default_stub, char **error) /* {{{ */ +int phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_default_stub, char **error) /* {{{ */ { static const char newstub[] = "fname); } - return; + return EOF; } if (phar->is_data) { @@ -1273,14 +1273,14 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def entry.fp = php_stream_fopen_tmpfile(); if (entry.fp == NULL) { spprintf(error, 0, "phar error: unable to create temporary file"); - return; + return EOF; } if (phar->alias_len != php_stream_write(entry.fp, phar->alias, phar->alias_len)) { php_stream_close(entry.fp); if (error) { spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname); } - return; + return EOF; } entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len; @@ -1295,7 +1295,7 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def /* register alias */ if (phar->alias_len) { if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error)) { - return; + return EOF; } } @@ -1307,7 +1307,7 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (error) { spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname); } - return; + return EOF; } size_t len = pos - ZSTR_VAL(user_stub) + strlen(halt_stub); @@ -1317,7 +1317,7 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def entry.fp = php_stream_fopen_tmpfile(); if (entry.fp == NULL) { spprintf(error, 0, "phar error: unable to create temporary file"); - return; + return EOF; } entry.uncompressed_filesize = len + end_sequence_len; @@ -1329,7 +1329,7 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname); } php_stream_close(entry.fp); - return; + return EOF; } entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1); @@ -1341,14 +1341,14 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def entry.fp = php_stream_fopen_tmpfile(); if (entry.fp == NULL) { spprintf(error, 0, "phar error: unable to create temporary file"); - return; + return EOF; } if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) { php_stream_close(entry.fp); if (error) { spprintf(error, 0, "unable to %s stub in%szip-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname); } - return; + return EOF; } entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1; @@ -1363,7 +1363,7 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (error) { spprintf(error, 0, "unable to create stub in zip-based phar \"%s\"", phar->fname); } - return; + return EOF; } } else { php_stream_close(entry.fp); @@ -1395,7 +1395,7 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (error) { spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname); } - return; + return EOF; } pass.centralfp = php_stream_fopen_tmpfile(); @@ -1435,7 +1435,7 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (must_close_old_file) { php_stream_close(oldfile); } - return; + return EOF; } if (FAILURE == phar_zip_applysignature(phar, &pass)) { @@ -1516,7 +1516,7 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (error) { spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname); } - return; + return EOF; } php_stream_rewind(pass.filefp); php_stream_copy_to_stream_ex(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL, NULL); @@ -1527,5 +1527,6 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (must_close_old_file) { php_stream_close(oldfile); } + return EOF; } /* }}} */ From d9e40372fcd62be0ee1c954a1a2172ab054815da Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 13 Nov 2025 23:19:04 +0100 Subject: [PATCH 043/252] Fix assertion failure when fseeking a phar file out of bounds In 61884c3b52 I added these FIXME comments after I noticed that this would cause an assertion failure. At that time I did not yet know what to do here. I took a look at the code now and other streams return -1 and leave the file position untouched. So we do the same for phar. This fixes the assertion failure and subsequent crashes, but also changes one test output. However, I believe the new test output is correct. Closes GH-20475. --- NEWS | 1 + ext/phar/stream.c | 2 -- ext/phar/tests/022.phpt | 14 ++++++------- ext/phar/tests/fseek_outside_bounds.phpt | 26 ++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 ext/phar/tests/fseek_outside_bounds.phpt diff --git a/NEWS b/NEWS index a7b5e126e3a41..c9ff9ac461d4d 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,7 @@ PHP NEWS . Fixed bug GH-20442 (Phar does not respect case-insensitiveness of __halt_compiler() when reading stub). (ndossche, TimWolla) . Fix broken return value of fflush() for phar file entries. (ndossche) + . Fix assertion failure when fseeking a phar file out of bounds. (ndossche) - PHPDBG: . Fixed ZPP type violation in phpdbg_get_executable() and phpdbg_end_oplog(). diff --git a/ext/phar/stream.c b/ext/phar/stream.c index a0049f9a6b1f0..1151b520bba09 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -429,11 +429,9 @@ static int phar_stream_seek(php_stream *stream, zend_off_t offset, int whence, z zend_off_t temp_signed = (zend_off_t) temp; if (temp_signed > data->zero + (zend_off_t) entry->uncompressed_filesize) { - *newoffset = -1; /* FIXME: this will invalidate the ZEND_ASSERT(stream->position >= 0); assertion in streams.c */ return -1; } if (temp_signed < data->zero) { - *newoffset = -1; /* FIXME: this will invalidate the ZEND_ASSERT(stream->position >= 0); assertion in streams.c */ return -1; } res = php_stream_seek(data->fp, temp_signed, SEEK_SET); diff --git a/ext/phar/tests/022.phpt b/ext/phar/tests/022.phpt index 5363a65be9423..c484c4d3c06dc 100644 --- a/ext/phar/tests/022.phpt +++ b/ext/phar/tests/022.phpt @@ -80,28 +80,28 @@ int(1) fseek($fp, -1, SEEK_END)int(0) int(6) fseek($fp, -8, SEEK_END)int(-1) -bool(false) +int(6) fseek($fp, -7, SEEK_END)int(0) int(0) fseek($fp, 0, SEEK_END)int(0) int(7) fseek($fp, 1, SEEK_END)int(-1) -bool(false) +int(7) fseek($fp, -8, SEEK_END)int(-1) -bool(false) +int(7) fseek($fp, 6)int(0) int(6) fseek($fp, 8)int(-1) -bool(false) +int(6) fseek($fp, -1)int(-1) -bool(false) +int(6) next int(4) fseek($fp, -5, SEEK_CUR)int(-1) -bool(false) +int(4) int(4) fseek($fp, 5, SEEK_CUR)int(-1) -bool(false) +int(4) int(4) fseek($fp, -4, SEEK_CUR)int(0) int(0) diff --git a/ext/phar/tests/fseek_outside_bounds.phpt b/ext/phar/tests/fseek_outside_bounds.phpt new file mode 100644 index 0000000000000..0a35abb360d5f --- /dev/null +++ b/ext/phar/tests/fseek_outside_bounds.phpt @@ -0,0 +1,26 @@ +--TEST-- +Assertion failure when fseeking outside of bounds of phar file +--EXTENSIONS-- +phar +--INI-- +phar.require_hash=0 +--FILE-- +setInfoClass('SplFileObject'); +$f = $phar['a.php']; +var_dump($f->fseek(1, SEEK_SET)); +var_dump($f->fseek(999999, SEEK_SET)); +var_dump($f->fseek(999999, SEEK_CUR)); +var_dump($f->ftell()); +var_dump($f->fseek(1, SEEK_CUR)); +var_dump($f->fread(3)); +?> +--EXPECT-- +int(0) +int(-1) +int(-1) +int(1) +int(0) +string(3) "php" From d2c5b3b25b188f27175011186c88b1002df25ac4 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 15 Nov 2025 15:14:25 +0000 Subject: [PATCH 044/252] Fix GH-20483: ASAN stack overflow with small fiber.stack_size INI value. close GH-20495 --- NEWS | 4 ++++ Zend/tests/fibers/gh20483.phpt | 16 ++++++++++++++++ Zend/zend_fibers.c | 7 ++++++- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/fibers/gh20483.phpt diff --git a/NEWS b/NEWS index c9ff9ac461d4d..5ddc2633d19c3 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,10 @@ PHP NEWS - DOM: . Fix missing NUL byte check on C14NFile(). (ndossche) +- Fibers: + . Fixed bug GH-20483 (ASAN stack overflow with fiber.stack_size INI + small value). (David Carlier) + - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) diff --git a/Zend/tests/fibers/gh20483.phpt b/Zend/tests/fibers/gh20483.phpt new file mode 100644 index 0000000000000..e06cf87258ea1 --- /dev/null +++ b/Zend/tests/fibers/gh20483.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-20483 (ASAN stack overflow with small fiber.stack_size INI value) +--INI-- +fiber.stack_size=1024 +--FILE-- +start(); +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} +?> +--EXPECTF-- +Fiber stack size is too small, it needs to be at least %d bytes diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 6b6c1eaae1a96..96f6e99e714f8 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -206,7 +206,12 @@ static zend_fiber_stack *zend_fiber_stack_allocate(size_t size) { void *pointer; const size_t page_size = zend_fiber_get_page_size(); - const size_t minimum_stack_size = page_size + ZEND_FIBER_GUARD_PAGES * page_size; + const size_t minimum_stack_size = page_size + ZEND_FIBER_GUARD_PAGES * page_size +#ifdef __SANITIZE_ADDRESS__ + // necessary correction due to ASAN redzones + * 6 +#endif + ; if (size < minimum_stack_size) { zend_throw_exception_ex(NULL, 0, "Fiber stack size is too small, it needs to be at least %zu bytes", minimum_stack_size); From 94c256f9973e6d3af4ba2e0c9a82ee5e65b66e9d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 15 Nov 2025 18:53:05 +0100 Subject: [PATCH 045/252] Properly silence set-but-unused-var warning --- ext/mbstring/mbstring.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index ceb182a0a258d..7fda240b7051a 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -5578,19 +5578,16 @@ static bool mb_check_str_encoding(zend_string *str, const mbfl_encoding *encodin static bool php_mb_check_encoding_recursive(HashTable *vars, const mbfl_encoding *encoding) { - zend_long idx; zend_string *key; zval *entry; bool valid = true; - (void)(idx); /* Suppress spurious compiler warning that `idx` is not used */ - if (GC_IS_RECURSIVE(vars)) { php_error_docref(NULL, E_WARNING, "Cannot not handle circular references"); return false; } GC_TRY_PROTECT_RECURSION(vars); - ZEND_HASH_FOREACH_KEY_VAL(vars, idx, key, entry) { + ZEND_HASH_FOREACH_STR_KEY_VAL(vars, key, entry) { ZVAL_DEREF(entry); if (key) { if (!mb_check_str_encoding(key, encoding)) { From 0194b381d6c0f38e4213910b7fba039923b4883a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 16 Nov 2025 01:17:50 +0100 Subject: [PATCH 046/252] [ci skip] Update my username --- NEWS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 7124d1bc278ae..0b1b0d9da64e1 100644 --- a/NEWS +++ b/NEWS @@ -6,7 +6,7 @@ PHP NEWS . Added first-class callable cache to share instances for the duration of the request. (ilutov) . It is now possible to use reference assign on WeakMap without the key - needing to be present beforehand. (nielsdos) + needing to be present beforehand. (ndossche) - Hash: . Upgrade xxHash to 0.8.2. (timwolla) @@ -25,9 +25,9 @@ PHP NEWS preloading). (Arnaud, welcomycozyhom) - Phar: - . Support reference values in Phar::mungServer(). (nielsdos) + . Support reference values in Phar::mungServer(). (ndossche) . Invalid values now throw in Phar::mungServer() instead of being silently - ignored. (nielsdos) + ignored. (ndossche) - Reflection: . Fixed bug GH-20217 (ReflectionClass::isIterable() incorrectly returns true From 035f95cf5e016236cca11bc293dc04d40b40e45c Mon Sep 17 00:00:00 2001 From: Marc Bennewitz Date: Sun, 16 Nov 2025 09:02:20 +0100 Subject: [PATCH 047/252] Deprecate ZEND_SIZE_MAX and point to SIZE_MAX directly (#19244) --- Zend/zend_types.h | 15 ++------------- ext/standard/formatted_print.c | 2 +- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Zend/zend_types.h b/Zend/zend_types.h index a3d3e4da6362d..43aa2aa86a00e 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -62,19 +62,8 @@ typedef enum { typedef ZEND_RESULT_CODE zend_result; -#ifdef ZEND_ENABLE_ZVAL_LONG64 -# ifdef ZEND_WIN32 -# define ZEND_SIZE_MAX _UI64_MAX -# else -# define ZEND_SIZE_MAX SIZE_MAX -# endif -#else -# if defined(ZEND_WIN32) -# define ZEND_SIZE_MAX _UI32_MAX -# else -# define ZEND_SIZE_MAX SIZE_MAX -# endif -#endif +/* This constant is deprecated, use SIZE_MAX instead */ +#define ZEND_SIZE_MAX SIZE_MAX #ifdef ZTS #define ZEND_TLS static TSRM_TLS diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index c0246653dfe3f..b0fbfcc89099f 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -103,7 +103,7 @@ php_sprintf_appendstring(zend_string **buffer, size_t *pos, char *add, if (req_size > ZSTR_LEN(*buffer)) { size_t size = ZSTR_LEN(*buffer); while (req_size > size) { - if (size > ZEND_SIZE_MAX/2) { + if (size > SIZE_MAX/2) { zend_error_noreturn(E_ERROR, "Field width %zd is too long", req_size); } size <<= 1; From 5bed2a8920f89ce07fdecd44ed845ce544947f50 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 16 Nov 2025 05:57:00 -0800 Subject: [PATCH 048/252] Avoid string copy in ZipArchive::addFromString() (#20497) Instead of copying the data we increment the refcount of the string. --- UPGRADING | 3 +++ ext/zip/php_zip.c | 11 +++++------ ext/zip/php_zip.h | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/UPGRADING b/UPGRADING index 3b0301522d460..1e84b8beee97a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -130,3 +130,6 @@ PHP 8.6 UPGRADE NOTES . Improved performance of array_walk(). . Improved performance of intval('+0b...', 2) and intval('0b...', 2). . Improved performance of str_split(). + +- Zip: + . Avoid string copies in ZipArchive::addFromString(). diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 40c098d8901ca..06bd41d602c06 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -1014,7 +1014,7 @@ static void php_zip_object_free_storage(zend_object *object) /* {{{ */ if (intern->buffers_cnt>0) { for (i=0; ibuffers_cnt; i++) { - efree(intern->buffers[i]); + zend_string_release(intern->buffers[i]); } efree(intern->buffers); } @@ -1868,17 +1868,16 @@ PHP_METHOD(ZipArchive, addFromString) ze_obj = Z_ZIP_P(self); if (ze_obj->buffers_cnt) { - ze_obj->buffers = (char **)safe_erealloc(ze_obj->buffers, sizeof(char *), (ze_obj->buffers_cnt+1), 0); + ze_obj->buffers = safe_erealloc(ze_obj->buffers, sizeof(*ze_obj->buffers), (ze_obj->buffers_cnt + 1), 0); pos = ze_obj->buffers_cnt++; } else { - ze_obj->buffers = (char **)emalloc(sizeof(char *)); + ze_obj->buffers = emalloc(sizeof(*ze_obj->buffers)); ze_obj->buffers_cnt++; pos = 0; } - ze_obj->buffers[pos] = (char *)safe_emalloc(ZSTR_LEN(buffer), 1, 1); - memcpy(ze_obj->buffers[pos], ZSTR_VAL(buffer), ZSTR_LEN(buffer) + 1); + ze_obj->buffers[pos] = zend_string_copy(buffer); - zs = zip_source_buffer(intern, ze_obj->buffers[pos], ZSTR_LEN(buffer), 0); + zs = zip_source_buffer(intern, ZSTR_VAL(buffer), ZSTR_LEN(buffer), 0); if (zs == NULL) { RETURN_FALSE; diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index 61e7fb8dad1ea..5dc7bab250042 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -68,7 +68,7 @@ typedef struct _ze_zip_read_rsrc { /* Extends zend object */ typedef struct _ze_zip_object { struct zip *za; - char **buffers; + zend_string **buffers; HashTable *prop_handler; char *filename; int filename_len; From e844700333a3f23f2166afdc5fd955f335d52861 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Oct 2025 13:05:42 +0100 Subject: [PATCH 049/252] ext/standard/tests/filter: Add zlib dependency And remove strange function to check if a filter is available --- ext/standard/tests/filters/filter_errors.inc | 11 ----------- .../filters/filter_errors_convert_base64_decode.phpt | 2 -- .../tests/filters/filter_errors_zlib_inflate.phpt | 4 ++-- ext/standard/tests/filters/gh13264.phpt | 4 ++-- 4 files changed, 4 insertions(+), 17 deletions(-) diff --git a/ext/standard/tests/filters/filter_errors.inc b/ext/standard/tests/filters/filter_errors.inc index 18711844f834e..e933eea725e2e 100644 --- a/ext/standard/tests/filters/filter_errors.inc +++ b/ext/standard/tests/filters/filter_errors.inc @@ -1,16 +1,5 @@ --FILE-- +--EXTENSIONS-- +zlib --FILE-- +--EXTENSIONS-- +zlib --FILE-- Date: Thu, 2 Oct 2025 18:38:39 +0100 Subject: [PATCH 050/252] Move stream filter tests to ext/standard/tests/filters/ folder --- {Zend/tests => ext/standard/tests/filters}/bug21478.phpt | 0 ext/standard/tests/{file => filters}/bug39551.phpt | 0 ext/standard/tests/{streams => filters}/bug77069.phpt | 0 ext/standard/tests/{streams => filters}/bug77080.phpt | 0 {Zend/tests => ext/standard/tests/filters}/bug78406.phpt | 0 ext/standard/tests/{streams => filters}/bug78506.phpt | 0 ext/standard/tests/{streams => filters}/bug79984.phpt | 0 ext/standard/tests/{streams => filters}/gh17650.phpt | 0 .../tests/{streams => filters}/stream_filter_register.phpt | 0 .../tests/{streams => filters}/stream_multi_filters_close.phpt | 0 .../tests/{streams => filters}/user_streams_consumed_bug.phpt | 0 ext/standard/tests/{file => filters}/userfilters.phpt | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename {Zend/tests => ext/standard/tests/filters}/bug21478.phpt (100%) rename ext/standard/tests/{file => filters}/bug39551.phpt (100%) rename ext/standard/tests/{streams => filters}/bug77069.phpt (100%) rename ext/standard/tests/{streams => filters}/bug77080.phpt (100%) rename {Zend/tests => ext/standard/tests/filters}/bug78406.phpt (100%) rename ext/standard/tests/{streams => filters}/bug78506.phpt (100%) rename ext/standard/tests/{streams => filters}/bug79984.phpt (100%) rename ext/standard/tests/{streams => filters}/gh17650.phpt (100%) rename ext/standard/tests/{streams => filters}/stream_filter_register.phpt (100%) rename ext/standard/tests/{streams => filters}/stream_multi_filters_close.phpt (100%) rename ext/standard/tests/{streams => filters}/user_streams_consumed_bug.phpt (100%) rename ext/standard/tests/{file => filters}/userfilters.phpt (100%) diff --git a/Zend/tests/bug21478.phpt b/ext/standard/tests/filters/bug21478.phpt similarity index 100% rename from Zend/tests/bug21478.phpt rename to ext/standard/tests/filters/bug21478.phpt diff --git a/ext/standard/tests/file/bug39551.phpt b/ext/standard/tests/filters/bug39551.phpt similarity index 100% rename from ext/standard/tests/file/bug39551.phpt rename to ext/standard/tests/filters/bug39551.phpt diff --git a/ext/standard/tests/streams/bug77069.phpt b/ext/standard/tests/filters/bug77069.phpt similarity index 100% rename from ext/standard/tests/streams/bug77069.phpt rename to ext/standard/tests/filters/bug77069.phpt diff --git a/ext/standard/tests/streams/bug77080.phpt b/ext/standard/tests/filters/bug77080.phpt similarity index 100% rename from ext/standard/tests/streams/bug77080.phpt rename to ext/standard/tests/filters/bug77080.phpt diff --git a/Zend/tests/bug78406.phpt b/ext/standard/tests/filters/bug78406.phpt similarity index 100% rename from Zend/tests/bug78406.phpt rename to ext/standard/tests/filters/bug78406.phpt diff --git a/ext/standard/tests/streams/bug78506.phpt b/ext/standard/tests/filters/bug78506.phpt similarity index 100% rename from ext/standard/tests/streams/bug78506.phpt rename to ext/standard/tests/filters/bug78506.phpt diff --git a/ext/standard/tests/streams/bug79984.phpt b/ext/standard/tests/filters/bug79984.phpt similarity index 100% rename from ext/standard/tests/streams/bug79984.phpt rename to ext/standard/tests/filters/bug79984.phpt diff --git a/ext/standard/tests/streams/gh17650.phpt b/ext/standard/tests/filters/gh17650.phpt similarity index 100% rename from ext/standard/tests/streams/gh17650.phpt rename to ext/standard/tests/filters/gh17650.phpt diff --git a/ext/standard/tests/streams/stream_filter_register.phpt b/ext/standard/tests/filters/stream_filter_register.phpt similarity index 100% rename from ext/standard/tests/streams/stream_filter_register.phpt rename to ext/standard/tests/filters/stream_filter_register.phpt diff --git a/ext/standard/tests/streams/stream_multi_filters_close.phpt b/ext/standard/tests/filters/stream_multi_filters_close.phpt similarity index 100% rename from ext/standard/tests/streams/stream_multi_filters_close.phpt rename to ext/standard/tests/filters/stream_multi_filters_close.phpt diff --git a/ext/standard/tests/streams/user_streams_consumed_bug.phpt b/ext/standard/tests/filters/user_streams_consumed_bug.phpt similarity index 100% rename from ext/standard/tests/streams/user_streams_consumed_bug.phpt rename to ext/standard/tests/filters/user_streams_consumed_bug.phpt diff --git a/ext/standard/tests/file/userfilters.phpt b/ext/standard/tests/filters/userfilters.phpt similarity index 100% rename from ext/standard/tests/file/userfilters.phpt rename to ext/standard/tests/filters/userfilters.phpt From 9c33091713f8d289c3fc924b8beefd6174b65bb1 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Oct 2025 19:30:20 +0100 Subject: [PATCH 051/252] ext/standard: add a bunch of whacky stream filter tests --- ...er_class_coerce_consumed_by_ref_param.phpt | 32 +++++++++++++++++ ...ter_register_class_completely_invalid.phpt | 33 +++++++++++++++++ ...mpletely_invalid_filtername_prop_type.phpt | 28 +++++++++++++++ ...s_completely_invalid_no_dynamic_props.phpt | 28 +++++++++++++++ ...egister_class_private_filtername_prop.phpt | 28 +++++++++++++++ ...ilter_register_class_throwing_onclose.phpt | 30 ++++++++++++++++ ...lter_register_class_throwing_oncreate.phpt | 27 ++++++++++++++ ...am_filter_register_filter_always_feed.phpt | 24 +++++++++++++ ...eam_filter_register_mock_class_filter.phpt | 30 ++++++++++++++++ ...ock_class_filter_incorect_return_type.phpt | 32 +++++++++++++++++ ...register_mock_class_filter_is_private.phpt | 35 +++++++++++++++++++ ...am_filter_register_non_existing_class.phpt | 21 +++++++++++ 12 files changed, 348 insertions(+) create mode 100644 ext/standard/tests/filters/stream_filter_register_class_coerce_consumed_by_ref_param.phpt create mode 100644 ext/standard/tests/filters/stream_filter_register_class_completely_invalid.phpt create mode 100644 ext/standard/tests/filters/stream_filter_register_class_completely_invalid_filtername_prop_type.phpt create mode 100644 ext/standard/tests/filters/stream_filter_register_class_completely_invalid_no_dynamic_props.phpt create mode 100644 ext/standard/tests/filters/stream_filter_register_class_private_filtername_prop.phpt create mode 100644 ext/standard/tests/filters/stream_filter_register_class_throwing_onclose.phpt create mode 100644 ext/standard/tests/filters/stream_filter_register_class_throwing_oncreate.phpt create mode 100644 ext/standard/tests/filters/stream_filter_register_filter_always_feed.phpt create mode 100644 ext/standard/tests/filters/stream_filter_register_mock_class_filter.phpt create mode 100644 ext/standard/tests/filters/stream_filter_register_mock_class_filter_incorect_return_type.phpt create mode 100644 ext/standard/tests/filters/stream_filter_register_mock_class_filter_is_private.phpt create mode 100644 ext/standard/tests/filters/stream_filter_register_non_existing_class.phpt diff --git a/ext/standard/tests/filters/stream_filter_register_class_coerce_consumed_by_ref_param.phpt b/ext/standard/tests/filters/stream_filter_register_class_coerce_consumed_by_ref_param.phpt new file mode 100644 index 0000000000000..f5f0ecca79751 --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_class_coerce_consumed_by_ref_param.phpt @@ -0,0 +1,32 @@ +--TEST-- +stream_filter_register() with a class that coerces the $consumed parameter of filter method +--XFAIL-- +This leaks memory +--FILE-- + +--EXPECTF-- +bool(true) +resource(4) of type (stream filter) + +Warning: Object of class stdClass could not be converted to int in %s on line %d + +Warning: fwrite(): Unprocessed filter buckets remaining on input brigade in %s on line %d +int(1) + +Warning: Object of class stdClass could not be converted to int in Unknown on line 0 diff --git a/ext/standard/tests/filters/stream_filter_register_class_completely_invalid.phpt b/ext/standard/tests/filters/stream_filter_register_class_completely_invalid.phpt new file mode 100644 index 0000000000000..6bd3e7bc1c9dc --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_class_completely_invalid.phpt @@ -0,0 +1,33 @@ +--TEST-- +stream_filter_register() with a class name that exist but does not extend php_user_filter nor mock anything +--FILE-- + +--EXPECTF-- +bool(true) + +Deprecated: Creation of dynamic property foo::$filtername is deprecated in %s on line %d + +Deprecated: Creation of dynamic property foo::$params is deprecated in %s on line %d +resource(%d) of type (stream filter) + +Warning: fwrite(): Unprocessed filter buckets remaining on input brigade in %s on line %d + +Fatal error: Uncaught Error: Invalid callback foo::filter, class foo does not have a method "filter" in %s:%d +Stack trace: +#0 %s(%d): fwrite(Resource id #%d, 'Hello\n') +#1 {main} + thrown in %s on line %d + +Fatal error: Invalid callback foo::filter, class foo does not have a method "filter" in Unknown on line 0 diff --git a/ext/standard/tests/filters/stream_filter_register_class_completely_invalid_filtername_prop_type.phpt b/ext/standard/tests/filters/stream_filter_register_class_completely_invalid_filtername_prop_type.phpt new file mode 100644 index 0000000000000..793c112791a99 --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_class_completely_invalid_filtername_prop_type.phpt @@ -0,0 +1,28 @@ +--TEST-- +stream_filter_register() with a class name exist but does not extend php_user_filter and defines a $filtername prop with different type +--FILE-- + +--EXPECTF-- +bool(true) + +Deprecated: Creation of dynamic property foo::$params is deprecated in %s on line %d + +Fatal error: Uncaught TypeError: Cannot assign string to property foo::$filtername of type array in %s:%d +Stack trace: +#0 %s(%d): stream_filter_append(Resource id #2, 'invalid_filter') +#1 {main} + thrown in %s on line %d + +Fatal error: Invalid callback foo::filter, class foo does not have a method "filter" in Unknown on line 0 diff --git a/ext/standard/tests/filters/stream_filter_register_class_completely_invalid_no_dynamic_props.phpt b/ext/standard/tests/filters/stream_filter_register_class_completely_invalid_no_dynamic_props.phpt new file mode 100644 index 0000000000000..86994d5c1b665 --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_class_completely_invalid_no_dynamic_props.phpt @@ -0,0 +1,28 @@ +--TEST-- +stream_filter_register() with a class name exist but does not extend php_user_filter and cannot have dynamic properties +--FILE-- + +--EXPECTF-- +bool(true) + +Fatal error: Uncaught Error: Cannot create dynamic property SensitiveParameter::$filtername in %s:%d +Stack trace: +#0 %s(%d): stream_filter_append(Resource id #%d, 'invalid_filter') +#1 {main} + +Next Error: Cannot create dynamic property SensitiveParameter::$params in %s:%d +Stack trace: +#0 %s(%d): stream_filter_append(Resource id #%d, 'invalid_filter') +#1 {main} + thrown in %s on line %d + +Fatal error: Invalid callback SensitiveParameter::filter, class SensitiveParameter does not have a method "filter" in Unknown on line 0 diff --git a/ext/standard/tests/filters/stream_filter_register_class_private_filtername_prop.phpt b/ext/standard/tests/filters/stream_filter_register_class_private_filtername_prop.phpt new file mode 100644 index 0000000000000..6b9be4bbbb4b7 --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_class_private_filtername_prop.phpt @@ -0,0 +1,28 @@ +--TEST-- +stream_filter_register() with a class name exist but does not extend php_user_filter and defines a private $filtername prop +--FILE-- + +--EXPECTF-- +bool(true) + +Deprecated: Creation of dynamic property foo::$params is deprecated in %s on line %d + +Fatal error: Uncaught Error: Cannot access private property foo::$filtername in %s:%d +Stack trace: +#0 %s(%d): stream_filter_append(Resource id #2, 'invalid_filter') +#1 {main} + thrown in %s on line %d + +Fatal error: Invalid callback foo::filter, class foo does not have a method "filter" in Unknown on line 0 diff --git a/ext/standard/tests/filters/stream_filter_register_class_throwing_onclose.phpt b/ext/standard/tests/filters/stream_filter_register_class_throwing_onclose.phpt new file mode 100644 index 0000000000000..76b925cc0483a --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_class_throwing_onclose.phpt @@ -0,0 +1,30 @@ +--TEST-- +stream_filter_register() with a class that has a onclose method that throws +--FILE-- + +--EXPECTF-- +bool(true) +resource(4) of type (stream filter) + +Warning: fwrite(): Unprocessed filter buckets remaining on input brigade in %s on line %d +bool(false) + +Fatal error: Uncaught Exception: No in %s:%d +Stack trace: +#0 [internal function]: foo->onclose() +#1 {main} + thrown in %s on line %d diff --git a/ext/standard/tests/filters/stream_filter_register_class_throwing_oncreate.phpt b/ext/standard/tests/filters/stream_filter_register_class_throwing_oncreate.phpt new file mode 100644 index 0000000000000..3e429a2cc343a --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_class_throwing_oncreate.phpt @@ -0,0 +1,27 @@ +--TEST-- +stream_filter_register() with a class that has a oncreate method that throws +--FILE-- + +--EXPECTF-- +bool(true) + +Fatal error: Uncaught Exception: No in %s:%d +Stack trace: +#0 [internal function]: foo->oncreate() +#1 %s(%d): stream_filter_append(Resource id #2, 'invalid_filter') +#2 {main} + thrown in %s on line %d diff --git a/ext/standard/tests/filters/stream_filter_register_filter_always_feed.phpt b/ext/standard/tests/filters/stream_filter_register_filter_always_feed.phpt new file mode 100644 index 0000000000000..7a3d3f8899938 --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_filter_always_feed.phpt @@ -0,0 +1,24 @@ +--TEST-- +stream_filter_register() with a filter method always returning PSFS_FEED_ME +--FILE-- + +--EXPECTF-- +bool(true) +resource(4) of type (stream filter) + +Warning: fwrite(): Unprocessed filter buckets remaining on input brigade in %s on line %d +int(0) diff --git a/ext/standard/tests/filters/stream_filter_register_mock_class_filter.phpt b/ext/standard/tests/filters/stream_filter_register_mock_class_filter.phpt new file mode 100644 index 0000000000000..4d2309a718eb5 --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_mock_class_filter.phpt @@ -0,0 +1,30 @@ +--TEST-- +stream_filter_register() with a class name exist that mocks php_user_filter with a filter method +--XFAIL-- +This leaks memory +--FILE-- + +--EXPECTF-- +bool(true) +resource(4) of type (stream filter) + +Warning: fwrite(): Unprocessed filter buckets remaining on input brigade in %s on line %d +int(0) diff --git a/ext/standard/tests/filters/stream_filter_register_mock_class_filter_incorect_return_type.phpt b/ext/standard/tests/filters/stream_filter_register_mock_class_filter_incorect_return_type.phpt new file mode 100644 index 0000000000000..b5ec8da3dde1b --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_mock_class_filter_incorect_return_type.phpt @@ -0,0 +1,32 @@ +--TEST-- +stream_filter_register() with a class name exist that mocks php_user_filter with a filter method returning not an int +--FILE-- + +--EXPECTF-- +bool(true) +resource(4) of type (stream filter) + +Warning: Object of class stdClass could not be converted to int in %s on line %d + +Warning: fwrite(): Unprocessed filter buckets remaining on input brigade in %s on line %d +int(0) + +Warning: Object of class stdClass could not be converted to int in Unknown on line 0 diff --git a/ext/standard/tests/filters/stream_filter_register_mock_class_filter_is_private.phpt b/ext/standard/tests/filters/stream_filter_register_mock_class_filter_is_private.phpt new file mode 100644 index 0000000000000..6abffe1ff5f80 --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_mock_class_filter_is_private.phpt @@ -0,0 +1,35 @@ +--TEST-- +stream_filter_register() with a class name exist that mocks php_user_filter with a private filter method +--FILE-- + +--EXPECTF-- +bool(true) +resource(%d) of type (stream filter) + +Warning: fwrite(): Unprocessed filter buckets remaining on input brigade in %s on line %d + +Fatal error: Uncaught Error: Invalid callback foo::filter, cannot access private method foo::filter() in %s:%d +Stack trace: +#0 %s(%d): fwrite(Resource id #%d, 'Hello\n') +#1 {main} + thrown in %s on line %d + +Fatal error: Invalid callback foo::filter, cannot access private method foo::filter() in Unknown on line 0 diff --git a/ext/standard/tests/filters/stream_filter_register_non_existing_class.phpt b/ext/standard/tests/filters/stream_filter_register_non_existing_class.phpt new file mode 100644 index 0000000000000..1c08201cff0c5 --- /dev/null +++ b/ext/standard/tests/filters/stream_filter_register_non_existing_class.phpt @@ -0,0 +1,21 @@ +--TEST-- +stream_filter_register() with a class name that does not exist +--FILE-- + +--EXPECTF-- +bool(true) + +Warning: stream_filter_append(): User-filter "not_existing_filter" requires class "not_existing", but that class is not defined in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "not_existing_filter" in %s on line %d +Hello +int(6) From ac7fddfaccc7069ada009804143e77478290c589 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sun, 16 Nov 2025 14:35:37 +0000 Subject: [PATCH 052/252] ext/zip: further micro optimisations. (#20362) - earlier return on invalid inputs from some ZipArchive methods. - apply comp_flags type check for setCompressionName()/setCompressionIndex(). and throw exception for these - add comp_flags check when passed as userland array option. - adding a wrapper for zip_file_set_encryption workaround. --- ext/zip/php_zip.c | 117 +++++++++++++++++---------- ext/zip/tests/oo_addglob2.phpt | 24 +++++- ext/zip/tests/oo_setcompression.phpt | 28 +++++++ ext/zip/zip_stream.c | 12 +-- 4 files changed, 130 insertions(+), 51 deletions(-) diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 06bd41d602c06..49506a198628e 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -74,6 +74,17 @@ static int le_zip_entry; # define add_ascii_assoc_string add_assoc_string # define add_ascii_assoc_long add_assoc_long +static bool php_zip_file_set_encryption(struct zip *intern, zend_long index, zend_long method, char *password) { + // FIXME: is a workaround to reset/free the password in case of consecutive calls. + // when libzip 1.11.5 is available, we can save this call in this case. + if (UNEXPECTED(zip_file_set_encryption(intern, (zip_uint64_t)index, ZIP_EM_NONE, NULL) < 0)) { + php_error_docref(NULL, E_WARNING, "password reset failed"); + return false; + } + + return (zip_file_set_encryption(intern, (zip_uint64_t)index, (zip_uint16_t)method, password) == 0); +} + /* Flatten a path by making a relative path (to .)*/ static char * php_zip_make_relative_path(char *path, size_t path_len) /* {{{ */ { @@ -367,14 +378,22 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts) php_error_docref(NULL, E_WARNING, "Option \"comp_method\" must be of type int, %s given", zend_zval_value_name(option)); } - opts->comp_method = zval_get_long(option); + zend_long comp_method = zval_get_long(option); + if (comp_method < 0 || comp_method > INT_MAX) { + php_error_docref(NULL, E_WARNING, "Option \"comp_method\" must be between 0 and %d", INT_MAX); + } + opts->comp_method = (zip_int32_t)comp_method; if ((option = zend_hash_str_find(options, "comp_flags", sizeof("comp_flags") - 1)) != NULL) { if (Z_TYPE_P(option) != IS_LONG) { php_error_docref(NULL, E_WARNING, "Option \"comp_flags\" must be of type int, %s given", zend_zval_value_name(option)); } - opts->comp_flags = zval_get_long(option); + zend_long comp_flags = zval_get_long(option); + if (comp_flags < 0 || comp_flags > USHRT_MAX) { + php_error_docref(NULL, E_WARNING, "Option \"comp_flags\" must be between 0 and %u", USHRT_MAX); + } + opts->comp_flags = (zip_uint32_t)comp_flags; } } @@ -1752,12 +1771,7 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* } #ifdef HAVE_ENCRYPTION if (opts.enc_method >= 0) { - if (UNEXPECTED(zip_file_set_encryption(ze_obj->za, ze_obj->last_id, ZIP_EM_NONE, NULL) < 0)) { - zend_array_destroy(Z_ARR_P(return_value)); - php_error_docref(NULL, E_WARNING, "password reset failed"); - RETURN_FALSE; - } - if (zip_file_set_encryption(ze_obj->za, ze_obj->last_id, opts.enc_method, opts.enc_password)) { + if (!php_zip_file_set_encryption(ze_obj->za, ze_obj->last_id, opts.enc_method, opts.enc_password)) { zend_array_destroy(Z_ARR_P(return_value)); RETURN_FALSE; } @@ -2279,15 +2293,7 @@ PHP_METHOD(ZipArchive, setEncryptionName) RETURN_FALSE; } - if (UNEXPECTED(zip_file_set_encryption(intern, idx, ZIP_EM_NONE, NULL) < 0)) { - php_error_docref(NULL, E_WARNING, "password reset failed"); - RETURN_FALSE; - } - - if (zip_file_set_encryption(intern, idx, (zip_uint16_t)method, password)) { - RETURN_FALSE; - } - RETURN_TRUE; + RETURN_BOOL(php_zip_file_set_encryption(intern, idx, method, password)); } /* }}} */ @@ -2307,12 +2313,7 @@ PHP_METHOD(ZipArchive, setEncryptionIndex) ZIP_FROM_OBJECT(intern, self); - if (UNEXPECTED(zip_file_set_encryption(intern, index, ZIP_EM_NONE, NULL) < 0)) { - php_error_docref(NULL, E_WARNING, "password reset failed"); - RETURN_FALSE; - } - - RETURN_BOOL(zip_file_set_encryption(intern, index, (zip_uint16_t)method, password) == 0); + RETURN_BOOL(php_zip_file_set_encryption(intern, index, method, password)); } /* }}} */ #endif @@ -2390,13 +2391,23 @@ PHP_METHOD(ZipArchive, setCompressionName) RETURN_THROWS(); } - ZIP_FROM_OBJECT(intern, this); - if (name_len == 0) { zend_argument_must_not_be_empty_error(1); RETURN_THROWS(); } + if (comp_method < -1 || comp_method > INT_MAX) { + zend_argument_value_error(2, "must be between -1 and %d", INT_MAX); + RETURN_THROWS(); + } + + if (comp_flags < 0 || comp_flags > USHRT_MAX) { + // comp_flags is cast down accordingly in libzip, zip_entry_t compression_level is of zip_uint16_t + zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX); + RETURN_THROWS(); + } + + ZIP_FROM_OBJECT(intern, this); idx = zip_name_locate(intern, name, 0); if (idx < 0) { @@ -2421,6 +2432,21 @@ PHP_METHOD(ZipArchive, setCompressionIndex) RETURN_THROWS(); } + if (index < 0) { + RETURN_FALSE; + } + + if (comp_method < -1 || comp_method > INT_MAX) { + zend_argument_value_error(2, "must be between -1 and %d", INT_MAX); + RETURN_THROWS(); + } + + if (comp_flags < 0 || comp_flags > USHRT_MAX) { + // comp_flags is cast down accordingly in libzip, zip_entry_t compression_level is of zip_uint16_t + zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX); + RETURN_THROWS(); + } + ZIP_FROM_OBJECT(intern, this); RETURN_BOOL(zip_set_file_compression(intern, (zip_uint64_t)index, @@ -2443,13 +2469,13 @@ PHP_METHOD(ZipArchive, setMtimeName) RETURN_THROWS(); } - ZIP_FROM_OBJECT(intern, this); - if (name_len == 0) { zend_argument_must_not_be_empty_error(1); RETURN_THROWS(); } + ZIP_FROM_OBJECT(intern, this); + idx = zip_name_locate(intern, name, 0); if (idx < 0) { @@ -2515,17 +2541,15 @@ PHP_METHOD(ZipArchive, deleteName) RETURN_THROWS(); } - ZIP_FROM_OBJECT(intern, self); - if (name_len < 1) { RETURN_FALSE; } + ZIP_FROM_OBJECT(intern, self); + PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb); - if (zip_delete(intern, sb.index)) { - RETURN_FALSE; - } - RETURN_TRUE; + + RETURN_BOOL(zip_delete(intern, sb.index) == 0); } /* }}} */ @@ -2546,13 +2570,13 @@ PHP_METHOD(ZipArchive, renameIndex) RETURN_FALSE; } - ZIP_FROM_OBJECT(intern, self); - if (new_name_len == 0) { zend_argument_must_not_be_empty_error(2); RETURN_THROWS(); } + ZIP_FROM_OBJECT(intern, self); + RETURN_BOOL(zip_file_rename(intern, index, (const char *)new_name, 0) == 0); } /* }}} */ @@ -2594,12 +2618,12 @@ PHP_METHOD(ZipArchive, unchangeIndex) RETURN_THROWS(); } - ZIP_FROM_OBJECT(intern, self); - if (index < 0) { RETURN_FALSE; } + ZIP_FROM_OBJECT(intern, self); + RETURN_BOOL(zip_unchange(intern, index) == 0); } /* }}} */ @@ -2617,12 +2641,12 @@ PHP_METHOD(ZipArchive, unchangeName) RETURN_THROWS(); } - ZIP_FROM_OBJECT(intern, self); - if (name_len < 1) { RETURN_FALSE; } + ZIP_FROM_OBJECT(intern, self); + PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb); RETURN_BOOL(zip_unchange(intern, sb.index) == 0); @@ -2686,8 +2710,6 @@ PHP_METHOD(ZipArchive, extractTo) Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(files_ht, files_str) ZEND_PARSE_PARAMETERS_END(); - ZIP_FROM_OBJECT(intern, self); - if (pathto_len < 1) { RETURN_FALSE; } @@ -2700,6 +2722,7 @@ PHP_METHOD(ZipArchive, extractTo) } uint32_t nelems, i; + ZIP_FROM_OBJECT(intern, self); if (files_str) { if (!php_zip_extract_file(intern, pathto, ZSTR_VAL(files_str), ZSTR_LEN(files_str), -1)) { @@ -2796,7 +2819,7 @@ static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ RETURN_FALSE; } - buffer = zend_string_safe_alloc(1, len, 0, 0); + buffer = zend_string_safe_alloc(1, len, 0, false); n = zip_fread(zf, ZSTR_VAL(buffer), ZSTR_LEN(buffer)); if (n < 1) { zend_string_efree(buffer); @@ -3005,6 +3028,10 @@ PHP_METHOD(ZipArchive, isCompressionMethodSupported) if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &method, &enc) == FAILURE) { return; } + if (method < -1 || method > INT_MAX) { + zend_argument_value_error(1, "must be between -1 and %d", INT_MAX); + RETURN_THROWS(); + } RETVAL_BOOL(zip_compression_method_supported((zip_int32_t)method, enc)); } /* }}} */ @@ -3018,7 +3045,11 @@ PHP_METHOD(ZipArchive, isEncryptionMethodSupported) if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &method, &enc) == FAILURE) { return; } - RETVAL_BOOL(zip_encryption_method_supported((zip_uint16_t)method, enc)); + if (method < 0 || method > USHRT_MAX) { + zend_argument_value_error(1, "must be between 0 and %u", USHRT_MAX); + RETURN_THROWS(); + } + RETURN_BOOL(zip_encryption_method_supported((zip_uint16_t)method, enc)); } /* }}} */ #endif diff --git a/ext/zip/tests/oo_addglob2.phpt b/ext/zip/tests/oo_addglob2.phpt index 517c0b7fd7fda..a98a222712052 100644 --- a/ext/zip/tests/oo_addglob2.phpt +++ b/ext/zip/tests/oo_addglob2.phpt @@ -33,10 +33,26 @@ if (!$zip->addGlob($dirname . 'foo.*', GLOB_BRACE, $options)) { $options = [ 'remove_all_path' => true, 'comp_method' => ZipArchive::CM_STORE, - 'comp_flags' => 5, + 'comp_flags' => PHP_INT_MIN, 'enc_method' => ZipArchive::EM_AES_256, 'enc_password' => 'secret', ]; + +try { + $zip->addGlob($dirname. 'bar.*', GLOB_BRACE, $options); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +$options['comp_flags'] = 65536; + +try { + $zip->addGlob($dirname. 'bar.*', GLOB_BRACE, $options); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +$options['comp_flags'] = 5; if (!$zip->addGlob($dirname . 'bar.*', GLOB_BRACE, $options)) { echo "failed 2\n"; } @@ -61,6 +77,10 @@ $dirname = __DIR__ . '/'; include $dirname . 'utils.inc'; rmdir_rf(__DIR__ . '/__tmp_oo_addglob2/'); ?> ---EXPECT-- +--EXPECTF-- + +Warning: ZipArchive::addGlob(): Option "comp_flags" must be between 0 and 65535 in %s on line %d + +Warning: ZipArchive::addGlob(): Option "comp_flags" must be between 0 and 65535 in %s on line %d 0: foo.txt, comp=8, enc=0 1: bar.txt, comp=0, enc=259 diff --git a/ext/zip/tests/oo_setcompression.phpt b/ext/zip/tests/oo_setcompression.phpt index 6b3291463659b..3d90d1e985cdd 100644 --- a/ext/zip/tests/oo_setcompression.phpt +++ b/ext/zip/tests/oo_setcompression.phpt @@ -28,6 +28,30 @@ var_dump($zip->setCompressionName('entry2.txt', ZipArchive::CM_DEFAULT)); var_dump($zip->setCompressionName('dir/entry3.txt', ZipArchive::CM_STORE)); var_dump($zip->setCompressionName('entry4.txt', ZipArchive::CM_DEFLATE)); +try { + $zip->setCompressionName('entry5.txt', PHP_INT_MIN); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + $zip->setCompressionName('entry5.txt', PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + $zip->setCompressionIndex(4, PHP_INT_MIN); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + $zip->setCompressionIndex(4, PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + var_dump($zip->setCompressionIndex(4, ZipArchive::CM_STORE)); var_dump($zip->setCompressionIndex(5, ZipArchive::CM_DEFLATE)); var_dump($zip->setCompressionIndex(6, ZipArchive::CM_DEFAULT)); @@ -57,6 +81,10 @@ unlink($tmpfile); bool(true) bool(true) bool(true) +ZipArchive::setCompressionName(): Argument #2 ($method) must be between -1 and %d +ZipArchive::setCompressionName(): Argument #2 ($method) must be between -1 and %d +ZipArchive::setCompressionIndex(): Argument #2 ($method) must be between -1 and %d +ZipArchive::setCompressionIndex(): Argument #2 ($method) must be between -1 and %d bool(true) bool(true) bool(true) diff --git a/ext/zip/zip_stream.c b/ext/zip/zip_stream.c index 76e4588f249e5..50f097de3c876 100644 --- a/ext/zip/zip_stream.c +++ b/ext/zip/zip_stream.c @@ -150,7 +150,7 @@ static int php_zip_ops_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ fragment++; if (ZIP_OPENBASEDIR_CHECKPATH(file_dirname)) { - zend_string_release_ex(file_basename, 0); + zend_string_release_ex(file_basename, false); return -1; } @@ -159,7 +159,7 @@ static int php_zip_ops_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ memset(ssb, 0, sizeof(php_stream_statbuf)); if (zip_stat(za, fragment, ZIP_FL_NOCASE, &sb) != 0) { zip_close(za); - zend_string_release_ex(file_basename, 0); + zend_string_release_ex(file_basename, false); return -1; } zip_close(za); @@ -183,7 +183,7 @@ static int php_zip_ops_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ #endif ssb->sb.st_ino = -1; } - zend_string_release_ex(file_basename, 0); + zend_string_release_ex(file_basename, false); return 0; } /* }}} */ @@ -312,7 +312,7 @@ php_stream *php_stream_zip_opener(php_stream_wrapper *wrapper, fragment++; if (ZIP_OPENBASEDIR_CHECKPATH(file_dirname)) { - zend_string_release_ex(file_basename, 0); + zend_string_release_ex(file_basename, false); return NULL; } @@ -344,14 +344,14 @@ php_stream *php_stream_zip_opener(php_stream_wrapper *wrapper, } if (opened_path) { - *opened_path = zend_string_init(path, strlen(path), 0); + *opened_path = zend_string_init(path, path_len, false); } } else { zip_close(za); } } - zend_string_release_ex(file_basename, 0); + zend_string_release_ex(file_basename, false); if (!stream) { return NULL; From bc08fa3b95f3590352da63bb7cf8ab5293aa45db Mon Sep 17 00:00:00 2001 From: Marc Bennewitz Date: Sun, 16 Nov 2025 15:41:00 +0100 Subject: [PATCH 053/252] ext/bz2: use `uint64_t` type instead of conditional defined code --- ext/bz2/bz2.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/ext/bz2/bz2.c b/ext/bz2/bz2.c index e7491f12cf315..200f2fdbf1a09 100644 --- a/ext/bz2/bz2.c +++ b/ext/bz2/bz2.c @@ -504,11 +504,7 @@ PHP_FUNCTION(bzdecompress) size_t source_len; int error; bool small = 0; -#ifdef PHP_WIN32 - unsigned __int64 size = 0; -#else - unsigned long long size = 0; -#endif + uint64_t size = 0; bz_stream bzs; if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &source, &source_len, &small)) { @@ -535,26 +531,22 @@ PHP_FUNCTION(bzdecompress) /* compression is better then 2:1, need to allocate more memory */ bzs.avail_out = source_len; size = (bzs.total_out_hi32 * (unsigned int) -1) + bzs.total_out_lo32; -#ifndef ZEND_ENABLE_ZVAL_LONG64 - if (size > SIZE_MAX) { + if (UNEXPECTED(size > SIZE_MAX)) { /* no reason to continue if we're going to drop it anyway */ break; } -#endif + dest = zend_string_safe_realloc(dest, 1, bzs.avail_out+1, (size_t) size, 0); bzs.next_out = ZSTR_VAL(dest) + size; } if (error == BZ_STREAM_END || error == BZ_OK) { size = (bzs.total_out_hi32 * (unsigned int) -1) + bzs.total_out_lo32; -#ifndef ZEND_ENABLE_ZVAL_LONG64 if (UNEXPECTED(size > SIZE_MAX)) { - php_error_docref(NULL, E_WARNING, "Decompressed size too big, max is %zd", SIZE_MAX); + php_error_docref(NULL, E_WARNING, "Decompressed size too big, max is %zu", SIZE_MAX); zend_string_efree(dest); RETVAL_LONG(BZ_MEM_ERROR); - } else -#endif - { + } else { dest = zend_string_safe_realloc(dest, 1, (size_t)size, 1, 0); ZSTR_LEN(dest) = (size_t)size; ZSTR_VAL(dest)[(size_t)size] = '\0'; From 46a15ed439c5e8dc52ac854541e61cc69223b62e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 15 Nov 2025 18:11:14 +0100 Subject: [PATCH 054/252] Fix crash in property existence test in ext/zip When type == 2, the zval is not initialized, so zval_ptr_dtor() on it will crash. Unfortunately couldn't test with property_exists() or Reflection because they have fast paths that go through the property info, but fortunately there are paths that don't implement a fast path (e.g. because it doesn't make sense at that point), like with array_column(). So we use array_column() to trigger the crash. Closes GH-20496. --- NEWS | 3 +++ ext/zip/php_zip.c | 3 +-- ext/zip/tests/property_existence_test.phpt | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 ext/zip/tests/property_existence_test.phpt diff --git a/NEWS b/NEWS index 5ddc2633d19c3..fc03fd36f15e0 100644 --- a/NEWS +++ b/NEWS @@ -42,6 +42,9 @@ PHP NEWS . Fixed bug GH-20439 (xml_set_default_handler() does not properly handle special characters in attributes when passing data to callback). (ndossche) +- Zip: + . Fix crash in property existence test. (ndossche) + 20 Nov 2025, PHP 8.3.28 - Core: diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 66f651e46e384..15f55cba71255 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -977,9 +977,8 @@ static int php_zip_has_property(zend_object *object, zend_string *name, int type } else if (type == 0) { retval = (Z_TYPE(tmp) != IS_NULL); } + zval_ptr_dtor(&tmp); } - - zval_ptr_dtor(&tmp); } else { retval = zend_std_has_property(object, name, type, cache_slot); } diff --git a/ext/zip/tests/property_existence_test.phpt b/ext/zip/tests/property_existence_test.phpt new file mode 100644 index 0000000000000..855bf73464ac1 --- /dev/null +++ b/ext/zip/tests/property_existence_test.phpt @@ -0,0 +1,20 @@ +--TEST-- +Property existence test can cause a crash +--EXTENSIONS-- +zip +--FILE-- + +--CLEAN-- + +--EXPECT-- +array(1) { + [0]=> + int(-1) +} From 88d811708baef9e3014ed195502385c6bf2adba8 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 17 Nov 2025 13:05:22 +0100 Subject: [PATCH 055/252] Remove PHP_HAVE_STREAMS (#20508) This was once an indicator for PHP extensions whether PHP 4.3.0 or later is used. Today, this is redundant. --- UPGRADING.INTERNALS | 1 + main/php.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 33632968368c7..e35557e71f462 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -48,6 +48,7 @@ PHP 8.6 INTERNALS UPGRADE NOTES . The WRONG_PARAM_COUNT and ZEND_WRONG_PARAM_COUNT() macros have been removed. Call zend_wrong_param_count(); followed by RETURN_THROWS(); instead. + . PHP_HAVE_STREAMS macro removed from . ======================== 2. Build system changes diff --git a/main/php.h b/main/php.h index 8bb47ed275825..32222cfca94e0 100644 --- a/main/php.h +++ b/main/php.h @@ -23,7 +23,6 @@ #endif #define PHP_API_VERSION 20250926 -#define PHP_HAVE_STREAMS #define YYDEBUG 0 #define PHP_DEFAULT_CHARSET "UTF-8" From f411e7fd0f407deaa4ae6780837620174e979264 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 17 Nov 2025 15:18:14 +0100 Subject: [PATCH 056/252] Fix 32-bit failure of ext-zip oo_setcompression.phpt (GH-20511) --- ext/zip/tests/oo_setcompression.phpt | 14 ----------- ext/zip/tests/oo_setcompression_64bit.phpt | 29 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 ext/zip/tests/oo_setcompression_64bit.phpt diff --git a/ext/zip/tests/oo_setcompression.phpt b/ext/zip/tests/oo_setcompression.phpt index 3d90d1e985cdd..1b7e817688dde 100644 --- a/ext/zip/tests/oo_setcompression.phpt +++ b/ext/zip/tests/oo_setcompression.phpt @@ -34,24 +34,12 @@ try { echo $e->getMessage(), PHP_EOL; } -try { - $zip->setCompressionName('entry5.txt', PHP_INT_MAX); -} catch (\ValueError $e) { - echo $e->getMessage(), PHP_EOL; -} - try { $zip->setCompressionIndex(4, PHP_INT_MIN); } catch (\ValueError $e) { echo $e->getMessage(), PHP_EOL; } -try { - $zip->setCompressionIndex(4, PHP_INT_MAX); -} catch (\ValueError $e) { - echo $e->getMessage(), PHP_EOL; -} - var_dump($zip->setCompressionIndex(4, ZipArchive::CM_STORE)); var_dump($zip->setCompressionIndex(5, ZipArchive::CM_DEFLATE)); var_dump($zip->setCompressionIndex(6, ZipArchive::CM_DEFAULT)); @@ -82,8 +70,6 @@ bool(true) bool(true) bool(true) ZipArchive::setCompressionName(): Argument #2 ($method) must be between -1 and %d -ZipArchive::setCompressionName(): Argument #2 ($method) must be between -1 and %d -ZipArchive::setCompressionIndex(): Argument #2 ($method) must be between -1 and %d ZipArchive::setCompressionIndex(): Argument #2 ($method) must be between -1 and %d bool(true) bool(true) diff --git a/ext/zip/tests/oo_setcompression_64bit.phpt b/ext/zip/tests/oo_setcompression_64bit.phpt new file mode 100644 index 0000000000000..cb093e8ccfc9d --- /dev/null +++ b/ext/zip/tests/oo_setcompression_64bit.phpt @@ -0,0 +1,29 @@ +--TEST-- +setCompressionName and setCompressionIndex methods +--EXTENSIONS-- +zip +--SKIPIF-- + +--FILE-- +setCompressionName('entry5.txt', PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + $zip->setCompressionIndex(4, PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +ZipArchive::setCompressionName(): Argument #2 ($method) must be between -1 and %d +ZipArchive::setCompressionIndex(): Argument #2 ($method) must be between -1 and %d From 93ce0500aa90623bb61abaacf98fe905d47dc5f6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 16 Nov 2025 01:50:09 +0100 Subject: [PATCH 057/252] Fix assertion failures resulting in crashes with stream filter object parameters This works for dynamic props but not for non-dynamic props due to the missing INDIRECT handling. Closes GH-20500. --- NEWS | 8 ++++++ ext/bz2/bz2_filter.c | 12 ++++++--- .../tests/filter_broken_object_options.phpt | 25 +++++++++++++++++++ ext/phar/stream.c | 6 ++--- .../tests/filter_broken_object_options.phpt | 21 ++++++++++++++++ ext/zlib/zlib_filter.c | 13 ++++++---- 6 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 ext/bz2/tests/filter_broken_object_options.phpt create mode 100644 ext/zlib/tests/filter_broken_object_options.phpt diff --git a/NEWS b/NEWS index fc03fd36f15e0..cacfdc28892d0 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,10 @@ PHP NEWS . Fixed bug GH-20435 (SensitiveParameter doesn't work for named argument passing to variadic parameter). (ndossche) +- Bz2: + . Fix assertion failures resulting in crashes with stream filter + object parameters. (ndossche) + - Date: . Fix crashes when trying to instantiate uninstantiable classes via date static constructors. (ndossche) @@ -45,6 +49,10 @@ PHP NEWS - Zip: . Fix crash in property existence test. (ndossche) +- Zlib: + . Fix assertion failures resulting in crashes with stream filter + object parameters. (ndossche) + 20 Nov 2025, PHP 8.3.28 - Core: diff --git a/ext/bz2/bz2_filter.c b/ext/bz2/bz2_filter.c index 9b3480b4a5ca9..b093ac7d7526d 100644 --- a/ext/bz2/bz2_filter.c +++ b/ext/bz2/bz2_filter.c @@ -337,12 +337,14 @@ static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *fi zval *tmpzval = NULL; if (Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) { - if ((tmpzval = zend_hash_str_find(HASH_OF(filterparams), "concatenated", sizeof("concatenated")-1))) { + HashTable *ht = HASH_OF(filterparams); + + if ((tmpzval = zend_hash_str_find_ind(ht, "concatenated", sizeof("concatenated")-1))) { data->expect_concatenated = zend_is_true(tmpzval); tmpzval = NULL; } - tmpzval = zend_hash_str_find(HASH_OF(filterparams), "small", sizeof("small")-1); + tmpzval = zend_hash_str_find_ind(ht, "small", sizeof("small")-1); } else { tmpzval = filterparams; } @@ -362,7 +364,9 @@ static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *fi zval *tmpzval; if (Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) { - if ((tmpzval = zend_hash_str_find(HASH_OF(filterparams), "blocks", sizeof("blocks")-1))) { + HashTable *ht = HASH_OF(filterparams); + + if ((tmpzval = zend_hash_str_find_ind(ht, "blocks", sizeof("blocks")-1))) { /* How much memory to allocate (1 - 9) x 100kb */ zend_long blocks = zval_get_long(tmpzval); if (blocks < 1 || blocks > 9) { @@ -372,7 +376,7 @@ static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *fi } } - if ((tmpzval = zend_hash_str_find(HASH_OF(filterparams), "work", sizeof("work")-1))) { + if ((tmpzval = zend_hash_str_find_ind(ht, "work", sizeof("work")-1))) { /* Work Factor (0 - 250) */ zend_long work = zval_get_long(tmpzval); if (work < 0 || work > 250) { diff --git a/ext/bz2/tests/filter_broken_object_options.phpt b/ext/bz2/tests/filter_broken_object_options.phpt new file mode 100644 index 0000000000000..84e49a64ccb62 --- /dev/null +++ b/ext/bz2/tests/filter_broken_object_options.phpt @@ -0,0 +1,25 @@ +--TEST-- +bz2 filter assertion failure with non-dynamic properties in filter param object +--EXTENSIONS-- +bz2 +--FILE-- + +--EXPECT-- +Hello world, hopefully not broken diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 1151b520bba09..c455a8880d222 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -211,18 +211,18 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha php_url_free(resource); efree(internal_file); - if (context && Z_TYPE(context->options) != IS_UNDEF && (pzoption = zend_hash_str_find(HASH_OF(&context->options), "phar", sizeof("phar")-1)) != NULL) { + if (context && Z_TYPE(context->options) != IS_UNDEF && (pzoption = zend_hash_str_find_ind(HASH_OF(&context->options), "phar", sizeof("phar")-1)) != NULL) { pharcontext = HASH_OF(pzoption); if (idata->internal_file->uncompressed_filesize == 0 && idata->internal_file->compressed_filesize == 0 - && (pzoption = zend_hash_str_find(pharcontext, "compress", sizeof("compress")-1)) != NULL + && (pzoption = zend_hash_str_find_ind(pharcontext, "compress", sizeof("compress")-1)) != NULL && Z_TYPE_P(pzoption) == IS_LONG && (Z_LVAL_P(pzoption) & ~PHAR_ENT_COMPRESSION_MASK) == 0 ) { idata->internal_file->flags &= ~PHAR_ENT_COMPRESSION_MASK; idata->internal_file->flags |= Z_LVAL_P(pzoption); } - if ((pzoption = zend_hash_str_find(pharcontext, "metadata", sizeof("metadata")-1)) != NULL) { + if ((pzoption = zend_hash_str_find_ind(pharcontext, "metadata", sizeof("metadata")-1)) != NULL) { phar_metadata_tracker_free(&idata->internal_file->metadata_tracker, idata->internal_file->is_persistent); metadata = pzoption; diff --git a/ext/zlib/tests/filter_broken_object_options.phpt b/ext/zlib/tests/filter_broken_object_options.phpt new file mode 100644 index 0000000000000..beb0fef9fb137 --- /dev/null +++ b/ext/zlib/tests/filter_broken_object_options.phpt @@ -0,0 +1,21 @@ +--TEST-- +zlib filter assertion failure with non-dynamic properties in filter param object +--EXTENSIONS-- +zlib +--FILE-- + +--EXPECT-- +Hello world, hopefully not broken diff --git a/ext/zlib/zlib_filter.c b/ext/zlib/zlib_filter.c index 24d418ae04cfa..e5491afec3919 100644 --- a/ext/zlib/zlib_filter.c +++ b/ext/zlib/zlib_filter.c @@ -323,7 +323,7 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f zval *tmpzval; if ((Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) && - (tmpzval = zend_hash_str_find(HASH_OF(filterparams), "window", sizeof("window") - 1))) { + (tmpzval = zend_hash_str_find_ind(HASH_OF(filterparams), "window", sizeof("window") - 1))) { /* log-2 base of history window (9 - 15) */ zend_long tmp = zval_get_long(tmpzval); if (tmp < -MAX_WBITS || tmp > MAX_WBITS + 32) { @@ -354,8 +354,10 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f switch (Z_TYPE_P(filterparams)) { case IS_ARRAY: - case IS_OBJECT: - if ((tmpzval = zend_hash_str_find(HASH_OF(filterparams), "memory", sizeof("memory") -1))) { + case IS_OBJECT: { + HashTable *ht = HASH_OF(filterparams); + + if ((tmpzval = zend_hash_str_find_ind(ht, "memory", sizeof("memory") -1))) { /* Memory Level (1 - 9) */ tmp = zval_get_long(tmpzval); if (tmp < 1 || tmp > MAX_MEM_LEVEL) { @@ -365,7 +367,7 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f } } - if ((tmpzval = zend_hash_str_find(HASH_OF(filterparams), "window", sizeof("window") - 1))) { + if ((tmpzval = zend_hash_str_find_ind(ht, "window", sizeof("window") - 1))) { /* log-2 base of history window (9 - 15) */ tmp = zval_get_long(tmpzval); if (tmp < -MAX_WBITS || tmp > MAX_WBITS + 16) { @@ -375,13 +377,14 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f } } - if ((tmpzval = zend_hash_str_find(HASH_OF(filterparams), "level", sizeof("level") - 1))) { + if ((tmpzval = zend_hash_str_find_ind(ht, "level", sizeof("level") - 1))) { tmp = zval_get_long(tmpzval); /* Pseudo pass through to catch level validating code */ goto factory_setlevel; } break; + } case IS_STRING: case IS_DOUBLE: case IS_LONG: From 8e6d37596693738a1bf22ae1739fad17ce9de3b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 17 Nov 2025 19:28:51 +0100 Subject: [PATCH 058/252] lexbor: Cherry pick "URL: the cloning function does not copy the type for IPv4 and IPv6." see lexbor/lexbor@dcfcd645c672816ca8dc2335dfd1402b0a4aa40c Fixes php/php-src#20501 --- NEWS | 4 ++++ ext/lexbor/lexbor/url/url.c | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 38d8d734a045c..ef086acf89f1a 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,10 @@ PHP NEWS . Fixed bug GH-20483 (ASAN stack overflow with fiber.stack_size INI small value). (David Carlier) +- Lexbor: + . Fixed bug GH-20501 (\Uri\WhatWg\Url lose host after calling + withPath() or withQuery()). (lexborisov) + - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) diff --git a/ext/lexbor/lexbor/url/url.c b/ext/lexbor/lexbor/url/url.c index 7bfc007e622ce..99ba809b05fe6 100644 --- a/ext/lexbor/lexbor/url/url.c +++ b/ext/lexbor/lexbor/url/url.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2024 Alexander Borisov + * Copyright (C) 2023-2025 Alexander Borisov * * Author: Alexander Borisov */ @@ -1106,9 +1106,9 @@ lxb_url_host_copy(const lxb_url_host_t *src, lxb_url_host_t *dst, } } - if (src->type <= LXB_URL_HOST_TYPE_OPAQUE) { - dst->type = src->type; + dst->type = src->type; + if (src->type <= LXB_URL_HOST_TYPE_OPAQUE) { if (src->type == LXB_URL_HOST_TYPE__UNDEF) { return LXB_STATUS_OK; } From 1ff90c4b1e20a06982b47d6040d831063fbfa915 Mon Sep 17 00:00:00 2001 From: Volker Dusch Date: Mon, 17 Nov 2025 20:47:04 +0100 Subject: [PATCH 059/252] [ci skip] Consolidate NEWS for PHP 8.5.0 --- NEWS | 976 +++++++++++++++++++++-------------------------------------- 1 file changed, 345 insertions(+), 631 deletions(-) diff --git a/NEWS b/NEWS index ef086acf89f1a..469edebbb67a2 100644 --- a/NEWS +++ b/NEWS @@ -54,175 +54,50 @@ PHP NEWS . Fix assertion failures resulting in crashes with stream filter object parameters. (ndossche) -13 Nov 2025, PHP 8.5.0RC5 +20 Nov 2025, PHP 8.5.0 - Core: + . Added the #[\NoDiscard] attribute to indicate that a function's return + value is important and should be consumed. (timwolla, edorian) + . Added the (void) cast to indicate that not using a value is intentional. + (timwolla, edorian) + . Added get_error_handler(), get_exception_handler() functions. (Arnaud) + . Added support for casts in constant expressions. (nielsdos) + . Added the pipe (|>) operator. (crell) + . Added support for `final` with constructor property promotion. + (DanielEScherzer) + . Added support for configuring the URI parser for the FTP/FTPS as well as + the SSL/TLS stream wrappers as described in + https://wiki.php.net/rfc/url_parsing_api#plugability. (kocsismate) + . Added PHP_BUILD_PROVIDER constant. (timwolla) + . Added PHP_BUILD_DATE constant. (cmb) + . Added support for Closures and first class callables in constant + expressions. (timwolla, edorian) + . Add support for backtraces for fatal errors. (enorris) + . Add clone-with support to the clone() function. (timwolla, edorian) + . Add RFC 3986 and WHATWG URL compliant APIs for URL parsing + and manipulation (kocsismate, timwolla) + . Fixed AST printing for immediately invoked Closure. (Dmitrii Derepko) + . Properly handle __debugInfo() returning an array reference. (nielsdos) + . Properly handle reference return value from __toString(). (nielsdos) + . Improved error message of UnhandledMatchError for + zend.exception_string_param_max_len=0. (timwolla) + . Fixed bug GH-15753 and GH-16198 (Bind traits before parent class). (ilutov) . Fixed bug GH-17951 (memory_limit is not always limited by max_memory_limit). (manuelm) - . Address bug GH-20384 (Confirm if ob_gzhandler is impacted by ob_start - handler changes) by reverting GH-18932 (Deprecate returning non-string - values from a user output handler). (DanielEScherzer) - -- URI: - . Fixed bug GH-20431 (Uri\Rfc3986\Uri::setHost(null) turns empty path into /) - by updating to a newer uriparser snapshot. (timwolla) - . Fixed the distinction between an empty and a missing query/fragment - when using Uri\WhatWg\Url::getQuery() and Uri\WhatWg\Url::getFragment(). - (kocsismate) - -06 Nov 2025, PHP 8.5.0RC4 - -- Core: - . Fixed bug GH-20270 (Broken parent hook call with named arguments). (ilutov) . Fixed bug GH-20183 (Stale EG(opline_before_exception) pointer through eval). (ilutov) - . Fixed bug GH-20194 (null offset deprecation not emitted for writes). - (Girgias) - . Fixed bug GH-GH-20377 (final promoted properties without explicit visibility - not automatically assigned). (DanielEScherzer) - -- Opcache: - . Fixed bug GH-20012 (heap buffer overflow in jit). (Arnaud) - . Partially fixed bug GH-17733 (Avoid calling wrong function when reusing file - caches across differing environments). (ilutov) - -- PCRE: - . Downgrade back to PCRE2 10.44, see GH-20341. (nielsdos) - -- PgSql: - . Fix segfaults when attempting to fetch row into a non-instantiable class - name. (Girgias, nielsdos) - -- Reflection: - . Fixed bug GH-20217 (ReflectionClass::isIterable() incorrectly returns true - for classes with property hooks). (alexandre-daubois) - -- Standard: - . Fixed bug GH-20257 (mail() heap overflow with an empty message in lf mode). - (David Carlier) - . Fixed bug GH-20201 (AVIF images misdetected as HEIF after introducing HEIF - support in getimagesize()). (nielsdos) - -- Streams: - . Fixed bug GH-19798: XP_SOCKET XP_SSL (Socket stream modules): Incorrect - condition for Win32/Win64. (Jakub Zelenka) - -- URI: - . Use the "includes credentials" rule of the WHATWG URL Standard to - decide whether Uri\WhatWg\Url::getUsername() and ::getPassword() - getters should return null or an empty string. (timwolla) - . Fixed the distinction between empty and missing username/password - components of Uri\Rfc3986\Uri. - (kocsismate) - -- Zip: - . Fixed missing zend_release_fcall_info_cache on the following methods - ZipArchive::registerProgressCallback() and ZipArchive::registerCancelCallback() - on failure. (David Carlier) - - -23 Oct 2025, PHP 8.5.0RC3 - -- Core: . Fixed bug GH-20113 (Missing new Foo(...) error in constant expressions). (ilutov) . Fixed bug GH-19844 (Don't bail when closing resources on shutdown). (ilutov) - . Fixed deprecation for default case statement followed by semicolon not - being emitted. (theodorejb) . Fixed bug GH-20177 (Accessing overridden private property in get_object_vars() triggers assertion error). (ilutov) - -- DOM: - . Fix getNamedItemNS() incorrect namespace check. (nielsdos) - -- FPM: - . Fixed bug GH-19817 (Decode SCRIPT_FILENAME issue in php 8.5). - (Jakub Zelenka) - . Fixed bug GH-19989 (PHP 8.5 FPM access log lines also go to STDERR). - (Jakub Zelenka) - -- Opcache: - . Fixed bug GH-20081 (access to uninitialized vars in preload_load()). - (Arnaud) - . Fixed bug GH-20121 (JIT broken in ZTS builds on MacOS 15). - (Arnaud, Shivam Mathur) - . Fixed bug GH-19875 (JIT 1205 segfault on large file compiled in subprocess). - (Arnaud) - -- OpenSSL: - . Fixed bug GH-19994 (openssl_get_cipher_methods inconsistent with fetching). - (Jakub Zelenka) - -- PDO: - . Fixed bug GH-20095 (Incorrect class name in deprecation message for PDO - mixins). (timwolla) - -- Phar: - . Fix potential buffer length truncation due to usage of type int instead - of type size_t. (Girgias) - -- SPL: - . Fixed bug GH-20101 (SplHeap/SplPriorityQueue serialization - exposes INDIRECTs). (nielsdos) - . Improve __unserialize() hardening for SplHeap/SplPriorityQueue. (nielsdos) - -- Tidy: - . Fixed GH-19021 (improved tidyOptGetCategory detection). - (arjendekorte, David Carlier, Peter Kokot) - -- URI: - . Fixed bug GH-20088 (Heap-use-after-free in PHP URI WHATWG parser - during malformed URL processing). (lexborisov) - . Fixed bug GH-19868 (Unable to use uri_parser_rfc3986.h from external - extensions). (timwolla) - . Make php_uri_parser structs available to extensions directly. (timwolla) - -09 Oct 2025, PHP 8.5.0RC2 - -- Core: . Fix OSS-Fuzz #447521098 (Fatal error during sccp shift eval). (ilutov) . Fixed bug GH-20002 (Broken build on *BSD with MSAN). (outtersg) . Fixed bug GH-19352 (Cross-compilation with musl C library). (henderkes, Peter Kokot) - . Revert deprecation of __sleep() and __wakeup(). - They're now soft-deprecated. (Nicolas Grekas) - -- CLI: - . Fix useless "Failed to poll event" error logs due to EAGAIN in CLI server - with PHP_CLI_SERVER_WORKERS. (leotaku) - -- BcMath: - . Fixed bug GH-20006 (Power of 0 of BcMath number causes UB). (nielsdos) - -- Opcache: - . Fixed segfault in function JIT due to NAN to bool warning. (Girgias) - . Fixed bug GH-19984 (Double-free of EG(errors)/persistent_script->warnings on - persist of already persisted file). (ilutov, Arnaud) - . Fixed bug GH-19889 (race condition in zend_runtime_jit(), - zend_jit_hot_func()). (Arnaud) - -- SOAP: - . Fixed bug GH-19773 (SIGSEGV due to uninitialized soap_globals->lang_en). - (nielsdos, KaseyJenkins) - -- Standard: - . Fixed bug GH-19926 (reset internal pointer earlier while splicing array - while COW violation flag is still set). (alexandre-daubois) - -- URI: - . Fixed Uri\WhatWg\Url::withPort() when an invalid value is passed. - (timwolla) - . Fixed Uri\WhatWg\Url::parse() when resolving a relative URL - against a base URL with query or fragment. (timwolla) - . Fixed normalization of paths starting with two slashes for - Uri\Rfc3986\Uri. (timwolla) - -25 Sep 2025, PHP 8.5.0RC1 - -- Core: . Fixed bug GH-19765 (object_properties_load() bypasses readonly property checks). (timwolla) - . The __sleep() and __wakeup() magic methods have been deprecated. (Girgias) . Fixed hard_timeout with --enable-zend-max-execution-timers. (Appla) . Fixed bug GH-19839 (Incorrect HASH_FLAG_HAS_EMPTY_IND flag on userland array). (ilutov) @@ -232,258 +107,59 @@ PHP NEWS configured). (nielsdos) . Fixed bug GH-19719 (Allow empty statements before declare(strict_types)). (nielsdos) - . Casting floats that are not representable as ints now emits a warning. - (Girgias) - . Casting NAN to other types now emits a warning. (Girgias) . Fixed bug GH-19934 (CGI with auto_globals_jit=0 causes uouv). (ilutov) - -- Bz2: - . Fixed bug GH-19810 (Broken bzopen() stream mode validation). (ilutov) - -- Curl: - . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead - of the curl_copy_handle() function to clone a CurlHandle. (timwolla) - -- Date: - . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. - (nielsdos) - . The __wakeup() magic method of DateTimeInterface, DateTime, - DateTimeImmutable, DateTimeZone, DateInterval, and DatePeriod has been - deprecated in favour of the __unserialize() magic method. (Girgias) - -- Exif: - . Fix OSS-Fuzz #442954659 (zero-size box in HEIF file causes infinite loop). - (nielsdos) - . Fix OSS-Fuzz #442954659 (Crash in exif_scan_HEIF_header). (nielsdos) - . Various hardening fixes to HEIF parsing. (nielsdos) - -- FPM: - . Fixed GH-8157 (post_max_size evaluates .user.ini too late in php-fpm). - (Jakub Zelenka) - -- Iconv: - . Extends the ICONV_CONST preprocessor for illumos/solaris. (jMichaelA) - -- Opcache: - . Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex). - (Arnaud) - . Fixed bug GH-19831 (function JIT may not deref property value). (Arnaud) - -- OpenSSL: - . Fixed build when --with-openssl-legacy-provider set. (Jakub Zelenka) - -- MBstring: - . Updated Unicode data tables to Unicode 17.0. (Yuya Hamada) - -- Reflection: - . ReflectionConstant is no longer final. (sasezaki) - -- SAPI: - . Fixed bug GH-18582 and #81451: http_response_code() does not override the - status code generated by header(). (ilutov, Jakub Zelenka) - -- Standard: - . Passing strings which are not one byte long to ord() is now deprecated. - (Girgias) - . Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()). - (alexandre-daubois) - . Fixed GH-14402 (SplPriorityQueue, SplMinHeap, and SplMaxHeap lost their - data on serialize()). (alexandre-daubois) - -- URI: - . Fixed bug GH-19780 (InvalidUrlException should check $errors argument). - (nielsdos) - . Prevent modifying Uri\WhatWg\Url and Uri\Rfc3986\Uri objects by manually - calling __construct() or __unserialize(). (timwolla) - . Add new Uri\UriError exception that is thrown for internal error - conditions. (timwolla) - . Further clean up the internal API. (timwolla) - . Fixed bug GH-19892 (Refcounting on zend_empty_array). (ilutov, timwolla) - . Fixed handling of port numbers > 65535 with the internal - `php_uri_parse_to_struct()` API. (timwolla) - . Fix Uri\WhatWg\Url::withHost(). (timwolla) - -- Windows: - . Fix GH-19722 (_get_osfhandle asserts in debug mode when given a socket). - (dktapps) - -11 Sep 2025, PHP 8.5.0beta3 - -- Core: - . Destructuring non-array values (other than NULL) using [] or list() now - emits a warning. (Girgias) - . Fixed bug GH-19637 (Incorrect Closure scope for FCC in constant - expression). (timwolla) . Fixed bug GH-19613 (Stale array iterator pointer). (ilutov) . Fixed bug GH-19679 (zend_ssa_range_widening may fail to converge). (Arnaud) - . Using null as an array offset or when calling array_key_exists() is now - deprecated. (alexandre-daubois) . Fixed bug GH-19681 (PHP_EXPAND_PATH broken with bash 5.3.0). (Remi) - . Marks the stack as non-executable on Haiku. (David Carlier) - . Deriving $_SERVER['argc'] and $_SERVER['argv'] from the query string is - now deprecated. (timwolla, nicolasgrekas) - -- CLI: - . Fixed bug GH-19461 (Improve error message on listening error with IPv6 - address). (alexandre-daubois) - -- Date: - . Fixed date_sunrise() and date_sunset() with partial-hour UTC offset. - (ilutov) - -- EXIF: - . Added support to retrieve Exif from HEIF file. (Benstone Zhang) - -- FPM: - . Fixed failed debug assertion when php_admin_value setting fails. (ilutov) - -- Filter: - . Fixed bug GH-16993 (filter_var_array with FILTER_VALIDATE_INT|FILTER_NULL_ON_FAILURE - should emit warning for invalid filter usage). (alexandre-daubois) - -- Intl: - . Added grapheme_strpos(), grapheme_stripos(), grapheme_strrpos(), - grapheme_strripos(), grapheme_substr(), grapheme_strstr(), grapheme_stristr() and - grapheme_levenshtein() functions add $locale parameter (Yuya Hamada). - . Fixed bug GH-11952 (Fix locale strings canonicalization for IntlDateFormatter - and NumberFormatter). (alexandre-daubois) - -- ODBC: - . Removed driver-specific build flags and support. (Calvin Buckley) - -- Opcache: - . Fixed bug GH-19486 (Incorrect opline after deoptimization). (Arnaud) - . Fixed bug GH-19601 (Wrong JIT stack setup on aarch64/clang). (Arnaud) - . Fixed bug GH-19388 (Broken opcache.huge_code_pages). (Arnaud) - . Fixed bug GH-19657 (Build fails on non-glibc/musl/freebsd/macos/win - platforms). (Arnaud) - -- PCRE: - . Upgraded to pcre2lib from 10.45 to 10.46. (nielsdos) - -- PDO: - . Driver specific methods in the PDO class are now deprecated. (Arnaud) - -- PDO_SQLITE: - . Add PDO\Sqlite::ATTR_TRANSACTION_MODE connection attribute. - (Samuel Štancl) - -- Reflection: - . Fix GH-19691 (getModifierNames() not reporting asymmetric visibility). - (DanielEScherzer) - -- Session: - . Fix RC violation of session SID constant deprecation attribute. (ilutov) - -- Standard: - . Fix GH-19610 (Deprecation warnings in functions taking as argument). - (Girgias) - . Fixed bug GH-19577 (Avoid integer overflow when using a small offset - and PHP_INT_MAX with LimitIterator). (alexandre-daubois) - . Implement GH-19188: Add support for new INI mail.cr_lf_mode. - (alexandre-daubois) - -- Streams: - . Fixed bug GH-14506 (Closing a userspace stream inside a userspace handler - causes heap corruption). (nielsdos) - . Avoid double conversion to string in php_userstreamop_readdir(). (nielsdos) - -- URI: - . Added support for Uri\Rfc3986\Uri::with*() methods. (kocsismate) - . Fixed memory management of Uri\WhatWg\Url objects. (timwolla) - . Fixed memory management of the internal "parse_url" URI parser. - (timwolla) - . Fixed double-free when assigning to $errors fails when using - the Uri\WhatWg\Url parser. (timwolla) - . Reject out-of-range ports when using the Uri\Rfc3986\Uri parser. - (timwolla) - . Return null instead of 0 for Uri\Rfc3986\Uri::getPort() when the - URI contains an empty port. (timwolla) - . Fixed creation of the InvalidUrlException when not passing an - errors zval to the internal whatwg parser. (timwolla) - . Clean up naming of internal API. (timwolla) - -28 Aug 2025, PHP 8.5.0beta2 - -- Core: . Fixed bug GH-18850 (Repeated inclusion of file with __halt_compiler() triggers "Constant already defined" warning). (ilutov) . Fixed bug GH-19476 (pipe operator fails to correctly handle returning by reference). (alexandre-daubois) + . Fixed bug GH-19081 (Wrong lineno in property error with constructor property + promotion). (ilutov) + . Fixed bug GH-17959 (Relax missing trait fatal error to error exception). + (ilutov) + . Fixed bug GH-18033 (NULL-ptr dereference when using register_tick_function + in destructor). (nielsdos) + . Fixed bug GH-18026 (Improve "expecting token" error for ampersand). (ilutov) . The report_memleaks INI directive has been deprecated. (alexandre-daubois) - . Constant redeclaration has been deprecated. (alexandre-daubois) . Fixed OSS-Fuzz #439125710 (Pipe cannot be used in write context). (nielsdos) - . Added support for configuring the URI parser for the FTP/FTPS as well as - the SSL/TLS stream wrappers as described in - https://wiki.php.net/rfc/url_parsing_api#plugability. (kocsismate) . Fixed bug GH-19548 (Shared memory violation on property inheritance). (alexandre-daubois) . Fixed bug GH-19544 (GC treats ZEND_WEAKREF_TAG_MAP references as WeakMap references). (Arnaud, timwolla) - . Introduced the TAILCALL VM, enabled by default when compiling with Clang>=19 - on x86_64 or aarch64. (Arnaud) - . Enacted the follow-up phase of the "Path to Saner Increment/Decrement - operators" RFC, meaning that incrementing non-numeric strings is now - deprecated. (Girgias). - . Various closure binding issues are now deprecated. (alexandre-daubois) . Fixed bug GH-18373 (Don't substitute self/parent with anonymous class). (ilutov) - . Prohibit pipe & arrow function combination that leads to confusing parse - trees. (ilutov) - . The disable_classes INI directive has been removed. (Girgias) - . The locally predefined variable $http_response_header is deprecated. - (Girgias) - -- Filter: - . Added support for configuring the URI parser for FILTER_VALIDATE_URL - as described in https://wiki.php.net/rfc/url_parsing_api#plugability. - (kocsismate) - -- ODBC: - . Remove ODBCVER and assume ODBC 3.5. (Calvin Buckley) - -- Opcache: - . Fixed bug GH-19493 (JIT variable not stored before YIELD). (Arnaud) - -- OpenSSL: - . Implement #81724 (openssl_cms_encrypt only allows specific ciphers). - (Jakub Zelenka) - -- PDO: - . Driver specific constants in the PDO class are now deprecated. (Arnaud) - -- Phar: - . Fixed memory leaks when verifying OpenSSL signature. (Girgias) - -- Session: - . Added support for partitioned cookies. (nielsdos) - -- SOAP: - . Added support for configuring the URI parser for SoapClient::__doRequest() - as described in https://wiki.php.net/rfc/url_parsing_api#plugability. - (kocsismate) - -- SPL: - . Deprecate ArrayObject and ArrayIterator with objects. (Girgias) - -- Standard: - . Fixed bug GH-16649 (UAF during array_splice). (alexandre-daubois) - . Passing integers outside the interval [0, 255] to chr() is now deprecated. + . Fix support for non-userland stream notifiers. (timwolla) + . Fixed bug GH-19305 (Operands may be being released during comparison). + (Arnaud) + . Fixed bug GH-19306 (Generator can be resumed while fetching next value from + delegated Generator). (Arnaud) + . Fixed bug GH-19326 (Calling Generator::throw() on a running generator with + a non-Generator delegate crashes). (Arnaud) + . Fix OSS-Fuzz #427814452 (pipe compilation fails with assert). + (nielsdos, ilutov) + . Fixed bug GH-16665 (\array and \callable should not be usable in + class_alias). (nielsdos) + . Use `clock_gettime_nsec_np()` for high resolution timer on macOS + if available. (timwolla) + . Make `clone()` a function. (timwolla, edorian) + . Introduced the TAILCALL VM, enabled by default when compiling with Clang>=19 + on x86_64 or aarch64. (Arnaud) + . Enacted the follow-up phase of the "Path to Saner Increment/Decrement + operators" RFC, meaning that incrementing non-numeric strings is now + deprecated. (Girgias). + . Various closure binding issues are now deprecated. (alexandre-daubois) + . Constant redeclaration has been deprecated. (alexandre-daubois) + . Marks the stack as non-executable on Haiku. (David Carlier) + . Deriving $_SERVER['argc'] and $_SERVER['argv'] from the query string is + now deprecated. (timwolla, nicolasgrekas) + . Using null as an array offset or when calling array_key_exists() is now + deprecated. (alexandre-daubois) + . The disable_classes INI directive has been removed. (Girgias) + . The locally predefined variable $http_response_header is deprecated. (Girgias) - . Added support for partitioned cookies. (nielsdos) - -- Tokenizer: - . Fixed bug GH-19507 (Corrupted result after recursive tokenization during - token_get_all()). (kubawerlos, nielsdos, Arnaud) - -- URI: - . Clean up naming of internal API (header names, symbol names). - (Máté Kocsis, timwolla) - -14 Aug 2025, PHP 8.5.0beta1 - -- Core: . Non-canonical cast names (boolean), (integer), (double), and (binary) have been deprecated. (Girgias) . The $exclude_disabled parameter of the get_defined_functions() function has @@ -494,192 +170,25 @@ PHP NEWS (timwolla) . Returning null from __debugInfo() has been deprecated. (DanielEScherzer) . Support #[\Override] on properties. (Jiří Pudil) - -- Curl: - . The curl_close() function has been deprecated. (DanielEScherzer) - . The curl_share_close() function has been deprecated. (DanielEScherzer) - -- Date: - . The DATE_RFC7231 and DateTimeInterface::RFC7231 constants have been - deprecated. (jorgsowa) - -- DOM: - . Fixed bug GH-18877 (\Dom\HTMLDocument querySelectorAll selecting only the - first when using ~ and :has). (nielsdos, lexborisov) - -- FileInfo - . The finfo_close() function has been deprecated. (timwolla) - . The $context parameter of the finfo_buffer() function has been deprecated - as it is ignored. (Girgias) - -- GD: - . The imagedestroy() function has been deprecated. (DanielEScherzer) - -- Intl: - . Intl's internal error mechanism has been modernized so that it - indicates more accurately which call site caused what error. - Moreover, some ext/date exceptions have been wrapped inside a - IntlException now. (Girgias) - . The intl.error_level INI setting has been deprecated. (Girgias) - -- MySQLi: - . The mysqli_execute() alias function has been deprecated. (timwolla) - -- OpenSSL: - . Fixed bug GH-19369 (8.5 | Regression in openssl_sign() - support for alias - algorithms appears to be broken). (Jakub Zelenka) - . The $key_length parameter for openssl_pkey_derive() has been deprecated. + . Destructing non-array values (other than NULL) using [] or list() now + emits a warning. (Girgias) + . Casting floats that are not representable as ints now emits a warning. (Girgias) - . Implement #80495 (Enable to set padding in openssl_(sign|verify). - (Jakub Zelenka) - . Implement #47728 (openssl_pkcs7_sign ignores new openssl flags). - (Jakub Zelenka) - -- PDO: - . The "uri:" DSN scheme has been deprecated due to security concerns with - DSNs coming from remote URIs. (timwolla) - -- Reflection: - . Fixed bug GH-17927 (Reflection: have some indication of property hooks in - `_property_string()`). (DanielEScherzer) - . The setAccessible() methods of various Reflection objects have been - deprecated, as those no longer have an effect. (timwolla) - . ReflectionClass::getConstant() for constants that do not exist has been - deprecated. (DanielEScherzer) - . ReflectionProperty::getDefaultValue() for properties without default values - has been deprecated. (DanielEScherzer) - -- SPL: - . Unregistering all autoloaders by passing the spl_autoload_call() function - as a callback argument to spl_autoload_unregister() has been deprecated. - Instead if this is needed, one should iterate over the return value of - spl_autoload_functions() and call spl_autoload_unregister() on each - value. (Girgias) - . The SplObjectStorage::contains(), SplObjectStorage::attach(), and - SplObjectStorage::detach() methods have been deprecated in favour of - SplObjectStorage::offsetExists(), SplObjectStorage::offsetSet(), and - SplObjectStorage::offsetUnset() respectively. (Girgias) - -- Standard: - . The socket_set_timeout() alias function has been deprecated. (timwolla) - . Passing null to to readdir(), rewinddir(), and closedir() to use the last - opened directory has been deprecated. (Girgias) - . Fixed bug GH-19153 (#[\Attribute] validation should error on - trait/interface/enum/abstract class). (DanielEScherzer) - -- XML: - . The xml_parser_free() function has been deprecated. (DanielEScherzer) - -31 Jul 2025, PHP 8.5.0alpha4 - -- Core: - . Add clone-with support to the clone() function. (timwolla, edorian) - . Fix support for non-userland stream notifiers. (timwolla) - . Added PHP_BUILD_PROVIDER constant. (timwolla) - . Fixed bug GH-19305 (Operands may be being released during comparison). - (Arnaud) - . Fixed bug GH-19306 (Generator can be resumed while fetching next value from - delegated Generator). (Arnaud) - . Fixed bug GH-19326 (Calling Generator::throw() on a running generator with - a non-Generator delegate crashes). (Arnaud) - -- Curl: - . Add support for CURLINFO_CONN_ID in curl_getinfo() (thecaliskan) - . Add support for CURLINFO_QUEUE_TIME_T in curl_getinfo() (thecaliskan) - . Add support for CURLOPT_SSL_SIGNATURE_ALGORITHMS. (Ayesh Karunaratne) - -- FPM: - . Make FPM access log limit configurable using log_limit. (Jakub Zelenka) - -- GD: - . Fix incorrect comparison with result of php_stream_can_cast(). (Girgias) - -- Intl: - . Fix return value on failure for resourcebundle count handler. (Girgias) - . Fixed bug GH-19307 (PGO builds of shared ext-intl are broken). (cmb) - -- OPcache: - . Disallow changing opcache.memory_consumption when SHM is already set up. - (timwolla) - . Fixed bug GH-15074 (Compiling opcache statically into ZTS PHP fails). - (Arnaud) - . Make OPcache non-optional (Arnaud, timwolla) - . Fixed bug GH-17422 (OPcache bypasses the user-defined error handler for - deprecations). (Arnaud, timwolla) - . Fixed bug GH-19301 (opcache build failure). (Remi) - -- OpenSSL: - . Add $digest_algo parameter to openssl_public_encrypt() and - openssl_private_decrypt() functions. (Jakub Zelenka) - -- POSIX: - . posix_kill and posix_setpgid throws a ValueError on invalid process_id. - (David Carlier) - . posix_setpgid throws a ValueError on invalid process_group_id, - posix_setrlimit throws a ValueError on invalid soft_limit and hard_limit - arguments. (David Carlier) - -- Reflection: - . Fixed bug GH-19187 (ReflectionNamedType::getName() prints nullable type when - retrieved from ReflectionProperty::getSettableType()). (ilutov) - -- Session: - . Fixed GH-19197: build broken with ZEND_STRL usage with memcpy - when implemented as macro. (David Carlier) - -- Soap: - . Fixed bug GH-19226 (Segfault when spawning new thread in soap extension). - (Florian Engelhardt) - -- Sockets: - . socket_set_option for multicast context throws a ValueError - when the socket family is not of AF_INET/AF_INET6 family. (David Carlier) - -- Standard: - . Add HEIF/HEIC support to getimagesize. (Benstone Zhang) - . Implement #71517 (Implement SVG support for getimagesize() and friends). - (nielsdos) - . Optimized PHP html_entity_decode function. (Artem Ukrainskiy) - . Minor optimization to array_chunk(). (nielsdos) - -- URI: - . Empty host handling is fixed. (Máté Kocsis) - . Error handling of Uri\WhatWg\Url::withHost() is fixed when the input - contains a port. Now, it triggers an exception; previously, the error - was silently swallowed. (Máté Kocsis) - . Support empty URIs with Uri\Rfc3986\Uri. (timwolla) - -17 Jul 2025, PHP 8.5.0alpha2 - -- Core: - . Fix OSS-Fuzz #427814452 (pipe compilation fails with assert). - (nielsdos, ilutov) - -- DOM: - . Make cloning DOM node lists, maps, and collections fail. (nielsdos) - . Added Dom\Element::getElementsByClassName(). (nielsdos) - -- PDO_ODBC - . Fetch larger block sizes and better handle SQL_NO_TOTAL when calling - SQLGetData. (Calvin Buckley, Saki Takamachi) - -- Standard: - . Optimized pack(). (nielsdos, divinity76) - . Fixed bug GH-19070 (setlocale($type, NULL) should not be deprecated). - (nielsdos) - -- URI: - . Return the singleton UrlValidationErrorType instances from Uri\WhatWg\Url - instead of creating new objects that are different from the singleton. - (timwolla) - -03 Jul 2025, PHP 8.5.0alpha1 + . Casting NAN to other types now emits a warning. (Girgias) + . Implement GH-15680 (Enhance zend_dump_op_array to properly represent + non-printable characters in string literals). (nielsdos, WangYihang) + . Fixed bug GH-17442 (Engine UAF with reference assign and dtor). (nielsdos) + . Do not use RTLD_DEEPBIND if dlmopen is available. (Daniil Gentili) - BCMath: . Simplify `bc_divide()` code. (SakiTakamachi) . If the result is 0, n_scale is set to 0. (SakiTakamachi) . If size of BC_VECTOR array is within 64 bytes, stack area is now used. (SakiTakamachi) + . Fixed bug GH-20006 (Power of 0 of BcMath number causes UB). (nielsdos) + +- Bz2: + . Fixed bug GH-19810 (Broken bzopen() stream mode validation). (ilutov) - CLI: . Add --ini=diff to print INI settings changed from the builtin default. @@ -687,69 +196,52 @@ PHP NEWS . Drop support for -z CLI/CGI flag. (nielsdos) . Fixed GH-17956 - development server 404 page does not adapt to mobiles. (pascalchevrel) - -- CURL: - . Added CURLFOLLOW_ALL, CURLFOLLOW_OBEYCODE and CURLFOLLOW_FIRSTONLY - values for CURLOPT_FOLLOWLOCATION curl_easy_setopt option. (David Carlier) + . Fix useless "Failed to poll event" error logs due to EAGAIN in CLI server + with PHP_CLI_SERVER_WORKERS. (leotaku) + . Fixed bug GH-19461 (Improve error message on listening error with IPv6 + address). (alexandre-daubois) - COM: . Fixed property access of PHP objects wrapped in variant. (cmb) . Fixed method calls for PHP objects wrapped in variant. (cmb) -- Core: - . Fixed bug GH-16665 (\array and \callable should not be usable in - class_alias). (nielsdos) - . Added PHP_BUILD_DATE constant. (cmb) - . Added support for Closures and first class callables in constant - expressions. (timwolla, Volker Dusch) - . Use `clock_gettime_nsec_np()` for high resolution timer on macOS - if available. (timwolla) - . Implement GH-15680 (Enhance zend_dump_op_array to properly represent - non-printable characters in string literals). (nielsdos, WangYihang) - . Add support for backtraces for fatal errors. (enorris) - . Fixed bug GH-17442 (Engine UAF with reference assign and dtor). (nielsdos) - . Improved error message of UnhandledMatchError for - zend.exception_string_param_max_len=0. (timwolla) - . Fixed bug GH-17959 (Relax missing trait fatal error to error exception). - (ilutov) - . Fixed bug GH-18033 (NULL-ptr dereference when using register_tick_function - in destructor). (nielsdos) - . Fixed bug GH-18026 (Improve "expecting token" error for ampersand). (ilutov) - . Added the #[\NoDiscard] attribute to indicate that a function's return - value is important and should be consumed. (timwolla, Volker Dusch) - . Added the (void) cast to indicate that not using a value is intentional. - (timwolla, Volker Dusch) - . Added get_error_handler(), get_exception_handler() functions. (Arnaud) - . Fixed bug GH-15753 and GH-16198 (Bind traits before parent class). (ilutov) - . Added support for casts in constant expressions. (nielsdos) - . Fixed bugs GH-17711 and GH-18022 (Infinite recursion on deprecated attribute - evaluation) and GH-18464 (Recursion protection for deprecation constants not - released on bailout). (DanielEScherzer and ilutov) - . Fixed AST printing for immediately invoked Closure. (Dmitrii Derepko) - . Properly handle __debugInfo() returning an array reference. (nielsdos) - . Properly handle reference return value from __toString(). (nielsdos) - . Added the pipe (|>) operator. (crell) - . Added support for `final` with constructor property promotion. - (DanielEScherzer) - . Do not use RTLD_DEEPBIND if dlmopen is available. (Daniil Gentili) - . Make `clone()` a function. (timwolla, edorian) - . Fixed bug GH-19081 (Wrong lineno in property error with constructor property - promotion). (ilutov) - - Curl: + . Added CURLFOLLOW_ALL, CURLFOLLOW_OBEYCODE and CURLFOLLOW_FIRSTONLY + values for CURLOPT_FOLLOWLOCATION curl_easy_setopt option. (David Carlier) . Added curl_multi_get_handles(). (timwolla) . Added curl_share_init_persistent(). (enorris) . Added CURLINFO_USED_PROXY, CURLINFO_HTTPAUTH_USED, and CURLINFO_PROXYAUTH_USED support to curl_getinfo. (Ayesh Karunaratne) + . Add support for CURLINFO_CONN_ID in curl_getinfo() (thecaliskan) + . Add support for CURLINFO_QUEUE_TIME_T in curl_getinfo() (thecaliskan) + . Add support for CURLOPT_SSL_SIGNATURE_ALGORITHMS. (Ayesh Karunaratne) + . The curl_close() function has been deprecated. (DanielEScherzer) + . The curl_share_close() function has been deprecated. (DanielEScherzer) + . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead + of the curl_copy_handle() function to clone a CurlHandle. (timwolla) - Date: . Fix undefined behaviour problems regarding integer overflow in extreme edge cases. (nielsdos, cmb, ilutov) + . The DATE_RFC7231 and DateTimeInterface::RFC7231 constants have been + deprecated. (jorgsowa) + . Fixed date_sunrise() and date_sunset() with partial-hour UTC offset. + (ilutov) + . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. + (nielsdos) + . The __wakeup() magic method of DateTimeInterface, DateTime, + DateTimeImmutable, DateTimeZone, DateInterval, and DatePeriod has been + deprecated in favour of the __unserialize() magic method. (Girgias) - DOM: . Added Dom\Element::$outerHTML. (nielsdos) . Added Dom\Element::insertAdjacentHTML(). (nielsdos) . Added $children property to ParentNode implementations. (nielsdos) + . Make cloning DOM node lists, maps, and collections fail. (nielsdos) + . Added Dom\Element::getElementsByClassName(). (nielsdos) + . Fixed bug GH-18877 (\Dom\HTMLDocument querySelectorAll selecting only the + first when using ~ and :has). (nielsdos, lexborisov) + . Fix getNamedItemNS() incorrect namespace check. (nielsdos) - Enchant: . Added enchant_dict_remove_from_session(). (nielsdos) @@ -758,19 +250,48 @@ PHP NEWS - EXIF: . Add OffsetTime* Exif tags. (acc987) + . Added support to retrieve Exif from HEIF file. (Benstone Zhang) + . Fix OSS-Fuzz #442954659 (zero-size box in HEIF file causes infinite loop). + (nielsdos) + . Fix OSS-Fuzz #442954659 (Crash in exif_scan_HEIF_header). (nielsdos) + . Various hardening fixes to HEIF parsing. (nielsdos) + -- Fileinfo: +- FileInfo: + . The finfo_close() function has been deprecated. (timwolla) + . The $context parameter of the finfo_buffer() function has been deprecated + as it is ignored. (Girgias) . Upgrade to file 5.46. (nielsdos) . Change return type of finfo_close() to true. (timwolla) +- Filter: + . Added support for configuring the URI parser for FILTER_VALIDATE_URL + as described in https://wiki.php.net/rfc/url_parsing_api#plugability. + (kocsismate) + . Fixed bug GH-16993 (filter_var_array with FILTER_VALIDATE_INT|FILTER_NULL_ON_FAILURE + should emit warning for invalid filter usage). (alexandre-daubois) + - FPM: + . Fixed bug GH-19817 (Decode SCRIPT_FILENAME issue in php 8.5). + (Jakub Zelenka) + . Fixed bug GH-19989 (PHP 8.5 FPM access log lines also go to STDERR). + (Jakub Zelenka) . Fixed GH-17645 (FPM with httpd ProxyPass does not decode script path). (Jakub Zelenka) + . Make FPM access log limit configurable using log_limit. (Jakub Zelenka) + . Fixed failed debug assertion when php_admin_value setting fails. (ilutov) + . Fixed GH-8157 (post_max_size evaluates .user.ini too late in php-fpm). + (Jakub Zelenka) - GD: . Fixed bug #68629 (Transparent artifacts when using imagerotate). (pierre, cmb) - . Fixed bug #64823 (ZTS GD fails to to find system TrueType font). (cmb) + . Fixed bug #64823 (ZTS GD fails to find system TrueType font). (cmb) + . Fix incorrect comparison with result of php_stream_can_cast(). (Girgias) + . The imagedestroy() function has been deprecated. (DanielEScherzer) + +- Iconv: + . Extends the ICONV_CONST preprocessor for illumos/solaris. (jMichaelA) - Intl: . Bumped ICU requirement to ICU >= 57.1. (cmb) @@ -786,31 +307,97 @@ PHP NEWS adding/removing likely subtags to a locale. (David Carlier) . Added IntlListFormatter class to format a list of items with a locale, operands types and units. (BogdanUngureanu) + . Added grapheme_strpos(), grapheme_stripos(), grapheme_strrpos(), + grapheme_strripos(), grapheme_substr(), grapheme_strstr(), grapheme_stristr() and + grapheme_levenshtein() functions add $locale parameter (Yuya Hamada). + . Fixed bug GH-11952 (Fix locale strings canonicalization for IntlDateFormatter + and NumberFormatter). (alexandre-daubois) . Fixed bug GH-18566 ([intl] Weird numeric sort in Collator). (nielsdos) + . Fix return value on failure for resourcebundle count handler. (Girgias) + . Fixed bug GH-19307 (PGO builds of shared ext-intl are broken). (cmb) + . Intl's internal error mechanism has been modernized so that it + indicates more accurately which call site caused what error. + Moreover, some ext/date exceptions have been wrapped inside a + IntlException now. (Girgias) + . The intl.error_level INI setting has been deprecated. (Girgias) - LDAP: . Allow ldap_get_option to retrieve global option by allowing NULL for connection instance ($ldap). (Remi) +- MBstring: + . Updated Unicode data tables to Unicode 17.0. (Yuya Hamada) + - MySQLi: . Fixed bugs GH-17900 and GH-8084 (calling mysqli::__construct twice). (nielsdos) + . The mysqli_execute() alias function has been deprecated. (timwolla) - MySQLnd: . Added mysqlnd.collect_memory_statistics to ini quick reference. (hauk92) +- ODBC: + . Removed driver-specific build flags and support. (Calvin Buckley) + . Remove ODBCVER and assume ODBC 3.5. (Calvin Buckley) + - Opcache: - . Fixed ZTS OPcache build on Cygwin. (cmb) + . Make OPcache non-optional (Arnaud, timwolla) . Added opcache.file_cache_read_only. (Samuel Melrose) . Updated default value of opcache.jit_hot_loop. (Arnaud) . Log a warning when opcache lock file permissions could not be changed. (Taavi Eomäe) + . Fixed bug GH-20012 (heap buffer overflow in jit). (Arnaud) + . Partially fixed bug GH-17733 (Avoid calling wrong function when reusing file + caches across differing environments). (ilutov) + . Disallow changing opcache.memory_consumption when SHM is already set up. + (timwolla) + . Fixed bug GH-15074 (Compiling opcache statically into ZTS PHP fails). + (Arnaud) + . Fixed bug GH-17422 (OPcache bypasses the user-defined error handler for + deprecations). (Arnaud, timwolla) + . Fixed bug GH-19301 (opcache build failure). (Remi) + . Fixed bug GH-20081 (access to uninitialized vars in preload_load()). + (Arnaud) + . Fixed bug GH-20121 (JIT broken in ZTS builds on MacOS 15). + (Arnaud, Shivam Mathur) + . Fixed bug GH-19875 (JIT 1205 segfault on large file compiled in subprocess). + (Arnaud) + . Fixed segfault in function JIT due to NAN to bool warning. (Girgias) + . Fixed bug GH-19984 (Double-free of EG(errors)/persistent_script->warnings on + persist of already persisted file). (ilutov, Arnaud) + . Fixed bug GH-19889 (race condition in zend_runtime_jit(), + zend_jit_hot_func()). (Arnaud) + . Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex). + (Arnaud) + . Fixed bug GH-19831 (function JIT may not deref property value). (Arnaud) + . Fixed bug GH-19486 (Incorrect opline after deoptimization). (Arnaud) + . Fixed bug GH-19601 (Wrong JIT stack setup on aarch64/clang). (Arnaud) + . Fixed bug GH-19388 (Broken opcache.huge_code_pages). (Arnaud) + . Fixed bug GH-19657 (Build fails on non-glibc/musl/freebsd/macos/win + platforms). (Arnaud) + . Fixed ZTS OPcache build on Cygwin. (cmb) + . Fixed bug GH-19493 (JIT variable not stored before YIELD). (Arnaud) - OpenSSL: . Added openssl.libctx INI that allows to select the OpenSSL library context - type and convert various parts of the extension to use the custom libctx. + type and convert various parts of the extension to use the custom libctx. (Jakub Zelenka) + . Add $digest_algo parameter to openssl_public_encrypt() and + openssl_private_decrypt() functions. (Jakub Zelenka) + . Implement #81724 (openssl_cms_encrypt only allows specific ciphers). + (Jakub Zelenka) + . Implement #80495 (Enable to set padding in openssl_(sign|verify). + (Jakub Zelenka) + . Implement #47728 (openssl_pkcs7_sign ignores new openssl flags). + (Jakub Zelenka) + . Fixed bug GH-19994 (openssl_get_cipher_methods inconsistent with fetching). + (Jakub Zelenka) + . Fixed build when --with-openssl-legacy-provider set. (Jakub Zelenka) + . Fixed bug GH-19369 (8.5 | Regression in openssl_sign() - support for alias + algorithms appears to be broken). (Jakub Zelenka) + . The $key_length parameter for openssl_pkey_derive() has been deprecated. + (Girgias) - Output: . Fixed calculation of aligned buffer size. (cmb) @@ -819,10 +406,21 @@ PHP NEWS . Extend pcntl_waitid with rusage parameter. (vrza) - PCRE: - . Upgraded to pcre2lib from 10.44 to 10.45. (nielsdos) . Remove PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK from pcre compile options. (mvorisek) +- PDO: + . Fixed bug GH-20095 (Incorrect class name in deprecation message for PDO + mixins). (timwolla) + . Driver specific methods and constants in the PDO class + are now deprecated. (Arnaud) + . The "uri:" DSN scheme has been deprecated due to security concerns with + DSNs coming from remote URIs. (timwolla) + +- PDO_ODBC: + . Fetch larger block sizes and better handle SQL_NO_TOTAL when calling + SQLGetData. (Calvin Buckley, Saki Takamachi) + - PDO_PGSQL: . Added Iterable support for PDO::pgsqlCopyFromArray. (KentarouTakeda) . Implement GH-15387 Pdo\Pgsql::setAttribute(PDO::ATTR_PREFETCH, 0) or @@ -830,7 +428,8 @@ PHP NEWS instead of storing the whole result set in memory (Guillaume Outters) - PDO_SQLITE: - . throw on null bytes / resolve GH-13952 (divinity76). + . Add PDO\Sqlite::ATTR_TRANSACTION_MODE connection attribute. + (Samuel Štancl) . Implement GH-17321: Add setAuthorizer to Pdo\Sqlite. (nielsdos) . PDO::sqliteCreateCollation now throws a TypeError if the callback has a wrong return type. (David Carlier) @@ -839,6 +438,8 @@ PHP NEWS . Added Pdo_Sqlite::ATTR_EXPLAIN_STATEMENT constant to set a statement in either EXPLAIN_MODE_PREPARED, EXPLAIN_MODE_EXPLAIN, EXPLAIN_MODE_EXPLAIN_QUERY_PLAN modes. (David Carlier) + . Fix bug GH-13952 (sqlite PDO::quote silently corrupts strings + with null bytes) by throwing on null bytes. (divinity76) - PGSQL: . Added pg_close_stmt to close a prepared statement while allowing @@ -849,6 +450,13 @@ PHP NEWS (David Carlier) . Added pg_service to get the connection current service identifier. (David Carlier) + . Fix segfaults when attempting to fetch row into a non-instantiable class + name. (Girgias, nielsdos) + +- Phar: + . Fix potential buffer length truncation due to usage of type int instead + of type size_t. (Girgias) + . Fixed memory leaks when verifying OpenSSL signature. (Girgias) - POSIX: . Added POSIX_SC_OPEN_MAX constant to get the number of file descriptors @@ -856,6 +464,11 @@ PHP NEWS . posix_ttyname() sets last_error to EBADF on invalid file descriptors, posix_isatty() raises E_WARNING on invalid file descriptors, posix_fpathconf checks invalid file descriptors. (David Carlier) + . posix_kill and posix_setpgid throws a ValueError on invalid process_id. + (David Carlier) + . posix_setpgid throws a ValueError on invalid process_group_id, + posix_setrlimit throws a ValueError on invalid soft_limit and hard_limit + arguments. (David Carlier) - Random: . Moves from /dev/urandom usage to arc4random_buf on Haiku. (David Carlier) @@ -863,16 +476,39 @@ PHP NEWS - Reflection: . Added ReflectionConstant::getExtension() and ::getExtensionName(). (DanielEScherzer) + . Added ReflectionProperty::getMangledName() method. (alexandre-daubois) + . ReflectionConstant is no longer final. (sasezaki) + . The setAccessible() methods of various Reflection objects have been + deprecated, as those no longer have an effect. (timwolla) + . ReflectionClass::getConstant() for constants that do not exist has been + deprecated. (DanielEScherzer) + . ReflectionProperty::getDefaultValue() for properties without default values + has been deprecated. (DanielEScherzer) . Fixed bug GH-12856 (ReflectionClass::getStaticPropertyValue() returns UNDEF zval for uninitialized typed properties). (nielsdos) . Fixed bug GH-15766 (ReflectionClass::__toString() should have better output for enums). (DanielEScherzer) - . Added ReflectionProperty::getMangledName() method. (alexandre-daubois) + . Fix GH-19691 (getModifierNames() not reporting asymmetric visibility). + (DanielEScherzer) + . Fixed bug GH-17927 (Reflection: have some indication of property hooks in + `_property_string()`). (DanielEScherzer) + . Fixed bug GH-19187 (ReflectionNamedType::getName() prints nullable type when + retrieved from ReflectionProperty::getSettableType()). (ilutov) + . Fixed bug GH-20217 (ReflectionClass::isIterable() incorrectly returns true + for classes with property hooks). (alexandre-daubois) + +- SAPI: + . Fixed bug GH-18582 and #81451: http_response_code() does not override the + status code generated by header(). (ilutov, Jakub Zelenka) - Session: . session_start() throws a ValueError on option argument if not a hashmap or a TypeError if read_and_close value is not compatible with int. (David Carlier) + . Added support for partitioned cookies. (nielsdos) + . Fix RC violation of session SID constant deprecation attribute. (ilutov) + . Fixed GH-19197: build broken with ZEND_STRL usage with memcpy + when implemented as macro. (David Carlier) - SimpleXML: . Fixed bug GH-12231 (SimpleXML xpath should warn when returning other return @@ -884,25 +520,38 @@ PHP NEWS timeout and retries arguments. (David Carlier) - SOAP: + . Added support for configuring the URI parser for SoapClient::__doRequest() + as described in https://wiki.php.net/rfc/url_parsing_api#plugability. + (kocsismate) + . Implement request #55503 (Extend __getTypes to support enumerations). + (nielsdos, datibbaw) + . Implement request #61105 (Support Soap 1.2 SoapFault Reason Text lang + attribute). (nielsdos) . Fixed bug #49169 (SoapServer calls wrong function, although "SOAP action" header is correct). (nielsdos) . Fix namespace handling of WSDL and XML schema in SOAP, fixing at least GH-16320 and bug #68576. (nielsdos) . Fixed bug #70951 (Segmentation fault on invalid WSDL cache). (nielsdos) - . Implement request #55503 (Extend __getTypes to support enumerations). - (nielsdos, datibbaw) - . Implement request #61105 (Support Soap 1.2 SoapFault Reason Text lang - attribute). (nielsdos) + . Fixed bug GH-19773 (SIGSEGV due to uninitialized soap_globals->lang_en). + (nielsdos, KaseyJenkins) + . Fixed bug GH-19226 (Segfault when spawning new thread in soap extension). + (Florian Engelhardt) - Sockets: . Added IPPROTO_ICMP/IPPROTO_ICMPV6 to create raw socket for ICMP usage. (David Carlier) . Added TCP_FUNCTION_BLK to change the TCP stack algorithm on FreeBSD. (David Carlier) - . socket_set_option() catches possible overflow with SO_RCVTIMEO/SO_SNDTIMEO - with timeout setting on windows. (David Carlier) + . Added IP_BINDANY for a socket to bind to any address. (David Carlier) + . Added SO_BUSY_POOL to reduce packets poll latency. (David Carlier) + . Added UDP_SEGMENT support to optimise multiple large datagrams over UDP + if the kernel and hardware supports it. (David Carlier) + . Added SHUT_RD, SHUT_WR and SHUT_RDWR constants for socket_shutdown(). + (David Carlier) . Added TCP_FUNCTION_ALIAS, TCP_REUSPORT_LB_NUMA, TCP_REUSPORT_LB_NUMA_NODOM, TCP_REUSPORT_LB_CURDOM, TCP_BBR_ALGORITHM constants. + . socket_set_option() catches possible overflow with SO_RCVTIMEO/SO_SNDTIMEO + with timeout setting on windows. (David Carlier) . socket_create_listen() throws an exception on invalid port value. (David Carlier) . socket_bind() throws an exception on invalid port value. @@ -920,26 +569,46 @@ PHP NEWS (David Carlier) . socket_getsockname/socket_create/socket_bind handled AF_PACKET family socket. (David Carlier) - . Added IP_BINDANY for a socket to bind to any address. (David Carlier) - . Added SO_BUSY_POOL to reduce packets poll latency. (David Carlier) - - Added UDP_SEGMENT support to optimise multiple large datagrams over UDP - if the kernel and hardware supports it. (David Carlier) - - Added SHUT_RD, SHUT_WR and SHUT_RDWR constants for socket_shutdown(). - (David Carlier) + . socket_set_option for multicast context throws a ValueError + when the socket family is not of AF_INET/AF_INET6 family. (David Carlier) - Sodium: . Fix overall theoretical overflows on zend_string buffer allocations. (David Carlier/nielsdos) +- SPL: + . Fixed bug GH-20101 (SplHeap/SplPriorityQueue serialization + exposes INDIRECTs). (nielsdos) + . Improve __unserialize() hardening for SplHeap/SplPriorityQueue. (nielsdos) + . Deprecate ArrayObject and ArrayIterator with objects. (Girgias) + . Unregistering all autoloaders by passing the spl_autoload_call() function + as a callback argument to spl_autoload_unregister() has been deprecated. + Instead if this is needed, one should iterate over the return value of + spl_autoload_functions() and call spl_autoload_unregister() on each + value. (Girgias) + . The SplObjectStorage::contains(), SplObjectStorage::attach(), and + SplObjectStorage::detach() methods have been deprecated in favour of + SplObjectStorage::offsetExists(), SplObjectStorage::offsetSet(), and + SplObjectStorage::offsetUnset() respectively. (Girgias) + - Sqlite: . Added Sqlite3Stmt::busy to check if a statement is still being executed. (David Carlier) - . Added Sqlite3Stmt::explain to produce a explain query plan from + . Added Sqlite3Stmt::explain to produce an explain query plan from the statement. (David Carlier) - . Added Sqlite3Result::fetchAll to returns all results at once from a query. + . Added Sqlite3Result::fetchAll to return all results at once from a query. (David Carlier) - Standard: + . Add HEIF/HEIC support to getimagesize. (Benstone Zhang) + . Added support for partitioned cookies. (nielsdos) + . Implement #71517 (Implement SVG support for getimagesize() and friends). + (nielsdos) + . Implement GH-19188: Add support for new INI mail.cr_lf_mode. + (alexandre-daubois) + . Optimized PHP html_entity_decode function. (Artem Ukrainskiy) + . Minor optimization to array_chunk(). (nielsdos) + . Optimized pack(). (nielsdos, divinity76) . Fixed crypt() tests on musl when using --with-external-libcrypt (Michael Orlitzky). . Fixed bug GH-18062 (is_callable(func(...), callable_name: $name) for first @@ -951,10 +620,41 @@ PHP NEWS (Jesse Hathaway) . Fixed bug GH-18897 (printf: empty precision is interpreted as precision 6, not as precision 0). (nielsdos) + . Fixed bug GH-20257 (mail() heap overflow with an empty message in lf mode). + (David Carlier) + . Fixed bug GH-20201 (AVIF images misdetected as HEIF after introducing HEIF + support in getimagesize()). (nielsdos) + . Fixed bug GH-19926 (reset internal pointer earlier while splicing array + while COW violation flag is still set). (alexandre-daubois) + . Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()). + (alexandre-daubois) + . Fixed GH-14402 (SplPriorityQueue, SplMinHeap, and SplMaxHeap lost their + data on serialize()). (alexandre-daubois) + . Fixed GH-19610 (Deprecation warnings in functions taking as argument). + (Girgias) + . Fixed bug GH-19577 (Avoid integer overflow when using a small offset + and PHP_INT_MAX with LimitIterator). (alexandre-daubois) + . Fixed bug GH-19153 (#[\Attribute] validation should error on + trait/interface/enum/abstract class). (DanielEScherzer) + . Fixed bug GH-19070 (setlocale($type, NULL) should not be deprecated). + (nielsdos) + . Fixed bug GH-16649 (UAF during array_splice). (alexandre-daubois) + . Passing strings which are not one byte long to ord() is now deprecated. + (Girgias) + . Passing integers outside the interval [0, 255] to chr() is now deprecated. + (Girgias) + . The socket_set_timeout() alias function has been deprecated. (timwolla) + . Passing null to readdir(), rewinddir(), and closedir() to use the last + opened directory has been deprecated. (Girgias) - Streams: . Fixed bug GH-16889 (stream_select() timeout useless for pipes on Windows). (cmb) + . Fixed bug GH-19798: XP_SOCKET XP_SSL (Socket stream modules): Incorrect + condition for Win32/Win64. (Jakub Zelenka) + . Fixed bug GH-14506 (Closing a userspace stream inside a userspace handler + causes heap corruption). (nielsdos) + . Avoid double conversion to string in php_userstreamop_readdir(). (nielsdos) - Tests: . Allow to shuffle tests even in non-parallel mode. (dhuang00) @@ -962,11 +662,22 @@ PHP NEWS - Tidy: . tidy::__construct/parseFile/parseString methods throw an exception if the configuration argument is invalid. (David Carlier) + . Fixed GH-19021 (improved tidyOptGetCategory detection). + (arjendekorte, David Carlier, Peter Kokot) + +- Tokenizer: + . Fixed bug GH-19507 (Corrupted result after recursive tokenization during + token_get_all()). (kubawerlos, nielsdos, Arnaud) - Windows: . Fixed bug GH-10992 (Improper long path support for relative paths). (cmb, nielsdos) . Fixed bug GH-16843 (Windows phpize builds ignore source subfolders). (cmb) + . Fix GH-19722 (_get_osfhandle asserts in debug mode when given a socket). + (dktapps) + +- XML: + . The xml_parser_free() function has been deprecated. (DanielEScherzer) - XMLWriter: . Improved performance and reduce memory consumption. (nielsdos) @@ -981,4 +692,7 @@ PHP NEWS opening HTTP URLs). (nielsdos) . Implemented GH-17668 (zlib streams should support locking). (nielsdos) -<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>> +- Zip: + . Fixed missing zend_release_fcall_info_cache on the following methods + ZipArchive::registerProgressCallback() and ZipArchive::registerCancelCallback() + on failure. (David Carlier) From 599d5ae64ce47ae7d4b75e1c55d6396fa840f083 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 17 Nov 2025 15:14:24 +0100 Subject: [PATCH 060/252] [skip ci] Fix Symfony PHP requirements in community build Symfony 8.1 and 8.0 now require PHP 8.4. Use 7.4 for PHP 8.3 and 8.2 builds. PHP 8.1 continues to be skipped. Sadly, this will need to be updated sporadically. Closes GH-20512 --- .github/workflows/nightly.yml | 11 ++++++----- .github/workflows/root.yml | 4 +++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index c360146508b9c..909a412e796e6 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -38,9 +38,9 @@ on: skip_laravel: required: true type: boolean - skip_symfony: + symfony_version: required: true - type: boolean + type: string skip_wordpress: required: true type: boolean @@ -599,9 +599,9 @@ jobs: exit 1 fi - name: Test Symfony - if: ${{ !cancelled() && !inputs.skip_symfony }} + if: ${{ !cancelled() && inputs.symfony_version != '' }} run: | - git clone https://github.com/symfony/symfony.git --depth=1 + git clone https://github.com/symfony/symfony.git --depth=1 --branch="${{ inputs.symfony_version }}" cd symfony git rev-parse HEAD php /usr/bin/composer install --no-progress --ignore-platform-req=php+ @@ -635,7 +635,8 @@ jobs: exit 1 fi - name: 'Symfony Preloading' - if: ${{ !cancelled() && !inputs.skip_symfony }} + # composer create-project will automatically pick the right Symfony version for us. + if: ${{ !cancelled() && inputs.symfony_version != '' }} run: | php /usr/bin/composer create-project symfony/symfony-demo symfony_demo --no-progress --ignore-platform-req=php+ cd symfony_demo diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index 4062358fcbc95..ea23349fd6928 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -57,7 +57,9 @@ jobs: windows_version: '2022' vs_crt_version: ${{ ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) && 'vs17') || 'vs16' }} skip_laravel: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} - skip_symfony: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} + symfony_version: ${{ (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9) && '8.1') + || ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 2) && '7.4') + || '' }} skip_wordpress: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} variation_enable_zend_max_execution_timers: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 3) || matrix.branch.version[0] >= 9 }} secrets: inherit From 7610527d756cb024215533950a41a3813e9b79e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 18 Nov 2025 17:30:51 +0100 Subject: [PATCH 061/252] lexbor: Cherry pick "URL: fixed "use-after-poison" for an empty path entry." see lexbor/lexbor@9259b169e3cdaed9c61622dab92abb457bb8ddf5 Fixes php/php-src#20502 Fixes php/php-src#20521 --- NEWS | 2 ++ ext/lexbor/lexbor/url/url.c | 27 +++++++++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 469edebbb67a2..daf490e95f4c0 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,8 @@ PHP NEWS - Lexbor: . Fixed bug GH-20501 (\Uri\WhatWg\Url lose host after calling withPath() or withQuery()). (lexborisov) + . Fixed bug GH-20502 (\Uri\WhatWg\Url crashes (SEGV) when parsing + malformed URL due to Lexbor memory corruption). (lexborisov) - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string diff --git a/ext/lexbor/lexbor/url/url.c b/ext/lexbor/lexbor/url/url.c index 99ba809b05fe6..3483013eeaafe 100644 --- a/ext/lexbor/lexbor/url/url.c +++ b/ext/lexbor/lexbor/url/url.c @@ -1029,27 +1029,34 @@ lxb_url_path_append_wo_slash(lxb_url_t *url, static lxb_status_t lxb_url_path_append(lxb_url_t *url, const lxb_char_t *data, size_t length) { - size_t len; - lxb_char_t *p; + lxb_char_t *p, *begin; lexbor_str_t *str; str = &url->path.str; if (str->data == NULL) { p = lexbor_str_init(str, url->mraw, length + 1); - if (p == NULL) { - return LXB_STATUS_ERROR_MEMORY_ALLOCATION; - } + } + else { + /* + 2 == begin '/' and end '\0' */ + p = lexbor_str_check_size(str, url->mraw, length + 2); } - len = str->length; - str->length += 1; + if (p == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } - p = lexbor_str_append(&url->path.str, url->mraw, data, length); + begin = &str->data[str->length]; + begin[0] = '/'; - str->data[len] = '/'; + if (length > 0) { + memcpy(&begin[1], data, sizeof(lxb_char_t) * length); + } - return (p != NULL) ? LXB_STATUS_OK : LXB_STATUS_ERROR_MEMORY_ALLOCATION; + str->length += length + 1; + str->data[str->length] = '\0'; + + return LXB_STATUS_OK; } static lxb_status_t From 4a12745705bcc863949f9b42abc0981f47052725 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 18 Nov 2025 09:49:13 -0800 Subject: [PATCH 062/252] xml: Optimize attribute array construction (#20515) Attributes can't be numeric strings by the definition of the grammar, so don't bother with the symbol table stuff. --- ext/xml/xml.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/xml/xml.c b/ext/xml/xml.c index 0b46aa6b2fe2f..89c0dcd0dc4a5 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -648,7 +648,7 @@ void xml_startElementHandler(void *userData, const XML_Char *name, const XML_Cha val = xml_utf8_decode(attributes[1], strlen((char *)attributes[1]), parser->target_encoding); ZVAL_STR(&tmp, val); - zend_symtable_update(Z_ARRVAL(args[2]), att, &tmp); + zend_hash_update(Z_ARRVAL(args[2]), att, &tmp); attributes += 2; @@ -688,7 +688,7 @@ void xml_startElementHandler(void *userData, const XML_Char *name, const XML_Cha val = xml_utf8_decode(attributes[1], strlen((char *)attributes[1]), parser->target_encoding); ZVAL_STR(&tmp, val); - zend_symtable_update(Z_ARRVAL(atr), att, &tmp); + zend_hash_update(Z_ARRVAL(atr), att, &tmp); atcnt++; attributes += 2; From 6054a900ff90a214df18e20e2544151a1dc11b4a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Mon, 17 Nov 2025 20:54:44 +0100 Subject: [PATCH 063/252] libxml: Fix some deprecations regarding input buffer/parser handling Closes GH-20514. --- NEWS | 4 ++++ ext/libxml/libxml.c | 16 +++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index cacfdc28892d0..0ef238afe42d8 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,10 @@ PHP NEWS . Fixed bug GH-20483 (ASAN stack overflow with fiber.stack_size INI small value). (David Carlier) +- LibXML: + . Fix some deprecations on newer libxml versions regarding input + buffer/parser handling. (ndossche) + - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index d5009889549b4..f70fa5bd8c859 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -644,11 +644,10 @@ php_libxml_output_buffer_create_filename(const char *URI, } /* Allocate the Output buffer front-end. */ - ret = xmlAllocOutputBuffer(encoder); - if (ret != NULL) { - ret->context = context; - ret->writecallback = php_libxml_streams_IO_write; - ret->closecallback = php_libxml_streams_IO_close; + ret = xmlOutputBufferCreateIO(php_libxml_streams_IO_write, php_libxml_streams_IO_close, context, encoder); + if (ret == NULL) { + php_libxml_streams_IO_close(context); + goto err; } return(ret); @@ -820,6 +819,7 @@ static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL, zend_string_release(callable_name); zval_ptr_dtor(&callable); } else { +#if LIBXML_VERSION < 21400 /* TODO: allow storing the encoding in the stream context? */ xmlCharEncoding enc = XML_CHAR_ENCODING_NONE; xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc); @@ -838,6 +838,12 @@ static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL, xmlFreeParserInputBuffer(pib); } } +#else + /* make stream not being closed when the zval is freed */ + GC_ADDREF(stream->res); + ret = xmlNewInputFromIO(NULL, php_libxml_streams_IO_read, php_libxml_streams_IO_close, stream, 0); + /* Note: if ret == NULL, the close operation will be executed, so don't DELREF stream->res upon failure! */ +#endif } } else if (Z_TYPE(retval) != IS_NULL) { /* retval not string nor resource nor null; convert to string */ From e929602ed06b33be834984acc072fb2c1f236eab Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Tue, 18 Nov 2025 18:32:59 +0000 Subject: [PATCH 064/252] ext/soap: HTTP request micro optimisations. (#20516) smart string is a bit overkill for the cookie id here. --- ext/soap/php_http.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index 48305ad627256..63c0093eb05c8 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -1014,8 +1014,7 @@ int make_http_soap_request( char *eqpos = strstr(cookie, "="); char *sempos = strstr(cookie, ";"); if (eqpos != NULL && (sempos == NULL || sempos > eqpos)) { - smart_str name = {0}; - int cookie_len; + size_t cookie_len; zval zcookie; if (sempos != NULL) { @@ -1024,8 +1023,7 @@ int make_http_soap_request( cookie_len = strlen(cookie)-(eqpos-cookie)-1; } - smart_str_appendl(&name, cookie, eqpos - cookie); - smart_str_0(&name); + zend_string *name = zend_string_init(cookie, eqpos - cookie, false); array_init(&zcookie); add_index_stringl(&zcookie, 0, eqpos + 1, cookie_len); @@ -1063,8 +1061,8 @@ int make_http_soap_request( GC_ADDREF(uri->host); } - zend_symtable_update(Z_ARRVAL_P(cookies), name.s, &zcookie); - smart_str_free(&name); + zend_symtable_update(Z_ARRVAL_P(cookies), name, &zcookie); + zend_string_release_ex(name, false); } cookie_itt = cookie_itt + cookie_len; From 2f05830a5fb8d8b00c9496fa64906c8a92be90aa Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:01:44 +0100 Subject: [PATCH 065/252] zip: Don't truncate return value of zip_fread() with user sizes The return type has been zip_int64_t since 2009, so we shouldn't truncate to an int because the user may have requested a size that won't fit in an int. Closes GH-20509. --- NEWS | 1 + ext/zip/php_zip.c | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 0ef238afe42d8..516f6abd462a2 100644 --- a/NEWS +++ b/NEWS @@ -52,6 +52,7 @@ PHP NEWS - Zip: . Fix crash in property existence test. (ndossche) + . Don't truncate return value of zip_fread() with user sizes. (ndossche) - Zlib: . Fix assertion failures resulting in crashes with stream filter diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 15f55cba71255..f1630192e601c 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -1332,7 +1332,6 @@ PHP_FUNCTION(zip_entry_read) zend_long len = 0; zip_read_rsrc * zr_rsrc; zend_string *buffer; - int n = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &zip_entry, &len) == FAILURE) { RETURN_THROWS(); @@ -1348,7 +1347,7 @@ PHP_FUNCTION(zip_entry_read) if (zr_rsrc->zf) { buffer = zend_string_safe_alloc(1, len, 0, 0); - n = zip_fread(zr_rsrc->zf, ZSTR_VAL(buffer), ZSTR_LEN(buffer)); + zip_int64_t n = zip_fread(zr_rsrc->zf, ZSTR_VAL(buffer), ZSTR_LEN(buffer)); if (n > 0) { ZSTR_VAL(buffer)[n] = '\0'; ZSTR_LEN(buffer) = n; @@ -2910,8 +2909,6 @@ static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ zend_string *filename; zend_string *buffer; - int n = 0; - if (type == 1) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|ll", &filename, &len, &flags) == FAILURE) { RETURN_THROWS(); @@ -2948,7 +2945,7 @@ static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ } buffer = zend_string_safe_alloc(1, len, 0, 0); - n = zip_fread(zf, ZSTR_VAL(buffer), ZSTR_LEN(buffer)); + zip_int64_t n = zip_fread(zf, ZSTR_VAL(buffer), ZSTR_LEN(buffer)); if (n < 1) { zend_string_efree(buffer); RETURN_EMPTY_STRING(); From 8c2407714fc380229232b8119c4ac2aef37979a9 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 18 Nov 2025 20:29:15 +0100 Subject: [PATCH 066/252] libxml: Fix input buffer deprecation While this fixed the last deprecation in ext/libxml, it's not a full fix: The full fix would be to move to the context-specific APIs to override the behaviour. However, that requires API/ABI incompatible changes so that can't be done on a stable branch. Closes GH-20525. --- ext/libxml/libxml.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index f70fa5bd8c859..d6f8f63d28282 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -513,6 +513,7 @@ static int php_libxml_streams_IO_close(void *context) return php_stream_close((php_stream*)context); } +/* TODO: This needs to be replaced by context-specific APIs in the future! */ static xmlParserInputBufferPtr php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc) { @@ -591,13 +592,10 @@ php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc) } /* Allocate the Input buffer front-end. */ - ret = xmlAllocParserInputBuffer(enc); - if (ret != NULL) { - ret->context = context; - ret->readcallback = php_libxml_streams_IO_read; - ret->closecallback = php_libxml_streams_IO_close; - } else + ret = xmlParserInputBufferCreateIO(php_libxml_streams_IO_read, php_libxml_streams_IO_close, context, enc); + if (ret == NULL) { php_libxml_streams_IO_close(context); + } return(ret); } From 9743977f920d1788a2bf5152ddd41d366d6dbf96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Wed, 19 Nov 2025 20:41:27 +0100 Subject: [PATCH 067/252] Fix GH-20366 ext/uri: Do not throw ValueError on null-byte (#20489) --- NEWS | 4 ++ ext/uri/php_uri.c | 10 ++-- ext/uri/php_uri_common.c | 4 +- ext/uri/tests/035.phpt | 39 -------------- .../modification/path_error_null_byte.phpt | 18 +++++++ ..._byte1.phpt => basic_error_null_byte.phpt} | 4 +- .../parsing/basic_error_null_byte2.phpt | 14 ----- .../resolve_error_null_byte.phpt | 18 +++++++ .../fragment_success_null_byte.phpt | 19 +++++++ .../modification/host_error_null_byte.phpt | 18 +++++++ .../password_success_null_byte.phpt | 20 +++++++ .../modification/path_success_null_byte.phpt | 19 +++++++ .../modification/query_success_null_byte.phpt | 19 +++++++ .../modification/scheme_error_null_byte.phpt | 18 +++++++ .../username_success_null_byte.phpt | 20 +++++++ .../parsing/basic_error_null_byte2.phpt | 14 ----- ...l_byte1.phpt => host_error_null_byte.phpt} | 4 +- .../parsing/password_success_null_byte.phpt | 31 +++++++++++ .../parsing/path_success_null_byte.phpt | 31 +++++++++++ .../parsing/query_success_null_byte.phpt | 31 +++++++++++ .../parsing/scheme_error_null_byte.phpt | 14 +++++ .../parsing/username_success_null_byte.phpt | 31 +++++++++++ .../resolve_error_host_null_byte.phpt | 18 +++++++ .../resolve_success_path_null_byte.phpt | 53 +++++++++++++++++++ 24 files changed, 393 insertions(+), 78 deletions(-) delete mode 100644 ext/uri/tests/035.phpt create mode 100644 ext/uri/tests/rfc3986/modification/path_error_null_byte.phpt rename ext/uri/tests/rfc3986/parsing/{basic_error_null_byte1.phpt => basic_error_null_byte.phpt} (63%) delete mode 100644 ext/uri/tests/rfc3986/parsing/basic_error_null_byte2.phpt create mode 100644 ext/uri/tests/rfc3986/reference_resolution/resolve_error_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/modification/fragment_success_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/modification/host_error_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/modification/password_success_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/modification/path_success_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/modification/query_success_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/modification/scheme_error_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/modification/username_success_null_byte.phpt delete mode 100644 ext/uri/tests/whatwg/parsing/basic_error_null_byte2.phpt rename ext/uri/tests/whatwg/parsing/{basic_error_null_byte1.phpt => host_error_null_byte.phpt} (63%) create mode 100644 ext/uri/tests/whatwg/parsing/password_success_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/parsing/path_success_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/parsing/query_success_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/parsing/scheme_error_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/parsing/username_success_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/reference_resolution/resolve_error_host_null_byte.phpt create mode 100644 ext/uri/tests/whatwg/reference_resolution/resolve_success_path_null_byte.phpt diff --git a/NEWS b/NEWS index 4e7e49df81742..253e597cf2d4b 100644 --- a/NEWS +++ b/NEWS @@ -61,6 +61,10 @@ PHP NEWS . Fix assertion failures resulting in crashes with stream filter object parameters. (ndossche) +- URI: + . Fixed bug GH-20366 (ext/uri incorrectly throws ValueError when encountering + null byte). (kocsismate) + 20 Nov 2025, PHP 8.5.0 - Core: diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 4a1207de89942..29b26f7b8ce27 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -377,7 +377,7 @@ static void create_rfc3986_uri(INTERNAL_FUNCTION_PARAMETERS, bool is_constructor zend_object *base_url_object = NULL; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_PATH_STR(uri_str) + Z_PARAM_STR(uri_str) Z_PARAM_OPTIONAL Z_PARAM_OBJ_OF_CLASS_OR_NULL(base_url_object, php_uri_ce_rfc3986_uri) ZEND_PARSE_PARAMETERS_END(); @@ -490,7 +490,7 @@ static void create_whatwg_uri(INTERNAL_FUNCTION_PARAMETERS, bool is_constructor) zval *errors = NULL; ZEND_PARSE_PARAMETERS_START(1, 3) - Z_PARAM_PATH_STR(uri_str) + Z_PARAM_STR(uri_str) Z_PARAM_OPTIONAL Z_PARAM_OBJ_OF_CLASS_OR_NULL(base_url_object, php_uri_ce_whatwg_url) Z_PARAM_ZVAL(errors) @@ -553,7 +553,7 @@ PHP_METHOD(Uri_Rfc3986_Uri, withUserInfo) zend_string *value; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_PATH_STR_OR_NULL(value) + Z_PARAM_STR_OR_NULL(value) ZEND_PARSE_PARAMETERS_END(); zval zv; @@ -769,7 +769,7 @@ PHP_METHOD(Uri_Rfc3986_Uri, resolve) zend_string *uri_str; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_PATH_STR(uri_str) + Z_PARAM_STR(uri_str) ZEND_PARSE_PARAMETERS_END(); php_uri_instantiate_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, @@ -956,7 +956,7 @@ PHP_METHOD(Uri_WhatWg_Url, resolve) zval *errors = NULL; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_PATH_STR(uri_str) + Z_PARAM_STR(uri_str) Z_PARAM_OPTIONAL Z_PARAM_ZVAL(errors) ZEND_PARSE_PARAMETERS_END(); diff --git a/ext/uri/php_uri_common.c b/ext/uri/php_uri_common.c index 780cc95074159..da73bc59bf9cf 100644 --- a/ext/uri/php_uri_common.c +++ b/ext/uri/php_uri_common.c @@ -96,7 +96,7 @@ void php_uri_property_write_str_helper(INTERNAL_FUNCTION_PARAMETERS, php_uri_pro zend_string *value; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_PATH_STR(value) + Z_PARAM_STR(value) ZEND_PARSE_PARAMETERS_END(); zval zv; @@ -110,7 +110,7 @@ void php_uri_property_write_str_or_null_helper(INTERNAL_FUNCTION_PARAMETERS, php zend_string *value; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_PATH_STR_OR_NULL(value) + Z_PARAM_STR_OR_NULL(value) ZEND_PARSE_PARAMETERS_END(); zval zv; diff --git a/ext/uri/tests/035.phpt b/ext/uri/tests/035.phpt deleted file mode 100644 index 3c39870373742..0000000000000 --- a/ext/uri/tests/035.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -Test URI parsing containing null bytes ---EXTENSIONS-- -uri ---FILE-- -getMessage() . "\n"; -} - -$uri = new Uri\Rfc3986\Uri("https://example.com"); -try { - $uri->withHost("exam\0ple.com"); -} catch (Error $e) { - echo $e->getMessage() . "\n"; -} - -try { - new Uri\WhatWg\Url("https://exam\0ple.com"); -} catch (Error $e) { - echo $e->getMessage() . "\n"; -} - -$url = new Uri\WhatWg\Url("https://example.com"); -try { - $url->withHost("exam\0ple.com"); -} catch (Error $e) { - echo $e->getMessage() . "\n"; -} - -?> ---EXPECT-- -Uri\Rfc3986\Uri::__construct(): Argument #1 ($uri) must not contain any null bytes -Uri\Rfc3986\Uri::withHost(): Argument #1 ($host) must not contain any null bytes -Uri\WhatWg\Url::__construct(): Argument #1 ($uri) must not contain any null bytes -Uri\WhatWg\Url::withHost(): Argument #1 ($host) must not contain any null bytes diff --git a/ext/uri/tests/rfc3986/modification/path_error_null_byte.phpt b/ext/uri/tests/rfc3986/modification/path_error_null_byte.phpt new file mode 100644 index 0000000000000..d02e3e8575f2d --- /dev/null +++ b/ext/uri/tests/rfc3986/modification/path_error_null_byte.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\Rfc3986\Uri component modification - path - null byte +--EXTENSIONS-- +uri +--FILE-- +withPath("/\0foo"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified path is malformed diff --git a/ext/uri/tests/rfc3986/parsing/basic_error_null_byte1.phpt b/ext/uri/tests/rfc3986/parsing/basic_error_null_byte.phpt similarity index 63% rename from ext/uri/tests/rfc3986/parsing/basic_error_null_byte1.phpt rename to ext/uri/tests/rfc3986/parsing/basic_error_null_byte.phpt index 5695b8487cd34..91d194e2ff5e5 100644 --- a/ext/uri/tests/rfc3986/parsing/basic_error_null_byte1.phpt +++ b/ext/uri/tests/rfc3986/parsing/basic_error_null_byte.phpt @@ -5,10 +5,10 @@ Test Uri\Rfc3986\Uri parsing - basic - URI contains null byte try { new Uri\Rfc3986\Uri("https://exam\0ple.com"); -} catch (ValueError $e) { +} catch (Throwable $e) { echo $e::class, ": ", $e->getMessage(), PHP_EOL; } ?> --EXPECT-- -ValueError: Uri\Rfc3986\Uri::__construct(): Argument #1 ($uri) must not contain any null bytes +Uri\InvalidUriException: The specified URI is malformed diff --git a/ext/uri/tests/rfc3986/parsing/basic_error_null_byte2.phpt b/ext/uri/tests/rfc3986/parsing/basic_error_null_byte2.phpt deleted file mode 100644 index 85c779cc5aff7..0000000000000 --- a/ext/uri/tests/rfc3986/parsing/basic_error_null_byte2.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -Test Uri\Rfc3986\Uri parsing - basic - URI contains null byte ---FILE-- -getMessage(), PHP_EOL; -} - -?> ---EXPECT-- -ValueError: Uri\Rfc3986\Uri::parse(): Argument #1 ($uri) must not contain any null bytes diff --git a/ext/uri/tests/rfc3986/reference_resolution/resolve_error_null_byte.phpt b/ext/uri/tests/rfc3986/reference_resolution/resolve_error_null_byte.phpt new file mode 100644 index 0000000000000..16b2f61dce78b --- /dev/null +++ b/ext/uri/tests/rfc3986/reference_resolution/resolve_error_null_byte.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\Rfc3986\Uri reference resolution - resolve() - null byte +--EXTENSIONS-- +uri +--FILE-- +resolve("/f\0o"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\InvalidUriException: The specified URI is malformed diff --git a/ext/uri/tests/whatwg/modification/fragment_success_null_byte.phpt b/ext/uri/tests/whatwg/modification/fragment_success_null_byte.phpt new file mode 100644 index 0000000000000..faaf41796c746 --- /dev/null +++ b/ext/uri/tests/whatwg/modification/fragment_success_null_byte.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test Uri\WhatWg\Url component modification - fragment - null byte +--EXTENSIONS-- +uri +--FILE-- +withFragment("frag\0ment"); + +var_dump($url1->getFragment()); +var_dump($url2->getFragment()); +var_dump($url2->toAsciiString()); + +?> +--EXPECT-- +NULL +string(11) "frag%00ment" +string(32) "https://example.com/#frag%00ment" diff --git a/ext/uri/tests/whatwg/modification/host_error_null_byte.phpt b/ext/uri/tests/whatwg/modification/host_error_null_byte.phpt new file mode 100644 index 0000000000000..84a6bc1ebdbc6 --- /dev/null +++ b/ext/uri/tests/whatwg/modification/host_error_null_byte.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\WhatWg\Url component modification - host - null byte +--EXTENSIONS-- +uri +--FILE-- +withHost("h\0st"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified host is malformed (DomainInvalidCodePoint) diff --git a/ext/uri/tests/whatwg/modification/password_success_null_byte.phpt b/ext/uri/tests/whatwg/modification/password_success_null_byte.phpt new file mode 100644 index 0000000000000..2216fa4e8e845 --- /dev/null +++ b/ext/uri/tests/whatwg/modification/password_success_null_byte.phpt @@ -0,0 +1,20 @@ +--TEST-- +Test Uri\WhatWg\Url component modification - password - null byte +--EXTENSIONS-- +uri +--FILE-- +withPassword("pass\0word"); + +var_dump($url1->getPassword()); +var_dump($url2->getPassword()); +var_dump($url2->toAsciiString()); + + +?> +--EXPECT-- +NULL +string(11) "pass%00word" +string(33) "https://:pass%00word@example.com/" diff --git a/ext/uri/tests/whatwg/modification/path_success_null_byte.phpt b/ext/uri/tests/whatwg/modification/path_success_null_byte.phpt new file mode 100644 index 0000000000000..c09a3aa2810f4 --- /dev/null +++ b/ext/uri/tests/whatwg/modification/path_success_null_byte.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test Uri\WhatWg\Url component modification - path - null byte +--EXTENSIONS-- +uri +--FILE-- +withPath("/p\0th\0"); + +var_dump($url1->getPath()); +var_dump($url2->getPath()); +var_dump($url2->toAsciiString()); + +?> +--EXPECT-- +string(1) "/" +string(10) "/p%00th%00" +string(29) "https://example.com/p%00th%00" diff --git a/ext/uri/tests/whatwg/modification/query_success_null_byte.phpt b/ext/uri/tests/whatwg/modification/query_success_null_byte.phpt new file mode 100644 index 0000000000000..81232d8b2aa2d --- /dev/null +++ b/ext/uri/tests/whatwg/modification/query_success_null_byte.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test Uri\WhatWg\Url component modification - query - null byte +--EXTENSIONS-- +uri +--FILE-- +withQuery("f\0o=bar&baz=q\0x"); + +var_dump($url1->getQuery()); +var_dump($url2->getQuery()); +var_dump($url2->toAsciiString()); + +?> +--EXPECT-- +NULL +string(19) "f%00o=bar&baz=q%00x" +string(40) "https://example.com/?f%00o=bar&baz=q%00x" diff --git a/ext/uri/tests/whatwg/modification/scheme_error_null_byte.phpt b/ext/uri/tests/whatwg/modification/scheme_error_null_byte.phpt new file mode 100644 index 0000000000000..ab2845ae64937 --- /dev/null +++ b/ext/uri/tests/whatwg/modification/scheme_error_null_byte.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\WhatWg\Url component modification - scheme - null byte +--EXTENSIONS-- +uri +--FILE-- +withScheme("sch\0me"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified scheme is malformed diff --git a/ext/uri/tests/whatwg/modification/username_success_null_byte.phpt b/ext/uri/tests/whatwg/modification/username_success_null_byte.phpt new file mode 100644 index 0000000000000..86f6a62e0f200 --- /dev/null +++ b/ext/uri/tests/whatwg/modification/username_success_null_byte.phpt @@ -0,0 +1,20 @@ +--TEST-- +Test Uri\WhatWg\Url component modification - username - null byte +--EXTENSIONS-- +uri +--FILE-- +withUsername("usern\0me"); + +var_dump($url1->getUsername()); +var_dump($url2->getUsername()); +var_dump($url2->toAsciiString()); + + +?> +--EXPECT-- +NULL +string(10) "usern%00me" +string(31) "https://usern%00me@example.com/" diff --git a/ext/uri/tests/whatwg/parsing/basic_error_null_byte2.phpt b/ext/uri/tests/whatwg/parsing/basic_error_null_byte2.phpt deleted file mode 100644 index 6aa631c74e93e..0000000000000 --- a/ext/uri/tests/whatwg/parsing/basic_error_null_byte2.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -Test Uri\WhatWg\Url parsing - basic - URL contains null byte ---FILE-- -getMessage(), PHP_EOL; -} - -?> ---EXPECT-- -ValueError: Uri\WhatWg\Url::parse(): Argument #1 ($uri) must not contain any null bytes diff --git a/ext/uri/tests/whatwg/parsing/basic_error_null_byte1.phpt b/ext/uri/tests/whatwg/parsing/host_error_null_byte.phpt similarity index 63% rename from ext/uri/tests/whatwg/parsing/basic_error_null_byte1.phpt rename to ext/uri/tests/whatwg/parsing/host_error_null_byte.phpt index db5cd075a6fa0..f450d26c41648 100644 --- a/ext/uri/tests/whatwg/parsing/basic_error_null_byte1.phpt +++ b/ext/uri/tests/whatwg/parsing/host_error_null_byte.phpt @@ -5,10 +5,10 @@ Test Uri\WhatWg\Url parsing - basic - URL contains null byte try { new Uri\WhatWg\Url("https://exam\0ple.com"); -} catch (ValueError $e) { +} catch (Throwable $e) { echo $e::class, ": ", $e->getMessage(), PHP_EOL; } ?> --EXPECT-- -ValueError: Uri\WhatWg\Url::__construct(): Argument #1 ($uri) must not contain any null bytes +Uri\WhatWg\InvalidUrlException: The specified URI is malformed (DomainInvalidCodePoint) diff --git a/ext/uri/tests/whatwg/parsing/password_success_null_byte.phpt b/ext/uri/tests/whatwg/parsing/password_success_null_byte.phpt new file mode 100644 index 0000000000000..9dada6a9f387e --- /dev/null +++ b/ext/uri/tests/whatwg/parsing/password_success_null_byte.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test Uri\WhatWg\Url parsing - password - null byte +--FILE-- +toAsciiString()); + +?> +--EXPECT-- +object(Uri\WhatWg\Url)#1 (8) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(8) "username" + ["password"]=> + string(7) "%00pass" + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +string(37) "https://username:%00pass@example.com/" diff --git a/ext/uri/tests/whatwg/parsing/path_success_null_byte.phpt b/ext/uri/tests/whatwg/parsing/path_success_null_byte.phpt new file mode 100644 index 0000000000000..d20242dcee53a --- /dev/null +++ b/ext/uri/tests/whatwg/parsing/path_success_null_byte.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test Uri\WhatWg\Url parsing - path - null byte +--FILE-- +toAsciiString()); + +?> +--EXPECT-- +object(Uri\WhatWg\Url)#1 (8) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(8) "/pa%00th" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +string(27) "https://example.com/pa%00th" diff --git a/ext/uri/tests/whatwg/parsing/query_success_null_byte.phpt b/ext/uri/tests/whatwg/parsing/query_success_null_byte.phpt new file mode 100644 index 0000000000000..bf1364365156b --- /dev/null +++ b/ext/uri/tests/whatwg/parsing/query_success_null_byte.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test Uri\WhatWg\Url parsing - query - null byte +--FILE-- +toAsciiString()); + +?> +--EXPECT-- +object(Uri\WhatWg\Url)#1 (8) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + string(9) "fo%00=bar" + ["fragment"]=> + NULL +} +string(30) "https://example.com/?fo%00=bar" diff --git a/ext/uri/tests/whatwg/parsing/scheme_error_null_byte.phpt b/ext/uri/tests/whatwg/parsing/scheme_error_null_byte.phpt new file mode 100644 index 0000000000000..5674c1f330932 --- /dev/null +++ b/ext/uri/tests/whatwg/parsing/scheme_error_null_byte.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test Uri\WhatWg\Url parsing - scheme - null byte +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl) diff --git a/ext/uri/tests/whatwg/parsing/username_success_null_byte.phpt b/ext/uri/tests/whatwg/parsing/username_success_null_byte.phpt new file mode 100644 index 0000000000000..d99c3cefdc153 --- /dev/null +++ b/ext/uri/tests/whatwg/parsing/username_success_null_byte.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test Uri\WhatWg\Url parsing - username - null byte +--FILE-- +toAsciiString()); + +?> +--EXPECT-- +object(Uri\WhatWg\Url)#1 (8) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(11) "user%00name" + ["password"]=> + string(4) "pass" + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +string(37) "https://user%00name:pass@example.com/" diff --git a/ext/uri/tests/whatwg/reference_resolution/resolve_error_host_null_byte.phpt b/ext/uri/tests/whatwg/reference_resolution/resolve_error_host_null_byte.phpt new file mode 100644 index 0000000000000..ed31e0e5daead --- /dev/null +++ b/ext/uri/tests/whatwg/reference_resolution/resolve_error_host_null_byte.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Uri\WhatWg\Url reference resolution - resolve() - null byte +--EXTENSIONS-- +uri +--FILE-- +resolve("https://ex\0mple.com"); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Uri\WhatWg\InvalidUrlException: The specified URI is malformed (DomainInvalidCodePoint) diff --git a/ext/uri/tests/whatwg/reference_resolution/resolve_success_path_null_byte.phpt b/ext/uri/tests/whatwg/reference_resolution/resolve_success_path_null_byte.phpt new file mode 100644 index 0000000000000..abdcec98e8421 --- /dev/null +++ b/ext/uri/tests/whatwg/reference_resolution/resolve_success_path_null_byte.phpt @@ -0,0 +1,53 @@ +--TEST-- +Test Uri\WhatWg\Url reference resolution - resolve() - null byte in path +--EXTENSIONS-- +uri +--FILE-- +resolve("/f\0o"); + +var_dump($url1); +var_dump($url2); +var_dump($url2->toAsciiString()); + +?> +--EXPECT-- +object(Uri\WhatWg\Url)#1 (8) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#2 (8) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(6) "/f%00o" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +string(25) "https://example.com/f%00o" From abc1910b4f3e7b6c1f1fb15a6af52aec4c13d311 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 14 Nov 2025 08:12:32 +0000 Subject: [PATCH 068/252] ext/standard/array.c: refactor php_valid_var_name() --- ext/standard/array.c | 85 +++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 53 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 54b26a3dadfd5..d8be2fc1f3024 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1675,9 +1675,8 @@ PHP_FUNCTION(array_search) } /* }}} */ -static zend_always_inline int php_valid_var_name(const char *var_name, size_t var_name_len) /* {{{ */ +static zend_always_inline bool php_valid_var_name(const zend_string *var_name) /* {{{ */ { -#if 1 /* first 256 bits for first character, and second 256 bits for the next */ static const uint32_t charset[8] = { /* 31 0 63 32 95 64 127 96 */ @@ -1687,48 +1686,28 @@ static zend_always_inline int php_valid_var_name(const char *var_name, size_t va /* 31 0 63 32 95 64 127 96 */ 0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}; -#endif - size_t i; - uint32_t ch; - if (UNEXPECTED(!var_name_len)) { - return 0; + if (UNEXPECTED(!ZSTR_LEN(var_name))) { + return false; } /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */ - ch = (uint32_t)((unsigned char *)var_name)[0]; -#if 1 + uint32_t ch = (uint32_t)((unsigned char *)ZSTR_VAL(var_name))[0]; if (UNEXPECTED(!ZEND_BIT_TEST(charset, ch))) { -#else - if (var_name[0] != '_' && - (ch < 65 /* A */ || /* Z */ ch > 90) && - (ch < 97 /* a */ || /* z */ ch > 122) && - (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255) - ) { -#endif - return 0; + return false; } /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */ - if (var_name_len > 1) { - i = 1; + if (ZSTR_LEN(var_name) > 1) { + size_t i = 1; do { - ch = (uint32_t)((unsigned char *)var_name)[i]; -#if 1 + ch = (uint32_t)((unsigned char *)ZSTR_VAL(var_name))[i]; if (UNEXPECTED(!ZEND_BIT_TEST(charset2, ch))) { -#else - if (var_name[i] != '_' && - (ch < 48 /* 0 */ || /* 9 */ ch > 57) && - (ch < 65 /* A */ || /* Z */ ch > 90) && - (ch < 97 /* a */ || /* z */ ch > 122) && - (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255) - ) { -#endif - return 0; + return false; } - } while (++i < var_name_len); + } while (++i < ZSTR_LEN(var_name)); } - return 1; + return true; } /* }}} */ @@ -1768,7 +1747,7 @@ static zend_long php_extract_ref_if_exists(zend_array *arr, zend_array *symbol_t continue; } } - if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + if (!php_valid_var_name(var_name)) { continue; } if (zend_string_equals_literal(var_name, "GLOBALS")) { @@ -1814,7 +1793,7 @@ static zend_long php_extract_if_exists(zend_array *arr, zend_array *symbol_table continue; } } - if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + if (!php_valid_var_name(var_name)) { continue; } if (zend_string_equals_literal(var_name, "GLOBALS")) { @@ -1850,7 +1829,7 @@ static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_t if (!var_name) { continue; } - if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + if (!php_valid_var_name(var_name)) { continue; } if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { @@ -1902,7 +1881,7 @@ static zend_long php_extract_overwrite(zend_array *arr, zend_array *symbol_table if (!var_name) { continue; } - if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + if (!php_valid_var_name(var_name)) { continue; } if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { @@ -1963,7 +1942,7 @@ static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *s } } php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (php_valid_var_name(Z_STR(final_name))) { if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { zend_throw_error(NULL, "Cannot re-assign $this"); return -1; @@ -2017,7 +1996,7 @@ static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbo } } php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (php_valid_var_name(Z_STR(final_name))) { if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { zend_throw_error(NULL, "Cannot re-assign $this"); return -1; @@ -2080,7 +2059,7 @@ static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol } prefix: php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (php_valid_var_name(Z_STR(final_name))) { if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { zend_throw_error(NULL, "Cannot re-assign $this"); return -1; @@ -2104,7 +2083,7 @@ static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol } zval_ptr_dtor_str(&final_name); } else { - if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + if (!php_valid_var_name(var_name)) { continue; } if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { @@ -2152,7 +2131,7 @@ static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_tab } prefix: php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (php_valid_var_name(Z_STR(final_name))) { if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { zend_throw_error(NULL, "Cannot re-assign $this"); return -1; @@ -2176,7 +2155,7 @@ static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_tab } zval_ptr_dtor_str(&final_name); } else { - if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + if (!php_valid_var_name(var_name)) { continue; } if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { @@ -2211,7 +2190,7 @@ static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_ php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), true); zend_string_release_ex(str, 0); } - if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (php_valid_var_name(Z_STR(final_name))) { if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { zend_throw_error(NULL, "Cannot re-assign $this"); return -1; @@ -2258,7 +2237,7 @@ static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_tabl php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), true); zend_string_release_ex(str, 0); } - if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (php_valid_var_name(Z_STR(final_name))) { if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { zend_throw_error(NULL, "Cannot re-assign $this"); return -1; @@ -2296,10 +2275,10 @@ static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *sym ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) { if (var_name) { - if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name)) + if (!php_valid_var_name(var_name) || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (!php_valid_var_name(Z_STR(final_name))) { zval_ptr_dtor_str(&final_name); continue; } @@ -2310,7 +2289,7 @@ static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *sym zend_string *str = zend_long_to_str(num_key); php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), true); zend_string_release_ex(str, 0); - if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (!php_valid_var_name(Z_STR(final_name))) { zval_ptr_dtor_str(&final_name); continue; } @@ -2351,10 +2330,10 @@ static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_ ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) { if (var_name) { - if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name)) + if (!php_valid_var_name(var_name) || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (!php_valid_var_name(Z_STR(final_name))) { zval_ptr_dtor_str(&final_name); continue; } @@ -2365,7 +2344,7 @@ static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_ zend_string *str = zend_long_to_str(num_key); php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), true); zend_string_release_ex(str, 0); - if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (!php_valid_var_name(Z_STR(final_name))) { zval_ptr_dtor_str(&final_name); continue; } @@ -2410,7 +2389,7 @@ static zend_long php_extract_ref_skip(zend_array *arr, zend_array *symbol_table) if (!var_name) { continue; } - if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + if (!php_valid_var_name(var_name)) { continue; } if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { @@ -2458,7 +2437,7 @@ static zend_long php_extract_skip(zend_array *arr, zend_array *symbol_table) /* if (!var_name) { continue; } - if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + if (!php_valid_var_name(var_name)) { continue; } if (zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { @@ -2519,7 +2498,7 @@ PHP_FUNCTION(extract) } if (prefix) { - if (ZSTR_LEN(prefix) && !php_valid_var_name(ZSTR_VAL(prefix), ZSTR_LEN(prefix))) { + if (ZSTR_LEN(prefix) && !php_valid_var_name(prefix)) { zend_argument_value_error(3, "must be a valid identifier"); RETURN_THROWS(); } From 6cd80f715f30ee1131eb1660faf45ea0f8e23501 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 14 Nov 2025 08:26:44 +0000 Subject: [PATCH 069/252] ext/standard/array.c: assert on conditions which are always false --- ext/standard/array.c | 267 ++++++++++++++++++++----------------------- 1 file changed, 127 insertions(+), 140 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index d8be2fc1f3024..321a688d1d13e 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1943,26 +1943,24 @@ static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *s } php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); if (php_valid_var_name(Z_STR(final_name))) { - if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { - zend_throw_error(NULL, "Cannot re-assign $this"); - return -1; + /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ + ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + + if (Z_ISREF_P(entry)) { + Z_ADDREF_P(entry); } else { - if (Z_ISREF_P(entry)) { - Z_ADDREF_P(entry); - } else { - ZVAL_MAKE_REF_EX(entry, 2); - } - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { - if (Z_TYPE_P(orig_var) == IS_INDIRECT) { - orig_var = Z_INDIRECT_P(orig_var); - } - zval_ptr_dtor(orig_var); - ZVAL_REF(orig_var, Z_REF_P(entry)); - } else { - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + ZVAL_MAKE_REF_EX(entry, 2); + } + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); } - count++; + zval_ptr_dtor(orig_var); + ZVAL_REF(orig_var, Z_REF_P(entry)); + } else { + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); } + count++; } zval_ptr_dtor_str(&final_name); } @@ -1997,26 +1995,24 @@ static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbo } php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); if (php_valid_var_name(Z_STR(final_name))) { - if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { - zend_throw_error(NULL, "Cannot re-assign $this"); - return -1; - } else { - ZVAL_DEREF(entry); - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { - if (Z_TYPE_P(orig_var) == IS_INDIRECT) { - orig_var = Z_INDIRECT_P(orig_var); - } - ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); - if (UNEXPECTED(EG(exception))) { - zend_string_release_ex(Z_STR(final_name), 0); - return -1; - } - } else { - Z_TRY_ADDREF_P(entry); - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ + ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + + ZVAL_DEREF(entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); } - count++; + ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); + if (UNEXPECTED(EG(exception))) { + zend_string_release_ex(Z_STR(final_name), 0); + return -1; + } + } else { + Z_TRY_ADDREF_P(entry); + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); } + count++; } zval_ptr_dtor_str(&final_name); } @@ -2060,26 +2056,24 @@ static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol prefix: php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); if (php_valid_var_name(Z_STR(final_name))) { - if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { - zend_throw_error(NULL, "Cannot re-assign $this"); - return -1; + /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ + ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + + if (Z_ISREF_P(entry)) { + Z_ADDREF_P(entry); } else { - if (Z_ISREF_P(entry)) { - Z_ADDREF_P(entry); - } else { - ZVAL_MAKE_REF_EX(entry, 2); - } - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { - if (Z_TYPE_P(orig_var) == IS_INDIRECT) { - orig_var = Z_INDIRECT_P(orig_var); - } - zval_ptr_dtor(orig_var); - ZVAL_REF(orig_var, Z_REF_P(entry)); - } else { - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + ZVAL_MAKE_REF_EX(entry, 2); + } + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); } - count++; + zval_ptr_dtor(orig_var); + ZVAL_REF(orig_var, Z_REF_P(entry)); + } else { + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); } + count++; } zval_ptr_dtor_str(&final_name); } else { @@ -2132,26 +2126,24 @@ static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_tab prefix: php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); if (php_valid_var_name(Z_STR(final_name))) { - if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { - zend_throw_error(NULL, "Cannot re-assign $this"); - return -1; - } else { - ZVAL_DEREF(entry); - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { - if (Z_TYPE_P(orig_var) == IS_INDIRECT) { - orig_var = Z_INDIRECT_P(orig_var); - } - ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); - if (UNEXPECTED(EG(exception))) { - zend_string_release_ex(Z_STR(final_name), 0); - return -1; - } - } else { - Z_TRY_ADDREF_P(entry); - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ + ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + + ZVAL_DEREF(entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); } - count++; + ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); + if (UNEXPECTED(EG(exception))) { + zend_string_release_ex(Z_STR(final_name), 0); + return -1; + } + } else { + Z_TRY_ADDREF_P(entry); + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); } + count++; } zval_ptr_dtor_str(&final_name); } else { @@ -2191,26 +2183,24 @@ static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_ zend_string_release_ex(str, 0); } if (php_valid_var_name(Z_STR(final_name))) { - if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { - zend_throw_error(NULL, "Cannot re-assign $this"); - return -1; + /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ + ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + + if (Z_ISREF_P(entry)) { + Z_ADDREF_P(entry); } else { - if (Z_ISREF_P(entry)) { - Z_ADDREF_P(entry); - } else { - ZVAL_MAKE_REF_EX(entry, 2); - } - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { - if (Z_TYPE_P(orig_var) == IS_INDIRECT) { - orig_var = Z_INDIRECT_P(orig_var); - } - zval_ptr_dtor(orig_var); - ZVAL_REF(orig_var, Z_REF_P(entry)); - } else { - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + ZVAL_MAKE_REF_EX(entry, 2); + } + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); } - count++; + zval_ptr_dtor(orig_var); + ZVAL_REF(orig_var, Z_REF_P(entry)); + } else { + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); } + count++; } zval_ptr_dtor_str(&final_name); } ZEND_HASH_FOREACH_END(); @@ -2238,26 +2228,24 @@ static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_tabl zend_string_release_ex(str, 0); } if (php_valid_var_name(Z_STR(final_name))) { - if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { - zend_throw_error(NULL, "Cannot re-assign $this"); - return -1; - } else { - ZVAL_DEREF(entry); - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { - if (Z_TYPE_P(orig_var) == IS_INDIRECT) { - orig_var = Z_INDIRECT_P(orig_var); - } - ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); - if (UNEXPECTED(EG(exception))) { - zend_string_release_ex(Z_STR(final_name), 0); - return -1; - } - } else { - Z_TRY_ADDREF_P(entry); - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ + ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + + ZVAL_DEREF(entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); } - count++; + ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); + if (UNEXPECTED(EG(exception))) { + zend_string_release_ex(Z_STR(final_name), 0); + return -1; + } + } else { + Z_TRY_ADDREF_P(entry); + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); } + count++; } zval_ptr_dtor_str(&final_name); } ZEND_HASH_FOREACH_END(); @@ -2294,26 +2282,25 @@ static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *sym continue; } } - if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { - zend_throw_error(NULL, "Cannot re-assign $this"); - return -1; + + /* We previously checked if the var name is "this" to prefix it */ + ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + if (Z_ISREF_P(entry)) { + Z_ADDREF_P(entry); } else { - if (Z_ISREF_P(entry)) { - Z_ADDREF_P(entry); - } else { - ZVAL_MAKE_REF_EX(entry, 2); - } - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { - if (Z_TYPE_P(orig_var) == IS_INDIRECT) { - orig_var = Z_INDIRECT_P(orig_var); - } - zval_ptr_dtor(orig_var); - ZVAL_REF(orig_var, Z_REF_P(entry)); - } else { - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + ZVAL_MAKE_REF_EX(entry, 2); + } + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); } - count++; + zval_ptr_dtor(orig_var); + ZVAL_REF(orig_var, Z_REF_P(entry)); + } else { + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); } + count++; + zval_ptr_dtor_str(&final_name); } ZEND_HASH_FOREACH_END(); @@ -2349,26 +2336,26 @@ static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_ continue; } } - if (zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))) { - zend_throw_error(NULL, "Cannot re-assign $this"); - return -1; - } else { - ZVAL_DEREF(entry); - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { - if (Z_TYPE_P(orig_var) == IS_INDIRECT) { - orig_var = Z_INDIRECT_P(orig_var); - } - ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); - if (UNEXPECTED(EG(exception))) { - zend_string_release_ex(Z_STR(final_name), 0); - return -1; - } - } else { - Z_TRY_ADDREF_P(entry); - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + + /* We previously checked if the var name is "this" to prefix it */ + ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + + ZVAL_DEREF(entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); } - count++; + ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); + if (UNEXPECTED(EG(exception))) { + zend_string_release_ex(Z_STR(final_name), 0); + return -1; + } + } else { + Z_TRY_ADDREF_P(entry); + zend_hash_add_new(symbol_table, Z_STR(final_name), entry); } + count++; + zval_ptr_dtor_str(&final_name); } ZEND_HASH_FOREACH_END(); From 8587bb2f93bf5378854542969a78cacf31610f66 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 14 Nov 2025 08:21:02 +0000 Subject: [PATCH 070/252] ext/standard/array.c: refactor php_prefix_varname() --- ext/standard/array.c | 180 ++++++++++++++++----------------- ext/standard/basic_functions.h | 1 - 2 files changed, 87 insertions(+), 94 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 321a688d1d13e..3ccb5d25a9db6 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1711,20 +1711,10 @@ static zend_always_inline bool php_valid_var_name(const zend_string *var_name) / } /* }}} */ -PHPAPI int php_prefix_varname(zval *result, zend_string *prefix, const char *var_name, size_t var_name_len, bool add_underscore) /* {{{ */ +static zend_string* php_prefix_varname(const zend_string *prefix, const zend_string *var_name) { - ZVAL_NEW_STR(result, zend_string_alloc(ZSTR_LEN(prefix) + (add_underscore ? 1 : 0) + var_name_len, 0)); - memcpy(Z_STRVAL_P(result), ZSTR_VAL(prefix), ZSTR_LEN(prefix)); - - if (add_underscore) { - Z_STRVAL_P(result)[ZSTR_LEN(prefix)] = '_'; - } - - memcpy(Z_STRVAL_P(result) + ZSTR_LEN(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1); - - return SUCCESS; + return zend_string_concat3(ZSTR_VAL(prefix), ZSTR_LEN(prefix), ZEND_STRL("_"), ZSTR_VAL(var_name), ZSTR_LEN(var_name)); } -/* }}} */ static zend_long php_extract_ref_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */ { @@ -1917,7 +1907,7 @@ static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *s { zend_long count = 0; zend_string *var_name; - zval *entry, *orig_var, final_name; + zval *entry, *orig_var; if (HT_IS_PACKED(arr)) { return 0; @@ -1941,28 +1931,28 @@ static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *s continue; } } - php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (php_valid_var_name(Z_STR(final_name))) { + zend_string *final_name = php_prefix_varname(prefix, var_name); + if (php_valid_var_name(final_name)) { /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ - ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS))); if (Z_ISREF_P(entry)) { Z_ADDREF_P(entry); } else { ZVAL_MAKE_REF_EX(entry, 2); } - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) { if (Z_TYPE_P(orig_var) == IS_INDIRECT) { orig_var = Z_INDIRECT_P(orig_var); } zval_ptr_dtor(orig_var); ZVAL_REF(orig_var, Z_REF_P(entry)); } else { - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + zend_hash_add_new(symbol_table, final_name, entry); } count++; } - zval_ptr_dtor_str(&final_name); + zend_string_release_ex(final_name, false); } } ZEND_HASH_FOREACH_END(); @@ -1974,7 +1964,7 @@ static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbo { zend_long count = 0; zend_string *var_name; - zval *entry, *orig_var, final_name; + zval *entry, *orig_var; if (HT_IS_PACKED(arr)) { return 0; @@ -1993,28 +1983,28 @@ static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbo continue; } } - php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (php_valid_var_name(Z_STR(final_name))) { + zend_string *final_name = php_prefix_varname(prefix, var_name); + if (php_valid_var_name(final_name)) { /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ - ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS))); ZVAL_DEREF(entry); - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) { if (Z_TYPE_P(orig_var) == IS_INDIRECT) { orig_var = Z_INDIRECT_P(orig_var); } ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); if (UNEXPECTED(EG(exception))) { - zend_string_release_ex(Z_STR(final_name), 0); + zend_string_release_ex(final_name, 0); return -1; } } else { Z_TRY_ADDREF_P(entry); - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + zend_hash_add_new(symbol_table, final_name, entry); } count++; } - zval_ptr_dtor_str(&final_name); + zend_string_release_ex(final_name, false); } } ZEND_HASH_FOREACH_END(); @@ -2026,7 +2016,7 @@ static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol { zend_long count = 0; zend_string *var_name; - zval *entry, *orig_var, final_name; + zval *entry, *orig_var; if (HT_IS_PACKED(arr)) { return 0; @@ -2053,29 +2043,29 @@ static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol continue; } } -prefix: - php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (php_valid_var_name(Z_STR(final_name))) { +prefix:; + zend_string *final_name = php_prefix_varname(prefix, var_name); + if (php_valid_var_name(final_name)) { /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ - ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS))); if (Z_ISREF_P(entry)) { Z_ADDREF_P(entry); } else { ZVAL_MAKE_REF_EX(entry, 2); } - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) { if (Z_TYPE_P(orig_var) == IS_INDIRECT) { orig_var = Z_INDIRECT_P(orig_var); } zval_ptr_dtor(orig_var); ZVAL_REF(orig_var, Z_REF_P(entry)); } else { - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + zend_hash_add_new(symbol_table, final_name, entry); } count++; } - zval_ptr_dtor_str(&final_name); + zend_string_release_ex(final_name, false); } else { if (!php_valid_var_name(var_name)) { continue; @@ -2101,7 +2091,7 @@ static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_tab { zend_long count = 0; zend_string *var_name; - zval *entry, *orig_var, final_name; + zval *entry, *orig_var; if (HT_IS_PACKED(arr)) { return 0; @@ -2123,29 +2113,29 @@ static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_tab continue; } } -prefix: - php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (php_valid_var_name(Z_STR(final_name))) { +prefix:; + zend_string *final_name = php_prefix_varname(prefix, var_name); + if (php_valid_var_name(final_name)) { /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ - ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS))); ZVAL_DEREF(entry); - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) { if (Z_TYPE_P(orig_var) == IS_INDIRECT) { orig_var = Z_INDIRECT_P(orig_var); } ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); if (UNEXPECTED(EG(exception))) { - zend_string_release_ex(Z_STR(final_name), 0); + zend_string_release_ex(final_name, false); return -1; } } else { Z_TRY_ADDREF_P(entry); - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + zend_hash_add_new(symbol_table, final_name, entry); } count++; } - zval_ptr_dtor_str(&final_name); + zend_string_release_ex(final_name, false); } else { if (!php_valid_var_name(var_name)) { continue; @@ -2169,40 +2159,41 @@ static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_ zend_long count = 0; zend_string *var_name; zend_ulong num_key; - zval *entry, *orig_var, final_name; + zval *entry, *orig_var; ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) { + zend_string *final_name; if (var_name) { if (ZSTR_LEN(var_name) == 0) { continue; } - php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); + final_name = php_prefix_varname(prefix, var_name); } else { zend_string *str = zend_long_to_str(num_key); - php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), true); - zend_string_release_ex(str, 0); + final_name = php_prefix_varname(prefix, str); + zend_string_release_ex(str, false); } - if (php_valid_var_name(Z_STR(final_name))) { + if (php_valid_var_name(final_name)) { /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ - ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS))); if (Z_ISREF_P(entry)) { Z_ADDREF_P(entry); } else { ZVAL_MAKE_REF_EX(entry, 2); } - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) { if (Z_TYPE_P(orig_var) == IS_INDIRECT) { orig_var = Z_INDIRECT_P(orig_var); } zval_ptr_dtor(orig_var); ZVAL_REF(orig_var, Z_REF_P(entry)); } else { - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + zend_hash_add_new(symbol_table, final_name, entry); } count++; } - zval_ptr_dtor_str(&final_name); + zend_string_release_ex(final_name, false); } ZEND_HASH_FOREACH_END(); return count; @@ -2214,40 +2205,41 @@ static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_tabl zend_long count = 0; zend_string *var_name; zend_ulong num_key; - zval *entry, *orig_var, final_name; + zval *entry, *orig_var; ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) { + zend_string *final_name; if (var_name) { if (ZSTR_LEN(var_name) == 0) { continue; } - php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); + final_name = php_prefix_varname(prefix, var_name); } else { zend_string *str = zend_long_to_str(num_key); - php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), true); - zend_string_release_ex(str, 0); + final_name = php_prefix_varname(prefix, str); + zend_string_release_ex(str, false); } - if (php_valid_var_name(Z_STR(final_name))) { + if (php_valid_var_name(final_name)) { /* Prefixed varname cannot be equal to "this" due to underscore between prefix and name */ - ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS))); ZVAL_DEREF(entry); - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) { if (Z_TYPE_P(orig_var) == IS_INDIRECT) { orig_var = Z_INDIRECT_P(orig_var); } ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); if (UNEXPECTED(EG(exception))) { - zend_string_release_ex(Z_STR(final_name), 0); + zend_string_release_ex(final_name, false); return -1; } } else { Z_TRY_ADDREF_P(entry); - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + zend_hash_add_new(symbol_table, final_name, entry); } count++; } - zval_ptr_dtor_str(&final_name); + zend_string_release_ex(final_name, false); } ZEND_HASH_FOREACH_END(); return count; @@ -2259,49 +2251,51 @@ static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *sym zend_long count = 0; zend_string *var_name; zend_ulong num_key; - zval *entry, *orig_var, final_name; + zval *entry, *orig_var; ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) { + zend_string *final_name; if (var_name) { - if (!php_valid_var_name(var_name) - || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { - php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (!php_valid_var_name(Z_STR(final_name))) { - zval_ptr_dtor_str(&final_name); + if (!php_valid_var_name(var_name) || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { + final_name = php_prefix_varname(prefix, var_name); + + if (!php_valid_var_name(final_name)) { + zend_string_release_ex(final_name, false); continue; } } else { - ZVAL_STR_COPY(&final_name, var_name); + final_name = zend_string_copy(var_name); } } else { zend_string *str = zend_long_to_str(num_key); - php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), true); - zend_string_release_ex(str, 0); - if (!php_valid_var_name(Z_STR(final_name))) { - zval_ptr_dtor_str(&final_name); + final_name = php_prefix_varname(prefix, str); + zend_string_release_ex(str, false); + if (!php_valid_var_name(final_name)) { + zend_string_release_ex(final_name, false); continue; } } /* We previously checked if the var name is "this" to prefix it */ - ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS))); + if (Z_ISREF_P(entry)) { Z_ADDREF_P(entry); } else { ZVAL_MAKE_REF_EX(entry, 2); } - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) { if (Z_TYPE_P(orig_var) == IS_INDIRECT) { orig_var = Z_INDIRECT_P(orig_var); } zval_ptr_dtor(orig_var); ZVAL_REF(orig_var, Z_REF_P(entry)); } else { - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + zend_hash_add_new(symbol_table, final_name, entry); } count++; - zval_ptr_dtor_str(&final_name); + zend_string_release_ex(final_name, false); } ZEND_HASH_FOREACH_END(); return count; @@ -2313,50 +2307,50 @@ static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_ zend_long count = 0; zend_string *var_name; zend_ulong num_key; - zval *entry, *orig_var, final_name; + zval *entry, *orig_var; ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, var_name, entry) { + zend_string *final_name; if (var_name) { - if (!php_valid_var_name(var_name) - || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { - php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), true); - if (!php_valid_var_name(Z_STR(final_name))) { - zval_ptr_dtor_str(&final_name); + if (!php_valid_var_name(var_name) || zend_string_equals(var_name, ZSTR_KNOWN(ZEND_STR_THIS))) { + final_name = php_prefix_varname(prefix, var_name); + if (!php_valid_var_name(final_name)) { + zend_string_release_ex(final_name, false); continue; } } else { - ZVAL_STR_COPY(&final_name, var_name); + final_name = zend_string_copy(var_name); } } else { zend_string *str = zend_long_to_str(num_key); - php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), true); + final_name = php_prefix_varname(prefix, str); zend_string_release_ex(str, 0); - if (!php_valid_var_name(Z_STR(final_name))) { - zval_ptr_dtor_str(&final_name); + if (!php_valid_var_name(final_name)) { + zend_string_release_ex(final_name, false); continue; } } /* We previously checked if the var name is "this" to prefix it */ - ZEND_ASSERT(!zend_string_equals(Z_STR(final_name), ZSTR_KNOWN(ZEND_STR_THIS))); + ZEND_ASSERT(!zend_string_equals(final_name, ZSTR_KNOWN(ZEND_STR_THIS))); ZVAL_DEREF(entry); - if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if ((orig_var = zend_hash_find(symbol_table, final_name)) != NULL) { if (Z_TYPE_P(orig_var) == IS_INDIRECT) { orig_var = Z_INDIRECT_P(orig_var); } ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0); if (UNEXPECTED(EG(exception))) { - zend_string_release_ex(Z_STR(final_name), 0); + zend_string_release_ex(final_name, false); return -1; } } else { Z_TRY_ADDREF_P(entry); - zend_hash_add_new(symbol_table, Z_STR(final_name), entry); + zend_hash_add_new(symbol_table, final_name, entry); } count++; - zval_ptr_dtor_str(&final_name); + zend_string_release_ex(final_name, false); } ZEND_HASH_FOREACH_END(); return count; diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h index bad6fcf4e645e..8c8c6ba58dfd6 100644 --- a/ext/standard/basic_functions.h +++ b/ext/standard/basic_functions.h @@ -49,7 +49,6 @@ PHP_RSHUTDOWN_FUNCTION(browscap); /* Left for BC (not binary safe!) */ PHPAPI int _php_error_log(int opt_err, const char *message, const char *opt, const char *headers); PHPAPI int _php_error_log_ex(int opt_err, const char *message, size_t message_len, const char *opt, const char *headers); -PHPAPI int php_prefix_varname(zval *result, zend_string *prefix, const char *var_name, size_t var_name_len, bool add_underscore); typedef struct _php_basic_globals { HashTable *user_shutdown_function_names; From 095fd26c95baa4025ba811762c1920709dcf93eb Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 14 Nov 2025 08:48:07 +0000 Subject: [PATCH 071/252] ext/standard/array.c: add const qualifiers for extract() related functions --- ext/standard/array.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 3ccb5d25a9db6..f1b25387db060 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1716,7 +1716,7 @@ static zend_string* php_prefix_varname(const zend_string *prefix, const zend_str return zend_string_concat3(ZSTR_VAL(prefix), ZSTR_LEN(prefix), ZEND_STRL("_"), ZSTR_VAL(var_name), ZSTR_LEN(var_name)); } -static zend_long php_extract_ref_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */ +static zend_long php_extract_ref_if_exists(const zend_array *arr, const zend_array *symbol_table) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -1762,7 +1762,7 @@ static zend_long php_extract_ref_if_exists(zend_array *arr, zend_array *symbol_t } /* }}} */ -static zend_long php_extract_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */ +static zend_long php_extract_if_exists(const zend_array *arr, const zend_array *symbol_table) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -1806,7 +1806,7 @@ static zend_long php_extract_if_exists(zend_array *arr, zend_array *symbol_table } /* }}} */ -static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */ +static zend_long php_extract_ref_overwrite(const zend_array *arr, zend_array *symbol_table) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -1858,7 +1858,7 @@ static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_t } /* }}} */ -static zend_long php_extract_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */ +static zend_long php_extract_overwrite(const zend_array *arr, zend_array *symbol_table) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -1903,7 +1903,7 @@ static zend_long php_extract_overwrite(zend_array *arr, zend_array *symbol_table } /* }}} */ -static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */ +static zend_long php_extract_ref_prefix_if_exists(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -1960,7 +1960,7 @@ static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *s } /* }}} */ -static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */ +static zend_long php_extract_prefix_if_exists(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -2012,7 +2012,7 @@ static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbo } /* }}} */ -static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */ +static zend_long php_extract_ref_prefix_same(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -2087,7 +2087,7 @@ prefix:; } /* }}} */ -static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */ +static zend_long php_extract_prefix_same(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -2154,7 +2154,7 @@ prefix:; } /* }}} */ -static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */ +static zend_long php_extract_ref_prefix_all(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -2200,7 +2200,7 @@ static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_ } /* }}} */ -static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */ +static zend_long php_extract_prefix_all(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -2246,7 +2246,7 @@ static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_tabl } /* }}} */ -static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */ +static zend_long php_extract_ref_prefix_invalid(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -2302,7 +2302,7 @@ static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *sym } /* }}} */ -static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */ +static zend_long php_extract_prefix_invalid(const zend_array *arr, zend_array *symbol_table, const zend_string *prefix) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -2357,7 +2357,7 @@ static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_ } /* }}} */ -static zend_long php_extract_ref_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */ +static zend_long php_extract_ref_skip(const zend_array *arr, zend_array *symbol_table) /* {{{ */ { zend_long count = 0; zend_string *var_name; @@ -2405,7 +2405,7 @@ static zend_long php_extract_ref_skip(zend_array *arr, zend_array *symbol_table) } /* }}} */ -static zend_long php_extract_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */ +static zend_long php_extract_skip(const zend_array *arr, zend_array *symbol_table) /* {{{ */ { zend_long count = 0; zend_string *var_name; From d026e2bca1d544ca2a3698087e5098199ada0bac Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sun, 16 Nov 2025 22:14:15 +0000 Subject: [PATCH 072/252] ext/standard/array.c: add tests when extracting negative keys --- .../tests/array/extract_negative_keys.phpt | 31 +++++++++++++++++++ .../tests/array/extract_negative_keys2.phpt | 11 +++++++ 2 files changed, 42 insertions(+) create mode 100644 ext/standard/tests/array/extract_negative_keys.phpt create mode 100644 ext/standard/tests/array/extract_negative_keys2.phpt diff --git a/ext/standard/tests/array/extract_negative_keys.phpt b/ext/standard/tests/array/extract_negative_keys.phpt new file mode 100644 index 0000000000000..55fce2a656570 --- /dev/null +++ b/ext/standard/tests/array/extract_negative_keys.phpt @@ -0,0 +1,31 @@ +--TEST-- +extract() with negative keys +--FILE-- + 'hello', 'world', 2 => 'positive', 'check' => 'extracted']; + +function foo(array $a) { + extract($a, EXTR_PREFIX_ALL, 'prefix'); + var_dump(get_defined_vars()); +} + +foo($a); +?> +--EXPECT-- +array(3) { + ["a"]=> + array(4) { + [-5]=> + string(5) "hello" + [-4]=> + string(5) "world" + [2]=> + string(8) "positive" + ["check"]=> + string(9) "extracted" + } + ["prefix_2"]=> + string(8) "positive" + ["prefix_check"]=> + string(9) "extracted" +} diff --git a/ext/standard/tests/array/extract_negative_keys2.phpt b/ext/standard/tests/array/extract_negative_keys2.phpt new file mode 100644 index 0000000000000..dd2a1fa7a18a4 --- /dev/null +++ b/ext/standard/tests/array/extract_negative_keys2.phpt @@ -0,0 +1,11 @@ +--TEST-- +extract() with negative keys +--FILE-- + "foo"]; +var_dump(extract($arr, EXTR_PREFIX_ALL | EXTR_REFS, "prefix")); + +?> +--EXPECT-- +int(0) From d40ae97f52b0c026e37072dbd896d67e949b91d3 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 19 Nov 2025 20:42:31 +0000 Subject: [PATCH 073/252] Zend language scanner: minor refactorings (#20480) * Use uint32_t type * Remove some useless size_t casts * Explain why we include zend_globals.h --- Zend/zend_language_scanner.h | 3 ++- Zend/zend_language_scanner.l | 15 ++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Zend/zend_language_scanner.h b/Zend/zend_language_scanner.h index 612c845479272..c494564ba2349 100644 --- a/Zend/zend_language_scanner.h +++ b/Zend/zend_language_scanner.h @@ -20,6 +20,7 @@ #ifndef ZEND_SCANNER_H #define ZEND_SCANNER_H +/* The zend_php_scanner_event enum is declared in zend_globals and we don't want everything to include zend_language_scanner.h */ #include "zend_globals.h" typedef struct _zend_lex_state { @@ -71,7 +72,7 @@ typedef struct _zend_heredoc_label { /* Track locations of unclosed {, [, (, etc. for better syntax error reporting */ typedef struct _zend_nest_location { char text; - int lineno; + uint32_t lineno; } zend_nest_location; BEGIN_EXTERN_C() diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 3ecb2f8d0ee45..1e26ddbd99199 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -30,6 +30,7 @@ #include "zend_language_scanner_defs.h" #include +#include #include "zend.h" #ifdef ZEND_WIN32 # include @@ -600,7 +601,7 @@ static zend_op_array *zend_compile(int type) CG(ast_arena) = zend_arena_create(1024 * 32); if (!zendparse()) { - int last_lineno = CG(zend_lineno); + uint32_t last_lineno = CG(zend_lineno); zend_file_context original_file_context; zend_oparray_context original_oparray_context; zend_op_array *original_active_op_array = CG(active_op_array); @@ -1140,7 +1141,7 @@ skip_escape_conversion: unsigned char *str; // TODO: avoid realocation ??? s = Z_STRVAL_P(zendlval); - SCNG(output_filter)(&str, &sz, (unsigned char *)s, (size_t)Z_STRLEN_P(zendlval)); + SCNG(output_filter)(&str, &sz, (unsigned char *)s, Z_STRLEN_P(zendlval)); zval_ptr_dtor(zendlval); ZVAL_STRINGL(zendlval, (char *) str, sz); efree(str); @@ -1172,7 +1173,7 @@ static bool strip_multiline_string_indentation( const char *str = Z_STRVAL_P(zendlval), *end = str + Z_STRLEN_P(zendlval); char *copy = Z_STRVAL_P(zendlval); - int newline_count = 0; + uint32_t newline_count = 0; size_t newline_len; const char *nl; @@ -1253,7 +1254,7 @@ static void copy_heredoc_label_stack(void *void_heredoc_label) } /* Check that { }, [ ], ( ) are nested correctly */ -static void report_bad_nesting(char opening, int opening_lineno, char closing) +static void report_bad_nesting(char opening, uint32_t opening_lineno, char closing) { char buf[256]; size_t used = 0; @@ -1361,7 +1362,7 @@ int ZEND_FASTCALL lex_scan(zval *zendlval, zend_parser_stack_elem *elem) { int token; int offset; -int start_line = CG(zend_lineno); +uint32_t start_line = CG(zend_lineno); ZVAL_UNDEF(zendlval); restart: @@ -2499,7 +2500,7 @@ inline_char_handler: if (YYCURSOR < YYLIMIT) { YYCURSOR++; } else { - zend_throw_exception_ex(zend_ce_parse_error, 0, "Unterminated comment starting line %d", CG(zend_lineno)); + zend_throw_exception_ex(zend_ce_parse_error, 0, "Unterminated comment starting line %" PRIu32, CG(zend_lineno)); if (PARSER_MODE()) { RETURN_TOKEN(T_ERROR); } @@ -2616,7 +2617,7 @@ skip_escape_conversion: zend_string *new_str; s = Z_STRVAL_P(zendlval); // TODO: avoid reallocation ??? - SCNG(output_filter)((unsigned char **)&str, &sz, (unsigned char *)s, (size_t)Z_STRLEN_P(zendlval)); + SCNG(output_filter)((unsigned char **)&str, &sz, (unsigned char *)s, Z_STRLEN_P(zendlval)); new_str = zend_string_init(str, sz, 0); if (str != s) { efree(str); From d9d55f011d0d1ecb0a1a5d7e3a19a9dd181f7361 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Thu, 20 Nov 2025 15:15:19 +0100 Subject: [PATCH 074/252] ext/gd: Enable HAVE_GD_GET_INTERPOLATION (#20535) Bundled libgd has gdImageGetInterpolationMethod() function. It is available as of libgd 2.1.1. --- ext/gd/config.m4 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/gd/config.m4 b/ext/gd/config.m4 index 0d024c9ea1cf3..a997238b7f75c 100644 --- a/ext/gd/config.m4 +++ b/ext/gd/config.m4 @@ -265,6 +265,10 @@ if test "$PHP_GD" != "no"; then AC_DEFINE([HAVE_GD_BUNDLED], [1], [Define to 1 if gd extension uses GD library bundled in PHP.]) + AC_DEFINE([HAVE_GD_GET_INTERPOLATION], [1], + [Define to 1 if GD library has the 'gdImageGetInterpolationMethod' + function.]) + dnl Various checks for GD features PHP_SETUP_ZLIB([GD_SHARED_LIBADD]) PHP_GD_PNG From a1912e3cdd9854ae5b99c239b9e32a5a224d1a34 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 16 Nov 2025 01:24:56 +0100 Subject: [PATCH 075/252] Fix GH-20491: SLES15 compile error with mbstring oniguruma The issue is specific to SLES15. Arguably this should be reported to them as it seems to me they meddled with the oniguruma source code. The definition in oniguruma.h on that platform looks like this (same as upstream): ```c ONIG_EXTERN int onig_error_code_to_str PV_((OnigUChar* s, int err_code, ...)); ``` Where `PV_` is defined as (differs): ```c #ifndef PV_ #ifdef HAVE_STDARG_PROTOTYPES # define PV_(args) args #else # define PV_(args) () #endif #endif ``` So that means that `HAVE_STDARG_PROTOTYPES` is unset. This can be set if we define `HAVE_STDARG_H`, which we can do because PHP requires at least C99 in which the header is always available. We could also use an autoconf check, but this isn't really necessary as it will always succeed. --- NEWS | 4 ++++ ext/mbstring/php_onig_compat.h | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/NEWS b/NEWS index 516f6abd462a2..48270575cb6df 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,10 @@ PHP NEWS . Fix some deprecations on newer libxml versions regarding input buffer/parser handling. (ndossche) +- MbString: + . Fixed bug GH-20491 (SLES15 compile error with mbstring oniguruma). + (ndossche) + - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) diff --git a/ext/mbstring/php_onig_compat.h b/ext/mbstring/php_onig_compat.h index c97ba0c5cb674..5a1fa8eeaaf1b 100644 --- a/ext/mbstring/php_onig_compat.h +++ b/ext/mbstring/php_onig_compat.h @@ -5,4 +5,10 @@ #define regex_t php_mb_regex_t #define re_registers php_mb_re_registers +/* Required for some distros that conditionally override PV_. + * As we're in C99 this header is always available. */ +#ifndef HAVE_STDARG_H +# define HAVE_STDARG_H +#endif + #endif /* _PHP_ONIG_COMPAT_H */ From 159ef1401cfd765f615cfe9fceb5da55185d965a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 15 Nov 2025 15:04:51 +0100 Subject: [PATCH 076/252] Fix GH-20492: mbstring compile warning due to non-strings This is a partial backport of ea69276f, but without changing public headers as that's not allowed at this point. Closes GH-20494. --- NEWS | 2 ++ ext/mbstring/libmbfl/filters/mbfilter_cjk.c | 12 +++++++++--- ext/mbstring/libmbfl/filters/mbfilter_utf8_mobile.c | 8 +++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 48270575cb6df..2ed91237253b5 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,8 @@ PHP NEWS - MbString: . Fixed bug GH-20491 (SLES15 compile error with mbstring oniguruma). (ndossche) + . Fixed bug GH-20492 (mbstring compile warning due to non-strings). + (ndossche) - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string diff --git a/ext/mbstring/libmbfl/filters/mbfilter_cjk.c b/ext/mbstring/libmbfl/filters/mbfilter_cjk.c index 13635764326f3..e4207edec1f4f 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_cjk.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_cjk.c @@ -21,15 +21,21 @@ * This macro converts uppercase ASCII values to Regional Indicator codepoints */ #define NFLAGS(c) (0x1F1A5+((unsigned int)(c))) -static const char nflags_s[10][2] = {"CN", "DE", "ES", "FR", "GB", "IT", "JP", "KR", "RU", "US"}; +#if __has_attribute(nonstring) && defined(__GNUC__) && ((!defined(__clang__) && __GNUC__ >= 15) || (defined(__clang_major__) && __clang_major__ >= 20)) +# define ZEND_NONSTRING __attribute__((nonstring)) +#else +# define ZEND_NONSTRING +#endif + +static const char nflags_s[10][2] ZEND_NONSTRING = {"CN", "DE", "ES", "FR", "GB", "IT", "JP", "KR", "RU", "US"}; static const int nflags_code_kddi[10] = { 0x2549, 0x2546, 0x24C0, 0x2545, 0x2548, 0x2547, 0x2750, 0x254A, 0x24C1, 0x27F7 }; static const int nflags_code_sb[10] = { 0x2B0A, 0x2B05, 0x2B08, 0x2B04, 0x2B07, 0x2B06, 0x2B02, 0x2B0B, 0x2B09, 0x2B03 }; #define EMIT_KEYPAD_EMOJI(c) do { *snd = (c); return 0x20E3; } while(0) #define EMIT_FLAG_EMOJI(country) do { *snd = NFLAGS((country)[0]); return NFLAGS((country)[1]); } while(0) -static const char nflags_kddi[6][2] = {"FR", "DE", "IT", "GB", "CN", "KR"}; -static const char nflags_sb[10][2] = {"JP", "US", "FR", "DE", "IT", "GB", "ES", "RU", "CN", "KR"}; +static const char nflags_kddi[6][2] ZEND_NONSTRING = {"FR", "DE", "IT", "GB", "CN", "KR"}; +static const char nflags_sb[10][2] ZEND_NONSTRING = {"JP", "US", "FR", "DE", "IT", "GB", "ES", "RU", "CN", "KR"}; /* number -> (ku*94)+ten value for telephone keypad character */ #define DOCOMO_KEYPAD(n) ((n) == 0 ? 0x296F : (0x2965 + (n))) diff --git a/ext/mbstring/libmbfl/filters/mbfilter_utf8_mobile.c b/ext/mbstring/libmbfl/filters/mbfilter_utf8_mobile.c index dd253cfe689fc..2fa4b3a06c01e 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_utf8_mobile.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_utf8_mobile.c @@ -397,7 +397,13 @@ int mbfl_filt_conv_wchar_utf8_mobile(int c, mbfl_convert_filter *filter) * This macro converts uppercase ASCII values to Regional Indicator codepoints */ #define NFLAGS(c) (0x1F1A5+(int)(c)) -static const char nflags_s[10][2] = {"CN","DE","ES","FR","GB","IT","JP","KR","RU","US"}; +#if __has_attribute(nonstring) && defined(__GNUC__) && ((!defined(__clang__) && __GNUC__ >= 15) || (defined(__clang_major__) && __clang_major__ >= 20)) +# define ZEND_NONSTRING __attribute__((nonstring)) +#else +# define ZEND_NONSTRING +#endif + +static const char nflags_s[10][2] ZEND_NONSTRING = {"CN","DE","ES","FR","GB","IT","JP","KR","RU","US"}; static const int nflags_code_kddi[10] = { 0x2549, 0x2546, 0x24C0, 0x2545, 0x2548, 0x2547, 0x2750, 0x254A, 0x24C1, 0x27F7 }; static const int nflags_code_sb[10] = { 0x2B0A, 0x2B05, 0x2B08, 0x2B04, 0x2B07, 0x2B06, 0x2B02, 0x2B0B, 0x2B09, 0x2B03 }; From 4e2bd0c5b78072b46b8da01297aa0a61ecd89ab8 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Thu, 20 Nov 2025 19:22:15 +0000 Subject: [PATCH 077/252] ext/soap: SoapClient::__setCookie() to deal with name as digits. (#20526) --- ext/soap/soap.c | 4 ++-- ext/soap/tests/soap_set_cookie.phpt | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 ext/soap/tests/soap_set_cookie.phpt diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 23e74606e996a..471b2d622d98a 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -2835,12 +2835,12 @@ PHP_METHOD(SoapClient, __setCookie) zval *cookies = Z_CLIENT_COOKIES_P(ZEND_THIS); SEPARATE_ARRAY(cookies); if (val == NULL) { - zend_hash_del(Z_ARRVAL_P(cookies), name); + zend_symtable_del(Z_ARRVAL_P(cookies), name); } else { zval zcookie; array_init(&zcookie); add_index_str(&zcookie, 0, zend_string_copy(val)); - zend_hash_update(Z_ARRVAL_P(cookies), name, &zcookie); + zend_symtable_update(Z_ARRVAL_P(cookies), name, &zcookie); } } /* }}} */ diff --git a/ext/soap/tests/soap_set_cookie.phpt b/ext/soap/tests/soap_set_cookie.phpt new file mode 100644 index 0000000000000..a23aa18bb4b75 --- /dev/null +++ b/ext/soap/tests/soap_set_cookie.phpt @@ -0,0 +1,22 @@ +--TEST-- +SoapClient::__setCookie with numeric keys +--EXTENSIONS-- +soap +--FILE-- + 'mo:http://www.w3.org/', 'location' => 'http://example.com')); +$client->__setCookie("123", "456"); +var_dump($client->__getCookies()); +$client->__setCookie("123", NULL); +var_dump($client->__getCookies()); +?> +--EXPECT-- +array(1) { + [123]=> + array(1) { + [0]=> + string(3) "456" + } +} +array(0) { +} From 586eba94d8a23934200656c2646f2c887a2906a6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 20 Nov 2025 20:24:31 +0000 Subject: [PATCH 078/252] [skip ci] Forgotten NEWS update --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 0b1b0d9da64e1..604945a77c701 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,10 @@ PHP NEWS . Fixed bug GH-20217 (ReflectionClass::isIterable() incorrectly returns true for classes with property hooks). (alexandre-daubois) +- Soap: + . Soap::__setCookie() when cookie name is a digit is now not stored and represented + as a string anymore but a int. (David Carlier) + - Standard: . Fixed bug GH-19926 (reset internal pointer earlier while splicing array while COW violation flag is still set). (alexandre-daubois) From ca7f55600232fdcde8da1cc27167006dca1cf967 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:54:38 -0800 Subject: [PATCH 079/252] xml: Use strlen() (#20544) --- ext/xml/xml.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/ext/xml/xml.c b/ext/xml/xml.c index 89c0dcd0dc4a5..7a9329bcc8041 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -131,7 +131,7 @@ inline static char xml_decode_iso_8859_1(unsigned short); inline static unsigned short xml_encode_us_ascii(unsigned char); inline static char xml_decode_us_ascii(unsigned short); static void xml_xmlchar_zval(const XML_Char *, int, const XML_Char *, zval *); -static int xml_xmlcharlen(const XML_Char *); +static size_t xml_xmlcharlen(const XML_Char *); static void xml_add_to_info(xml_parser *parser, zend_string *name); inline static zend_string *xml_decode_tag(xml_parser *parser, const XML_Char *tag); @@ -536,15 +536,9 @@ static zend_string *xml_utf8_decode(const XML_Char *s, size_t len, const XML_Cha /* }}} */ /* {{{ xml_xmlcharlen() */ -static int xml_xmlcharlen(const XML_Char *s) +static size_t xml_xmlcharlen(const XML_Char *s) { - int len = 0; - - while (*s) { - len++; - s++; - } - return len; + return strlen((const char *) s); } /* }}} */ From 9d71c1e0b60cd152a47528dbe514efc443fce920 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 20 Nov 2025 02:58:45 +0100 Subject: [PATCH 080/252] Fix GH-20528: Regression breaks mysql connexion using an IPv6 address enclosed in square brackets --- ext/mysqli/tests/mysqli_connect_port.phpt | 31 +++++++++++++++++++++++ ext/mysqlnd/mysqlnd_connection.c | 17 ++++++++++--- 2 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 ext/mysqli/tests/mysqli_connect_port.phpt diff --git a/ext/mysqli/tests/mysqli_connect_port.phpt b/ext/mysqli/tests/mysqli_connect_port.phpt new file mode 100644 index 0000000000000..cb7fd1d8d1628 --- /dev/null +++ b/ext/mysqli/tests/mysqli_connect_port.phpt @@ -0,0 +1,31 @@ +--TEST-- +mysqli_connect() with port in host +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--FILE-- + +Done +--EXPECTF-- +Done diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index d8e7304e9665f..8268034e8b798 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -553,13 +553,24 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_ port = 3306; } - /* ipv6 addresses are in the format [address]:port */ if (hostname.s[0] != '[' && mysqlnd_fast_is_ipv6_address(hostname.s)) { + /* IPv6 without square brackets so without port */ transport.l = mnd_sprintf(&transport.s, 0, "tcp://[%s]:%u", hostname.s, port); } else { - /* Not ipv6, but could already contain a port number, in which case we should not add an extra port. + char *p; + + /* IPv6 addresses are in the format [address]:port */ + if (hostname.s[0] == '[') { /* IPv6 */ + p = strchr(hostname.s, ']'); + if (p && p[1] != ':') { + p = NULL; + } + } else { /* IPv4 or name */ + p = strchr(hostname.s, ':'); + } + /* Could already contain a port number, in which case we should not add an extra port. * See GH-8978. In a port doubling scenario, the first port would be used so we do the same to keep BC. */ - if (strchr(hostname.s, ':')) { + if (p) { /* TODO: Ideally we should be able to get rid of this workaround in the future. */ transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s", hostname.s); } else { From 769f31986798f322d98a492abb83bfb7f3f28747 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 21 Nov 2025 09:19:38 +0100 Subject: [PATCH 081/252] NEWS --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 2ed91237253b5..ad27dba473a26 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,10 @@ PHP NEWS . Fixed bug GH-20492 (mbstring compile warning due to non-strings). (ndossche) +- MySQLnd: + . Fixed bug GH-20528 (Regression breaks mysql connexion using an IPv6 address + enclosed in square brackets). (Remi) + - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) From 74c4510da92b92bd9323edcfe88bc18835a608c5 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 21 Nov 2025 09:20:22 +0100 Subject: [PATCH 082/252] NEWS --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 06aa7adcc0279..6e03a774df247 100644 --- a/NEWS +++ b/NEWS @@ -36,6 +36,10 @@ PHP NEWS . Fixed bug GH-20492 (mbstring compile warning due to non-strings). (ndossche) +- MySQLnd: + . Fixed bug GH-20528 (Regression breaks mysql connexion using an IPv6 address + enclosed in square brackets). (Remi) + - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) From e2219488ba31b0e018a945e4b7d0b28b53716f2d Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 21 Nov 2025 09:21:03 +0100 Subject: [PATCH 083/252] NEWS --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 253e597cf2d4b..b6efe0d85e51d 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,10 @@ PHP NEWS . Fix some deprecations on newer libxml versions regarding input buffer/parser handling. (ndossche) +- MySQLnd: + . Fixed bug GH-20528 (Regression breaks mysql connexion using an IPv6 address + enclosed in square brackets). (Remi) + - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) From 3abdef26fe9d4c71e1d7313f38eafbe1fa938aa2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 22 Nov 2025 08:58:41 -0800 Subject: [PATCH 084/252] VM: Reuse result variable in ICALL_0 implementation (#20561) This reduces the assembly size from 52 to 46 bytes on x86-64 with GCC 15.2.1, strangely. Before: ``` <+0>: sub $0x8,%rsp <+4>: movslq 0x10(%r15),%rax <+8>: movslq 0x10(%r15),%rdi <+12>: mov %r15,(%r14) <+15>: mov 0x14(%r15),%edx <+19>: movl $0x1,0x8(%r14,%rax,1) <+28>: mov 0x140695d(%rip),%rax # 0x1addfe0 <+35>: add %r14,%rdi <+38>: call *(%rax,%rdx,8) <+41>: mov (%r14),%r15 <+44>: add $0x8,%rsp <+48>: add $0x20,%r15 <+52>: ret ``` After: ``` <+0>: sub $0x8,%rsp <+4>: movslq 0x10(%r15),%rdi <+8>: mov 0x14(%r15),%edx <+12>: mov %r15,(%r14) <+15>: mov 0xace58a(%rip),%rax # 0x10d9840 <+22>: add %r14,%rdi <+25>: movl $0x1,0x8(%rdi) <+32>: call *(%rax,%rdx,8) <+35>: mov (%r14),%r15 <+38>: add $0x8,%rsp <+42>: add $0x20,%r15 <+46>: ret ``` --- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 07a0ecf1e2631..1b91f11662c7a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -9800,7 +9800,7 @@ ZEND_VM_HANDLER(204, ZEND_FRAMELESS_ICALL_0, UNUSED, UNUSED, SPEC(OBSERVER)) #endif { zend_frameless_function_0 function = (zend_frameless_function_0)ZEND_FLF_HANDLER(opline); - function(EX_VAR(opline->result.var)); + function(result); } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index a6b79495d7c03..801bf0ee69e0d 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -39304,7 +39304,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FRAMELESS_ICA #endif { zend_frameless_function_0 function = (zend_frameless_function_0)ZEND_FLF_HANDLER(opline); - function(EX_VAR(opline->result.var)); + function(result); } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -39324,7 +39324,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FRAMELESS_ICA #endif { zend_frameless_function_0 function = (zend_frameless_function_0)ZEND_FLF_HANDLER(opline); - function(EX_VAR(opline->result.var)); + function(result); } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -94770,7 +94770,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FRAMELESS_ICALL_0_ #endif { zend_frameless_function_0 function = (zend_frameless_function_0)ZEND_FLF_HANDLER(opline); - function(EX_VAR(opline->result.var)); + function(result); } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -94790,7 +94790,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FRAMELESS_ICALL_0_ #endif { zend_frameless_function_0 function = (zend_frameless_function_0)ZEND_FLF_HANDLER(opline); - function(EX_VAR(opline->result.var)); + function(result); } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } From f88d247ce26f11b6efb4aa95e44b79892fc99593 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 21 Nov 2025 19:59:08 +0000 Subject: [PATCH 085/252] Fix GH-20551: imagegammacorrect out of range gamma value. close GH-20552 --- NEWS | 4 ++++ ext/gd/gd.c | 10 ++++++++++ ext/gd/tests/gh20551.phpt | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 ext/gd/tests/gh20551.phpt diff --git a/NEWS b/NEWS index ad27dba473a26..b04792c07f8a8 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,10 @@ PHP NEWS . Fixed bug GH-20483 (ASAN stack overflow with fiber.stack_size INI small value). (David Carlier) +- GD: + . Fixed bug GH-20511 (imagegammacorrect out of range input/output values). + (David Carlier) + - LibXML: . Fix some deprecations on newer libxml versions regarding input buffer/parser handling. (ndossche) diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 2c3fce862eaea..558d0764d666a 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -2286,11 +2286,21 @@ PHP_FUNCTION(imagegammacorrect) RETURN_THROWS(); } + if (!zend_finite(input)) { + zend_argument_value_error(2, "must be finite"); + RETURN_THROWS(); + } + if (output <= 0.0) { zend_argument_value_error(3, "must be greater than 0"); RETURN_THROWS(); } + if (!zend_finite(output)) { + zend_argument_value_error(3, "must be finite"); + RETURN_THROWS(); + } + gamma = input / output; im = php_gd_libgdimageptr_from_zval_p(IM); diff --git a/ext/gd/tests/gh20551.phpt b/ext/gd/tests/gh20551.phpt new file mode 100644 index 0000000000000..32ca50ca5f626 --- /dev/null +++ b/ext/gd/tests/gh20551.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-20551: (imagegammacorrect out of range input/output value) +--EXTENSIONS-- +gd +--FILE-- +getMessage(), PHP_EOL; + } +} +?> +--EXPECT-- +imagegammacorrect(): Argument #2 ($input_gamma) must be finite +imagegammacorrect(): Argument #2 ($input_gamma) must be finite +imagegammacorrect(): Argument #2 ($input_gamma) must be finite +imagegammacorrect(): Argument #2 ($input_gamma) must be greater than 0 +imagegammacorrect(): Argument #3 ($output_gamma) must be finite +imagegammacorrect(): Argument #3 ($output_gamma) must be finite +imagegammacorrect(): Argument #3 ($output_gamma) must be finite +imagegammacorrect(): Argument #3 ($output_gamma) must be greater than 0 From 9149c3551434d48220d452ea42438d4cb49b7c89 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 22 Nov 2025 06:03:41 +0000 Subject: [PATCH 086/252] Fix GH-20554: php_cli_server() get http status as string build issue. due to the signature of this helper it needs to be const also bsearch key argument needs to be too. close GH-20556 --- sapi/cli/php_cli_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 8d67fb2864c6d..df01d3df91137 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -304,7 +304,7 @@ static int status_comp(const void *a, const void *b) /* {{{ */ static const char *get_status_string(int code) /* {{{ */ { - http_response_status_code_pair needle = {code, NULL}, + const http_response_status_code_pair needle = {code, NULL}, *result = NULL; result = bsearch(&needle, http_status_map, http_status_map_len, sizeof(needle), status_comp); From 178776569620b64cee308211dd4329e8f08d7e76 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 22 Nov 2025 11:05:39 +0000 Subject: [PATCH 087/252] Fix GH-20546: Zend preserve_none attribute config check on macOs issue. This attribute fails on macOs due to the inline assembly test. Due to an old Darwin C ABI convention, symbols are prefixed with an underscore so we need to take in account also for x86_64. close GH-20559 --- NEWS | 2 ++ Zend/Zend.m4 | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index b6efe0d85e51d..cf93f7af6561b 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ PHP NEWS . Sync all boost.context files with release 1.86.0. (mvorisek) . Fixed bug GH-20435 (SensitiveParameter doesn't work for named argument passing to variadic parameter). (ndossche) + . Fixed bug GH-20546 (preserve_none attribute configure check on macOs + issue). (David Carlier/cho-m) - Bz2: . Fix assertion failures resulting in crashes with stream filter diff --git a/Zend/Zend.m4 b/Zend/Zend.m4 index 1e1853167cfe3..33009e9909f5a 100644 --- a/Zend/Zend.m4 +++ b/Zend/Zend.m4 @@ -474,7 +474,7 @@ dnl expectations. dnl AC_DEFUN([ZEND_CHECK_PRESERVE_NONE], [dnl AC_CACHE_CHECK([for preserve_none calling convention], - [php_cv_preverve_none], + [php_cv_preserve_none], [AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include @@ -504,7 +504,11 @@ uintptr_t __attribute__((preserve_none)) test(void) { "movq %2, %%r13\n" "xorq %3, %%r13\n" "xorq %%rax, %%rax\n" +#if defined(__APPLE__) + "call _fun\n" +#else "call fun\n" +#endif : "=a" (ret) : "r" (const1), "r" (const2), "r" (key) : "r12", "r13" @@ -515,7 +519,11 @@ uintptr_t __attribute__((preserve_none)) test(void) { "eor x20, %1, %3\n" "eor x21, %2, %3\n" "eor x0, x0, x0\n" +#if defined(__APPLE__) + "bl _fun\n" +#else "bl fun\n" +#endif "mov %0, x0\n" : "=r" (ret) : "r" (const1), "r" (const2), "r" (key) From 27f17c33227f4c76b23e2113e36ca533d4522615 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 15 Nov 2025 13:40:20 +0100 Subject: [PATCH 088/252] Fix GH-20286: use-after-destroy during userland stream_close() Closes GH-20493. Co-authored-by: David Carlier --- NEWS | 2 ++ Zend/zend_list.c | 15 ++++++++- ext/standard/tests/streams/gh20286.phpt | 43 +++++++++++++++++++++++++ main/streams/userspace.c | 1 + 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/streams/gh20286.phpt diff --git a/NEWS b/NEWS index b04792c07f8a8..b56302cff0fce 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ PHP NEWS . Sync all boost.context files with release 1.86.0. (mvorisek) . Fixed bug GH-20435 (SensitiveParameter doesn't work for named argument passing to variadic parameter). (ndossche) + . Fixed bug GH-20286 (use-after-destroy during userland stream_close()). + (ndossche, David Carlier) - Bz2: . Fix assertion failures resulting in crashes with stream filter diff --git a/Zend/zend_list.c b/Zend/zend_list.c index 5add19256a691..10aa9174cfccb 100644 --- a/Zend/zend_list.c +++ b/Zend/zend_list.c @@ -214,21 +214,34 @@ void zend_init_rsrc_plist(void) void zend_close_rsrc_list(HashTable *ht) { - /* Reload ht->arData on each iteration, as it may be reallocated. */ uint32_t i = ht->nNumUsed; + uint32_t num = ht->nNumUsed; retry: zend_try { while (i-- > 0) { + /* Reload ht->arData on each iteration, as it may be reallocated. */ zval *p = ZEND_HASH_ELEMENT(ht, i); if (Z_TYPE_P(p) != IS_UNDEF) { zend_resource *res = Z_PTR_P(p); if (res->type >= 0) { zend_resource_dtor(res); + + if (UNEXPECTED(ht->nNumUsed != num)) { + /* New resources were added, reloop from the start. + * We need to keep the top->down order to avoid freeing resources + * in use by the newly created resources. */ + i = num = ht->nNumUsed; + } } } } } zend_catch { + if (UNEXPECTED(ht->nNumUsed != num)) { + /* See above */ + i = num = ht->nNumUsed; + } + /* If we have bailed, we probably executed user code (e.g. user stream * API). Keep closing resources so they don't leak. User handlers must be * called now so they aren't called in zend_deactivate() on diff --git a/ext/standard/tests/streams/gh20286.phpt b/ext/standard/tests/streams/gh20286.phpt new file mode 100644 index 0000000000000..b4d340a4390f0 --- /dev/null +++ b/ext/standard/tests/streams/gh20286.phpt @@ -0,0 +1,43 @@ +--TEST-- +GH-20286 use after destroy on userland stream_close +--CREDITS-- +vi3tL0u1s +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Cannot redeclare a() (previously declared in %s:%d) in %s on line %d + +Fatal error: Cannot redeclare a() (previously declared in %s on line %d + +Fatal error: Cannot redeclare a() (previously declared in %s on line %d + +Fatal error: Cannot redeclare a() (previously declared in %s on line %d diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 8d15172ef1319..ba66d32465977 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -254,6 +254,7 @@ typedef struct _php_userstream_data php_userstream_data_t; static zend_result call_method_if_exists( zval *object, zval *method_name, zval *retval, uint32_t param_count, zval *params) { + ZEND_ASSERT(EG(active)); return zend_call_method_if_exists( Z_OBJ_P(object), Z_STR_P(method_name), retval, param_count, params); } From 1ee8dfd6fcf02ce7cc3662381e4cfa76de47ec3b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:09:23 -0800 Subject: [PATCH 089/252] Remove pointless EG(exception) checks when parsing coercive string argument (#20568) The is_numeric_str_function() family cannot throw. --- Zend/zend_API.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 4a08952677627..601d753bd2aa6 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -592,9 +592,6 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(const zval *arg, zend_long return 0; } } - if (UNEXPECTED(EG(exception))) { - return 0; - } } else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) { if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("int", arg_num)) { return 0; @@ -641,9 +638,6 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, double * return 0; } } - if (UNEXPECTED(EG(exception))) { - return 0; - } } else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) { if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("float", arg_num)) { return 0; From ada74006907ec630d6dce764ddc2f5d9e0e18331 Mon Sep 17 00:00:00 2001 From: Sayed Zayeem <6727845+returntruejoy@users.noreply.github.com> Date: Thu, 27 Nov 2025 04:53:13 +0600 Subject: [PATCH 090/252] [skip ci] Update year in LICENSE (GH-20598) Co-authored-by: Sayed Zayeem Abdullah --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 0815d7eb79119..b155a18c2fb73 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -------------------------------------------------------------------- The PHP License, version 3.01 -Copyright (c) 1999 - 2024 The PHP Group. All rights reserved. +Copyright (c) 1999 - 2025 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without From 292a7f73baf7c2ce3164feda6e6da779b8056d51 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:58:40 +0100 Subject: [PATCH 091/252] Fix GH-20583: Stack overflow in http_build_query via deep structures Closes GH-20590. --- NEWS | 2 ++ ext/standard/http.c | 15 +++++++++++ .../tests/http/http_build_query/gh20583.phpt | 27 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 ext/standard/tests/http/http_build_query/gh20583.phpt diff --git a/NEWS b/NEWS index b56302cff0fce..b87cba17aad1e 100644 --- a/NEWS +++ b/NEWS @@ -58,6 +58,8 @@ PHP NEWS - Standard: . Fix memory leak in array_diff() with custom type checks. (ndossche) + . Fixed bug GH-20583 (Stack overflow in http_build_query + via deep structures). (ndossche) - Tidy: . Fixed bug GH-20374 (PHP with tidy and custom-tags). (ndossche) diff --git a/ext/standard/http.c b/ext/standard/http.c index 1145329a794ff..d79c25c38a0ad 100644 --- a/ext/standard/http.c +++ b/ext/standard/http.c @@ -92,6 +92,15 @@ static void php_url_encode_scalar(zval *scalar, smart_str *form_str, } } +static zend_always_inline bool php_url_check_stack_limit(void) +{ +#ifdef ZEND_CHECK_STACK_LIMIT + return zend_call_stack_overflowed(EG(stack_limit)); +#else + return false; +#endif +} + /* {{{ php_url_encode_hash */ PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, const char *num_prefix, size_t num_prefix_len, @@ -110,6 +119,12 @@ PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, return; } + /* Very deeply structured data could trigger a stack overflow, even without recursion. */ + if (UNEXPECTED(php_url_check_stack_limit())) { + zend_throw_error(NULL, "Maximum call stack size reached."); + return; + } + if (!arg_sep) { arg_sep = zend_ini_str("arg_separator.output", strlen("arg_separator.output"), false); if (ZSTR_LEN(arg_sep) == 0) { diff --git a/ext/standard/tests/http/http_build_query/gh20583.phpt b/ext/standard/tests/http/http_build_query/gh20583.phpt new file mode 100644 index 0000000000000..c0331a830b1e0 --- /dev/null +++ b/ext/standard/tests/http/http_build_query/gh20583.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-20583 (Stack overflow in http_build_query via deep structures) +--SKIPIF-- + +--INI-- +zend.max_allowed_stack_size=512K +--FILE-- + $a]; +} +try { + http_build_query($a, 'p'); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Error: Maximum call stack size reached. From 8fe79305331f12852afe2137a01fda373d8b37cb Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:11:38 +0100 Subject: [PATCH 092/252] Fix GH-20584: Information Leak of Memory The string added had uninitialized memory due to php_read_stream_all_chunks() not moving the buffer position, resulting in the same data always being overwritten instead of new data being added to the end of the buffer. Closes GH-20592. --- NEWS | 1 + ext/standard/image.c | 1 + ext/standard/tests/image/gh20584.phpt | 39 +++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 ext/standard/tests/image/gh20584.phpt diff --git a/NEWS b/NEWS index b87cba17aad1e..863d672d736d6 100644 --- a/NEWS +++ b/NEWS @@ -60,6 +60,7 @@ PHP NEWS . Fix memory leak in array_diff() with custom type checks. (ndossche) . Fixed bug GH-20583 (Stack overflow in http_build_query via deep structures). (ndossche) + . Fixed bug GH-20584 (Information Leak of Memory). (ndossche) - Tidy: . Fixed bug GH-20374 (PHP with tidy and custom-tags). (ndossche) diff --git a/ext/standard/image.c b/ext/standard/image.c index 2bd5429efac35..15761364c3410 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -403,6 +403,7 @@ static size_t php_read_stream_all_chunks(php_stream *stream, char *buffer, size_ if (read_now < stream->chunk_size && read_total != length) { return 0; } + buffer += read_now; } while (read_total < length); return read_total; diff --git a/ext/standard/tests/image/gh20584.phpt b/ext/standard/tests/image/gh20584.phpt new file mode 100644 index 0000000000000..d117f21820264 --- /dev/null +++ b/ext/standard/tests/image/gh20584.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-20584 (Information Leak of Memory) +--CREDITS-- +Nikita Sveshnikov (Positive Technologies) +--FILE-- + +--CLEAN-- + +--EXPECT-- +bool(true) From dcac024bb0857d97091b1d0e9fb16c9cb41cd505 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 29 Nov 2025 02:21:24 -0800 Subject: [PATCH 093/252] spl: Avoid pointless copies for internal construction calls (#20610) --- ext/spl/spl_directory.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 5ef7540f822d1..ad6fc54aeb3ea 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -480,9 +480,8 @@ static spl_filesystem_object *spl_filesystem_object_create_info(zend_string *fil RETVAL_OBJ(&intern->std); if (ce->constructor->common.scope != spl_ce_SplFileInfo) { - ZVAL_STR_COPY(&arg1, file_path); + ZVAL_STR(&arg1, file_path); zend_call_method_with_1_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1); - zval_ptr_dtor(&arg1); } else { spl_filesystem_info_set_filename(intern, file_path); } @@ -520,9 +519,8 @@ static spl_filesystem_object *spl_filesystem_object_create_type(int num_args, sp } if (ce->constructor->common.scope != spl_ce_SplFileInfo) { - ZVAL_STR_COPY(&arg1, source->file_name); + ZVAL_STR(&arg1, source->file_name); zend_call_method_with_1_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1); - zval_ptr_dtor(&arg1); } else { intern->file_name = zend_string_copy(source->file_name); intern->path = spl_filesystem_object_get_path(source); @@ -549,11 +547,9 @@ static spl_filesystem_object *spl_filesystem_object_create_type(int num_args, sp } if (ce->constructor->common.scope != spl_ce_SplFileObject) { - ZVAL_STR_COPY(&arg1, source->file_name); - ZVAL_STR_COPY(&arg2, open_mode); + ZVAL_STR(&arg1, source->file_name); + ZVAL_STR(&arg2, open_mode); zend_call_method_with_2_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1, &arg2); - zval_ptr_dtor(&arg1); - zval_ptr_dtor(&arg2); } else { intern->file_name = source->file_name; intern->path = spl_filesystem_object_get_path(source); From 5ae1261c6f8ff2738ab0e9262da0d17cf440e3e7 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 29 Nov 2025 02:21:33 -0800 Subject: [PATCH 094/252] phar: Remove dead store (#20611) This is overwritten later anyway by contents_len. --- ext/phar/phar_object.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index acd9aa0cff655..11a0dd17a4e57 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -1622,8 +1622,6 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ data->internal_file->offset_abs = data->internal_file->offset = php_stream_tell(p_obj->fp); data->fp = NULL; php_stream_copy_to_stream_ex(fp, p_obj->fp, PHP_STREAM_COPY_ALL, &contents_len); - data->internal_file->uncompressed_filesize = data->internal_file->compressed_filesize = - php_stream_tell(p_obj->fp) - data->internal_file->offset; if (php_stream_stat(fp, &ssb) != -1) { data->internal_file->flags = ssb.sb.st_mode & PHAR_ENT_PERM_MASK ; } else { From c8e13af4558659ba91cec07e502733e127dcb1ce Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 28 Nov 2025 12:40:33 +0000 Subject: [PATCH 095/252] Fix GH-20602: imagescale() overflow with large height values. close GH-20605 --- NEWS | 2 ++ ext/gd/gd.c | 8 ++++++++ ext/gd/tests/gh20602.phpt | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 ext/gd/tests/gh20602.phpt diff --git a/NEWS b/NEWS index 863d672d736d6..214f1105b5c2a 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,8 @@ PHP NEWS - GD: . Fixed bug GH-20511 (imagegammacorrect out of range input/output values). (David Carlier) + . Fixed bug GH-20602 (imagescale overflow with large height values). + (David Carlier) - LibXML: . Fix some deprecations on newer libxml versions regarding input diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 558d0764d666a..925d64f01c5e7 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -3689,9 +3689,17 @@ PHP_FUNCTION(imagescale) src_y = gdImageSY(im); if (src_x && tmp_h < 0) { + if (tmp_w > (ZEND_LONG_MAX / src_y)) { + zend_argument_value_error(2, "must be less than or equal to " ZEND_LONG_FMT, (zend_long)(ZEND_LONG_MAX / src_y)); + RETURN_THROWS(); + } tmp_h = tmp_w * src_y / src_x; } if (src_y && tmp_w < 0) { + if (tmp_h > (ZEND_LONG_MAX / src_x)) { + zend_argument_value_error(3, "must be less than or equal to " ZEND_LONG_FMT, (zend_long)(ZEND_LONG_MAX / src_x)); + RETURN_THROWS(); + } tmp_w = tmp_h * src_x / src_y; } } diff --git a/ext/gd/tests/gh20602.phpt b/ext/gd/tests/gh20602.phpt new file mode 100644 index 0000000000000..29c781e76a2d1 --- /dev/null +++ b/ext/gd/tests/gh20602.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-20551: (imagegammacorrect out of range input/output value) +--EXTENSIONS-- +gd +--FILE-- +getMessage(), PHP_EOL; +} +try { + imagescale($im, -1, PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} +?> +--EXPECTF-- +imagescale(): Argument #2 ($width) must be less than or equal to %d +imagescale(): Argument #3 ($height) must be less than or equal to %d From 927830da8668d58422b3d16645b3dcce160cd233 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 29 Nov 2025 05:36:59 -0800 Subject: [PATCH 096/252] phar: Remove unused min_timestamp field (#20617) --- ext/phar/phar.c | 10 ++-------- ext/phar/phar_internal.h | 1 - 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 23624ce6bcc78..30c3b371e6bbd 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1149,15 +1149,9 @@ static zend_result phar_parse_pharfile(php_stream *fp, char *fname, size_t fname PHAR_GET_32(buffer, entry.uncompressed_filesize); PHAR_GET_32(buffer, entry.timestamp); - if (offset == halt_offset + manifest_len + 4) { - mydata->min_timestamp = entry.timestamp; + if (offset == halt_offset + manifest_len + 4 + || mydata->max_timestamp < entry.timestamp) { mydata->max_timestamp = entry.timestamp; - } else { - if (mydata->min_timestamp > entry.timestamp) { - mydata->min_timestamp = entry.timestamp; - } else if (mydata->max_timestamp < entry.timestamp) { - mydata->max_timestamp = entry.timestamp; - } } PHAR_GET_32(buffer, entry.compressed_filesize); diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 46e45ec61b723..5fc4354545318 100644 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -258,7 +258,6 @@ struct _phar_archive_data { /* hash of mounted directory paths */ HashTable mounted_dirs; uint32_t flags; - uint32_t min_timestamp; uint32_t max_timestamp; int refcount; php_stream *fp; From 4312a446d0a101ffa7fa5d4c624e487a32706b59 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 27 Nov 2025 19:37:01 +0000 Subject: [PATCH 097/252] Fix GH-20601: ftp_connect() timeout argument overflow. close GH-20603 --- NEWS | 3 +++ ext/ftp/php_ftp.c | 7 +++++++ ext/ftp/tests/gh20601.phpt | 19 +++++++++++++++++++ main/network.c | 2 ++ 4 files changed, 31 insertions(+) create mode 100644 ext/ftp/tests/gh20601.phpt diff --git a/NEWS b/NEWS index 214f1105b5c2a..3ec3f8c096d03 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,9 @@ PHP NEWS . Fixed bug GH-20483 (ASAN stack overflow with fiber.stack_size INI small value). (David Carlier) +- FTP: + . Fixed bug GH-20601 (ftp_connect overflow on timeout). (David Carlier) + - GD: . Fixed bug GH-20511 (imagegammacorrect out of range input/output values). (David Carlier) diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c index 4d33e4d82535a..0df3aa6e75524 100644 --- a/ext/ftp/php_ftp.c +++ b/ext/ftp/php_ftp.c @@ -158,11 +158,18 @@ PHP_FUNCTION(ftp_connect) RETURN_THROWS(); } + const zend_long timeoutmax = (zend_long)((double) PHP_TIMEOUT_ULL_MAX / 1000000.0); + if (timeout_sec <= 0) { zend_argument_value_error(3, "must be greater than 0"); RETURN_THROWS(); } + if (timeout_sec >= timeoutmax) { + zend_argument_value_error(3, "must be less than " ZEND_LONG_FMT, timeoutmax); + RETURN_THROWS(); + } + /* connect */ if (!(ftp = ftp_open(host, (short)port, timeout_sec))) { RETURN_FALSE; diff --git a/ext/ftp/tests/gh20601.phpt b/ext/ftp/tests/gh20601.phpt new file mode 100644 index 0000000000000..3ece7736c3aaa --- /dev/null +++ b/ext/ftp/tests/gh20601.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-20601 (ftp_connect timeout overflow) +--EXTENSIONS-- +ftp +--SKIPIF-- + +--FILE-- +getMessage(); +} +?> +--EXPECTF-- +ftp_connect(): Argument #3 ($timeout) must be less than %d diff --git a/main/network.c b/main/network.c index fecec0545e8f0..7b69614d533dd 100644 --- a/main/network.c +++ b/main/network.c @@ -319,6 +319,8 @@ static inline void php_network_set_limit_time(struct timeval *limit_time, struct timeval *timeout) { gettimeofday(limit_time, NULL); + const double timeoutmax = (double) PHP_TIMEOUT_ULL_MAX / 1000000.0; + ZEND_ASSERT(limit_time->tv_sec < (timeoutmax - timeout->tv_sec)); limit_time->tv_sec += timeout->tv_sec; limit_time->tv_usec += timeout->tv_usec; if (limit_time->tv_usec >= 1000000) { From b074c5cc551f3f985cefe0267e50968191403114 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 28 Nov 2025 18:45:32 +0100 Subject: [PATCH 098/252] reflection: Remove dead code Since the executor needs to be active at this point, the only way you could get an UNDEF return value is by having an exception. Therefore, `!EG(exception)` is always false. The check doesn't make sense, remove it. --- ext/reflection/php_reflection.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a86ce16feb407..3327fdc7f6f6d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -2141,12 +2141,6 @@ ZEND_METHOD(ReflectionFunction, invoke) zend_call_known_fcc(&fcc, &retval, num_args, params, named_params); - if (Z_TYPE(retval) == IS_UNDEF && !EG(exception)) { - zend_throw_exception_ex(reflection_exception_ptr, 0, - "Invocation of function %s() failed", ZSTR_VAL(fptr->common.function_name)); - RETURN_THROWS(); - } - if (Z_ISREF(retval)) { zend_unwrap_reference(&retval); } @@ -2180,12 +2174,6 @@ ZEND_METHOD(ReflectionFunction, invokeArgs) zend_call_known_fcc(&fcc, &retval, /* num_params */ 0, /* params */ NULL, params); - if (Z_TYPE(retval) == IS_UNDEF && !EG(exception)) { - zend_throw_exception_ex(reflection_exception_ptr, 0, - "Invocation of function %s() failed", ZSTR_VAL(fptr->common.function_name)); - RETURN_THROWS(); - } - if (Z_ISREF(retval)) { zend_unwrap_reference(&retval); } @@ -3496,12 +3484,6 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic) callback = _copy_function(mptr); zend_call_known_function(callback, (object ? Z_OBJ_P(object) : NULL), intern->ce, &retval, argc, params, named_params); - if (Z_TYPE(retval) == IS_UNDEF && !EG(exception)) { - zend_throw_exception_ex(reflection_exception_ptr, 0, - "Invocation of method %s::%s() failed", ZSTR_VAL(mptr->common.scope->name), ZSTR_VAL(mptr->common.function_name)); - RETURN_THROWS(); - } - if (Z_ISREF(retval)) { zend_unwrap_reference(&retval); } From b64cd429ca4f6cadf9220c0a0cba4afbfe101687 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 28 Nov 2025 18:46:52 +0100 Subject: [PATCH 099/252] reflection: Use RETURN_COPY_VALUE() --- ext/reflection/php_reflection.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 3327fdc7f6f6d..05454b24c4e68 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -2144,7 +2144,7 @@ ZEND_METHOD(ReflectionFunction, invoke) if (Z_ISREF(retval)) { zend_unwrap_reference(&retval); } - ZVAL_COPY_VALUE(return_value, &retval); + RETURN_COPY_VALUE(&retval); } /* }}} */ @@ -2177,7 +2177,7 @@ ZEND_METHOD(ReflectionFunction, invokeArgs) if (Z_ISREF(retval)) { zend_unwrap_reference(&retval); } - ZVAL_COPY_VALUE(return_value, &retval); + RETURN_COPY_VALUE(&retval); } /* }}} */ @@ -3487,7 +3487,7 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic) if (Z_ISREF(retval)) { zend_unwrap_reference(&retval); } - ZVAL_COPY_VALUE(return_value, &retval); + RETURN_COPY_VALUE(&retval); } /* }}} */ From e025f2a14846ed5f9a83df7ed811aaf3043a4dd4 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 30 Nov 2025 01:39:04 -0800 Subject: [PATCH 100/252] reflection: Test ReflectionFunction::__toString() with bound variables (#20608) --- ...ionFunction__toString_bound_variables.phpt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 ext/reflection/tests/ReflectionFunction__toString_bound_variables.phpt diff --git a/ext/reflection/tests/ReflectionFunction__toString_bound_variables.phpt b/ext/reflection/tests/ReflectionFunction__toString_bound_variables.phpt new file mode 100644 index 0000000000000..142e661361496 --- /dev/null +++ b/ext/reflection/tests/ReflectionFunction__toString_bound_variables.phpt @@ -0,0 +1,32 @@ +--TEST-- +ReflectionFunction::__toString() with bound variables +--FILE-- + 0; + +$rf = new ReflectionFunction($closure_without_bounds); +echo (string) $rf; + +$global = ""; +$closure_with_bounds = function() use($global) { + static $counter = 0; + return $counter++; +}; + +$rf = new ReflectionFunction($closure_with_bounds); +echo (string) $rf; + +?> +--EXPECTF-- +Closure [ function {closure:%s:%d} ] { + @@ %sReflectionFunction__toString_bound_variables.php 3 - 3 +} +Closure [ function {closure:%s:%d} ] { + @@ %sReflectionFunction__toString_bound_variables.php 9 - 12 + + - Bound Variables [2] { + Variable #0 [ $global ] + Variable #1 [ $counter ] + } +} From 13bf672cdb1bcd9684225f6f0473c071434bdc8c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 28 Nov 2025 19:03:22 +0100 Subject: [PATCH 101/252] reflection: Use zend_hash_str_find_ptr_lc() where possible --- ext/reflection/php_reflection.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 05454b24c4e68..4cf3edd47375e 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1432,13 +1432,7 @@ static void reflection_extension_factory_ex(zval *object, zend_module_entry *mod static void reflection_extension_factory(zval *object, const char *name_str) { size_t name_len = strlen(name_str); - zend_string *lcname; - struct _zend_module_entry *module; - - lcname = zend_string_alloc(name_len, 0); - zend_str_tolower_copy(ZSTR_VAL(lcname), name_str, name_len); - module = zend_hash_find_ptr(&module_registry, lcname); - zend_string_efree(lcname); + struct _zend_module_entry *module = zend_hash_str_find_ptr_lc(&module_registry, name_str, name_len); if (!module) { return; } @@ -6643,12 +6637,10 @@ ZEND_METHOD(ReflectionProperty, isFinal) ZEND_METHOD(ReflectionExtension, __construct) { zval *object; - char *lcname; reflection_object *intern; zend_module_entry *module; char *name_str; size_t name_len; - ALLOCA_FLAG(use_heap) if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name_str, &name_len) == FAILURE) { RETURN_THROWS(); @@ -6656,15 +6648,11 @@ ZEND_METHOD(ReflectionExtension, __construct) object = ZEND_THIS; intern = Z_REFLECTION_P(object); - lcname = do_alloca(name_len + 1, use_heap); - zend_str_tolower_copy(lcname, name_str, name_len); - if ((module = zend_hash_str_find_ptr(&module_registry, lcname, name_len)) == NULL) { - free_alloca(lcname, use_heap); + if ((module = zend_hash_str_find_ptr_lc(&module_registry, name_str, name_len)) == NULL) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Extension \"%s\" does not exist", name_str); RETURN_THROWS(); } - free_alloca(lcname, use_heap); zval *prop_name = reflection_prop_name(object); zval_ptr_dtor(prop_name); ZVAL_STRING(prop_name, module->name); From 157864af49eab6561e02412e7838d9a195968efe Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 28 Nov 2025 19:47:44 +0100 Subject: [PATCH 102/252] reflection: Use zend_hash_find_ptr_lc() where possible --- ext/reflection/php_reflection.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 4cf3edd47375e..d6e55c982b425 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6639,18 +6639,17 @@ ZEND_METHOD(ReflectionExtension, __construct) zval *object; reflection_object *intern; zend_module_entry *module; - char *name_str; - size_t name_len; + zend_string *name_str; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name_str, &name_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name_str) == FAILURE) { RETURN_THROWS(); } object = ZEND_THIS; intern = Z_REFLECTION_P(object); - if ((module = zend_hash_str_find_ptr_lc(&module_registry, name_str, name_len)) == NULL) { + if ((module = zend_hash_find_ptr_lc(&module_registry, name_str)) == NULL) { zend_throw_exception_ex(reflection_exception_ptr, 0, - "Extension \"%s\" does not exist", name_str); + "Extension \"%s\" does not exist", ZSTR_VAL(name_str)); RETURN_THROWS(); } zval *prop_name = reflection_prop_name(object); From 366ed4c7508af61dd4d0397d758abfc77ffe2b8a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 29 Nov 2025 12:07:15 +0100 Subject: [PATCH 103/252] Fix GH-20614: SplFixedArray incorrectly handles references in deserialization All other code caters to dereferencing array elements, except the unserialize handler. This causes references to be present in the fixed array even though this seems not intentional as reference assign is otherwise impossible. On 8.5+ this causes an assertion failure. On 8.3+ this causes references to be present where they shouldn't be. Closes GH-20616. --- NEWS | 4 ++++ ext/spl/spl_fixedarray.c | 4 ++-- ext/spl/tests/gh20614.phpt | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 ext/spl/tests/gh20614.phpt diff --git a/NEWS b/NEWS index 3ec3f8c096d03..5070f818aca29 100644 --- a/NEWS +++ b/NEWS @@ -61,6 +61,10 @@ PHP NEWS . Fixed ZPP type violation in phpdbg_get_executable() and phpdbg_end_oplog(). (Girgias) +- SPL: + . Fixed bug GH-20614 (SplFixedArray incorrectly handles references + in deserialization). (ndossche) + - Standard: . Fix memory leak in array_diff() with custom type checks. (ndossche) . Fixed bug GH-20583 (Stack overflow in http_build_query diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index 49eb8841de1b2..53dba1b727cf7 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -652,7 +652,7 @@ PHP_METHOD(SplFixedArray, __unserialize) intern->array.size = 0; ZEND_HASH_FOREACH_STR_KEY_VAL(data, key, elem) { if (key == NULL) { - ZVAL_COPY(&intern->array.elements[intern->array.size], elem); + ZVAL_COPY_DEREF(&intern->array.elements[intern->array.size], elem); intern->array.size++; } else { Z_TRY_ADDREF_P(elem); @@ -833,7 +833,7 @@ PHP_METHOD(SplFixedArray, offsetGet) value = spl_fixedarray_object_read_dimension_helper(intern, zindex); if (value) { - RETURN_COPY_DEREF(value); + RETURN_COPY(value); } else { RETURN_NULL(); } diff --git a/ext/spl/tests/gh20614.phpt b/ext/spl/tests/gh20614.phpt new file mode 100644 index 0000000000000..c13630d76469b --- /dev/null +++ b/ext/spl/tests/gh20614.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-20614 (SplFixedArray incorrectly handles references in deserialization) +--FILE-- +__unserialize($array); +var_dump($fa); +unset($fa[0]); +var_dump($fa); + +?> +--EXPECT-- +object(SplFixedArray)#1 (1) { + [0]=> + int(1) +} +object(SplFixedArray)#1 (1) { + [0]=> + NULL +} From 6b197ee4edfcd8c9bc2d0cc9d89f663b21187594 Mon Sep 17 00:00:00 2001 From: Tobias Vorwachs Date: Tue, 18 Nov 2025 17:15:06 +0100 Subject: [PATCH 104/252] mbstring: fix missing copying of detect_order_list to current_detect_order_list on ini_set('mbstring.detect_order', string) Closes GH-20523. --- NEWS | 4 ++ UPGRADING | 7 +++ ext/mbstring/mbstring.c | 10 ++++ .../tests/mb_detect_order_with_ini_set.phpt | 60 +++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 ext/mbstring/tests/mb_detect_order_with_ini_set.phpt diff --git a/NEWS b/NEWS index 604945a77c701..18d931bccded7 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,10 @@ PHP NEWS . Fixed bug GH-20483 (ASAN stack overflow with fiber.stack_size INI small value). (David Carlier) +- Mbstring: + . ini_set() with mbstring.detect_order changes the order of mb_detect_order + as intended, since mbstring.detect_order is an INI_ALL setting. (tobee94) + - Opcache: . Fixed bug GH-20051 (apache2 shutdowns when restart is requested during preloading). (Arnaud, welcomycozyhom) diff --git a/UPGRADING b/UPGRADING index 1e84b8beee97a..7f0fcaf8943a8 100644 --- a/UPGRADING +++ b/UPGRADING @@ -98,6 +98,13 @@ PHP 8.6 UPGRADE NOTES When used along with ZEND_JIT_DEBUG_TRACE_EXIT_INFO, the source of exit points is printed in exit info output, in debug builds. +- Mbstring: + . The mbstring.detect_order INI directive now updates the internal detection + order when changed at runtime via ini_set(). Previously, runtime changes + using ini_set() did not take effect for mb_detect_order(). Setting the + directive to NULL or an empty string at runtime now leaves the previously + configured detection order unchanged. + ======================================== 12. Windows Support ======================================== diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 7fda240b7051a..8f94e50ddc861 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -718,6 +718,11 @@ static PHP_INI_MH(OnUpdate_mbstring_detect_order) } MBSTRG(detect_order_list) = list; MBSTRG(detect_order_list_size) = size; + + if (stage == PHP_INI_STAGE_RUNTIME) { + php_mb_populate_current_detect_order_list(); + } + return SUCCESS; } /* }}} */ @@ -5981,6 +5986,11 @@ static void php_mb_populate_current_detect_order_list(void) entry[i] = mbfl_no2encoding(src[i]); } } + + if (MBSTRG(current_detect_order_list) != NULL) { + efree(ZEND_VOIDP(MBSTRG(current_detect_order_list))); + } + MBSTRG(current_detect_order_list) = entry; MBSTRG(current_detect_order_list_size) = nentries; } diff --git a/ext/mbstring/tests/mb_detect_order_with_ini_set.phpt b/ext/mbstring/tests/mb_detect_order_with_ini_set.phpt new file mode 100644 index 0000000000000..ba59f3388b85e --- /dev/null +++ b/ext/mbstring/tests/mb_detect_order_with_ini_set.phpt @@ -0,0 +1,60 @@ +--TEST-- +Test mb_detect_order() function : ini set changes order +--EXTENSIONS-- +mbstring +--INI-- +mbstring.detect_order=UTF-8,ISO-8859-15,ISO-8859-1,ASCII +--FILE-- + +--EXPECT-- +array(4) { + [0]=> + string(5) "UTF-8" + [1]=> + string(11) "ISO-8859-15" + [2]=> + string(10) "ISO-8859-1" + [3]=> + string(5) "ASCII" +} +array(3) { + [0]=> + string(5) "UTF-8" + [1]=> + string(10) "ISO-8859-1" + [2]=> + string(5) "ASCII" +} +array(1) { + [0]=> + string(5) "UTF-8" +} +array(1) { + [0]=> + string(5) "UTF-8" +} +array(1) { + [0]=> + string(5) "UTF-8" +} + From f8c7dc19a47d24c89ed45802a18cdb1358d71e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Mon, 1 Dec 2025 21:57:19 +0100 Subject: [PATCH 105/252] Add "since PHP 8.1" to ReflectionXxx::setAccessible() deprecations (#20555) --- ext/reflection/php_reflection.stub.php | 4 ++-- ext/reflection/php_reflection_arginfo.h | 6 +++--- .../tests/ReflectionMethod_setAccessible.phpt | 8 ++++---- .../tests/ReflectionProperty_setAccessible.phpt | 14 +++++++------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index be372ac729912..91c70d6ffdb1a 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -226,7 +226,7 @@ public function getPrototype(): ReflectionMethod {} public function hasPrototype(): bool {} /** @tentative-return-type */ - #[\Deprecated(since: '8.5', message: "as it has no effect")] + #[\Deprecated(since: '8.5', message: "as it has no effect since PHP 8.1")] public function setAccessible(bool $accessible): void {} } @@ -543,7 +543,7 @@ public function getDeclaringClass(): ReflectionClass {} public function getDocComment(): string|false {} /** @tentative-return-type */ - #[\Deprecated(since: '8.5', message: "as it has no effect")] + #[\Deprecated(since: '8.5', message: "as it has no effect since PHP 8.1")] public function setAccessible(bool $accessible): void {} /** @tentative-return-type */ diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 6465c659c733b..bee9cbfc764a8 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 576229f7a0c4afd2f8902db6ce87daa51256965e */ + * Stub hash: fd645a0b0db39d94ca25b39ffe64d7f05bad6bea */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -1497,7 +1497,7 @@ static zend_class_entry *register_class_ReflectionMethod(zend_class_entry *class zend_attribute *attribute_Deprecated_func_setaccessible_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setaccessible", sizeof("setaccessible") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); ZVAL_STR(&attribute_Deprecated_func_setaccessible_0->args[0].value, ZSTR_KNOWN(ZEND_STR_8_DOT_5)); attribute_Deprecated_func_setaccessible_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); - zend_string *attribute_Deprecated_func_setaccessible_0_arg1_str = zend_string_init("as it has no effect", strlen("as it has no effect"), 1); + zend_string *attribute_Deprecated_func_setaccessible_0_arg1_str = zend_string_init("as it has no effect since PHP 8.1", strlen("as it has no effect since PHP 8.1"), 1); ZVAL_STR(&attribute_Deprecated_func_setaccessible_0->args[1].value, attribute_Deprecated_func_setaccessible_0_arg1_str); attribute_Deprecated_func_setaccessible_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); @@ -1662,7 +1662,7 @@ static zend_class_entry *register_class_ReflectionProperty(zend_class_entry *cla zend_attribute *attribute_Deprecated_func_setaccessible_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setaccessible", sizeof("setaccessible") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); ZVAL_STR(&attribute_Deprecated_func_setaccessible_0->args[0].value, ZSTR_KNOWN(ZEND_STR_8_DOT_5)); attribute_Deprecated_func_setaccessible_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); - zend_string *attribute_Deprecated_func_setaccessible_0_arg1_str = zend_string_init("as it has no effect", strlen("as it has no effect"), 1); + zend_string *attribute_Deprecated_func_setaccessible_0_arg1_str = zend_string_init("as it has no effect since PHP 8.1", strlen("as it has no effect since PHP 8.1"), 1); ZVAL_STR(&attribute_Deprecated_func_setaccessible_0->args[1].value, attribute_Deprecated_func_setaccessible_0_arg1_str); attribute_Deprecated_func_setaccessible_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); diff --git a/ext/reflection/tests/ReflectionMethod_setAccessible.phpt b/ext/reflection/tests/ReflectionMethod_setAccessible.phpt index ba4864d28ed86..be720a4044c78 100644 --- a/ext/reflection/tests/ReflectionMethod_setAccessible.phpt +++ b/ext/reflection/tests/ReflectionMethod_setAccessible.phpt @@ -47,13 +47,13 @@ A::aProtected A::aProtectedStatic A::aProtectedStatic -Deprecated: Method ReflectionMethod::setAccessible() is deprecated since 8.5, as it has no effect in %s on line %d +Deprecated: Method ReflectionMethod::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d -Deprecated: Method ReflectionMethod::setAccessible() is deprecated since 8.5, as it has no effect in %s on line %d +Deprecated: Method ReflectionMethod::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d -Deprecated: Method ReflectionMethod::setAccessible() is deprecated since 8.5, as it has no effect in %s on line %d +Deprecated: Method ReflectionMethod::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d -Deprecated: Method ReflectionMethod::setAccessible() is deprecated since 8.5, as it has no effect in %s on line %d +Deprecated: Method ReflectionMethod::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d A::aPrivate A::aPrivate A::aPrivateStatic diff --git a/ext/reflection/tests/ReflectionProperty_setAccessible.phpt b/ext/reflection/tests/ReflectionProperty_setAccessible.phpt index b63ab38c15981..5ccc1366060b0 100644 --- a/ext/reflection/tests/ReflectionProperty_setAccessible.phpt +++ b/ext/reflection/tests/ReflectionProperty_setAccessible.phpt @@ -96,13 +96,13 @@ string(1) "f" string(1) "g" string(1) "h" -Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect in %s on line %d +Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d -Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect in %s on line %d +Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d -Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect in %s on line %d +Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d -Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect in %s on line %d +Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d string(1) "e" string(1) "f" string(1) "g" @@ -120,11 +120,11 @@ string(1) "e" string(1) "f" string(1) "g" -Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect in %s on line %d +Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d -Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect in %s on line %d +Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d -Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect in %s on line %d +Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d string(1) "e" string(1) "f" string(1) "g" From c343ede18def157834a747e82666d58d1927943e Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 1 Dec 2025 13:37:49 -0800 Subject: [PATCH 106/252] Fix GH-20426: fix Spoofchecker::setRestrictionLevel() error (#20427) --- NEWS | 4 ++++ ext/intl/spoofchecker/spoofchecker_main.c | 4 ++-- ext/intl/tests/spoofchecker_unknown_restriction_level.phpt | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 5070f818aca29..ff4eb04e5b668 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,10 @@ PHP NEWS . Fixed bug GH-20602 (imagescale overflow with large height values). (David Carlier) +- Intl: + . Fixed bug GH-20426 (Spoofchecker::setRestrictionLevel() error message + suggests missing constants). (DanielEScherzer) + - LibXML: . Fix some deprecations on newer libxml versions regarding input buffer/parser handling. (ndossche) diff --git a/ext/intl/spoofchecker/spoofchecker_main.c b/ext/intl/spoofchecker/spoofchecker_main.c index e168dc16c846b..81b3b40fcf231 100644 --- a/ext/intl/spoofchecker/spoofchecker_main.c +++ b/ext/intl/spoofchecker/spoofchecker_main.c @@ -154,8 +154,8 @@ PHP_METHOD(Spoofchecker, setRestrictionLevel) USPOOF_MINIMALLY_RESTRICTIVE != level && USPOOF_UNRESTRICTIVE != level) { zend_argument_value_error(1, "must be one of Spoofchecker::ASCII, Spoofchecker::SINGLE_SCRIPT_RESTRICTIVE, " - "Spoofchecker::SINGLE_HIGHLY_RESTRICTIVE, Spoofchecker::SINGLE_MODERATELY_RESTRICTIVE, " - "Spoofchecker::SINGLE_MINIMALLY_RESTRICTIVE, or Spoofchecker::UNRESTRICTIVE"); + "Spoofchecker::HIGHLY_RESTRICTIVE, Spoofchecker::MODERATELY_RESTRICTIVE, " + "Spoofchecker::MINIMALLY_RESTRICTIVE, or Spoofchecker::UNRESTRICTIVE"); RETURN_THROWS(); } diff --git a/ext/intl/tests/spoofchecker_unknown_restriction_level.phpt b/ext/intl/tests/spoofchecker_unknown_restriction_level.phpt index e8ed48d60a3eb..6b7e9474755de 100644 --- a/ext/intl/tests/spoofchecker_unknown_restriction_level.phpt +++ b/ext/intl/tests/spoofchecker_unknown_restriction_level.phpt @@ -20,4 +20,4 @@ try { ?> --EXPECT-- -Spoofchecker::setRestrictionLevel(): Argument #1 ($level) must be one of Spoofchecker::ASCII, Spoofchecker::SINGLE_SCRIPT_RESTRICTIVE, Spoofchecker::SINGLE_HIGHLY_RESTRICTIVE, Spoofchecker::SINGLE_MODERATELY_RESTRICTIVE, Spoofchecker::SINGLE_MINIMALLY_RESTRICTIVE, or Spoofchecker::UNRESTRICTIVE +Spoofchecker::setRestrictionLevel(): Argument #1 ($level) must be one of Spoofchecker::ASCII, Spoofchecker::SINGLE_SCRIPT_RESTRICTIVE, Spoofchecker::HIGHLY_RESTRICTIVE, Spoofchecker::MODERATELY_RESTRICTIVE, Spoofchecker::MINIMALLY_RESTRICTIVE, or Spoofchecker::UNRESTRICTIVE From d8fbe40efb41e5cfcf486116ac2604f4bc597c17 Mon Sep 17 00:00:00 2001 From: Volker Dusch Date: Tue, 2 Dec 2025 12:14:28 +0100 Subject: [PATCH 107/252] PHP-8.5 is now for PHP 8.5.2-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index b89c399f3fee4..7863b56edaad6 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.5.1 +?? ??? ????, PHP 8.5.2 + + +18 Dec 2025, PHP 8.5.1 - Core: . Sync all boost.context files with release 1.86.0. (mvorisek) diff --git a/Zend/zend.h b/Zend/zend.h index e2e500022a85e..596178d8c604c 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.5.1-dev" +#define ZEND_VERSION "4.5.2-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index f81d8cf609054..ecff21dad0aa6 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.5.1-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.5.2-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 87cf375f2e0f9..659b07639e5fb 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 5 -#define PHP_RELEASE_VERSION 1 +#define PHP_RELEASE_VERSION 2 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.5.1-dev" -#define PHP_VERSION_ID 80501 +#define PHP_VERSION "8.5.2-dev" +#define PHP_VERSION_ID 80502 From f7fb13eb07e8a75c1ed24dcf4edb177879c24f5d Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 2 Dec 2025 16:26:50 +0100 Subject: [PATCH 108/252] Suppress libxml deprecations Closes GH-20538 --- ext/dom/document.c | 12 ++++++++++++ ext/libxml/libxml.c | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/ext/dom/document.c b/ext/dom/document.c index 431d69a89dcf7..15b5d98131c1d 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -1437,12 +1437,16 @@ PHP_METHOD(DOMDocument, save) doc_props = dom_get_doc_props(intern->document); format = doc_props->formatoutput; if (options & LIBXML_SAVE_NOEMPTYTAG) { + ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations") saveempty = xmlSaveNoEmptyTags; xmlSaveNoEmptyTags = 1; + ZEND_DIAGNOSTIC_IGNORED_END } bytes = xmlSaveFormatFileEnc(file, docp, NULL, format); if (options & LIBXML_SAVE_NOEMPTYTAG) { + ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations") xmlSaveNoEmptyTags = saveempty; + ZEND_DIAGNOSTIC_IGNORED_END } if (bytes == -1) { RETURN_FALSE; @@ -1489,12 +1493,16 @@ PHP_METHOD(DOMDocument, saveXML) RETURN_FALSE; } if (options & LIBXML_SAVE_NOEMPTYTAG) { + ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations") saveempty = xmlSaveNoEmptyTags; xmlSaveNoEmptyTags = 1; + ZEND_DIAGNOSTIC_IGNORED_END } xmlNodeDump(buf, docp, node, 0, format); if (options & LIBXML_SAVE_NOEMPTYTAG) { + ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations") xmlSaveNoEmptyTags = saveempty; + ZEND_DIAGNOSTIC_IGNORED_END } mem = (xmlChar*) xmlBufferContent(buf); if (!mem) { @@ -1505,13 +1513,17 @@ PHP_METHOD(DOMDocument, saveXML) xmlBufferFree(buf); } else { if (options & LIBXML_SAVE_NOEMPTYTAG) { + ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations") saveempty = xmlSaveNoEmptyTags; xmlSaveNoEmptyTags = 1; + ZEND_DIAGNOSTIC_IGNORED_END } /* Encoding is handled from the encoding property set on the document */ xmlDocDumpFormatMemory(docp, &mem, &size, format); if (options & LIBXML_SAVE_NOEMPTYTAG) { + ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations") xmlSaveNoEmptyTags = saveempty; + ZEND_DIAGNOSTIC_IGNORED_END } if (!size || !mem) { RETURN_FALSE; diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 3311346d4bcfb..f60aa1bf76b60 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -429,9 +429,11 @@ php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc) /* Allocate the Input buffer front-end. */ ret = xmlAllocParserInputBuffer(enc); if (ret != NULL) { + ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations") ret->context = context; ret->readcallback = php_libxml_streams_IO_read; ret->closecallback = php_libxml_streams_IO_close; + ZEND_DIAGNOSTIC_IGNORED_END } else php_libxml_streams_IO_close(context); @@ -679,9 +681,12 @@ static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL, } else { /* make stream not being closed when the zval is freed */ GC_ADDREF(stream->res); + + ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations") pib->context = stream; pib->readcallback = php_libxml_streams_IO_read; pib->closecallback = php_libxml_streams_IO_close; + ZEND_DIAGNOSTIC_IGNORED_END ret = xmlNewIOInputStream(context, pib, enc); if (ret == NULL) { From bd67ba66a892ea63cd70c9a7fec72c7094183037 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 6 Nov 2025 22:42:03 +0100 Subject: [PATCH 109/252] dom: Fix compile warning due to misplaced const cast --- ext/dom/document.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/dom/document.c b/ext/dom/document.c index 15b5d98131c1d..5f06f8dc2a80d 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -1120,7 +1120,7 @@ PHP_METHOD(DOMDocument, __construct) } if (encoding_len > 0) { - docp->encoding = (const xmlChar *) xmlStrdup((xmlChar *) encoding); + docp->encoding = xmlStrdup((const xmlChar *) encoding); } intern = Z_DOMOBJ_P(ZEND_THIS); From e10f6d702f7e5167ec63215efe9def4deb94ef84 Mon Sep 17 00:00:00 2001 From: Eric Mann Date: Tue, 2 Dec 2025 09:05:38 -0800 Subject: [PATCH 110/252] PHP-8.3 is now for PHP 8.3.30-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index ff4eb04e5b668..a439bfc2ad664 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.29 +?? ??? ????, PHP 8.3.30 + + +18 Dec 2025, PHP 8.3.29 - Core: . Sync all boost.context files with release 1.86.0. (mvorisek) diff --git a/Zend/zend.h b/Zend/zend.h index 9f57b50997a05..6655c9e09ad82 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.29-dev" +#define ZEND_VERSION "4.3.30-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index cb5f4e7057223..f11e3c9f56fe2 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.3.29-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.30-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index d9bd5c65f21cf..10a9cd7c6afa4 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 3 -#define PHP_RELEASE_VERSION 29 +#define PHP_RELEASE_VERSION 30 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.29-dev" -#define PHP_VERSION_ID 80329 +#define PHP_VERSION "8.3.30-dev" +#define PHP_VERSION_ID 80330 From 903fcb45c35c36857840ec7e2c5be4b3c4e0b6e9 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 19 May 2025 19:10:27 +0200 Subject: [PATCH 111/252] Fix deprecation warning for libxml SAX header (#18594) This header is deprecated, but fortunately it isn't actually used. --- ext/dom/document.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/dom/document.c b/ext/dom/document.c index 5f06f8dc2a80d..1987cc38320b8 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -22,7 +22,6 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" -#include #include #ifdef LIBXML_SCHEMAS_ENABLED #include From dd2179433c0b85a5a841ab6658781f6339b85376 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 2 Dec 2025 20:09:31 +0100 Subject: [PATCH 112/252] xml: Backport more deprecation fixes --- ext/xml/compat.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ext/xml/compat.c b/ext/xml/compat.c index 6bd69bec19616..18a7beaea2d35 100644 --- a/ext/xml/compat.c +++ b/ext/xml/compat.c @@ -473,13 +473,13 @@ XML_ParserCreate_MM(const XML_Char *encoding, const XML_Memory_Handling_Suite *m } php_libxml_sanitize_parse_ctxt_options(parser->parser); - xmlCtxtUseOptions(parser->parser, XML_PARSE_OLDSAX); + xmlCtxtUseOptions(parser->parser, XML_PARSE_OLDSAX | XML_PARSE_NOENT); - parser->parser->replaceEntities = 1; parser->parser->wellFormed = 0; if (sep != NULL) { + /* Note: sax2 flag will be set due to the magic number in `initialized` in php_xml_compat_handlers */ + ZEND_ASSERT(parser->parser->sax->initialized == XML_SAX2_MAGIC); parser->use_namespace = 1; - parser->parser->sax2 = 1; parser->_ns_separator = xmlStrdup(sep); } else { /* Reset flag as XML_SAX2_MAGIC is needed for xmlCreatePushParserCtxt @@ -565,10 +565,14 @@ XML_SetEndNamespaceDeclHandler(XML_Parser parser, XML_EndNamespaceDeclHandler en PHP_XML_API int XML_Parse(XML_Parser parser, const XML_Char *data, int data_len, int is_final) { - int error; + int error = xmlParseChunk(parser->parser, (char *) data, data_len, is_final); - error = xmlParseChunk(parser->parser, (char *) data, data_len, is_final); - return !error && parser->parser->lastError.level <= XML_ERR_WARNING; + if (!error) { + const xmlError *error_data = xmlCtxtGetLastError(parser->parser); + return !error_data || error_data->level <= XML_ERR_WARNING; + } + + return 0; } PHP_XML_API int From ad867ce651512fd53df0d60dd2f52a5a8d38c12c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 2 Dec 2025 11:48:24 -0800 Subject: [PATCH 113/252] Tweak values for test on Windows (#20633) --- ext/standard/tests/http/http_build_query/gh20583.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/http/http_build_query/gh20583.phpt b/ext/standard/tests/http/http_build_query/gh20583.phpt index c0331a830b1e0..5ea0dbe9006bc 100644 --- a/ext/standard/tests/http/http_build_query/gh20583.phpt +++ b/ext/standard/tests/http/http_build_query/gh20583.phpt @@ -10,11 +10,11 @@ if (getenv('SKIP_ASAN')) { } ?> --INI-- -zend.max_allowed_stack_size=512K +zend.max_allowed_stack_size=128K --FILE-- $a]; } try { From 688902d4553d9f483fd07a1343e419b705c77dad Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 2 Dec 2025 20:52:20 +0100 Subject: [PATCH 114/252] dom: Backport test for libxml changes --- ext/dom/tests/gh10234.phpt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/dom/tests/gh10234.phpt b/ext/dom/tests/gh10234.phpt index 5edc8fc6c1ff1..11d39cd625a79 100644 --- a/ext/dom/tests/gh10234.phpt +++ b/ext/dom/tests/gh10234.phpt @@ -55,7 +55,7 @@ $document->documentElement->textContent = "quote 'test'"; var_dump($document->documentElement->textContent); var_dump($document->saveHTML()); ?> ---EXPECT-- +--EXPECTF-- -- Attribute tests -- string(38) " " @@ -67,10 +67,10 @@ string(13) "hello & world" string(50) " " string(9) "hi" -string(54) " +string(%d) "hi<\/b>")%r> " string(12) "quote "test"" -string(45) " +string(%d) " " string(12) "quote 'test'" string(45) " From 2b04e0831adc687a4a9666b9ec78fcdfd50c25af Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 2 Dec 2025 20:59:26 +0100 Subject: [PATCH 115/252] intl: Fix tests for icu update --- ext/intl/tests/msgfmt_format_intlcalendar_variant4.phpt | 4 ++-- ext/intl/tests/timezone_getDisplayName_variant4.phpt | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/intl/tests/msgfmt_format_intlcalendar_variant4.phpt b/ext/intl/tests/msgfmt_format_intlcalendar_variant4.phpt index be470d3d0d300..f434985cd08ca 100644 --- a/ext/intl/tests/msgfmt_format_intlcalendar_variant4.phpt +++ b/ext/intl/tests/msgfmt_format_intlcalendar_variant4.phpt @@ -29,5 +29,5 @@ echo "msgf2: ", $msgf->format(array($time, 'date')), " ", */ ?> ---EXPECT-- -quinta-feira, 17 de maio de 2012 5:35:36 da tarde ptlis +--EXPECTF-- +quinta-feira, 17 de maio de 2012 5:35:36 %r(da tarde|p.m.)%r ptlis diff --git a/ext/intl/tests/timezone_getDisplayName_variant4.phpt b/ext/intl/tests/timezone_getDisplayName_variant4.phpt index 4bab07c744fa9..d3e83d92a1a75 100644 --- a/ext/intl/tests/timezone_getDisplayName_variant4.phpt +++ b/ext/intl/tests/timezone_getDisplayName_variant4.phpt @@ -23,12 +23,12 @@ var_dump($lsb->getDisplayName(false, IntlTimeZone::DISPLAY_SHORT_COMMONLY_USED)) var_dump($lsb->getDisplayName(false, IntlTimeZone::DISPLAY_GENERIC_LOCATION)); ?> ---EXPECT-- -string(3) "GMT" +--EXPECTF-- +string(%d) "%r(GMT|GMT\+0)%r" string(30) "Western European Standard Time" string(13) "Portugal Time" string(21) "Western European Time" string(5) "+0000" -string(3) "GMT" -string(3) "GMT" +string(%d) "%r(GMT|GMT\+00:00)%r" +string(%d) "%r(GMT|GMT\+0)%r" string(13) "Portugal Time" From 2ec8c29fda7819606292e4e63bc99c4c97593d4d Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Wed, 3 Dec 2025 14:52:20 +0900 Subject: [PATCH 116/252] PHP-8.4 is now for PHP 8.4.17-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 59d46ef25dd51..0d7dcc01aa0f2 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.4.16 +?? ??? ????, PHP 8.4.17 + + +18 Dec 2025, PHP 8.4.16 - Core: . Sync all boost.context files with release 1.86.0. (mvorisek) diff --git a/Zend/zend.h b/Zend/zend.h index 845bb073a890f..5267a4315f288 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.16-dev" +#define ZEND_VERSION "4.4.17-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index d26930bc82d0b..38cab6ace89ad 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.4.16-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.17-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 6b456049a0fc8..03ef1f029d8d3 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 4 -#define PHP_RELEASE_VERSION 16 +#define PHP_RELEASE_VERSION 17 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.16-dev" -#define PHP_VERSION_ID 80416 +#define PHP_VERSION "8.4.17-dev" +#define PHP_VERSION_ID 80417 From fe070fcc7568e16536ca9d5c1a8f9cd750bd45de Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 3 Dec 2025 14:51:52 +0100 Subject: [PATCH 117/252] Fix yet another xml deprecation --- ext/xml/compat.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/xml/compat.c b/ext/xml/compat.c index 3afb100eb7300..db01141bbda5b 100644 --- a/ext/xml/compat.c +++ b/ext/xml/compat.c @@ -716,6 +716,7 @@ XML_GetCurrentByteIndex(XML_Parser parser) * Although that should probably be corrected at one point? (TODO) */ xmlCharEncodingHandlerPtr encoder = NULL; xmlParserInputPtr input = parser->parser->input; + ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations") if (input->buf) { encoder = input->buf->encoder; input->buf->encoder = NULL; @@ -724,6 +725,7 @@ XML_GetCurrentByteIndex(XML_Parser parser) if (encoder) { input->buf->encoder = encoder; } + ZEND_DIAGNOSTIC_IGNORED_END /* TODO: at one point this should return long probably to make sure that files greater than 2 GiB are handled correctly. */ return (int) result; } From ff51ac161d6ccf088f5d434f914619506b8ab2b6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 2 Dec 2025 19:04:36 +0000 Subject: [PATCH 118/252] Fix GH-20603 issue on windows 32 bits. the timeout needed to be unsigned. close GH-20634 --- ext/ftp/php_ftp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c index 0df3aa6e75524..f47fe5988176b 100644 --- a/ext/ftp/php_ftp.c +++ b/ext/ftp/php_ftp.c @@ -158,7 +158,7 @@ PHP_FUNCTION(ftp_connect) RETURN_THROWS(); } - const zend_long timeoutmax = (zend_long)((double) PHP_TIMEOUT_ULL_MAX / 1000000.0); + const uint64_t timeoutmax = (uint64_t)((double) PHP_TIMEOUT_ULL_MAX / 1000000.0); if (timeout_sec <= 0) { zend_argument_value_error(3, "must be greater than 0"); @@ -166,7 +166,7 @@ PHP_FUNCTION(ftp_connect) } if (timeout_sec >= timeoutmax) { - zend_argument_value_error(3, "must be less than " ZEND_LONG_FMT, timeoutmax); + zend_argument_value_error(3, "must be less than " ZEND_ULONG_FMT, timeoutmax); RETURN_THROWS(); } From d635c8788b2a97b37c7799593223110a4bf8d7a9 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:16:54 +0100 Subject: [PATCH 119/252] xml: Fix deprecation properly by backporting the modern-but-actually-old implementation --- ext/xml/compat.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/ext/xml/compat.c b/ext/xml/compat.c index db01141bbda5b..3de77d0723e5f 100644 --- a/ext/xml/compat.c +++ b/ext/xml/compat.c @@ -711,23 +711,8 @@ XML_GetCurrentColumnNumber(XML_Parser parser) PHP_XML_API int XML_GetCurrentByteIndex(XML_Parser parser) { - /* We have to temporarily disable the encoder to satisfy the note from the manual: - * "This function returns byte index according to UTF-8 encoded text disregarding if input is in another encoding." - * Although that should probably be corrected at one point? (TODO) */ - xmlCharEncodingHandlerPtr encoder = NULL; - xmlParserInputPtr input = parser->parser->input; - ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations") - if (input->buf) { - encoder = input->buf->encoder; - input->buf->encoder = NULL; - } - long result = xmlByteConsumed(parser->parser); - if (encoder) { - input->buf->encoder = encoder; - } - ZEND_DIAGNOSTIC_IGNORED_END - /* TODO: at one point this should return long probably to make sure that files greater than 2 GiB are handled correctly. */ - return (int) result; + return parser->parser->input->consumed + + (parser->parser->input->cur - parser->parser->input->base); } PHP_XML_API int From 6a0da6dc2e53875ee98edfa4ec89a76585897a78 Mon Sep 17 00:00:00 2001 From: Oblivionsage Date: Tue, 2 Dec 2025 18:57:05 +0100 Subject: [PATCH 120/252] Fix GH-20631: Integer underflow in exif HEIF parsing When pos.size is less than 2, the subtraction pos.size - 2 causes an unsigned integer underflow, resulting in a ~4GB allocation attempt. Add minimum size check (pos.size >= 2) to prevent the underflow. Closes GH-20630. --- NEWS | 3 +++ ext/exif/exif.c | 2 +- ext/exif/tests/heic_iloc_underflow.phpt | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 ext/exif/tests/heic_iloc_underflow.phpt diff --git a/NEWS b/NEWS index 7863b56edaad6..7dc690d675833 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.5.2 +- EXIF: + . Fixed bug GH-20631 (Integer underflow in exif HEIF parsing + when pos.size < 2). (Oblivionsage) 18 Dec 2025, PHP 8.5.1 diff --git a/ext/exif/exif.c b/ext/exif/exif.c index d0c16413062ae..6ed86c88e5629 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -4421,7 +4421,7 @@ static bool exif_scan_HEIF_header(image_info_type *ImageInfo, unsigned char *buf if (exif_read_from_stream_file_looped(ImageInfo->infile, (char*)(data + remain), limit - remain) == limit - remain) { exif_isobmff_parse_meta(data, data + limit, &pos); } - if ((pos.size) && + if ((pos.size >= 2) && (pos.size < ImageInfo->FileSize) && (ImageInfo->FileSize - pos.size >= pos.offset) && (php_stream_seek(ImageInfo->infile, pos.offset + 2, SEEK_SET) >= 0)) { diff --git a/ext/exif/tests/heic_iloc_underflow.phpt b/ext/exif/tests/heic_iloc_underflow.phpt new file mode 100644 index 0000000000000..9dd1878b60dd7 --- /dev/null +++ b/ext/exif/tests/heic_iloc_underflow.phpt @@ -0,0 +1,19 @@ +--TEST-- +HEIC iloc extent_length underflow +--EXTENSIONS-- +exif +--FILE-- + +--CLEAN-- + +--EXPECTF-- +Warning: exif_read_data(heic_iloc_underflow.heic): Invalid HEIF file in %s on line %d +bool(false) From 77f2d1284942aa69db806239954875d9b5f510e0 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com> Date: Thu, 4 Dec 2025 09:09:30 +0100 Subject: [PATCH 121/252] Fix GH-20370: forbid user stream filters to violate typed property constraints (#20373) --- NEWS | 2 + ext/standard/tests/filters/gh20370.phpt | 44 ++++++++++++++++ .../gh20370_dynamic_stream_property.phpt | 51 +++++++++++++++++++ .../filters/gh20370_no_stream_property.phpt | 37 ++++++++++++++ .../gh20370_private_stream_property.phpt | 38 ++++++++++++++ ext/standard/user_filters.c | 40 +++++++++++---- 6 files changed, 203 insertions(+), 9 deletions(-) create mode 100644 ext/standard/tests/filters/gh20370.phpt create mode 100644 ext/standard/tests/filters/gh20370_dynamic_stream_property.phpt create mode 100644 ext/standard/tests/filters/gh20370_no_stream_property.phpt create mode 100644 ext/standard/tests/filters/gh20370_private_stream_property.phpt diff --git a/NEWS b/NEWS index a439bfc2ad664..8cb21eb94fb8d 100644 --- a/NEWS +++ b/NEWS @@ -176,6 +176,8 @@ PHP NEWS - Streams: . Fixed bug GH-19798: XP_SOCKET XP_SSL (Socket stream modules): Incorrect condition for Win32/Win64. (Jakub Zelenka) + . Fixed bug GH-20370 (User stream filters could violate typed property + constraints). (alexandre-daubois) - Tidy: . Fixed GH-19021 (improved tidyOptGetCategory detection). diff --git a/ext/standard/tests/filters/gh20370.phpt b/ext/standard/tests/filters/gh20370.phpt new file mode 100644 index 0000000000000..abcf49bfc9f4c --- /dev/null +++ b/ext/standard/tests/filters/gh20370.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-20370 (User filters should respect typed properties) +--FILE-- +datalen; + stream_bucket_append($out, $bucket); + } + return PSFS_PASS_ON; + } +} + +stream_filter_register("pass", "pass_filter"); +$fp = fopen("php://memory", "w"); +stream_filter_append($fp, "pass"); + +try { + fwrite($fp, "data"); +} catch (TypeError $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +try { + fclose($fp); +} catch (TypeError $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +unset($fp); // prevent cleanup at shutdown + +?> +--EXPECTF-- +Warning: fwrite(): Unprocessed filter buckets remaining on input brigade in %s on line %d +TypeError: Cannot assign resource to property pass_filter::$stream of type int +TypeError: Cannot assign resource to property pass_filter::$stream of type int diff --git a/ext/standard/tests/filters/gh20370_dynamic_stream_property.phpt b/ext/standard/tests/filters/gh20370_dynamic_stream_property.phpt new file mode 100644 index 0000000000000..97f24b854c5ea --- /dev/null +++ b/ext/standard/tests/filters/gh20370_dynamic_stream_property.phpt @@ -0,0 +1,51 @@ +--TEST-- +GH-20370 (User filters should update dynamic stream property if it exists) +--FILE-- +stream = null; + return true; + } + + function filter($in, $out, &$consumed, $closing): int + { + while ($bucket = stream_bucket_make_writeable($in)) { + $consumed += $bucket->datalen; + stream_bucket_append($out, $bucket); + } + var_dump(property_exists($this, 'stream')); + if (is_resource($this->stream)) { + var_dump(get_resource_type($this->stream)); + } + return PSFS_PASS_ON; + } +} + +stream_filter_register("pass", "pass_filter"); +$fp = fopen("php://memory", "w"); +stream_filter_append($fp, "pass"); + +fwrite($fp, "data"); +rewind($fp); +echo fread($fp, 1024) . "\n"; + +?> +--EXPECTF-- +bool(true) +string(6) "stream" +bool(true) +string(6) "stream" +bool(true) +string(6) "stream" +bool(true) +string(6) "stream" +data +bool(true) diff --git a/ext/standard/tests/filters/gh20370_no_stream_property.phpt b/ext/standard/tests/filters/gh20370_no_stream_property.phpt new file mode 100644 index 0000000000000..1b52c4c4155b8 --- /dev/null +++ b/ext/standard/tests/filters/gh20370_no_stream_property.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-20370 (User filters should not create stream property if not declared) +--FILE-- +datalen; + stream_bucket_append($out, $bucket); + } + + var_dump(property_exists($this, 'stream')); + return PSFS_PASS_ON; + } +} + +stream_filter_register("pass", "pass_filter"); +$fp = fopen("php://memory", "w"); +stream_filter_append($fp, "pass"); +fwrite($fp, "data"); +rewind($fp); +echo fread($fp, 1024) . "\n"; + +?> +--EXPECT-- +bool(false) +bool(false) +bool(false) +bool(false) +data +bool(false) diff --git a/ext/standard/tests/filters/gh20370_private_stream_property.phpt b/ext/standard/tests/filters/gh20370_private_stream_property.phpt new file mode 100644 index 0000000000000..bfbbba6099ad0 --- /dev/null +++ b/ext/standard/tests/filters/gh20370_private_stream_property.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-20370 (User filters should handle private stream property correctly) +--FILE-- +datalen; + stream_bucket_append($out, $bucket); + } + return PSFS_PASS_ON; + } + + function onClose() + { + var_dump($this->stream); // should be null + } +} + +stream_filter_register("pass", "pass_filter"); +$fp = fopen("php://memory", "w"); +stream_filter_append($fp, "pass", STREAM_FILTER_WRITE); + +fwrite($fp, "data"); +rewind($fp); +echo fread($fp, 1024) . "\n"; + +?> +--EXPECT-- +data +NULL diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index 962eaaba7d6b0..9691b8f95ae21 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -147,14 +147,31 @@ php_stream_filter_status_t userfilter_filter( uint32_t orig_no_fclose = stream->flags & PHP_STREAM_FLAG_NO_FCLOSE; stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE; - zval *stream_prop = zend_hash_str_find_ind(Z_OBJPROP_P(obj), "stream", sizeof("stream")-1); - if (stream_prop) { - /* Give the userfilter class a hook back to the stream */ - zval_ptr_dtor(stream_prop); - php_stream_to_zval(stream, stream_prop); - Z_ADDREF_P(stream_prop); + /* Give the userfilter class a hook back to the stream */ + zend_class_entry *old_scope = EG(fake_scope); + EG(fake_scope) = Z_OBJCE_P(obj); + + zend_string *stream_name = ZSTR_INIT_LITERAL("stream", 0); + bool stream_property_exists = Z_OBJ_HT_P(obj)->has_property(Z_OBJ_P(obj), stream_name, ZEND_PROPERTY_EXISTS, NULL); + if (stream_property_exists) { + zval stream_zval; + php_stream_to_zval(stream, &stream_zval); + zend_update_property_ex(Z_OBJCE_P(obj), Z_OBJ_P(obj), stream_name, &stream_zval); + /* If property update threw an exception, skip filter execution */ + if (EG(exception)) { + EG(fake_scope) = old_scope; + if (buckets_in->head) { + php_error_docref(NULL, E_WARNING, "Unprocessed filter buckets remaining on input brigade"); + } + zend_string_release(stream_name); + stream->flags &= ~PHP_STREAM_FLAG_NO_FCLOSE; + stream->flags |= orig_no_fclose; + return PSFS_ERR_FATAL; + } } + EG(fake_scope) = old_scope; + ZVAL_STRINGL(&func_name, "filter", sizeof("filter")-1); /* Setup calling arguments */ @@ -195,11 +212,16 @@ php_stream_filter_status_t userfilter_filter( /* filter resources are cleaned up by the stream destructor, * keeping a reference to the stream resource here would prevent it - * from being destroyed properly */ - if (stream_prop) { - convert_to_null(stream_prop); + * from being destroyed properly. + * Since the property accepted a resource assignment above, it must have + * no type hint or be typed as mixed, so we can safely assign null. + */ + if (stream_property_exists) { + zend_update_property_null(Z_OBJCE_P(obj), Z_OBJ_P(obj), ZSTR_VAL(stream_name), ZSTR_LEN(stream_name)); } + zend_string_release(stream_name); + zval_ptr_dtor(&args[3]); zval_ptr_dtor(&args[2]); zval_ptr_dtor(&args[1]); From f39ae2fc67a1905a90783edc44aa699e6a6bb5f1 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 4 Dec 2025 13:51:16 +0100 Subject: [PATCH 122/252] Fix warning on 8.5 --- ext/standard/user_filters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index 400546c06e24f..9f577029f69ef 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -149,7 +149,7 @@ static php_stream_filter_status_t userfilter_filter( stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE; /* Give the userfilter class a hook back to the stream */ - zend_class_entry *old_scope = EG(fake_scope); + const zend_class_entry *old_scope = EG(fake_scope); EG(fake_scope) = Z_OBJCE_P(obj); zend_string *stream_name = ZSTR_INIT_LITERAL("stream", 0); From fc592c78f22c6e542f066c4c579c6a7da597f8b9 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com> Date: Thu, 4 Dec 2025 17:09:36 +0100 Subject: [PATCH 123/252] WeakMap: convert zend_parse_parameters_none() to fast ZPP (#20643) --- Zend/zend_weakrefs.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index 83a28808811d5..8c1263885bf6c 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -776,9 +776,7 @@ ZEND_METHOD(WeakMap, offsetUnset) ZEND_METHOD(WeakMap, count) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); zend_long count; zend_weakmap_count_elements(Z_OBJ_P(ZEND_THIS), &count); @@ -787,9 +785,7 @@ ZEND_METHOD(WeakMap, count) ZEND_METHOD(WeakMap, getIterator) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); zend_create_internal_iterator_zval(return_value, ZEND_THIS); } From 9f654decdc12cad9575c6ebecb28adf0172e20bd Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 29 Nov 2025 22:19:37 +0000 Subject: [PATCH 124/252] Fix GH-20622: imagestring/imagestringup overflow/underflow. close GH-20623 --- NEWS | 3 +++ ext/gd/gd.c | 9 +++++---- ext/gd/tests/gh20622.phpt | 13 +++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 ext/gd/tests/gh20622.phpt diff --git a/NEWS b/NEWS index 8cb21eb94fb8d..d6b832917dfd9 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.30 +- GD: + . Fixed bug GH-20622 (imagestring/imagestringup overflow). (David Carlier) + 18 Dec 2025, PHP 8.3.29 diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 925d64f01c5e7..5efc8e4d52cdb 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -2763,7 +2763,8 @@ static void php_imagechar(INTERNAL_FUNCTION_PARAMETERS, int mode) char *C; size_t C_len; gdImagePtr im; - int ch = 0, col, x, y, i, l = 0; + int ch = 0, col, i, l = 0; + unsigned int x, y; unsigned char *str = NULL; zend_object *font_obj = NULL; zend_long font_int = 0; @@ -2795,21 +2796,21 @@ static void php_imagechar(INTERNAL_FUNCTION_PARAMETERS, int mode) switch (mode) { case 0: - gdImageChar(im, font, x, y, ch, col); + gdImageChar(im, font, (int)x, (int)y, ch, col); break; case 1: php_gdimagecharup(im, font, x, y, ch, col); break; case 2: for (i = 0; (i < l); i++) { - gdImageChar(im, font, x, y, (int) ((unsigned char) str[i]), col); + gdImageChar(im, font, (int)x, (int)y, (int) ((unsigned char) str[i]), col); x += font->w; } break; case 3: { for (i = 0; (i < l); i++) { /* php_gdimagecharup(im, font, x, y, (int) str[i], col); */ - gdImageCharUp(im, font, x, y, (int) str[i], col); + gdImageCharUp(im, font, (int)x, (int)y, (int) str[i], col); y -= font->w; } break; diff --git a/ext/gd/tests/gh20622.phpt b/ext/gd/tests/gh20622.phpt new file mode 100644 index 0000000000000..42109ddc13e4d --- /dev/null +++ b/ext/gd/tests/gh20622.phpt @@ -0,0 +1,13 @@ +--TEST-- +GH-20622 (imagestring/imagestringup overflow/underflow) +--EXTENSIONS-- +gd +--FILE-- + +--EXPECT-- +OK From e910bbf14404f55ec2e18a571a0f62c1e8e78c72 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 4 Dec 2025 23:25:29 +0000 Subject: [PATCH 125/252] ext/gd: fix build --- ext/gd/gd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 35c1b8b50a76f..2456afa15c7a7 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -2976,7 +2976,7 @@ static void php_imagechar(INTERNAL_FUNCTION_PARAMETERS, int mode) zend_long X, Y, COL; zend_string *C; gdImagePtr im; - int ch = 0, col, i, l = 0; + int ch = 0, col, i; unsigned int x, y; size_t l = 0; unsigned char *str = NULL; From 26e0bfa34138b029910c690747ffc4183f6ba3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schleusner?= Date: Fri, 5 Dec 2025 20:20:40 +0100 Subject: [PATCH 126/252] Fixed some typos in docs and fpm pool config (GH-20587) * Fix some typos in docs * Fix typo in output header example of fpm pool config --- docs/release-process.md | 8 ++++---- docs/source/miscellaneous/stubs.rst | 2 +- docs/source/miscellaneous/writing-tests.rst | 8 ++++---- sapi/fpm/www.conf.in | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/release-process.md b/docs/release-process.md index 08b95df53513f..e053728feb0aa 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -169,10 +169,10 @@ slightly different steps. We'll call attention where the steps differ. 4. Using your local-only release branch, bump the version numbers in `main/php_version.h`, `Zend/zend.h`, `configure.ac`, and possibly `NEWS`. - + The date for NEWS should be the date of the announcement (Thursday), *not* the date of the tagging (Tuesday). - + For examples, see [Update versions for PHP 8.1.0beta3][] (for a pre-GA example) or [Update versions for PHP 8.1.6RC1][] along with [Update NEWS for PHP 8.1.6RC1][] (for a post-GA example). @@ -506,8 +506,8 @@ slightly different steps. We'll call attention where the steps differ. You can send a PR to [toot-together](https://github.com/derickr/toot-together/) with highlights from the NEWS file yourself, if you want. - * [Annonce 8.5.0alpha1](https://github.com/derickr/toot-together/pull/42) - * [Annonce 8.5.0alpha2](https://github.com/derickr/toot-together/pull/47) + * [Announce 8.5.0alpha1](https://github.com/derickr/toot-together/pull/42) + * [Announce 8.5.0alpha2](https://github.com/derickr/toot-together/pull/47) We post to [@php@fosstodon.org](https://fosstodon.org/@php). diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst index 616d306895542..395034afe8d5d 100644 --- a/docs/source/miscellaneous/stubs.rst +++ b/docs/source/miscellaneous/stubs.rst @@ -489,7 +489,7 @@ generated. You can include this file conditionally, such as: #endif When ``@generate-legacy-arginfo`` is passed the minimum PHP version ID that needs to be supported, -then only one arginfo file is going to be generated, and ``#if`` prepocessor directives will ensure +then only one arginfo file is going to be generated, and ``#if`` preprocessor directives will ensure compatibility with all the required PHP 8 versions. PHP Version IDs are as follows: ``80000`` for PHP 8.0, ``80100`` for PHP PHP 8.1, ``80200`` for PHP diff --git a/docs/source/miscellaneous/writing-tests.rst b/docs/source/miscellaneous/writing-tests.rst index fd09d80f1275e..4c46dad638b6c 100644 --- a/docs/source/miscellaneous/writing-tests.rst +++ b/docs/source/miscellaneous/writing-tests.rst @@ -132,7 +132,7 @@ below illustrates a minimal test. string(32) "# hello All, I sAid hi planet! #" As you can see the file is divided into several sections. The TEST section holds a one line title of -the phpt test, this should be a simple description and shouldn't ever excede one line, if you need +the phpt test, this should be a simple description and shouldn't ever exceed one line, if you need to write more explanation add comments in the body of the test case. The phpt files name is used when generating a .php file. The FILE section is used as the body of the .php file, so don't forget to open and close your php tags. The EXPECT section is the part used as a comparison to see if the @@ -580,7 +580,7 @@ Example 1 (snippet): .. code:: text --DESCRIPTION-- - This test covers both valid and invalid usages of filter_input() with INPUT_GET and INPUT_POST data and several differnet filter sanitizers. + This test covers both valid and invalid usages of filter_input() with INPUT_GET and INPUT_POST data and several different filter sanitizers. Example 1 (full): :ref:`sample001.phpt` @@ -1310,7 +1310,7 @@ Example 1 (full): :ref:`sample017.phpt` ``--FLAKY--`` ------------- -**Description:** This section identifies this test as one that occassionally fails. If the test +**Description:** This section identifies this test as one that occasionally fails. If the test actually fails, it will be retried one more time, and that result will be reported. The section should include a brief description of why the test is flaky. Reasons for this include tests that rely on relatively precise timing, or temporary disc states. Available as of PHP 8.1.22 and 8.2.9, @@ -1884,7 +1884,7 @@ sample001.phpt --DESCRIPTION-- This test covers both valid and invalid usages of filter_input() with INPUT_GET and INPUT_POST data - and several differnt filter sanitizers. + and several different filter sanitizers. --CREDITS-- Felipe Pena --INI-- diff --git a/sapi/fpm/www.conf.in b/sapi/fpm/www.conf.in index 69df3e6630047..f39e371ea70fe 100644 --- a/sapi/fpm/www.conf.in +++ b/sapi/fpm/www.conf.in @@ -329,7 +329,7 @@ pm.max_spare_servers = 3 ; it must be associated with embraces to specify the name of the header: ; - %{Content-Type}o ; - %{X-Powered-By}o -; - %{Transfert-Encoding}o +; - %{Transfer-Encoding}o ; - .... ; %p: PID of the child that serviced the request ; %P: PID of the parent of the child that serviced the request From 9ed85aaf9ae9b6ce94f28665bd8d167319c1f217 Mon Sep 17 00:00:00 2001 From: Sadetdin EYILI Date: Fri, 5 Dec 2025 20:22:43 +0100 Subject: [PATCH 127/252] Add php.github.io/php-src to CONTRIBUTING.md Technical Resources list (GH-20580) --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index caaca316575e7..32e8098c08e48 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -100,6 +100,7 @@ scattered across different websites, and often outdated. Nonetheless, they can provide a good starting point for learning about the fundamentals of the code base. +* https://php.github.io/php-src/ * https://www.phpinternalsbook.com/ * https://www.npopov.com/ * [Internal value representation](https://www.npopov.com/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html), [part 2](https://www.npopov.com/2015/06/19/Internal-value-representation-in-PHP-7-part-2.html) From 1f1147a66633f5a3ef11d3ab8429a6e3695c68f2 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 29 Nov 2025 21:32:19 +0000 Subject: [PATCH 128/252] Fix GH-20620: bzcompress() overflow on large source size. close GH-20621 --- NEWS | 4 ++++ ext/bz2/bz2.c | 10 +++++++++- ext/bz2/tests/gh20620.phpt | 21 +++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 ext/bz2/tests/gh20620.phpt diff --git a/NEWS b/NEWS index d6b832917dfd9..472f57a38f6bb 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.30 +- Bz2: + . Fixed bug GH-20620 (bzcompress overflow on large source size). + (David Carlier) + - GD: . Fixed bug GH-20622 (imagestring/imagestringup overflow). (David Carlier) diff --git a/ext/bz2/bz2.c b/ext/bz2/bz2.c index 7f546f95cd0eb..060a2df640048 100644 --- a/ext/bz2/bz2.c +++ b/ext/bz2/bz2.c @@ -459,7 +459,15 @@ PHP_FUNCTION(bzcompress) + .01 x length of data + 600 which is the largest size the results of the compression could possibly be, at least that's what the libbz2 docs say (thanks to jeremy@nirvani.net for pointing this out). */ - dest_len = (unsigned int) (source_len + (0.01 * source_len) + 600); + size_t chunk_len = source_len + source_len / 100 + 600; + const size_t min = MIN(ZSTR_MAX_LEN, UINT_MAX); + + if (chunk_len < source_len || chunk_len > min) { + zend_argument_value_error(1, "must have a length less than or equal to %zu", min); + RETURN_THROWS(); + } + + dest_len = (unsigned int) chunk_len; /* Allocate the destination buffer */ dest = zend_string_alloc(dest_len, 0); diff --git a/ext/bz2/tests/gh20620.phpt b/ext/bz2/tests/gh20620.phpt new file mode 100644 index 0000000000000..351ba488b2bd6 --- /dev/null +++ b/ext/bz2/tests/gh20620.phpt @@ -0,0 +1,21 @@ +--TEST-- +Bug GH-20620 (bzcompress with large source) +--EXTENSIONS-- +bz2 +--SKIPIF-- + +--INI-- +memory_limit=-1 +--FILE-- +getMessage(), PHP_EOL; +} +?> +--EXPECTF-- +bzcompress(): Argument #1 ($data) must have a length less than or equal to %d From 5528df1a2080e249337ee9565f03058c52d55b87 Mon Sep 17 00:00:00 2001 From: Takuya Aramaki Date: Sun, 7 Dec 2025 02:06:19 +0900 Subject: [PATCH 129/252] Fix TypeError message of `setlocale()` (#20625) --- ext/standard/string.c | 2 +- ext/standard/tests/strings/gh18823_strict.phpt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/standard/string.c b/ext/standard/string.c index 8b76a179061cc..9b4681367a39a 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -4957,7 +4957,7 @@ PHP_FUNCTION(setlocale) for (uint32_t i = 0; i < num_args; i++) { if (UNEXPECTED(Z_TYPE(args[i]) != IS_ARRAY && !zend_parse_arg_str(&args[i], &strings[i], true, i + 2))) { - zend_wrong_parameter_type_error(i + 2, Z_EXPECTED_ARRAY_OR_STRING, &args[i]); + zend_wrong_parameter_type_error(i + 2, Z_EXPECTED_ARRAY_OR_STRING_OR_NULL, &args[i]); goto out; } } diff --git a/ext/standard/tests/strings/gh18823_strict.phpt b/ext/standard/tests/strings/gh18823_strict.phpt index 80b21d2093172..3735eab006707 100644 --- a/ext/standard/tests/strings/gh18823_strict.phpt +++ b/ext/standard/tests/strings/gh18823_strict.phpt @@ -15,5 +15,5 @@ try { } ?> --EXPECT-- -setlocale(): Argument #2 ($locales) must be of type array|string, int given -setlocale(): Argument #3 must be of type array|string, int given +setlocale(): Argument #2 ($locales) must be of type array|string|null, int given +setlocale(): Argument #3 must be of type array|string|null, int given From 88a323ecd0918e854c8c2b73eb9752671eab72ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Sun, 7 Dec 2025 13:18:54 +0100 Subject: [PATCH 130/252] Fix Zend/zend_simd.h file whitespaces sync (#20486) --- Zend/zend_simd.h | 778 +++++++++++++++++++++++------------------------ 1 file changed, 389 insertions(+), 389 deletions(-) diff --git a/Zend/zend_simd.h b/Zend/zend_simd.h index 9bd16ce9e9afb..2f0df203733ca 100644 --- a/Zend/zend_simd.h +++ b/Zend/zend_simd.h @@ -1,17 +1,17 @@ /******************************************************************************** * MIT License * Copyright (c) 2025 Saki Takamachi - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -22,389 +22,389 @@ *********************************************************************************/ - #ifndef XSSE_H - #define XSSE_H - - #define XSSE_VERSION 10000 - - #ifdef _MSC_VER - # define XSSE_FORCE_INLINE __forceinline - #elif defined(__GNUC__) || defined(__clang__) - # define XSSE_FORCE_INLINE inline __attribute__((always_inline)) - # define XSSE_HAS_MACRO_EXTENSION - #else - # define XSSE_FORCE_INLINE inline - #endif - - - #if defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) - #include - #define XSSE2 - - - #elif defined(__aarch64__) || defined(_M_ARM64) - #include - #define XSSE2 - - typedef int8x16_t __m128i; - - - /***************************************************************************** - * Load / Store * - *****************************************************************************/ - - #define _mm_set_epi8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) \ - ((int8x16_t) { \ - (int8_t) (x15), (int8_t) (x14), (int8_t) (x13), (int8_t) (x12), \ - (int8_t) (x11), (int8_t) (x10), (int8_t) (x9), (int8_t) (x8), \ - (int8_t) (x7), (int8_t) (x6), (int8_t) (x5), (int8_t) (x4), \ - (int8_t) (x3), (int8_t) (x2), (int8_t) (x1), (int8_t) (x0) }) - #define _mm_set_epi16(x0, x1, x2, x3, x4, x5, x6, x7) \ - (vreinterpretq_s8_s16((int16x8_t) { \ - (int16_t) (x7), (int16_t) (x6), (int16_t) (x5), (int16_t) (x4), \ - (int16_t) (x3), (int16_t) (x2), (int16_t) (x1), (int16_t) (x0) })) - #define _mm_set_epi32(x0, x1, x2, x3) \ - (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x3), (int32_t) (x2), (int32_t) (x1), (int32_t) (x0) })) - #define _mm_set_epi64x(x0, x1) (vreinterpretq_s8_s64((int64x2_t) { (int64_t) (x1), (int64_t) (x0) })) - #define _mm_set1_epi8(x) (vdupq_n_s8((int8_t) (x))) - #define _mm_set1_epi16(x) (vreinterpretq_s8_s16(vdupq_n_s16((int16_t) (x)))) - #define _mm_set1_epi32(x) (vreinterpretq_s8_s32(vdupq_n_s32((int32_t) (x)))) - #define _mm_set1_epi64x(x) (vreinterpretq_s8_s64(vdupq_n_s64((int64_t) (x)))) - - #define _mm_setr_epi8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) \ - ((int8x16_t) { \ - (int8_t) (x0), (int8_t) (x1), (int8_t) (x2), (int8_t) (x3), \ - (int8_t) (x4), (int8_t) (x5), (int8_t) (x6), (int8_t) (x7), \ - (int8_t) (x8), (int8_t) (x9), (int8_t) (x10), (int8_t) (x11), \ - (int8_t) (x12), (int8_t) (x13), (int8_t) (x14), (int8_t) (x15) }) - #define _mm_setr_epi16(x0, x1, x2, x3, x4, x5, x6, x7) \ - (vreinterpretq_s8_s16((int16x8_t) { \ - (int16_t) (x0), (int16_t) (x1), (int16_t) (x2), (int16_t) (x3), \ - (int16_t) (x4), (int16_t) (x5), (int16_t) (x6), (int16_t) (x7) })) - #define _mm_setr_epi32(x0, x1, x2, x3) \ - (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x0), (int32_t) (x1), (int32_t) (x2), (int32_t) (x3) })) - - #define _mm_setzero_si128() (vdupq_n_s8(0)) - - #define _mm_load_si128(x) (vld1q_s8((const int8_t *) (x))) - #define _mm_loadu_si128(x) _mm_load_si128(x) - - #define _mm_store_si128(to, x) (vst1q_s8((int8_t *) (to), x)) - #define _mm_storeu_si128(to, x) _mm_store_si128(to, x) - #define _mm_stream_si128(to, x) _mm_store_si128(to, x) - #define _mm_stream_si32(to, x) (*(volatile int32_t *)(to) = (int32_t)(x)) - - - /***************************************************************************** - * Bit shift / Bit wise * - *****************************************************************************/ - - #define _mm_or_si128(a, b) (vorrq_s8((a), (b))) - #define _mm_xor_si128(a, b) (veorq_s8((a), (b))) - #define _mm_and_si128(a, b) (vandq_s8((a), (b))) - #define _mm_andnot_si128(a, b) (vbicq_s8((b), (a))) - - #define _mm_slli_epi16(x, count) (vreinterpretq_s8_u16(vshlq_n_u16(vreinterpretq_u16_s8(x), (count)))) - #define _mm_slli_epi32(x, count) (vreinterpretq_s8_u32(vshlq_n_u32(vreinterpretq_u32_s8(x), (count)))) - #define _mm_slli_epi64(x, count) (vreinterpretq_s8_u64(vshlq_n_u64(vreinterpretq_u64_s8(x), (count)))) - static XSSE_FORCE_INLINE __m128i _mm_sll_epi16(__m128i x, __m128i count) - { - uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); - return vreinterpretq_s8_u16( - vshlq_u16(vreinterpretq_u16_s8(x), vdupq_n_s16((int16_t) shift)) - ); - } - static XSSE_FORCE_INLINE __m128i _mm_sll_epi32(__m128i x, __m128i count) - { - uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); - return vreinterpretq_s8_u32( - vshlq_u32(vreinterpretq_u32_s8(x), vdupq_n_s32((int32_t) shift)) - ); - } - static XSSE_FORCE_INLINE __m128i _mm_sll_epi64(__m128i x, __m128i count) - { - uint64_t shift = (uint64_t) vgetq_lane_s64(vreinterpretq_s64_s8(count), 0); - return vreinterpretq_s8_u64( - vshlq_u64(vreinterpretq_u64_s8(x), vdupq_n_s64((int64_t) shift)) - ); - } - - #define _mm_slli_si128(x, imm) \ - ((imm) >= 16 ? vdupq_n_s8(0) : vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 16 - (imm)))) - - #define _mm_srai_epi16(x, count) (vreinterpretq_s8_s16(vshrq_n_s16(vreinterpretq_s16_s8(x), (count)))) - #define _mm_srai_epi32(x, count) (vreinterpretq_s8_s32(vshrq_n_s32(vreinterpretq_s32_s8(x), (count)))) - static inline __m128i _mm_sra_epi16(__m128i x, __m128i count) - { - uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); - return vreinterpretq_s8_s16( - vshlq_s16(vreinterpretq_s16_s8(x), vdupq_n_s16(-(int16_t) shift)) - ); - } - static inline __m128i _mm_sra_epi32(__m128i x, __m128i count) - { - uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); - return vreinterpretq_s8_s32( - vshlq_s32(vreinterpretq_s32_s8(x), vdupq_n_s32(-(int32_t) shift)) - ); - } - - #define _mm_srli_epi16(x, count) (vreinterpretq_s8_u16(vshrq_n_u16(vreinterpretq_u16_s8(x), (count)))) - #define _mm_srli_epi32(x, count) (vreinterpretq_s8_u32(vshrq_n_u32(vreinterpretq_u32_s8(x), (count)))) - #define _mm_srli_epi64(x, count) (vreinterpretq_s8_u64(vshrq_n_u64(vreinterpretq_u64_s8(x), (count)))) - static XSSE_FORCE_INLINE __m128i _mm_srl_epi16(__m128i x, __m128i count) - { - uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); - return vreinterpretq_s8_u16( - vshlq_u16(vreinterpretq_u16_s8(x), vdupq_n_s16(-(int16_t) shift)) - ); - } - static XSSE_FORCE_INLINE __m128i _mm_srl_epi32(__m128i x, __m128i count) - { - uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); - return vreinterpretq_s8_u32( - vshlq_u32(vreinterpretq_u32_s8(x), vdupq_n_s32(-(int32_t) shift)) - ); - } - static XSSE_FORCE_INLINE __m128i _mm_srl_epi64(__m128i x, __m128i count) - { - uint64_t shift = (uint64_t) vgetq_lane_s64(vreinterpretq_s64_s8(count), 0); - return vreinterpretq_s8_u64( - vshlq_u64(vreinterpretq_u64_s8(x), vdupq_n_s64(-(int64_t) shift)) - ); - } - - #define _mm_srli_si128(x, imm) \ - ((imm) >= 16 ? vdupq_n_s8(0) : vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), (imm)))) - - - /***************************************************************************** - * Integer Arithmetic Operations * - *****************************************************************************/ - - /** - * In practice, there is no problem, but a runtime error for signed integer overflow is triggered by UBSAN, - * so perform the calculation as unsigned. Since it is optimized at compile time, there are no unnecessary casts at runtime. - */ - #define _mm_add_epi8(a, b) (vreinterpretq_s8_u8(vaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) - #define _mm_add_epi16(a, b) (vreinterpretq_s8_u16(vaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) - #define _mm_add_epi32(a, b) (vreinterpretq_s8_u32(vaddq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) - #define _mm_add_epi64(a, b) (vreinterpretq_s8_u64(vaddq_u64(vreinterpretq_u64_s8(a), vreinterpretq_u64_s8(b)))) - - #define _mm_adds_epi8(a, b) (vqaddq_s8((a), (b))) - #define _mm_adds_epi16(a, b) (vreinterpretq_s8_s16(vqaddq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) - #define _mm_adds_epu8(a, b) (vreinterpretq_s8_u8(vqaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) - #define _mm_adds_epu16(a, b) (vreinterpretq_s8_u16(vqaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) - - #define _mm_avg_epu8(a, b) (vreinterpretq_s8_u8(vrhaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) - #define _mm_avg_epu16(a, b) (vreinterpretq_s8_u16(vrhaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) - - static XSSE_FORCE_INLINE __m128i _mm_madd_epi16(__m128i a, __m128i b) - { - int32x4_t mul_lo = vmull_s16(vget_low_s16(vreinterpretq_s16_s8(a)), vget_low_s16(vreinterpretq_s16_s8(b))); - int32x4_t mul_hi = vmull_s16(vget_high_s16(vreinterpretq_s16_s8(a)), vget_high_s16(vreinterpretq_s16_s8(b))); - - return vreinterpretq_s8_s32(vcombine_s32( - vpadd_s32(vget_low_s32(mul_lo), vget_high_s32(mul_lo)), - vpadd_s32(vget_low_s32(mul_hi), vget_high_s32(mul_hi)) - )); - } - - #define _mm_max_epu8(a, b) (vreinterpretq_s8_u8(vmaxq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) - #define _mm_max_epi16(a, b) (vreinterpretq_s8_s16(vmaxq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) - #define _mm_min_epu8(a, b) (vreinterpretq_s8_u8(vminq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) - #define _mm_min_epi16(a, b) (vreinterpretq_s8_s16(vminq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) - - static XSSE_FORCE_INLINE __m128i _mm_mulhi_epi16(__m128i a, __m128i b) - { - int32x4_t lo = vmull_s16(vget_low_s16(vreinterpretq_s16_s8(a)), vget_low_s16(vreinterpretq_s16_s8(b))); - int32x4_t hi = vmull_s16(vget_high_s16(vreinterpretq_s16_s8(a)), vget_high_s16(vreinterpretq_s16_s8(b))); - return vreinterpretq_s8_s16(vcombine_s16(vshrn_n_s32(lo, 16), vshrn_n_s32(hi, 16))); - } - static XSSE_FORCE_INLINE __m128i _mm_mulhi_epu16(__m128i a, __m128i b) - { - uint32x4_t lo = vmull_u16(vget_low_u16(vreinterpretq_u16_s8(a)), vget_low_u16(vreinterpretq_u16_s8(b))); - uint32x4_t hi = vmull_u16(vget_high_u16(vreinterpretq_u16_s8(a)), vget_high_u16(vreinterpretq_u16_s8(b))); - return vreinterpretq_s8_u16(vcombine_u16(vshrn_n_u32(lo, 16), vshrn_n_u32(hi, 16))); - } - static XSSE_FORCE_INLINE __m128i _mm_mullo_epi16(__m128i a, __m128i b) - { - int32x4_t lo = vmull_s16(vget_low_s16(vreinterpretq_s16_s8(a)), vget_low_s16(vreinterpretq_s16_s8(b))); - int32x4_t hi = vmull_s16(vget_high_s16(vreinterpretq_s16_s8(a)), vget_high_s16(vreinterpretq_s16_s8(b))); - return vreinterpretq_s8_s16(vcombine_s16(vmovn_s32(lo), vmovn_s32(hi))); - } - static XSSE_FORCE_INLINE __m128i _mm_mul_epu32(__m128i a, __m128i b) - { - uint32x4_t evens = vuzpq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)).val[0]; - return vreinterpretq_s8_u64(vmull_u32(vget_low_u32(evens), vget_high_u32(evens))); - } - static XSSE_FORCE_INLINE __m128i _mm_sad_epu8(__m128i a, __m128i b) - { - uint16x8_t abs_diffs_16 = vpaddlq_u8(vabdq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b))); - uint32x4_t abs_diffs_32 = vpaddlq_u16(abs_diffs_16); - uint64x2_t abs_diffs_64 = vpaddlq_u32(abs_diffs_32); - - return vreinterpretq_s8_u16((uint16x8_t) { - (int16_t) vgetq_lane_u64(abs_diffs_64, 0), 0, 0, 0, - (int16_t) vgetq_lane_u64(abs_diffs_64, 1), 0, 0, 0 - }); - } - - #define _mm_sub_epi8(a, b) (vreinterpretq_s8_u8(vsubq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) - #define _mm_sub_epi16(a, b) (vreinterpretq_s8_u16(vsubq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) - #define _mm_sub_epi32(a, b) (vreinterpretq_s8_u32(vsubq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) - #define _mm_sub_epi64(a, b) (vreinterpretq_s8_u64(vsubq_u64(vreinterpretq_u64_s8(a), vreinterpretq_u64_s8(b)))) - - #define _mm_subs_epi8(a, b) (vqsubq_s8((a), (b))) - #define _mm_subs_epi16(a, b) (vreinterpretq_s8_s16(vqsubq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) - #define _mm_subs_epu8(a, b) (vreinterpretq_s8_u8(vqsubq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) - #define _mm_subs_epu16(a, b) (vreinterpretq_s8_u16(vqsubq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) - - - /***************************************************************************** - * Comparison * - *****************************************************************************/ - - #define _mm_cmpeq_epi8(a, b) (vreinterpretq_s8_u8(vceqq_s8((a), (b)))) - #define _mm_cmpeq_epi16(a, b) (vreinterpretq_s8_u16(vceqq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) - #define _mm_cmpeq_epi32(a, b) (vreinterpretq_s8_u32(vceqq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) - - #define _mm_cmplt_epi8(a, b) (vreinterpretq_s8_u8(vcltq_s8((a), (b)))) - #define _mm_cmplt_epi16(a, b) (vreinterpretq_s8_u16(vcltq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) - #define _mm_cmplt_epi32(a, b) (vreinterpretq_s8_u32(vcltq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) - - #define _mm_cmpgt_epi8(a, b) (vreinterpretq_s8_u8(vcgtq_s8((a), (b)))) - #define _mm_cmpgt_epi16(a, b) (vreinterpretq_s8_u16(vcgtq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) - #define _mm_cmpgt_epi32(a, b) (vreinterpretq_s8_u32(vcgtq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) - - - /***************************************************************************** - * Convert * - *****************************************************************************/ - - #define _mm_cvtsi32_si128(x) (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x), 0, 0, 0 })) - #define _mm_cvtsi64_si128(x) (vreinterpretq_s8_s64((int64x2_t) { (int64_t) (x), 0 })) - #define _mm_cvtsi128_si32(x) (vgetq_lane_s32(vreinterpretq_s32_s8(x), 0)) - #define _mm_cvtsi128_si64(x) (vgetq_lane_s64(vreinterpretq_s64_s8(x), 0)) - - - /***************************************************************************** - * Others * - *****************************************************************************/ - - #define _mm_packs_epi16(a, b) (vcombine_s8(vqmovn_s16(vreinterpretq_s16_s8(a)), vqmovn_s16(vreinterpretq_s16_s8(b)))) - #define _mm_packs_epi32(a, b) \ - (vreinterpretq_s8_s16(vcombine_s16(vqmovn_s32(vreinterpretq_s32_s8(a)), vqmovn_s32(vreinterpretq_s32_s8(b))))) - #define _mm_packus_epi16(a, b) \ - (vreinterpretq_s8_u8(vcombine_u8(vqmovun_s16(vreinterpretq_s16_s8(a)), vqmovun_s16(vreinterpretq_s16_s8(b))))) - - #define _mm_extract_epi16(x, imm) (vgetq_lane_s16(vreinterpretq_s16_s8(x), (imm))) - #define _mm_insert_epi16(x, val, imm) (vreinterpretq_s8_s16(vsetq_lane_s16((int16_t) (val), vreinterpretq_s16_s8(x), (imm)))) - - static XSSE_FORCE_INLINE int _mm_movemask_epi8(__m128i x) - { - /** - * based on code from - * https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon - */ - uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(vreinterpretq_u8_s8(x), 7)); - uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); - uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); - uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); - return vgetq_lane_u8(paired64, 0) | ((int) vgetq_lane_u8(paired64, 8) << 8); - } - - #define _MM_SHUFFLE(a, b, c, d) (((a) << 6) | ((b) << 4) | ((c) << 2) | (d)) - #ifdef XSSE_HAS_MACRO_EXTENSION - #define _mm_shuffle_epi32(x, imm) __extension__({ \ - int32x4_t __xsse_tmp = vreinterpretq_s32_s8(x); \ - vreinterpretq_s8_s32((int32x4_t) { \ - (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 0) & 0x3), \ - (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 2) & 0x3), \ - (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 4) & 0x3), \ - (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 6) & 0x3) \ - }); \ - }) - #define _mm_shufflehi_epi16(x, imm) __extension__({ \ - int16x8_t __xsse_tmp = vreinterpretq_s16_s8(x); \ - vreinterpretq_s8_s16(vcombine_s16( \ - vget_low_s16(__xsse_tmp), \ - (int16x4_t) { \ - (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 0) & 0x3) + 4), \ - (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 2) & 0x3) + 4), \ - (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 4) & 0x3) + 4), \ - (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 6) & 0x3) + 4) \ - } \ - )); \ - }) - #define _mm_shufflelo_epi16(x, imm) __extension__({ \ - int16x8_t __xsse_tmp = vreinterpretq_s16_s8(x); \ - vreinterpretq_s8_s16(vcombine_s16( \ - (int16x4_t) { \ - (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 0) & 0x3)), \ - (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 2) & 0x3)), \ - (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 4) & 0x3)), \ - (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 6) & 0x3)) \ - }, \ - vget_high_s16(__xsse_tmp) \ - )); \ - }) - #else - static XSSE_FORCE_INLINE __m128i _mm_shuffle_epi32(__m128i x, int imm) - { - int32x4_t vec = vreinterpretq_s32_s8(x); - int32_t arr[4]; - vst1q_s32(arr, vec); - - return vreinterpretq_s8_s32((int32x4_t) { - arr[(imm >> 0) & 0x3], - arr[(imm >> 2) & 0x3], - arr[(imm >> 4) & 0x3], - arr[(imm >> 6) & 0x3] - }); - } - static XSSE_FORCE_INLINE __m128i _mm_shufflehi_epi16(__m128i x, int imm) - { - int16x8_t vec = vreinterpretq_s16_s8(x); - int16_t arr[8]; - vst1q_s16(arr, vec); - - return vreinterpretq_s8_s16((int16x8_t) { - arr[0], arr[1], arr[2], arr[3], - arr[((imm >> 0) & 0x3) + 4], - arr[((imm >> 2) & 0x3) + 4], - arr[((imm >> 4) & 0x3) + 4], - arr[((imm >> 6) & 0x3) + 4] - }); - } - static XSSE_FORCE_INLINE __m128i _mm_shufflelo_epi16(__m128i x, int imm) - { - int16x8_t vec = vreinterpretq_s16_s8(x); - int16_t arr[8]; - vst1q_s16(arr, vec); - - return vreinterpretq_s8_s16((int16x8_t) { - arr[((imm >> 0) & 0x3)], - arr[((imm >> 2) & 0x3)], - arr[((imm >> 4) & 0x3)], - arr[((imm >> 6) & 0x3)], - arr[4], arr[5], arr[6], arr[7] - }); - } - #endif - - #define _mm_unpackhi_epi8(a, b) (vzip2q_s8((a), (b))) - #define _mm_unpackhi_epi16(a, b) (vreinterpretq_s8_s16(vzip2q_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) - #define _mm_unpackhi_epi32(a, b) (vreinterpretq_s8_s32(vzip2q_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) - #define _mm_unpackhi_epi64(a, b) (vreinterpretq_s8_s64(vzip2q_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) - - #define _mm_unpacklo_epi8(a, b) (vzip1q_s8((a), (b))) - #define _mm_unpacklo_epi16(a, b) (vreinterpretq_s8_s16(vzip1q_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) - #define _mm_unpacklo_epi32(a, b) (vreinterpretq_s8_s32(vzip1q_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) - #define _mm_unpacklo_epi64(a, b) (vreinterpretq_s8_s64(vzip1q_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) - - #define _mm_move_epi64(x) (vreinterpretq_s8_s64((int64x2_t) { vgetq_lane_s64(vreinterpretq_s64_s8(x), 0), 0 })) - - #endif - - #endif /* XSSE_H */ +#ifndef XSSE_H +#define XSSE_H + +#define XSSE_VERSION 10000 + +#ifdef _MSC_VER +# define XSSE_FORCE_INLINE __forceinline +#elif defined(__GNUC__) || defined(__clang__) +# define XSSE_FORCE_INLINE inline __attribute__((always_inline)) +# define XSSE_HAS_MACRO_EXTENSION +#else +# define XSSE_FORCE_INLINE inline +#endif + + +#if defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) +#include +#define XSSE2 + + +#elif defined(__aarch64__) || defined(_M_ARM64) +#include +#define XSSE2 + +typedef int8x16_t __m128i; + + +/***************************************************************************** + * Load / Store * + *****************************************************************************/ + +#define _mm_set_epi8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) \ + ((int8x16_t) { \ + (int8_t) (x15), (int8_t) (x14), (int8_t) (x13), (int8_t) (x12), \ + (int8_t) (x11), (int8_t) (x10), (int8_t) (x9), (int8_t) (x8), \ + (int8_t) (x7), (int8_t) (x6), (int8_t) (x5), (int8_t) (x4), \ + (int8_t) (x3), (int8_t) (x2), (int8_t) (x1), (int8_t) (x0) }) +#define _mm_set_epi16(x0, x1, x2, x3, x4, x5, x6, x7) \ + (vreinterpretq_s8_s16((int16x8_t) { \ + (int16_t) (x7), (int16_t) (x6), (int16_t) (x5), (int16_t) (x4), \ + (int16_t) (x3), (int16_t) (x2), (int16_t) (x1), (int16_t) (x0) })) +#define _mm_set_epi32(x0, x1, x2, x3) \ + (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x3), (int32_t) (x2), (int32_t) (x1), (int32_t) (x0) })) +#define _mm_set_epi64x(x0, x1) (vreinterpretq_s8_s64((int64x2_t) { (int64_t) (x1), (int64_t) (x0) })) +#define _mm_set1_epi8(x) (vdupq_n_s8((int8_t) (x))) +#define _mm_set1_epi16(x) (vreinterpretq_s8_s16(vdupq_n_s16((int16_t) (x)))) +#define _mm_set1_epi32(x) (vreinterpretq_s8_s32(vdupq_n_s32((int32_t) (x)))) +#define _mm_set1_epi64x(x) (vreinterpretq_s8_s64(vdupq_n_s64((int64_t) (x)))) + +#define _mm_setr_epi8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) \ + ((int8x16_t) { \ + (int8_t) (x0), (int8_t) (x1), (int8_t) (x2), (int8_t) (x3), \ + (int8_t) (x4), (int8_t) (x5), (int8_t) (x6), (int8_t) (x7), \ + (int8_t) (x8), (int8_t) (x9), (int8_t) (x10), (int8_t) (x11), \ + (int8_t) (x12), (int8_t) (x13), (int8_t) (x14), (int8_t) (x15) }) +#define _mm_setr_epi16(x0, x1, x2, x3, x4, x5, x6, x7) \ + (vreinterpretq_s8_s16((int16x8_t) { \ + (int16_t) (x0), (int16_t) (x1), (int16_t) (x2), (int16_t) (x3), \ + (int16_t) (x4), (int16_t) (x5), (int16_t) (x6), (int16_t) (x7) })) +#define _mm_setr_epi32(x0, x1, x2, x3) \ + (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x0), (int32_t) (x1), (int32_t) (x2), (int32_t) (x3) })) + +#define _mm_setzero_si128() (vdupq_n_s8(0)) + +#define _mm_load_si128(x) (vld1q_s8((const int8_t *) (x))) +#define _mm_loadu_si128(x) _mm_load_si128(x) + +#define _mm_store_si128(to, x) (vst1q_s8((int8_t *) (to), x)) +#define _mm_storeu_si128(to, x) _mm_store_si128(to, x) +#define _mm_stream_si128(to, x) _mm_store_si128(to, x) +#define _mm_stream_si32(to, x) (*(volatile int32_t *)(to) = (int32_t)(x)) + + +/***************************************************************************** + * Bit shift / Bit wise * + *****************************************************************************/ + +#define _mm_or_si128(a, b) (vorrq_s8((a), (b))) +#define _mm_xor_si128(a, b) (veorq_s8((a), (b))) +#define _mm_and_si128(a, b) (vandq_s8((a), (b))) +#define _mm_andnot_si128(a, b) (vbicq_s8((b), (a))) + +#define _mm_slli_epi16(x, count) (vreinterpretq_s8_u16(vshlq_n_u16(vreinterpretq_u16_s8(x), (count)))) +#define _mm_slli_epi32(x, count) (vreinterpretq_s8_u32(vshlq_n_u32(vreinterpretq_u32_s8(x), (count)))) +#define _mm_slli_epi64(x, count) (vreinterpretq_s8_u64(vshlq_n_u64(vreinterpretq_u64_s8(x), (count)))) +static XSSE_FORCE_INLINE __m128i _mm_sll_epi16(__m128i x, __m128i count) +{ + uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); + return vreinterpretq_s8_u16( + vshlq_u16(vreinterpretq_u16_s8(x), vdupq_n_s16((int16_t) shift)) + ); +} +static XSSE_FORCE_INLINE __m128i _mm_sll_epi32(__m128i x, __m128i count) +{ + uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); + return vreinterpretq_s8_u32( + vshlq_u32(vreinterpretq_u32_s8(x), vdupq_n_s32((int32_t) shift)) + ); +} +static XSSE_FORCE_INLINE __m128i _mm_sll_epi64(__m128i x, __m128i count) +{ + uint64_t shift = (uint64_t) vgetq_lane_s64(vreinterpretq_s64_s8(count), 0); + return vreinterpretq_s8_u64( + vshlq_u64(vreinterpretq_u64_s8(x), vdupq_n_s64((int64_t) shift)) + ); +} + +#define _mm_slli_si128(x, imm) \ + ((imm) >= 16 ? vdupq_n_s8(0) : vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 16 - (imm)))) + +#define _mm_srai_epi16(x, count) (vreinterpretq_s8_s16(vshrq_n_s16(vreinterpretq_s16_s8(x), (count)))) +#define _mm_srai_epi32(x, count) (vreinterpretq_s8_s32(vshrq_n_s32(vreinterpretq_s32_s8(x), (count)))) +static inline __m128i _mm_sra_epi16(__m128i x, __m128i count) +{ + uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); + return vreinterpretq_s8_s16( + vshlq_s16(vreinterpretq_s16_s8(x), vdupq_n_s16(-(int16_t) shift)) + ); +} +static inline __m128i _mm_sra_epi32(__m128i x, __m128i count) +{ + uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); + return vreinterpretq_s8_s32( + vshlq_s32(vreinterpretq_s32_s8(x), vdupq_n_s32(-(int32_t) shift)) + ); +} + +#define _mm_srli_epi16(x, count) (vreinterpretq_s8_u16(vshrq_n_u16(vreinterpretq_u16_s8(x), (count)))) +#define _mm_srli_epi32(x, count) (vreinterpretq_s8_u32(vshrq_n_u32(vreinterpretq_u32_s8(x), (count)))) +#define _mm_srli_epi64(x, count) (vreinterpretq_s8_u64(vshrq_n_u64(vreinterpretq_u64_s8(x), (count)))) +static XSSE_FORCE_INLINE __m128i _mm_srl_epi16(__m128i x, __m128i count) +{ + uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); + return vreinterpretq_s8_u16( + vshlq_u16(vreinterpretq_u16_s8(x), vdupq_n_s16(-(int16_t) shift)) + ); +} +static XSSE_FORCE_INLINE __m128i _mm_srl_epi32(__m128i x, __m128i count) +{ + uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); + return vreinterpretq_s8_u32( + vshlq_u32(vreinterpretq_u32_s8(x), vdupq_n_s32(-(int32_t) shift)) + ); +} +static XSSE_FORCE_INLINE __m128i _mm_srl_epi64(__m128i x, __m128i count) +{ + uint64_t shift = (uint64_t) vgetq_lane_s64(vreinterpretq_s64_s8(count), 0); + return vreinterpretq_s8_u64( + vshlq_u64(vreinterpretq_u64_s8(x), vdupq_n_s64(-(int64_t) shift)) + ); +} + +#define _mm_srli_si128(x, imm) \ + ((imm) >= 16 ? vdupq_n_s8(0) : vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), (imm)))) + + +/***************************************************************************** + * Integer Arithmetic Operations * + *****************************************************************************/ + +/** + * In practice, there is no problem, but a runtime error for signed integer overflow is triggered by UBSAN, + * so perform the calculation as unsigned. Since it is optimized at compile time, there are no unnecessary casts at runtime. + */ +#define _mm_add_epi8(a, b) (vreinterpretq_s8_u8(vaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_add_epi16(a, b) (vreinterpretq_s8_u16(vaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_add_epi32(a, b) (vreinterpretq_s8_u32(vaddq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) +#define _mm_add_epi64(a, b) (vreinterpretq_s8_u64(vaddq_u64(vreinterpretq_u64_s8(a), vreinterpretq_u64_s8(b)))) + +#define _mm_adds_epi8(a, b) (vqaddq_s8((a), (b))) +#define _mm_adds_epi16(a, b) (vreinterpretq_s8_s16(vqaddq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_adds_epu8(a, b) (vreinterpretq_s8_u8(vqaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_adds_epu16(a, b) (vreinterpretq_s8_u16(vqaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) + +#define _mm_avg_epu8(a, b) (vreinterpretq_s8_u8(vrhaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_avg_epu16(a, b) (vreinterpretq_s8_u16(vrhaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) + +static XSSE_FORCE_INLINE __m128i _mm_madd_epi16(__m128i a, __m128i b) +{ + int32x4_t mul_lo = vmull_s16(vget_low_s16(vreinterpretq_s16_s8(a)), vget_low_s16(vreinterpretq_s16_s8(b))); + int32x4_t mul_hi = vmull_s16(vget_high_s16(vreinterpretq_s16_s8(a)), vget_high_s16(vreinterpretq_s16_s8(b))); + + return vreinterpretq_s8_s32(vcombine_s32( + vpadd_s32(vget_low_s32(mul_lo), vget_high_s32(mul_lo)), + vpadd_s32(vget_low_s32(mul_hi), vget_high_s32(mul_hi)) + )); +} + +#define _mm_max_epu8(a, b) (vreinterpretq_s8_u8(vmaxq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_max_epi16(a, b) (vreinterpretq_s8_s16(vmaxq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_min_epu8(a, b) (vreinterpretq_s8_u8(vminq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_min_epi16(a, b) (vreinterpretq_s8_s16(vminq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) + +static XSSE_FORCE_INLINE __m128i _mm_mulhi_epi16(__m128i a, __m128i b) +{ + int32x4_t lo = vmull_s16(vget_low_s16(vreinterpretq_s16_s8(a)), vget_low_s16(vreinterpretq_s16_s8(b))); + int32x4_t hi = vmull_s16(vget_high_s16(vreinterpretq_s16_s8(a)), vget_high_s16(vreinterpretq_s16_s8(b))); + return vreinterpretq_s8_s16(vcombine_s16(vshrn_n_s32(lo, 16), vshrn_n_s32(hi, 16))); +} +static XSSE_FORCE_INLINE __m128i _mm_mulhi_epu16(__m128i a, __m128i b) +{ + uint32x4_t lo = vmull_u16(vget_low_u16(vreinterpretq_u16_s8(a)), vget_low_u16(vreinterpretq_u16_s8(b))); + uint32x4_t hi = vmull_u16(vget_high_u16(vreinterpretq_u16_s8(a)), vget_high_u16(vreinterpretq_u16_s8(b))); + return vreinterpretq_s8_u16(vcombine_u16(vshrn_n_u32(lo, 16), vshrn_n_u32(hi, 16))); +} +static XSSE_FORCE_INLINE __m128i _mm_mullo_epi16(__m128i a, __m128i b) +{ + int32x4_t lo = vmull_s16(vget_low_s16(vreinterpretq_s16_s8(a)), vget_low_s16(vreinterpretq_s16_s8(b))); + int32x4_t hi = vmull_s16(vget_high_s16(vreinterpretq_s16_s8(a)), vget_high_s16(vreinterpretq_s16_s8(b))); + return vreinterpretq_s8_s16(vcombine_s16(vmovn_s32(lo), vmovn_s32(hi))); +} +static XSSE_FORCE_INLINE __m128i _mm_mul_epu32(__m128i a, __m128i b) +{ + uint32x4_t evens = vuzpq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)).val[0]; + return vreinterpretq_s8_u64(vmull_u32(vget_low_u32(evens), vget_high_u32(evens))); +} +static XSSE_FORCE_INLINE __m128i _mm_sad_epu8(__m128i a, __m128i b) +{ + uint16x8_t abs_diffs_16 = vpaddlq_u8(vabdq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b))); + uint32x4_t abs_diffs_32 = vpaddlq_u16(abs_diffs_16); + uint64x2_t abs_diffs_64 = vpaddlq_u32(abs_diffs_32); + + return vreinterpretq_s8_u16((uint16x8_t) { + (int16_t) vgetq_lane_u64(abs_diffs_64, 0), 0, 0, 0, + (int16_t) vgetq_lane_u64(abs_diffs_64, 1), 0, 0, 0 + }); +} + +#define _mm_sub_epi8(a, b) (vreinterpretq_s8_u8(vsubq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_sub_epi16(a, b) (vreinterpretq_s8_u16(vsubq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_sub_epi32(a, b) (vreinterpretq_s8_u32(vsubq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) +#define _mm_sub_epi64(a, b) (vreinterpretq_s8_u64(vsubq_u64(vreinterpretq_u64_s8(a), vreinterpretq_u64_s8(b)))) + +#define _mm_subs_epi8(a, b) (vqsubq_s8((a), (b))) +#define _mm_subs_epi16(a, b) (vreinterpretq_s8_s16(vqsubq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_subs_epu8(a, b) (vreinterpretq_s8_u8(vqsubq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_subs_epu16(a, b) (vreinterpretq_s8_u16(vqsubq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) + + +/***************************************************************************** + * Comparison * + *****************************************************************************/ + +#define _mm_cmpeq_epi8(a, b) (vreinterpretq_s8_u8(vceqq_s8((a), (b)))) +#define _mm_cmpeq_epi16(a, b) (vreinterpretq_s8_u16(vceqq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_cmpeq_epi32(a, b) (vreinterpretq_s8_u32(vceqq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) + +#define _mm_cmplt_epi8(a, b) (vreinterpretq_s8_u8(vcltq_s8((a), (b)))) +#define _mm_cmplt_epi16(a, b) (vreinterpretq_s8_u16(vcltq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_cmplt_epi32(a, b) (vreinterpretq_s8_u32(vcltq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) + +#define _mm_cmpgt_epi8(a, b) (vreinterpretq_s8_u8(vcgtq_s8((a), (b)))) +#define _mm_cmpgt_epi16(a, b) (vreinterpretq_s8_u16(vcgtq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_cmpgt_epi32(a, b) (vreinterpretq_s8_u32(vcgtq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) + + +/***************************************************************************** + * Convert * + *****************************************************************************/ + +#define _mm_cvtsi32_si128(x) (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x), 0, 0, 0 })) +#define _mm_cvtsi64_si128(x) (vreinterpretq_s8_s64((int64x2_t) { (int64_t) (x), 0 })) +#define _mm_cvtsi128_si32(x) (vgetq_lane_s32(vreinterpretq_s32_s8(x), 0)) +#define _mm_cvtsi128_si64(x) (vgetq_lane_s64(vreinterpretq_s64_s8(x), 0)) + + +/***************************************************************************** + * Others * + *****************************************************************************/ + +#define _mm_packs_epi16(a, b) (vcombine_s8(vqmovn_s16(vreinterpretq_s16_s8(a)), vqmovn_s16(vreinterpretq_s16_s8(b)))) +#define _mm_packs_epi32(a, b) \ + (vreinterpretq_s8_s16(vcombine_s16(vqmovn_s32(vreinterpretq_s32_s8(a)), vqmovn_s32(vreinterpretq_s32_s8(b))))) +#define _mm_packus_epi16(a, b) \ + (vreinterpretq_s8_u8(vcombine_u8(vqmovun_s16(vreinterpretq_s16_s8(a)), vqmovun_s16(vreinterpretq_s16_s8(b))))) + +#define _mm_extract_epi16(x, imm) (vgetq_lane_s16(vreinterpretq_s16_s8(x), (imm))) +#define _mm_insert_epi16(x, val, imm) (vreinterpretq_s8_s16(vsetq_lane_s16((int16_t) (val), vreinterpretq_s16_s8(x), (imm)))) + +static XSSE_FORCE_INLINE int _mm_movemask_epi8(__m128i x) +{ + /** + * based on code from + * https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon + */ + uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(vreinterpretq_u8_s8(x), 7)); + uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); + uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); + uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); + return vgetq_lane_u8(paired64, 0) | ((int) vgetq_lane_u8(paired64, 8) << 8); +} + +#define _MM_SHUFFLE(a, b, c, d) (((a) << 6) | ((b) << 4) | ((c) << 2) | (d)) +#ifdef XSSE_HAS_MACRO_EXTENSION +#define _mm_shuffle_epi32(x, imm) __extension__({ \ + int32x4_t __xsse_tmp = vreinterpretq_s32_s8(x); \ + vreinterpretq_s8_s32((int32x4_t) { \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 0) & 0x3), \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 2) & 0x3), \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 4) & 0x3), \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 6) & 0x3) \ + }); \ + }) +#define _mm_shufflehi_epi16(x, imm) __extension__({ \ + int16x8_t __xsse_tmp = vreinterpretq_s16_s8(x); \ + vreinterpretq_s8_s16(vcombine_s16( \ + vget_low_s16(__xsse_tmp), \ + (int16x4_t) { \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 0) & 0x3) + 4), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 2) & 0x3) + 4), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 4) & 0x3) + 4), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 6) & 0x3) + 4) \ + } \ + )); \ + }) +#define _mm_shufflelo_epi16(x, imm) __extension__({ \ + int16x8_t __xsse_tmp = vreinterpretq_s16_s8(x); \ + vreinterpretq_s8_s16(vcombine_s16( \ + (int16x4_t) { \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 0) & 0x3)), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 2) & 0x3)), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 4) & 0x3)), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 6) & 0x3)) \ + }, \ + vget_high_s16(__xsse_tmp) \ + )); \ + }) +#else +static XSSE_FORCE_INLINE __m128i _mm_shuffle_epi32(__m128i x, int imm) +{ + int32x4_t vec = vreinterpretq_s32_s8(x); + int32_t arr[4]; + vst1q_s32(arr, vec); + + return vreinterpretq_s8_s32((int32x4_t) { + arr[(imm >> 0) & 0x3], + arr[(imm >> 2) & 0x3], + arr[(imm >> 4) & 0x3], + arr[(imm >> 6) & 0x3] + }); +} +static XSSE_FORCE_INLINE __m128i _mm_shufflehi_epi16(__m128i x, int imm) +{ + int16x8_t vec = vreinterpretq_s16_s8(x); + int16_t arr[8]; + vst1q_s16(arr, vec); + + return vreinterpretq_s8_s16((int16x8_t) { + arr[0], arr[1], arr[2], arr[3], + arr[((imm >> 0) & 0x3) + 4], + arr[((imm >> 2) & 0x3) + 4], + arr[((imm >> 4) & 0x3) + 4], + arr[((imm >> 6) & 0x3) + 4] + }); +} +static XSSE_FORCE_INLINE __m128i _mm_shufflelo_epi16(__m128i x, int imm) +{ + int16x8_t vec = vreinterpretq_s16_s8(x); + int16_t arr[8]; + vst1q_s16(arr, vec); + + return vreinterpretq_s8_s16((int16x8_t) { + arr[((imm >> 0) & 0x3)], + arr[((imm >> 2) & 0x3)], + arr[((imm >> 4) & 0x3)], + arr[((imm >> 6) & 0x3)], + arr[4], arr[5], arr[6], arr[7] + }); +} +#endif + +#define _mm_unpackhi_epi8(a, b) (vzip2q_s8((a), (b))) +#define _mm_unpackhi_epi16(a, b) (vreinterpretq_s8_s16(vzip2q_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_unpackhi_epi32(a, b) (vreinterpretq_s8_s32(vzip2q_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) +#define _mm_unpackhi_epi64(a, b) (vreinterpretq_s8_s64(vzip2q_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) + +#define _mm_unpacklo_epi8(a, b) (vzip1q_s8((a), (b))) +#define _mm_unpacklo_epi16(a, b) (vreinterpretq_s8_s16(vzip1q_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_unpacklo_epi32(a, b) (vreinterpretq_s8_s32(vzip1q_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) +#define _mm_unpacklo_epi64(a, b) (vreinterpretq_s8_s64(vzip1q_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) + +#define _mm_move_epi64(x) (vreinterpretq_s8_s64((int64x2_t) { vgetq_lane_s64(vreinterpretq_s64_s8(x), 0), 0 })) + +#endif + +#endif /* XSSE_H */ From d4f13000114dc838f466fa5bd2659554f980dd6a Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Sun, 7 Dec 2025 21:48:43 +0000 Subject: [PATCH 131/252] opcache: Remove unused sys/ipc.h include from ZendAccelerator.c (#20662) As far as I can tell that's been there since the initial open source release (commit 528006a). The header defines ftok() and a handful of IPC_* constants, none of which are used here. System V IPC is increasingly being replaced by POSIX IPC and may therefore not be implemented on new and/or hobbyist operating systems such as SerenityOS[1]. In the past that wasn't an issue as the OPCache could be disabled, which is no longer possible as of PHP 8.5[2]. I was able to build with the include patched out, but we would prefer this to be addressed upstream. 1: https://github.com/SerenityOS/serenity/pull/26465 2: https://github.com/php/php-src/pull/18961 --- ext/opcache/ZendAccelerator.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index e6a2b90e8fffc..d1d49ca391a69 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -89,7 +89,6 @@ typedef int gid_t; #ifndef ZEND_WIN32 # include # include -# include # include # include #endif From 1498694e879e9ff3539a0339314714fc52edba3d Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com> Date: Mon, 8 Dec 2025 09:18:44 +0100 Subject: [PATCH 132/252] ext/ffi: convert zend_parse_parameters_none() to fast ZPP (#20644) --- ext/ffi/ffi.c | 53 ++++++++++++++------------------------------------- 1 file changed, 14 insertions(+), 39 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 86b8d29209f40..d5b8eb59f0c90 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -4717,9 +4717,8 @@ ZEND_METHOD(FFI, isNull) /* {{{ */ ZEND_METHOD(FFI_CType, getName) /* {{{ */ { zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS)); - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + + ZEND_PARSE_PARAMETERS_NONE(); zend_ffi_ctype_name_buf buf; @@ -4739,9 +4738,7 @@ ZEND_METHOD(FFI_CType, getKind) /* {{{ */ const zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS)); const zend_ffi_type *type; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); RETURN_LONG(type->kind); @@ -4753,9 +4750,7 @@ ZEND_METHOD(FFI_CType, getSize) /* {{{ */ const zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS)); const zend_ffi_type *type; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); RETURN_LONG(type->size); @@ -4767,9 +4762,7 @@ ZEND_METHOD(FFI_CType, getAlignment) /* {{{ */ const zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS)); const zend_ffi_type *type; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); RETURN_LONG(type->align); @@ -4781,9 +4774,7 @@ ZEND_METHOD(FFI_CType, getAttributes) /* {{{ */ const zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS)); const zend_ffi_type *type; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); RETURN_LONG(type->attr); @@ -4795,9 +4786,7 @@ ZEND_METHOD(FFI_CType, getEnumKind) /* {{{ */ const zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS)); const zend_ffi_type *type; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); if (type->kind != ZEND_FFI_TYPE_ENUM) { @@ -4814,9 +4803,7 @@ ZEND_METHOD(FFI_CType, getArrayElementType) /* {{{ */ zend_ffi_type *type; zend_ffi_ctype *ret; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); if (type->kind != ZEND_FFI_TYPE_ARRAY) { @@ -4835,9 +4822,7 @@ ZEND_METHOD(FFI_CType, getArrayLength) /* {{{ */ const zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS)); const zend_ffi_type *type; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); if (type->kind != ZEND_FFI_TYPE_ARRAY) { @@ -4854,9 +4839,7 @@ ZEND_METHOD(FFI_CType, getPointerType) /* {{{ */ zend_ffi_ctype *ret; zend_ffi_type *type; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); if (type->kind != ZEND_FFI_TYPE_POINTER) { @@ -4878,9 +4861,7 @@ ZEND_METHOD(FFI_CType, getStructFieldNames) /* {{{ */ zend_string* name; zval zv; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); if (type->kind != ZEND_FFI_TYPE_STRUCT) { @@ -4958,9 +4939,7 @@ ZEND_METHOD(FFI_CType, getFuncABI) /* {{{ */ const zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS)); const zend_ffi_type *type; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); if (type->kind != ZEND_FFI_TYPE_FUNC) { @@ -4977,9 +4956,7 @@ ZEND_METHOD(FFI_CType, getFuncReturnType) /* {{{ */ zend_ffi_ctype *ret; zend_ffi_type *type; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); if (type->kind != ZEND_FFI_TYPE_FUNC) { @@ -4998,9 +4975,7 @@ ZEND_METHOD(FFI_CType, getFuncParameterCount) /* {{{ */ const zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS)); const zend_ffi_type *type; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); type = ZEND_FFI_TYPE(ctype->type); if (type->kind != ZEND_FFI_TYPE_FUNC) { From 26c0cbd93cb4f2501a9d07144c8b46ae5c91b9e6 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 5 Dec 2025 19:32:40 +0100 Subject: [PATCH 133/252] Fix dumping function signature with dynamic class const lookup default argument Fixes OSS-Fuzz #465488618 Closes GH-20651 --- NEWS | 4 ++++ Zend/tests/oss-fuzz-465488618.phpt | 16 ++++++++++++++++ Zend/zend_inheritance.c | 4 +++- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/oss-fuzz-465488618.phpt diff --git a/NEWS b/NEWS index 472f57a38f6bb..b4a15b3ddd8fd 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.30 +- Core: + . Fix OSS-Fuzz #465488618 (Wrong assumptions when dumping function signature + with dynamic class const lookup default argument). (ilutov) + - Bz2: . Fixed bug GH-20620 (bzcompress overflow on large source size). (David Carlier) diff --git a/Zend/tests/oss-fuzz-465488618.phpt b/Zend/tests/oss-fuzz-465488618.phpt new file mode 100644 index 0000000000000..517c481b33e0a --- /dev/null +++ b/Zend/tests/oss-fuzz-465488618.phpt @@ -0,0 +1,16 @@ +--TEST-- +OSS-Fuzz #465488618: Dump function signature with dynamic class const lookup default argument +--FILE-- + +--EXPECTF-- +Fatal error: Declaration of B::test(string $x = ) must be compatible with A::test(int $x) in %s on line %d diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 5ba883addca55..a89114b80deea 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -973,7 +973,9 @@ static ZEND_COLD zend_string *zend_get_function_declaration( zend_ast *ast = Z_ASTVAL_P(zv); if (ast->kind == ZEND_AST_CONSTANT) { smart_str_append(&str, zend_ast_get_constant_name(ast)); - } else if (ast->kind == ZEND_AST_CLASS_CONST) { + } else if (ast->kind == ZEND_AST_CLASS_CONST + && ast->child[1]->kind == ZEND_AST_ZVAL + && Z_TYPE_P(zend_ast_get_zval(ast->child[1])) == IS_STRING) { smart_str_append(&str, zend_ast_get_str(ast->child[0])); smart_str_appends(&str, "::"); smart_str_append(&str, zend_ast_get_str(ast->child[1])); From c24d51e59cd59dfd73c0c7bf9a1c19100e18c360 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Sun, 7 Dec 2025 21:48:43 +0000 Subject: [PATCH 134/252] opcache: Remove unused sys/ipc.h include from ZendAccelerator.c (#20662) As far as I can tell that's been there since the initial open source release (commit 528006a). The header defines ftok() and a handful of IPC_* constants, none of which are used here. System V IPC is increasingly being replaced by POSIX IPC and may therefore not be implemented on new and/or hobbyist operating systems such as SerenityOS[1]. In the past that wasn't an issue as the OPCache could be disabled, which is no longer possible as of PHP 8.5[2]. I was able to build with the include patched out, but we would prefer this to be addressed upstream. 1: https://github.com/SerenityOS/serenity/pull/26465 2: https://github.com/php/php-src/pull/18961 --- ext/opcache/ZendAccelerator.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 84acae980ce3a..904a9c2ae08fd 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -89,7 +89,6 @@ typedef int gid_t; #ifndef ZEND_WIN32 # include # include -# include # include # include #endif From 8908a81973076967c53f0818b893dd18295ebffc Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Mon, 8 Dec 2025 13:28:03 -0800 Subject: [PATCH 135/252] ldap: Use cheaper checks for getting a string (#20652) Avoids loading globals, the return register can be used directly. --- ext/ldap/ldap.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index e9acd18990664..c2b08d0c67050 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -425,7 +425,7 @@ static void _php_ldap_control_to_array(LDAP *ld, LDAPControl* ctrl, zval* array, static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashTable *control_ht) { zval* val; - zend_string *control_oid; + zend_string *control_oid, *control_oid_tmp; char** ldap_attrs = NULL; LDAPSortKey** sort_keys = NULL; zend_string *tmpstring = NULL, **tmpstrings1 = NULL, **tmpstrings2 = NULL; @@ -436,8 +436,8 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT return -1; } - control_oid = zval_get_string(val); - if (EG(exception)) { + control_oid = zval_try_get_tmp_string(val, &control_oid_tmp); + if (!control_oid) { return -1; } @@ -453,8 +453,8 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT if ((val = zend_hash_find(control_ht, ZSTR_KNOWN(ZEND_STR_VALUE))) != NULL) { if (Z_TYPE_P(val) != IS_ARRAY) { - tmpstring = zval_get_string(val); - if (EG(exception)) { + tmpstring = zval_try_get_string(val); + if (!tmpstring) { rc = -1; goto failure; } @@ -468,8 +468,8 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT pagesize = zval_get_long(tmp); } if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "cookie", sizeof("cookie") - 1)) != NULL) { - tmpstring = zval_get_string(tmp); - if (EG(exception)) { + tmpstring = zval_try_get_string(tmp); + if (!tmpstring) { rc = -1; goto failure; } @@ -488,8 +488,8 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT rc = -1; zend_value_error("%s(): Control must have a \"filter\" key", get_active_function_name()); } else { - zend_string* assert = zval_get_string(tmp); - if (EG(exception)) { + zend_string* assert = zval_try_get_string(tmp); + if (!assert) { rc = -1; goto failure; } @@ -516,8 +516,8 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT rc = -1; php_error_docref(NULL, E_WARNING, "Failed to allocate control value"); } else { - tmpstring = zval_get_string(tmp); - if (EG(exception)) { + tmpstring = zval_try_get_string(tmp); + if (!tmpstring) { rc = -1; goto failure; } @@ -555,8 +555,8 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT goto failure; } - tmpstrings1[num_tmpstrings1] = zval_get_string(attr); - if (EG(exception)) { + tmpstrings1[num_tmpstrings1] = zval_try_get_string(attr); + if (!tmpstrings1[num_tmpstrings1]) { rc = -1; goto failure; } @@ -603,8 +603,8 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT goto failure; } sort_keys[i] = emalloc(sizeof(LDAPSortKey)); - tmpstrings1[num_tmpstrings1] = zval_get_string(tmp); - if (EG(exception)) { + tmpstrings1[num_tmpstrings1] = zval_try_get_string(tmp); + if (!tmpstrings1[num_tmpstrings1]) { rc = -1; goto failure; } @@ -612,8 +612,8 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT ++num_tmpstrings1; if ((tmp = zend_hash_str_find(Z_ARRVAL_P(sortkey), "oid", sizeof("oid") - 1)) != NULL) { - tmpstrings2[num_tmpstrings2] = zval_get_string(tmp); - if (EG(exception)) { + tmpstrings2[num_tmpstrings2] = zval_try_get_string(tmp); + if (!tmpstrings2[num_tmpstrings2]) { rc = -1; goto failure; } @@ -659,8 +659,8 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT } if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "attrvalue", sizeof("attrvalue") - 1)) != NULL) { - tmpstring = zval_get_string(tmp); - if (EG(exception)) { + tmpstring = zval_try_get_string(tmp); + if (!tmpstring) { rc = -1; goto failure; } @@ -685,8 +685,8 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT } if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "context", sizeof("context") - 1)) != NULL) { - tmpstring = zval_get_string(tmp); - if (EG(exception)) { + tmpstring = zval_try_get_string(tmp); + if (!tmpstring) { rc = -1; goto failure; } @@ -714,7 +714,7 @@ static int php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, const HashT } failure: - zend_string_release(control_oid); + zend_tmp_string_release(control_oid_tmp); if (tmpstring != NULL) { zend_string_release(tmpstring); } @@ -2791,8 +2791,8 @@ PHP_FUNCTION(ldap_modify_batch) zend_ulong value_index = 0; zval *modification_value_zv = NULL; ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(modification_values), modification_value_zv) { - zend_string *modval = zval_get_string(modification_value_zv); - if (EG(exception)) { + zend_string *modval = zval_try_get_string(modification_value_zv); + if (!modval) { RETVAL_FALSE; ldap_mods[modification_index]->mod_bvalues[value_index] = NULL; num_mods = modification_index + 1; From 02a7c49564bebb0af476db7a37ad14b658119691 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 6 Dec 2025 14:09:01 +0100 Subject: [PATCH 136/252] ldap: Fix memory leak in ldap_set_options() Closes GH-20659. --- NEWS | 2 ++ ext/ldap/ldap.c | 10 ++++-- ...dap_set_option_leak_attrvalue_context.phpt | 34 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 ext/ldap/tests/ldap_set_option_leak_attrvalue_context.phpt diff --git a/NEWS b/NEWS index b4a15b3ddd8fd..be1c55a7c8e89 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,8 @@ PHP NEWS - GD: . Fixed bug GH-20622 (imagestring/imagestringup overflow). (David Carlier) +- LDAP: + . Fix memory leak in ldap_set_options(). (ndossche) 18 Dec 2025, PHP 8.3.29 diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index 0b0a0c21df4ad..303ba055458e6 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -664,14 +664,15 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra goto failure; } + zend_string *context_str = NULL; if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "context", sizeof("context") - 1)) != NULL) { - tmpstring = zval_get_string(tmp); + context_str = zval_get_string(tmp); if (EG(exception)) { rc = -1; goto failure; } - context.bv_val = ZSTR_VAL(tmpstring); - context.bv_len = ZSTR_LEN(tmpstring); + context.bv_val = ZSTR_VAL(context_str); + context.bv_len = ZSTR_LEN(context_str); vlvInfo.ldvlv_context = &context; } else { vlvInfo.ldvlv_context = NULL; @@ -683,6 +684,9 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra if (rc != LDAP_SUCCESS) { php_error_docref(NULL, E_WARNING, "Failed to create VLV control value: %s (%d)", ldap_err2string(rc), rc); } + if (context_str) { + zend_string_release_ex(context_str, false); + } } else { zend_type_error("%s(): Control OID %s cannot be of type array", get_active_function_name(), ZSTR_VAL(control_oid)); rc = -1; diff --git a/ext/ldap/tests/ldap_set_option_leak_attrvalue_context.phpt b/ext/ldap/tests/ldap_set_option_leak_attrvalue_context.phpt new file mode 100644 index 0000000000000..5829ad24faf57 --- /dev/null +++ b/ext/ldap/tests/ldap_set_option_leak_attrvalue_context.phpt @@ -0,0 +1,34 @@ +--TEST-- +ldap_set_option() - Leaks attrvalue and context +--EXTENSIONS-- +ldap +--FILE-- + "2.16.840.1.113730.3.4.9", "value" => ["attrvalue" => $attrvalue, "context" => $context, "before" => 0, "after" => 0]], +]; + +ldap_set_option($link, LDAP_OPT_CLIENT_CONTROLS, $controls); +ldap_get_option($link, LDAP_OPT_CLIENT_CONTROLS, $controls_out); + +var_dump($controls_out); +?> +--EXPECTF-- +array(1) { + ["2.16.840.1.113730.3.4.9"]=> + array(3) { + ["oid"]=> + string(23) "2.16.840.1.113730.3.4.9" + ["iscritical"]=> + bool(false) + ["value"]=> + string(28) "0%0%0� attrvaluecontext" + } +} From cb413b5d5f96ad97a8cc2d4f0ad0b065051f122f Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 4 Dec 2025 14:31:29 +0100 Subject: [PATCH 137/252] Update clang in macOS build This resolves a crash in release builds. This may be dropped again in the future once the bugfix lands. Co-authored by Alexandre Daubois Co-authored by Arnaud Le Blanc Co-authored by Jakub Zelenka Closes GH-20669 --- .github/actions/macos-update-clang/action.yml | 17 +++++++++++++++++ .github/workflows/nightly.yml | 2 ++ .github/workflows/push.yml | 2 ++ 3 files changed, 21 insertions(+) create mode 100644 .github/actions/macos-update-clang/action.yml diff --git a/.github/actions/macos-update-clang/action.yml b/.github/actions/macos-update-clang/action.yml new file mode 100644 index 0000000000000..bceb431aed5a5 --- /dev/null +++ b/.github/actions/macos-update-clang/action.yml @@ -0,0 +1,17 @@ +name: Update clang +runs: + using: composite + steps: + - shell: bash + run: | + softwareupdate -l + label=$((softwareupdate -l 2>/dev/null | grep 'Label:' | grep -o 'Command Line Tools for Xcode.*' | head -1) || echo '') + if [ -n "$label" ]; then + softwareupdate -i "$label" + xcode_path=$(ls -1 '/Applications' | grep 'Xcode_.*\.app' | head -1) + sudo xcode-select -s "/Applications/$xcode_path" + else + echo "Not found." + fi + which clang + clang -v diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 909a412e796e6..e67deb8ffe4ff 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -367,6 +367,8 @@ jobs: uses: actions/checkout@v5 with: ref: ${{ inputs.branch }} + - name: Update clang + uses: ./.github/actions/macos-update-clang - name: brew uses: ./.github/actions/brew - name: ./configure diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 028255330d1fc..054f4a15286d2 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -117,6 +117,8 @@ jobs: steps: - name: git checkout uses: actions/checkout@v5 + - name: Update clang + uses: ./.github/actions/macos-update-clang - name: brew uses: ./.github/actions/brew - name: ccache From 8fd69e15e0bc623fe317e21261d88e176de43bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 9 Dec 2025 11:52:42 +0100 Subject: [PATCH 138/252] uri: Update to uriparser-0.9.9-79-gf47a7f0 (#20671) This is in preparation of importing a fix for the uriparser/uriparser#282 security issue, which will likely depend on this refactoring to cleanly apply. --- ext/uri/uriparser/src/UriCommon.c | 28 +- ext/uri/uriparser/src/UriEscape.c | 114 +----- ext/uri/uriparser/src/UriIp4.c | 23 +- ext/uri/uriparser/src/UriParse.c | 412 +++------------------- ext/uri/uriparser/src/UriSetFragment.c | 107 +----- ext/uri/uriparser/src/UriSetHostRegName.c | 103 +----- ext/uri/uriparser/src/UriSetPath.c | 107 +----- ext/uri/uriparser/src/UriSetPort.c | 14 +- ext/uri/uriparser/src/UriSetQuery.c | 107 +----- ext/uri/uriparser/src/UriSetScheme.c | 81 +---- ext/uri/uriparser/src/UriSetUserInfo.c | 103 +----- ext/uri/uriparser/src/UriSets.h | 174 +++++++++ 12 files changed, 253 insertions(+), 1120 deletions(-) create mode 100644 ext/uri/uriparser/src/UriSets.h diff --git a/ext/uri/uriparser/src/UriCommon.c b/ext/uri/uriparser/src/UriCommon.c index a594fcceed716..3644e8828f35f 100644 --- a/ext/uri/uriparser/src/UriCommon.c +++ b/ext/uri/uriparser/src/UriCommon.c @@ -62,6 +62,7 @@ # ifndef URI_DOXYGEN # include # include "UriCommon.h" +# include "UriSets.h" # endif # include @@ -468,32 +469,11 @@ UriBool URI_FUNC(RemoveDotSegmentsAbsolute)(URI_TYPE(Uri) * uri, unsigned char URI_FUNC(HexdigToInt)(URI_CHAR hexdig) { switch (hexdig) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): + case URI_SET_DIGIT(_UT): return (unsigned char)(9 + hexdig - _UT('9')); - - case _UT('a'): - case _UT('b'): - case _UT('c'): - case _UT('d'): - case _UT('e'): - case _UT('f'): + case URI_SET_HEX_LETTER_LOWER(_UT): return (unsigned char)(15 + hexdig - _UT('f')); - - case _UT('A'): - case _UT('B'): - case _UT('C'): - case _UT('D'): - case _UT('E'): - case _UT('F'): + case URI_SET_HEX_LETTER_UPPER(_UT): return (unsigned char)(15 + hexdig - _UT('F')); default: diff --git a/ext/uri/uriparser/src/UriEscape.c b/ext/uri/uriparser/src/UriEscape.c index b23050783fb33..a1763f97153cf 100644 --- a/ext/uri/uriparser/src/UriEscape.c +++ b/ext/uri/uriparser/src/UriEscape.c @@ -62,6 +62,7 @@ # ifndef URI_DOXYGEN # include # include "UriCommon.h" +# include "UriSets.h" # endif URI_CHAR * URI_FUNC(Escape)(const URI_CHAR * in, URI_CHAR * out, UriBool spaceToPlus, @@ -108,72 +109,7 @@ URI_CHAR * URI_FUNC(EscapeEx)(const URI_CHAR * inFirst, const URI_CHAR * inAfter prevWasCr = URI_FALSE; break; - case _UT('a'): /* ALPHA */ - case _UT('A'): - case _UT('b'): - case _UT('B'): - case _UT('c'): - case _UT('C'): - case _UT('d'): - case _UT('D'): - case _UT('e'): - case _UT('E'): - case _UT('f'): - case _UT('F'): - case _UT('g'): - case _UT('G'): - case _UT('h'): - case _UT('H'): - case _UT('i'): - case _UT('I'): - case _UT('j'): - case _UT('J'): - case _UT('k'): - case _UT('K'): - case _UT('l'): - case _UT('L'): - case _UT('m'): - case _UT('M'): - case _UT('n'): - case _UT('N'): - case _UT('o'): - case _UT('O'): - case _UT('p'): - case _UT('P'): - case _UT('q'): - case _UT('Q'): - case _UT('r'): - case _UT('R'): - case _UT('s'): - case _UT('S'): - case _UT('t'): - case _UT('T'): - case _UT('u'): - case _UT('U'): - case _UT('v'): - case _UT('V'): - case _UT('w'): - case _UT('W'): - case _UT('x'): - case _UT('X'): - case _UT('y'): - case _UT('Y'): - case _UT('z'): - case _UT('Z'): - case _UT('0'): /* DIGIT */ - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): - case _UT('-'): /* "-" / "." / "_" / "~" */ - case _UT('.'): - case _UT('_'): - case _UT('~'): + case URI_SET_UNRESERVED(_UT): /* Copy unmodified */ write[0] = read[0]; write++; @@ -263,51 +199,9 @@ const URI_CHAR * URI_FUNC(UnescapeInPlaceEx)(URI_CHAR * inout, UriBool plusToSpa case _UT('%'): switch (read[1]) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): - case _UT('a'): - case _UT('b'): - case _UT('c'): - case _UT('d'): - case _UT('e'): - case _UT('f'): - case _UT('A'): - case _UT('B'): - case _UT('C'): - case _UT('D'): - case _UT('E'): - case _UT('F'): + case URI_SET_HEXDIG(_UT): switch (read[2]) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): - case _UT('a'): - case _UT('b'): - case _UT('c'): - case _UT('d'): - case _UT('e'): - case _UT('f'): - case _UT('A'): - case _UT('B'): - case _UT('C'): - case _UT('D'): - case _UT('E'): - case _UT('F'): { + case URI_SET_HEXDIG(_UT): { /* Percent group found */ const unsigned char left = URI_FUNC(HexdigToInt)(read[1]); const unsigned char right = URI_FUNC(HexdigToInt)(read[2]); diff --git a/ext/uri/uriparser/src/UriIp4.c b/ext/uri/uriparser/src/UriIp4.c index 162a75a556d49..ae61141f7a3ad 100644 --- a/ext/uri/uriparser/src/UriIp4.c +++ b/ext/uri/uriparser/src/UriIp4.c @@ -68,6 +68,7 @@ # include # include "UriIp4Base.h" # include +# include "UriSets.h" # endif /* Prototypes */ @@ -194,16 +195,7 @@ URI_FUNC(ParseDecOctetOne)(UriIp4Parser * parser, const URI_CHAR * first, } switch (*first) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): + case URI_SET_DIGIT(_UT): uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, afterLast); @@ -272,16 +264,7 @@ URI_FUNC(ParseDecOctetThree)(UriIp4Parser * parser, const URI_CHAR * first, } switch (*first) { - case _UT('0'): - case _UT('1'): - case _UT('2'): - case _UT('3'): - case _UT('4'): - case _UT('5'): - case _UT('6'): - case _UT('7'): - case _UT('8'): - case _UT('9'): + case URI_SET_DIGIT(_UT): uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9'))); return first + 1; diff --git a/ext/uri/uriparser/src/UriParse.c b/ext/uri/uriparser/src/UriParse.c index db48b380464b1..ed851e94fbd9c 100644 --- a/ext/uri/uriparser/src/UriParse.c +++ b/ext/uri/uriparser/src/UriParse.c @@ -71,82 +71,9 @@ # include "UriCommon.h" # include "UriMemory.h" # include "UriParseBase.h" +# include "UriSets.h" # endif -# define URI_SET_DIGIT \ - _UT('0') : case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - -# define URI_SET_HEX_LETTER_UPPER \ - _UT('A') : case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - -# define URI_SET_HEX_LETTER_LOWER \ - _UT('a') : case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - -# define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - -# define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - static const URI_CHAR * URI_FUNC(ParseAuthority)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, @@ -340,26 +267,7 @@ static URI_INLINE const URI_CHAR * URI_FUNC(ParseAuthority)(URI_TYPE(ParserState return URI_FUNC(ParseAuthorityTwo)(state, afterIpLit2, afterLast); } - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: + case URI_SET_PCHAR(_UT): state->uri->userInfo.first = first; /* USERINFO BEGIN */ return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast, memory); @@ -411,7 +319,7 @@ static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state, } switch (*first) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): return URI_FUNC(ParseHexZero)(state, first + 1, afterLast); default: @@ -433,26 +341,7 @@ static URI_INLINE const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState) } switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: + case URI_SET_PCHAR(_UT): return URI_FUNC(ParsePathRootless)(state, first, afterLast, memory); case _UT('/'): @@ -478,24 +367,9 @@ static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state, } switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): case _UT(':'): - case _UT(';'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: + case URI_SET_SUB_DELIMS(_UT): + case URI_SET_UNRESERVED(_UT): return URI_FUNC(ParseIpFutStopGo)(state, first + 1, afterLast, memory); default: @@ -517,24 +391,9 @@ static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state } switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): case _UT(':'): - case _UT(';'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: + case URI_SET_SUB_DELIMS(_UT): + case URI_SET_UNRESERVED(_UT): return URI_FUNC(ParseIpFutLoop)(state, first, afterLast, memory); default: @@ -568,7 +427,7 @@ static const URI_CHAR * URI_FUNC(ParseIpFuture)(URI_TYPE(ParserState) * state, } switch (first[1]) { - case URI_SET_HEXDIG: { + case URI_SET_HEXDIG(_UT): { const URI_CHAR * afterIpFutLoop; const URI_CHAR * const afterHexZero = URI_FUNC(ParseHexZero)(state, first + 2, afterLast); @@ -643,7 +502,7 @@ static URI_INLINE const URI_CHAR * URI_FUNC(ParseIpLit2)(URI_TYPE(ParserState) * case _UT(':'): case _UT(']'): - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): state->uri->hostData.ip6 = memory->malloc( memory, 1 * sizeof(UriIp6)); /* Freed when stopping on parse error */ if (state->uri->hostData.ip6 == NULL) { @@ -685,7 +544,7 @@ static const URI_CHAR * URI_FUNC(ParseIPv6address2)(URI_TYPE(ParserState) * stat /* Eat rest of IPv4 address */ for (;;) { switch (*first) { - case URI_SET_DIGIT: + case URI_SET_DIGIT(_UT): if (digitCount == 4) { URI_FUNC(StopSyntax)(state, first, memory); return NULL; @@ -780,7 +639,7 @@ static const URI_CHAR * URI_FUNC(ParseIPv6address2)(URI_TYPE(ParserState) * stat int walking = 1; do { switch (*first) { - case URI_SET_HEX_LETTER_LOWER: + case URI_SET_HEX_LETTER_LOWER(_UT): letterAmong = 1; if (digitCount == 4) { URI_FUNC(StopSyntax)(state, first, memory); @@ -790,7 +649,7 @@ static const URI_CHAR * URI_FUNC(ParseIPv6address2)(URI_TYPE(ParserState) * stat digitCount++; break; - case URI_SET_HEX_LETTER_UPPER: + case URI_SET_HEX_LETTER_UPPER(_UT): letterAmong = 1; if (digitCount == 4) { URI_FUNC(StopSyntax)(state, first, memory); @@ -800,7 +659,7 @@ static const URI_CHAR * URI_FUNC(ParseIPv6address2)(URI_TYPE(ParserState) * stat digitCount++; break; - case URI_SET_DIGIT: + case URI_SET_DIGIT(_UT): if (digitCount == 4) { URI_FUNC(StopSyntax)(state, first, memory); return NULL; @@ -995,23 +854,8 @@ static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) * } case _UT('@'): - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('*'): - case _UT(','): - case _UT(';'): - case _UT('\''): - case _UT('+'): - case _UT('='): - case _UT('-'): - case _UT('.'): - case _UT('_'): - case _UT('~'): - case URI_SET_DIGIT: - case URI_SET_ALPHA: + case URI_SET_SUB_DELIMS(_UT): + case URI_SET_UNRESERVED(_UT): return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast, memory); case _UT('/'): { @@ -1118,24 +962,9 @@ static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state, } switch (*first) { - case _UT('!'): - case _UT('$'): case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(';'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: { + case URI_SET_SUB_DELIMS(_UT): + case URI_SET_UNRESERVED(_UT): { const URI_CHAR * const afterPctSubUnres = URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory); if (afterPctSubUnres == NULL) { @@ -1193,26 +1022,7 @@ URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * f } switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: + case URI_SET_PCHAR(_UT): return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast, memory); default: @@ -1239,24 +1049,9 @@ static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * } switch (*first) { - case _UT('!'): - case _UT('$'): case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(';'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: { + case URI_SET_SUB_DELIMS(_UT): + case URI_SET_UNRESERVED(_UT): { const URI_CHAR * const afterPctSubUnres = URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory); if (afterPctSubUnres == NULL) { @@ -1331,19 +1126,7 @@ static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * s } switch (*first) { - /* begin sub-delims */ - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('\''): - case _UT('('): - case _UT(')'): - case _UT('*'): - case _UT('+'): - case _UT(','): - case _UT(';'): - case _UT('='): - /* end sub-delims */ + case URI_SET_SUB_DELIMS(_UT): /* begin unreserved (except alpha and digit) */ case _UT('-'): case _UT('.'): @@ -1351,12 +1134,12 @@ static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * s case _UT('~'): /* end unreserved (except alpha and digit) */ case _UT(':'): - case URI_SET_ALPHA: + case URI_SET_ALPHA(_UT): state->uri->hostText.afterLast = NULL; /* Not a host, reset */ state->uri->portText.first = NULL; /* Not a port, reset */ return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast, memory); - case URI_SET_DIGIT: + case URI_SET_DIGIT(_UT): return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast, memory); case _UT('%'): @@ -1399,24 +1182,9 @@ static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state } switch (*first) { - case _UT('!'): - case _UT('$'): case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(';'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: { + case URI_SET_SUB_DELIMS(_UT): + case URI_SET_UNRESERVED(_UT): { const URI_CHAR * const afterPctSubUnres = URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory); if (afterPctSubUnres == NULL) { @@ -1522,26 +1290,7 @@ URI_FUNC(ParsePathAbsNoLeadSlash)(URI_TYPE(ParserState) * state, const URI_CHAR } switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: { + case URI_SET_PCHAR(_UT): { const URI_CHAR * const afterSegmentNz = URI_FUNC(ParseSegmentNz)(state, first, afterLast, memory); if (afterSegmentNz == NULL) { @@ -1600,25 +1349,7 @@ static const URI_CHAR * URI_FUNC(ParsePchar)(URI_TYPE(ParserState) * state, case _UT('%'): return URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); - case _UT(':'): - case _UT('@'): - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('*'): - case _UT(','): - case _UT(';'): - case _UT('\''): - case _UT('+'): - case _UT('='): - case _UT('-'): - case _UT('.'): - case _UT('_'): - case _UT('~'): - case URI_SET_DIGIT: - case URI_SET_ALPHA: + case URI_SET_PCHAR_WITHOUT_PERCENT(_UT): return first + 1; default: @@ -1652,14 +1383,14 @@ static const URI_CHAR * URI_FUNC(ParsePctEncoded)(URI_TYPE(ParserState) * state, } switch (first[1]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): if (afterLast - first < 3) { URI_FUNC(StopSyntax)(state, afterLast, memory); return NULL; } switch (first[2]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): return first + 3; default: @@ -1698,23 +1429,8 @@ static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state case _UT('%'): return URI_FUNC(ParsePctEncoded)(state, first, afterLast, memory); - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('*'): - case _UT(','): - case _UT(';'): - case _UT('\''): - case _UT('+'): - case _UT('='): - case _UT('-'): - case _UT('.'): - case _UT('_'): - case _UT('~'): - case URI_SET_DIGIT: - case URI_SET_ALPHA: + case URI_SET_SUB_DELIMS(_UT): + case URI_SET_UNRESERVED(_UT): return first + 1; default: @@ -1735,7 +1451,7 @@ static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, } switch (*first) { - case URI_SET_DIGIT: + case URI_SET_DIGIT(_UT): return URI_FUNC(ParsePort)(state, first + 1, afterLast); default: @@ -1758,26 +1474,7 @@ static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state, } switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: { + case URI_SET_PCHAR(_UT): { const URI_CHAR * const afterPchar = URI_FUNC(ParsePchar)(state, first, afterLast, memory); if (afterPchar == NULL) { @@ -1808,26 +1505,7 @@ static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state, } switch (*first) { - case _UT('!'): - case _UT('$'): - case _UT('%'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('-'): - case _UT('*'): - case _UT(','): - case _UT('.'): - case _UT(':'): - case _UT(';'): - case _UT('@'): - case _UT('\''): - case _UT('_'): - case _UT('~'): - case _UT('+'): - case _UT('='): - case URI_SET_DIGIT: - case URI_SET_ALPHA: { + case URI_SET_PCHAR(_UT): { const URI_CHAR * const afterPchar = URI_FUNC(ParsePchar)(state, first, afterLast, memory); if (afterPchar == NULL) { @@ -1906,8 +1584,8 @@ static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState case _UT('.'): case _UT('+'): case _UT('-'): - case URI_SET_ALPHA: - case URI_SET_DIGIT: + case URI_SET_ALPHA(_UT): + case URI_SET_DIGIT(_UT): return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast, memory); case _UT('%'): { @@ -2002,22 +1680,12 @@ static const URI_CHAR * URI_FUNC(ParseUriReference)(URI_TYPE(ParserState) * stat } switch (*first) { - case URI_SET_ALPHA: + case URI_SET_ALPHA(_UT): state->uri->scheme.first = first; /* SCHEME BEGIN */ return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast, memory); - case URI_SET_DIGIT: - case _UT('!'): - case _UT('$'): - case _UT('&'): - case _UT('('): - case _UT(')'): - case _UT('*'): - case _UT(','): - case _UT(';'): - case _UT('\''): - case _UT('+'): - case _UT('='): + case URI_SET_DIGIT(_UT): + case URI_SET_SUB_DELIMS(_UT): case _UT('.'): case _UT('_'): case _UT('~'): diff --git a/ext/uri/uriparser/src/UriSetFragment.c b/ext/uri/uriparser/src/UriSetFragment.c index b9c5c53b04202..4479391d859dc 100644 --- a/ext/uri/uriparser/src/UriSetFragment.c +++ b/ext/uri/uriparser/src/UriSetFragment.c @@ -62,104 +62,11 @@ # include # include "UriCommon.h" # include "UriMemory.h" +# include "UriSets.h" # endif # include -# define URI_SET_DIGIT \ - _UT('0') : case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - -# define URI_SET_HEX_LETTER_UPPER \ - _UT('A') : case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - -# define URI_SET_HEX_LETTER_LOWER \ - _UT('a') : case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - -# define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - -# define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - -# define URI_SET_SUB_DELIMS \ - _UT('!') : case _UT('$'): \ - case _UT('&'): \ - case _UT('\''): \ - case _UT('('): \ - case _UT(')'): \ - case _UT('*'): \ - case _UT('+'): \ - case _UT(','): \ - case _UT(';'): \ - case _UT('=') - -# define URI_SET_UNRESERVED \ - URI_SET_ALPHA: \ - case URI_SET_DIGIT: \ - case _UT('-'): \ - case _UT('.'): \ - case _UT('_'): \ - case _UT('~') - UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, const URI_CHAR * afterLast) { if ((first == NULL) || (afterLast == NULL)) { @@ -173,7 +80,7 @@ UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, */ while (first < afterLast) { switch (first[0]) { - case URI_SET_UNRESERVED: + case URI_SET_PCHAR_WITHOUT_PERCENT(_UT): break; /* pct-encoded */ @@ -182,13 +89,13 @@ UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, return URI_FALSE; } switch (first[1]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): break; default: return URI_FALSE; } switch (first[2]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): break; default: return URI_FALSE; @@ -196,12 +103,6 @@ UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, first += 2; break; - case URI_SET_SUB_DELIMS: - break; - - /* ":" / "@" and "/" / "?" */ - case _UT(':'): - case _UT('@'): case _UT('/'): case _UT('?'): break; diff --git a/ext/uri/uriparser/src/UriSetHostRegName.c b/ext/uri/uriparser/src/UriSetHostRegName.c index 61694b248adc6..01bc4e47f16ef 100644 --- a/ext/uri/uriparser/src/UriSetHostRegName.c +++ b/ext/uri/uriparser/src/UriSetHostRegName.c @@ -63,102 +63,9 @@ # include "UriMemory.h" # include "UriSetHostBase.h" # include "UriSetHostCommon.h" +# include "UriSets.h" # endif -# define URI_SET_DIGIT \ - _UT('0') : case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - -# define URI_SET_HEX_LETTER_UPPER \ - _UT('A') : case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - -# define URI_SET_HEX_LETTER_LOWER \ - _UT('a') : case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - -# define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - -# define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - -# define URI_SET_SUB_DELIMS \ - _UT('!') : case _UT('$'): \ - case _UT('&'): \ - case _UT('\''): \ - case _UT('('): \ - case _UT(')'): \ - case _UT('*'): \ - case _UT('+'): \ - case _UT(','): \ - case _UT(';'): \ - case _UT('=') - -# define URI_SET_UNRESERVED \ - URI_SET_ALPHA: \ - case URI_SET_DIGIT: \ - case _UT('-'): \ - case _UT('.'): \ - case _UT('_'): \ - case _UT('~') - UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, const URI_CHAR * afterLast) { if ((first == NULL) || (afterLast == NULL)) { @@ -168,7 +75,7 @@ UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, /* reg-name = *( unreserved / pct-encoded / sub-delims ) */ while (first < afterLast) { switch (first[0]) { - case URI_SET_UNRESERVED: + case URI_SET_UNRESERVED(_UT): break; /* pct-encoded */ @@ -177,13 +84,13 @@ UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, return URI_FALSE; } switch (first[1]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): break; default: return URI_FALSE; } switch (first[2]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): break; default: return URI_FALSE; @@ -191,7 +98,7 @@ UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, first += 2; break; - case URI_SET_SUB_DELIMS: + case URI_SET_SUB_DELIMS(_UT): break; default: diff --git a/ext/uri/uriparser/src/UriSetPath.c b/ext/uri/uriparser/src/UriSetPath.c index d9e8bec0aa802..17aef0fca42d4 100644 --- a/ext/uri/uriparser/src/UriSetPath.c +++ b/ext/uri/uriparser/src/UriSetPath.c @@ -62,104 +62,11 @@ # include # include "UriCommon.h" # include "UriMemory.h" +# include "UriSets.h" # endif # include -# define URI_SET_DIGIT \ - _UT('0') : case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - -# define URI_SET_HEX_LETTER_UPPER \ - _UT('A') : case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - -# define URI_SET_HEX_LETTER_LOWER \ - _UT('a') : case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - -# define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - -# define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - -# define URI_SET_SUB_DELIMS \ - _UT('!') : case _UT('$'): \ - case _UT('&'): \ - case _UT('\''): \ - case _UT('('): \ - case _UT(')'): \ - case _UT('*'): \ - case _UT('+'): \ - case _UT(','): \ - case _UT(';'): \ - case _UT('=') - -# define URI_SET_UNRESERVED \ - URI_SET_ALPHA: \ - case URI_SET_DIGIT: \ - case _UT('-'): \ - case _UT('.'): \ - case _UT('_'): \ - case _UT('~') - UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, const URI_CHAR * afterLast, UriBool hasHost) { if ((first == NULL) || (afterLast == NULL)) { @@ -200,7 +107,7 @@ UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, const URI_CHAR * afte */ while (first < afterLast) { switch (first[0]) { - case URI_SET_UNRESERVED: + case URI_SET_PCHAR_WITHOUT_PERCENT(_UT): break; /* pct-encoded */ @@ -209,13 +116,13 @@ UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, const URI_CHAR * afte return URI_FALSE; } switch (first[1]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): break; default: return URI_FALSE; } switch (first[2]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): break; default: return URI_FALSE; @@ -223,12 +130,6 @@ UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, const URI_CHAR * afte first += 2; break; - case URI_SET_SUB_DELIMS: - break; - - /* ":" / "@" and "/" */ - case _UT(':'): - case _UT('@'): case _UT('/'): break; diff --git a/ext/uri/uriparser/src/UriSetPort.c b/ext/uri/uriparser/src/UriSetPort.c index 1c373013f66d0..5e2160e309765 100644 --- a/ext/uri/uriparser/src/UriSetPort.c +++ b/ext/uri/uriparser/src/UriSetPort.c @@ -62,21 +62,11 @@ # include # include "UriCommon.h" # include "UriMemory.h" +# include "UriSets.h" # endif # include -# define URI_SET_DIGIT \ - _UT('0') : case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - UriBool URI_FUNC(IsWellFormedPort)(const URI_CHAR * first, const URI_CHAR * afterLast) { if ((first == NULL) || (afterLast == NULL)) { return URI_FALSE; @@ -85,7 +75,7 @@ UriBool URI_FUNC(IsWellFormedPort)(const URI_CHAR * first, const URI_CHAR * afte /* NOTE: Grammar reads "port = *DIGIT" which includes the empty string. */ while (first < afterLast) { switch (first[0]) { - case URI_SET_DIGIT: + case URI_SET_DIGIT(_UT): break; default: return URI_FALSE; diff --git a/ext/uri/uriparser/src/UriSetQuery.c b/ext/uri/uriparser/src/UriSetQuery.c index a189c14bb1ed9..4f58c8286ed70 100644 --- a/ext/uri/uriparser/src/UriSetQuery.c +++ b/ext/uri/uriparser/src/UriSetQuery.c @@ -62,104 +62,11 @@ # include # include "UriCommon.h" # include "UriMemory.h" +# include "UriSets.h" # endif # include -# define URI_SET_DIGIT \ - _UT('0') : case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - -# define URI_SET_HEX_LETTER_UPPER \ - _UT('A') : case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - -# define URI_SET_HEX_LETTER_LOWER \ - _UT('a') : case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - -# define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - -# define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - -# define URI_SET_SUB_DELIMS \ - _UT('!') : case _UT('$'): \ - case _UT('&'): \ - case _UT('\''): \ - case _UT('('): \ - case _UT(')'): \ - case _UT('*'): \ - case _UT('+'): \ - case _UT(','): \ - case _UT(';'): \ - case _UT('=') - -# define URI_SET_UNRESERVED \ - URI_SET_ALPHA: \ - case URI_SET_DIGIT: \ - case _UT('-'): \ - case _UT('.'): \ - case _UT('_'): \ - case _UT('~') - UriBool URI_FUNC(IsWellFormedQuery)(const URI_CHAR * first, const URI_CHAR * afterLast) { if ((first == NULL) || (afterLast == NULL)) { return URI_FALSE; @@ -172,7 +79,7 @@ UriBool URI_FUNC(IsWellFormedQuery)(const URI_CHAR * first, const URI_CHAR * aft */ while (first < afterLast) { switch (first[0]) { - case URI_SET_UNRESERVED: + case URI_SET_PCHAR_WITHOUT_PERCENT(_UT): break; /* pct-encoded */ @@ -181,13 +88,13 @@ UriBool URI_FUNC(IsWellFormedQuery)(const URI_CHAR * first, const URI_CHAR * aft return URI_FALSE; } switch (first[1]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): break; default: return URI_FALSE; } switch (first[2]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): break; default: return URI_FALSE; @@ -195,12 +102,6 @@ UriBool URI_FUNC(IsWellFormedQuery)(const URI_CHAR * first, const URI_CHAR * aft first += 2; break; - case URI_SET_SUB_DELIMS: - break; - - /* ":" / "@" and "/" / "?" */ - case _UT(':'): - case _UT('@'): case _UT('/'): case _UT('?'): break; diff --git a/ext/uri/uriparser/src/UriSetScheme.c b/ext/uri/uriparser/src/UriSetScheme.c index 9a21d45f26319..3dfaf1e9f151b 100644 --- a/ext/uri/uriparser/src/UriSetScheme.c +++ b/ext/uri/uriparser/src/UriSetScheme.c @@ -62,84 +62,11 @@ # include # include "UriCommon.h" # include "UriMemory.h" +# include "UriSets.h" # endif # include -# define URI_SET_DIGIT \ - _UT('0') : case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - -# define URI_SET_HEX_LETTER_UPPER \ - _UT('A') : case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - -# define URI_SET_HEX_LETTER_LOWER \ - _UT('a') : case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - -# define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - -# define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - UriBool URI_FUNC(IsWellFormedScheme)(const URI_CHAR * first, const URI_CHAR * afterLast) { if ((first == NULL) || (afterLast == NULL)) { return URI_FALSE; @@ -154,7 +81,7 @@ UriBool URI_FUNC(IsWellFormedScheme)(const URI_CHAR * first, const URI_CHAR * af } switch (first[0]) { - case URI_SET_ALPHA: + case URI_SET_ALPHA(_UT): break; default: @@ -165,8 +92,8 @@ UriBool URI_FUNC(IsWellFormedScheme)(const URI_CHAR * first, const URI_CHAR * af while (first < afterLast) { switch (first[0]) { - case URI_SET_ALPHA: - case URI_SET_DIGIT: + case URI_SET_ALPHA(_UT): + case URI_SET_DIGIT(_UT): case _UT('+'): case _UT('-'): case _UT('.'): diff --git a/ext/uri/uriparser/src/UriSetUserInfo.c b/ext/uri/uriparser/src/UriSetUserInfo.c index af1ec41a0763d..7865e837deb66 100644 --- a/ext/uri/uriparser/src/UriSetUserInfo.c +++ b/ext/uri/uriparser/src/UriSetUserInfo.c @@ -62,104 +62,11 @@ # include # include "UriCommon.h" # include "UriMemory.h" +# include "UriSets.h" # endif # include -# define URI_SET_DIGIT \ - _UT('0') : case _UT('1'): \ - case _UT('2'): \ - case _UT('3'): \ - case _UT('4'): \ - case _UT('5'): \ - case _UT('6'): \ - case _UT('7'): \ - case _UT('8'): \ - case _UT('9') - -# define URI_SET_HEX_LETTER_UPPER \ - _UT('A') : case _UT('B'): \ - case _UT('C'): \ - case _UT('D'): \ - case _UT('E'): \ - case _UT('F') - -# define URI_SET_HEX_LETTER_LOWER \ - _UT('a') : case _UT('b'): \ - case _UT('c'): \ - case _UT('d'): \ - case _UT('e'): \ - case _UT('f') - -# define URI_SET_HEXDIG \ - URI_SET_DIGIT: \ - case URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER - -# define URI_SET_ALPHA \ - URI_SET_HEX_LETTER_UPPER: \ - case URI_SET_HEX_LETTER_LOWER: \ - case _UT('g'): \ - case _UT('G'): \ - case _UT('h'): \ - case _UT('H'): \ - case _UT('i'): \ - case _UT('I'): \ - case _UT('j'): \ - case _UT('J'): \ - case _UT('k'): \ - case _UT('K'): \ - case _UT('l'): \ - case _UT('L'): \ - case _UT('m'): \ - case _UT('M'): \ - case _UT('n'): \ - case _UT('N'): \ - case _UT('o'): \ - case _UT('O'): \ - case _UT('p'): \ - case _UT('P'): \ - case _UT('q'): \ - case _UT('Q'): \ - case _UT('r'): \ - case _UT('R'): \ - case _UT('s'): \ - case _UT('S'): \ - case _UT('t'): \ - case _UT('T'): \ - case _UT('u'): \ - case _UT('U'): \ - case _UT('v'): \ - case _UT('V'): \ - case _UT('w'): \ - case _UT('W'): \ - case _UT('x'): \ - case _UT('X'): \ - case _UT('y'): \ - case _UT('Y'): \ - case _UT('z'): \ - case _UT('Z') - -# define URI_SET_SUB_DELIMS \ - _UT('!') : case _UT('$'): \ - case _UT('&'): \ - case _UT('\''): \ - case _UT('('): \ - case _UT(')'): \ - case _UT('*'): \ - case _UT('+'): \ - case _UT(','): \ - case _UT(';'): \ - case _UT('=') - -# define URI_SET_UNRESERVED \ - URI_SET_ALPHA: \ - case URI_SET_DIGIT: \ - case _UT('-'): \ - case _UT('.'): \ - case _UT('_'): \ - case _UT('~') - UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, const URI_CHAR * afterLast) { if ((first == NULL) || (afterLast == NULL)) { @@ -169,7 +76,7 @@ UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, /* userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) */ while (first < afterLast) { switch (first[0]) { - case URI_SET_UNRESERVED: + case URI_SET_UNRESERVED(_UT): break; /* pct-encoded */ @@ -178,13 +85,13 @@ UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, return URI_FALSE; } switch (first[1]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): break; default: return URI_FALSE; } switch (first[2]) { - case URI_SET_HEXDIG: + case URI_SET_HEXDIG(_UT): break; default: return URI_FALSE; @@ -192,7 +99,7 @@ UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, first += 2; break; - case URI_SET_SUB_DELIMS: + case URI_SET_SUB_DELIMS(_UT): break; /* ":" */ diff --git a/ext/uri/uriparser/src/UriSets.h b/ext/uri/uriparser/src/UriSets.h new file mode 100644 index 0000000000000..a6a2c46a14d77 --- /dev/null +++ b/ext/uri/uriparser/src/UriSets.h @@ -0,0 +1,174 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file UriSets.h + * Holds character set definitions. + */ + +// NOTE: We cannot use a regular include-once guard here because the +// file must support being included twice, e.g. from file UriParse.c. +#if !defined(URI_SET_DIGIT) + +// clang-format off +# define URI_SET_DIGIT(ut) \ + ut('0'): \ + case ut('1'): \ + /* clang-format on */ \ + case ut('2'): \ + case ut('3'): \ + case ut('4'): \ + case ut('5'): \ + case ut('6'): \ + case ut('7'): \ + case ut('8'): \ + case ut('9') + +// clang-format off +# define URI_SET_HEX_LETTER_LOWER(ut) \ + ut('a'): \ + case ut('b'): \ + /* clang-format on */ \ + case ut('c'): \ + case ut('d'): \ + case ut('e'): \ + case ut('f') + +// clang-format off +# define URI_SET_HEX_LETTER_UPPER(ut) \ + ut('A'): \ + case ut('B'): \ + /* clang-format on */ \ + case ut('C'): \ + case ut('D'): \ + case ut('E'): \ + case ut('F') + +// clang-format off +# define URI_SET_HEXDIG(ut) \ + URI_SET_DIGIT(ut): \ + case URI_SET_HEX_LETTER_LOWER(ut): \ + /* clang-format on */ \ + case URI_SET_HEX_LETTER_UPPER(ut) + +// clang-format off +# define URI_SET_ALPHA(ut) \ + URI_SET_HEX_LETTER_UPPER(ut): \ + case URI_SET_HEX_LETTER_LOWER(ut): \ + /* clang-format on */ \ + case ut('g'): \ + case ut('G'): \ + case ut('h'): \ + case ut('H'): \ + case ut('i'): \ + case ut('I'): \ + case ut('j'): \ + case ut('J'): \ + case ut('k'): \ + case ut('K'): \ + case ut('l'): \ + case ut('L'): \ + case ut('m'): \ + case ut('M'): \ + case ut('n'): \ + case ut('N'): \ + case ut('o'): \ + case ut('O'): \ + case ut('p'): \ + case ut('P'): \ + case ut('q'): \ + case ut('Q'): \ + case ut('r'): \ + case ut('R'): \ + case ut('s'): \ + case ut('S'): \ + case ut('t'): \ + case ut('T'): \ + case ut('u'): \ + case ut('U'): \ + case ut('v'): \ + case ut('V'): \ + case ut('w'): \ + case ut('W'): \ + case ut('x'): \ + case ut('X'): \ + case ut('y'): \ + case ut('Y'): \ + case ut('z'): \ + case ut('Z') + +// clang-format off +# define URI_SET_SUB_DELIMS(ut) \ + ut('!'): \ + case ut('$'): \ + /* clang-format on */ \ + case ut('&'): \ + case ut('\''): \ + case ut('('): \ + case ut(')'): \ + case ut('*'): \ + case ut('+'): \ + case ut(','): \ + case ut(';'): \ + case ut('=') + +// clang-format off +# define URI_SET_UNRESERVED(ut) \ + URI_SET_ALPHA(ut): \ + case URI_SET_DIGIT(ut): \ + /* clang-format on */ \ + case ut('-'): \ + case ut('.'): \ + case ut('_'): \ + case ut('~') + +// clang-format off +# define URI_SET_PCHAR_WITHOUT_PERCENT(ut) \ + URI_SET_UNRESERVED(ut): \ + case URI_SET_SUB_DELIMS(ut): \ + /* clang-format on */ \ + case ut(':'): \ + case ut('@') + +// clang-format off +# define URI_SET_PCHAR(ut) \ + URI_SET_PCHAR_WITHOUT_PERCENT(ut): \ + case ut('%') +/* clang-format on */ + +#endif // ! defined(URI_SET_DIGIT) From a3454b80e6d68a5ea6ebe9aef08eba1457f6718f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 26 Nov 2025 11:43:34 +0000 Subject: [PATCH 139/252] Fix GH-20553: PDO::FETCH_CLASSTYPE ignores $constructorArgs in PHP 8.5.0 We must assign the ctor_arguments regardless of modes. This regression was introduced during the refactoring of PDO's internals Closes GH-20595 --- NEWS | 4 ++ ext/pdo/pdo_stmt.c | 3 +- ext/pdo/tests/gh20553.phpt | 97 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 ext/pdo/tests/gh20553.phpt diff --git a/NEWS b/NEWS index 57ffb2b7ae584..947abfeaad514 100644 --- a/NEWS +++ b/NEWS @@ -62,6 +62,10 @@ PHP NEWS . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) +- PDO: + . Fixed bug GH-20553 (PDO::FETCH_CLASSTYPE ignores $constructorArgs in + PHP 8.5.0). (Girgias) + - Phar: . Fixed bug GH-20442 (Phar does not respect case-insensitiveness of __halt_compiler() when reading stub). (ndossche, TimWolla) diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 697940d94260d..5d8b4089ca04f 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -771,9 +771,10 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch class specified"); goto in_fetch_error; } - ctor_arguments = stmt->fetch.cls.ctor_args; } ZEND_ASSERT(ce != NULL); + + ctor_arguments = stmt->fetch.cls.ctor_args; if (flags & PDO_FETCH_SERIALIZE) { if (!ce->unserialize) { /* As this option is deprecated we do not bother to mention the class name. */ diff --git a/ext/pdo/tests/gh20553.phpt b/ext/pdo/tests/gh20553.phpt new file mode 100644 index 0000000000000..fe0b84c27ebb0 --- /dev/null +++ b/ext/pdo/tests/gh20553.phpt @@ -0,0 +1,97 @@ +--TEST-- +GH-20553: PHP 8.5.0 regression: PDO::FETCH_CLASSTYPE ignores $constructorArgs +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- + PDO::FETCH_CLASS, + 'PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE' + => PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, + 'PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE' + => PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE, + 'PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE | PDO::FETCH_PROPS_LATE' + => PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE | PDO::FETCH_PROPS_LATE, +]; + +foreach ($fetchModes as $combinedModes => $fetchMode) { + echo '## ' . $combinedModes . PHP_EOL; + $db->query($sql)->fetchAll( + $fetchMode, + 'dumpy', + ['constructor argument #1'] + ); + echo PHP_EOL; +} +?> +--EXPECT-- +## PDO::FETCH_CLASS +'pdo_fetch_class_type_class' = 'dummy' +'foo' = 'bar' +'abc' = 'dfg' +constructor called, + my class is 'dumpy' + input parameters: array ( + 0 => 'constructor argument #1', +) + +## PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE +constructor called, + my class is 'dumpy' + input parameters: array ( + 0 => 'constructor argument #1', +) +'pdo_fetch_class_type_class' = 'dummy' +'foo' = 'bar' +'abc' = 'dfg' + +## PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE +'foo' = 'bar' +'abc' = 'dfg' +constructor called, + my class is 'dummy' + input parameters: array ( + 0 => 'constructor argument #1', +) + +## PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE | PDO::FETCH_PROPS_LATE +constructor called, + my class is 'dummy' + input parameters: array ( + 0 => 'constructor argument #1', +) +'foo' = 'bar' +'abc' = 'dfg' From 3e7710c97d54d19544af000f1375910fe92d4459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 10 Dec 2025 12:12:31 +0100 Subject: [PATCH 140/252] lexbor: Cherry pick "URL: fixed "heap-buffer-overflow" for scheme is "file"." see lexbor/lexbor@65435d3e4008d0f29e7b52a100a39fbed298a171 Fixes php/php-src#20668 --- NEWS | 3 +++ ext/lexbor/lexbor/url/url.c | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 947abfeaad514..1833ce6f9caba 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,9 @@ PHP NEWS - LDAP: . Fix memory leak in ldap_set_options(). (ndossche) +- Lexbor: + . Fixed bug GH-20668 (\Uri\WhatWg\Url::withHost() crashes (SEGV) for URLs + using the file: scheme). (lexborisov) 18 Dec 2025, PHP 8.5.1 diff --git a/ext/lexbor/lexbor/url/url.c b/ext/lexbor/lexbor/url/url.c index 3483013eeaafe..0eef7a6deba46 100644 --- a/ext/lexbor/lexbor/url/url.c +++ b/ext/lexbor/lexbor/url/url.c @@ -1817,7 +1817,6 @@ lxb_url_parse_basic_h(lxb_url_parser_t *parser, lxb_url_t *url, if (override_state != LXB_URL_STATE__UNDEF && url->scheme.type == LXB_URL_SCHEMEL_TYPE_FILE) { - p -= 1; state = LXB_URL_STATE_FILE_HOST_STATE; goto again; } From 50131de9f3cafffe4b41e24235a9f1d3cd1ab405 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:57:43 +0100 Subject: [PATCH 141/252] Zend: Use zend_hash_str_find_ptr_lc() in zend_get_module_version() --- Zend/zend_API.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 601d753bd2aa6..ca001f0e0c434 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -4384,14 +4384,8 @@ ZEND_API void zend_get_callable_zval_from_fcc(const zend_fcall_info_cache *fcc, ZEND_API const char *zend_get_module_version(const char *module_name) /* {{{ */ { - zend_string *lname; size_t name_len = strlen(module_name); - zend_module_entry *module; - - lname = zend_string_alloc(name_len, 0); - zend_str_tolower_copy(ZSTR_VAL(lname), module_name, name_len); - module = zend_hash_find_ptr(&module_registry, lname); - zend_string_efree(lname); + zend_module_entry *module = zend_hash_str_find_ptr_lc(&module_registry, module_name, name_len); return module ? module->version : NULL; } /* }}} */ From c27368c9392c46047c20d86f536753c29e73b8a0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:57:50 +0100 Subject: [PATCH 142/252] cli: Use zend_hash_str_find_ptr_lc() in get_mime_type() --- sapi/cli/php_cli_server.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index ae56d143c90c8..046e2174dc1d8 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -395,13 +395,7 @@ static void append_essential_headers(smart_str* buffer, php_cli_server_client *c static const char *get_mime_type(const php_cli_server *server, const char *ext, size_t ext_len) /* {{{ */ { - char *ret; - ALLOCA_FLAG(use_heap) - char *ext_lower = do_alloca(ext_len + 1, use_heap); - zend_str_tolower_copy(ext_lower, ext, ext_len); - ret = zend_hash_str_find_ptr(&server->extension_mime_types, ext_lower, ext_len); - free_alloca(ext_lower, use_heap); - return (const char*)ret; + return zend_hash_str_find_ptr_lc(&server->extension_mime_types, ext, ext_len); } /* }}} */ PHP_FUNCTION(apache_request_headers) /* {{{ */ From 19deb91002a53958893903aa3315e30e3285e614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Mon, 24 Nov 2025 12:03:38 +0000 Subject: [PATCH 143/252] pdo_pgsql: Reset persistent session state on disconnect-equivalent processing close GH-20572 --- NEWS | 4 ++ ext/pdo_pgsql/pgsql_driver.c | 16 +++++- ext/pdo_pgsql/tests/session_state_reset.phpt | 55 +++++++++++++++++++ .../session_state_reset_advisory_lock.phpt | 51 +++++++++++++++++ ...session_state_reset_after_interrupted.phpt | 52 ++++++++++++++++++ 5 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 ext/pdo_pgsql/tests/session_state_reset.phpt create mode 100644 ext/pdo_pgsql/tests/session_state_reset_advisory_lock.phpt create mode 100644 ext/pdo_pgsql/tests/session_state_reset_after_interrupted.phpt diff --git a/NEWS b/NEWS index 1ec90015ef21a..af1bb92003d32 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,10 @@ PHP NEWS . Fixed bug GH-20051 (apache2 shutdowns when restart is requested during preloading). (Arnaud, welcomycozyhom) +- PDO_PGSQL: + . Clear session-local state disconnect-equivalent processing. + (KentarouTakeda) + - Phar: . Support reference values in Phar::mungServer(). (ndossche) . Invalid values now throw in Phar::mungServer() instead of being silently diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index be865c1f86838..44f70e00236d2 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -1340,6 +1340,20 @@ static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, i } } +static void pdo_pgsql_request_shutdown(pdo_dbh_t *dbh) +{ + PGresult *res; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + if(H->server) { + res = PQexec(H->server, "DISCARD ALL"); + + if(res) { + PQclear(res); + } + } +} + static bool pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { bool bval; @@ -1383,7 +1397,7 @@ static const struct pdo_dbh_methods pgsql_methods = { pdo_pgsql_get_attribute, pdo_pgsql_check_liveness, /* check_liveness */ pdo_pgsql_get_driver_methods, /* get_driver_methods */ - NULL, + pdo_pgsql_request_shutdown, pgsql_handle_in_transaction, NULL, /* get_gc */ pdo_pgsql_scanner diff --git a/ext/pdo_pgsql/tests/session_state_reset.phpt b/ext/pdo_pgsql/tests/session_state_reset.phpt new file mode 100644 index 0000000000000..ad892bb0da52e --- /dev/null +++ b/ext/pdo_pgsql/tests/session_state_reset.phpt @@ -0,0 +1,55 @@ +--TEST-- +Persistent connections: session state reset when performing disconnect-equivalent processing (general case) +--EXTENSIONS-- +pdo_pgsql +--SKIPIF-- + +--FILE-- + true])); + +require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc'; + +$pdo1 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid1 = (int)$pdo1 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +$defaultValue = (int)$pdo1 + ->query('show log_min_duration_statement;') + ->fetchColumn(0); + +$setValue = $defaultValue + 1; + +$pdo1->exec("set log_min_duration_statement = {$setValue};"); + +$pdo1 = null; + +$pdo2 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid2 = (int)$pdo2 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +assert($pid1 === $pid2); + +$expectedValue = (int)$pdo2 + ->query('show log_min_duration_statement;') + ->fetchColumn(0); + +echo "defaultValue: {$defaultValue}\n"; +echo "setValue: {$setValue}\n"; +echo "expectedValue: {$expectedValue}\n"; +echo "expected value should be reset to default: " . (($expectedValue === $defaultValue) ? 'success' : 'failure') . "\n"; + +?> +--EXPECTF-- +defaultValue: %i +setValue: %d +expectedValue: %i +expected value should be reset to default: success diff --git a/ext/pdo_pgsql/tests/session_state_reset_advisory_lock.phpt b/ext/pdo_pgsql/tests/session_state_reset_advisory_lock.phpt new file mode 100644 index 0000000000000..3435f6b7b402f --- /dev/null +++ b/ext/pdo_pgsql/tests/session_state_reset_advisory_lock.phpt @@ -0,0 +1,51 @@ +--TEST-- +Persistent connections: session state reset when performing disconnect-equivalent processing (advisory lock case) +--EXTENSIONS-- +pdo_pgsql +--SKIPIF-- + +--FILE-- + true])); + +require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc'; + +$pdo1 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid1 = (int)$pdo1 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +$lockResult1 = (bool)$pdo1 + ->query('select pg_try_advisory_lock(42)::int;') + ->fetchColumn(0); + +$pdo1 = null; + +$dsn = getenv('PDO_PGSQL_TEST_DSN'); +$dsn .= ';'; +putenv('PDO_PGSQL_TEST_DSN='.$dsn); + +$pdo2 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid2 = (int)$pdo2 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +assert($pid1 !== $pid2); + +$lockResult2 = (bool)$pdo2 + ->query('select pg_try_advisory_lock(42)::int;') + ->fetchColumn(0); + +echo "lock1: " . ($lockResult1 ? 'success' : 'failure') . "\n"; +echo "lock2: " . ($lockResult2 ? 'success' : 'failure') . "\n"; + +?> +--EXPECT-- +lock1: success +lock2: success diff --git a/ext/pdo_pgsql/tests/session_state_reset_after_interrupted.phpt b/ext/pdo_pgsql/tests/session_state_reset_after_interrupted.phpt new file mode 100644 index 0000000000000..fdafe8a00fc39 --- /dev/null +++ b/ext/pdo_pgsql/tests/session_state_reset_after_interrupted.phpt @@ -0,0 +1,52 @@ +--TEST-- +Persistent connections: session state reset after backend termination (interrupted case) +--EXTENSIONS-- +pdo_pgsql +--SKIPIF-- + +--FILE-- + true])); + +require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc'; + +$pdo1 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid1 = (int)$pdo1 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +$pid1 = (int)$pdo1 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +$dsn = getenv('PDO_PGSQL_TEST_DSN'); +$dsn .= ';'; +putenv('PDO_PGSQL_TEST_DSN='.$dsn); + +$pdo2 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid2 = (int)$pdo2 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +assert($pid1 !== $pid2); + +$terminateResult = (bool)$pdo2 + ->query("select pg_terminate_backend({$pid1})::int") + ->fetchColumn(0); + +// Disconnect after being terminated by another connection +$pdo1 = null; + +echo 'pid of pdo1: ' . $pid1 . "\n"; +echo 'terminate result of pdo1 by pdo2: ' . ($terminateResult ? 'success' : 'failure') . "\n"; + +?> +--EXPECTF-- +pid of pdo1: %d +terminate result of pdo1 by pdo2: success From 1f3fe93eff69f994be72ba47e143a0f6d01279e4 Mon Sep 17 00:00:00 2001 From: Heran Yang Date: Fri, 12 Dec 2025 10:58:37 +0800 Subject: [PATCH 144/252] Add GB18030-2022 to default encoding list for zh-CN (#20604) GB18030-2022 is the current official standard, superseding the previous 2005 and 2000 versions. It is essential for modern Chinese text processing for the following reasons: 1. Superset Relationship: GB18030 is a strict superset of CP936 (GBK) and EUC-CN (GB2312). Using GB18030 as the detection target covers all characters in these older encodings while enabling support for a much wider range of characters. 2. Extended Character Coverage: The 2022 standard includes significant updates, covering over 87,000 characters. It adds support for CJK Extensions (C, D, E, F, G) and updates mappings for rare characters that were previously mapped to the Private Use Area (PUA) in the 2005 version. This is critical for correctly handling names containing rare characters (e.g., in banking or government data). 3. Backward Compatibility: It is safe to promote GB18030-2022 as the preferred encoding. Files encoded in EUC-CN or CP936 are valid GB18030 streams. This PR adds GB18030-2022 to the default encoding list for CN. --- NEWS | 1 + UPGRADING.INTERNALS | 3 +++ ext/mbstring/mbstring.c | 3 ++- .../tests/zh_CN_default_encodings.phpt | 24 +++++++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 ext/mbstring/tests/zh_CN_default_encodings.phpt diff --git a/NEWS b/NEWS index af1bb92003d32..f2e81284bdf89 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,7 @@ PHP NEWS - Mbstring: . ini_set() with mbstring.detect_order changes the order of mb_detect_order as intended, since mbstring.detect_order is an INI_ALL setting. (tobee94) + . Added GB18030-2022 to default encoding list for zh-CN. (HeRaNO) - Opcache: . Fixed bug GH-20051 (apache2 shutdowns when restart is requested during diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index e35557e71f462..71b6e1fd01ecc 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -64,6 +64,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES . Removed the XML_GetCurrentByteCount() libxml compatibility wrapper, as it was unused and could return the wrong result. +- ext/mbstring: + . Added GB18030-2022 to default encoding list for zh-CN. + ======================== 4. OpCode changes ======================== diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 8f94e50ddc861..7422c600284d5 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -116,7 +116,8 @@ static const enum mbfl_no_encoding php_mb_default_identify_list_cn[] = { mbfl_no_encoding_ascii, mbfl_no_encoding_utf8, mbfl_no_encoding_euc_cn, - mbfl_no_encoding_cp936 + mbfl_no_encoding_cp936, + mbfl_no_encoding_gb18030_2022 }; static const enum mbfl_no_encoding php_mb_default_identify_list_tw_hk[] = { diff --git a/ext/mbstring/tests/zh_CN_default_encodings.phpt b/ext/mbstring/tests/zh_CN_default_encodings.phpt new file mode 100644 index 0000000000000..213c304b52c69 --- /dev/null +++ b/ext/mbstring/tests/zh_CN_default_encodings.phpt @@ -0,0 +1,24 @@ +--TEST-- +Default encodings in Simplified Chinese +--EXTENSIONS-- +mbstring +--INI-- +mbstring.language=Simplified Chinese +--FILE-- + +--EXPECT-- +array(5) { + [0]=> + string(5) "ASCII" + [1]=> + string(5) "UTF-8" + [2]=> + string(6) "EUC-CN" + [3]=> + string(5) "CP936" + [4]=> + string(12) "GB18030-2022" +} From 2ee5e6b432c7ed51ff7296522c123551975be95b Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Fri, 12 Dec 2025 14:06:01 -0600 Subject: [PATCH 145/252] Fix GH-7737: openssl_seal/openssl_open do not handle tagged algorithms (#20687) This commit adds a seventh parameter to both two OpenSSL functions: * openssl_seal(): The new parameter is by-ref and is populated with the computed tag. * openssl_open(): The new parameter is by-value to provide the computed tag. Closes GH-7737 --- ext/openssl/openssl.c | 149 +++++++++++++++++++++++----------- ext/openssl/openssl.stub.php | 5 +- ext/openssl/openssl_arginfo.h | 4 +- ext/openssl/tests/gh7737.phpt | 57 +++++++++++++ 4 files changed, 165 insertions(+), 50 deletions(-) create mode 100644 ext/openssl/tests/gh7737.phpt diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 2c09b89e31200..f770dc6deac8b 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -4171,22 +4171,24 @@ PHP_FUNCTION(openssl_verify) /* {{{ Seals data */ PHP_FUNCTION(openssl_seal) { - zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL; + zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL, *tag = NULL; HashTable *pubkeysht; - EVP_PKEY **pkeys; - int i, len1, len2, *eksl, nkeys, iv_len; - unsigned char iv_buf[EVP_MAX_IV_LENGTH + 1], *buf = NULL, **eks; + EVP_PKEY **pkeys = NULL; + int i, len1, len2, *eksl = NULL, nkeys = 0, iv_len; + unsigned char iv_buf[EVP_MAX_IV_LENGTH + 1], *buf = NULL, **eks = NULL; char * data; size_t data_len; char *method; size_t method_len; const EVP_CIPHER *cipher; - EVP_CIPHER_CTX *ctx; + EVP_CIPHER_CTX *ctx = NULL; + size_t tag_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzas|z", &data, &data_len, - &sealdata, &ekeys, &pubkeys, &method, &method_len, &iv) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzas|z!z!", &data, &data_len, + &sealdata, &ekeys, &pubkeys, &method, &method_len, &iv, &tag) == FAILURE) { RETURN_THROWS(); } + RETVAL_FALSE; PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1); @@ -4194,19 +4196,32 @@ PHP_FUNCTION(openssl_seal) nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0; if (!nkeys) { zend_argument_must_not_be_empty_error(4); - RETURN_THROWS(); + goto clean_exit; } cipher = php_openssl_get_evp_cipher_by_name(method); if (!cipher) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); - RETURN_FALSE; + goto clean_exit; } iv_len = EVP_CIPHER_iv_length(cipher); if (!iv && iv_len > 0) { zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm"); - RETURN_THROWS(); + goto clean_exit; + } + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) { + php_openssl_store_errors(); + goto clean_exit; + } + + tag_len = EVP_CIPHER_CTX_get_tag_length(ctx); + if ((tag != NULL) != (tag_len > 0)) { + const char *imp = tag ? "cannot" : "must"; + zend_argument_value_error(7, "%s be specified for the chosen cipher algorithm", imp); + goto clean_exit; } pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0); @@ -4223,21 +4238,12 @@ PHP_FUNCTION(openssl_seal) if (!EG(exception)) { php_error_docref(NULL, E_WARNING, "Not a public key (%dth member of pubkeys)", i+1); } - RETVAL_FALSE; goto clean_exit; } eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1); i++; } ZEND_HASH_FOREACH_END(); - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) { - EVP_CIPHER_CTX_free(ctx); - php_openssl_store_errors(); - RETVAL_FALSE; - goto clean_exit; - } - /* allocate one byte extra to make room for \0 */ buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(ctx)); EVP_CIPHER_CTX_reset(ctx); @@ -4246,19 +4252,23 @@ PHP_FUNCTION(openssl_seal) !EVP_SealUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) || !EVP_SealFinal(ctx, buf + len1, &len2)) { efree(buf); - EVP_CIPHER_CTX_free(ctx); php_openssl_store_errors(); - RETVAL_FALSE; goto clean_exit; } + if (tag) { + zend_string *tag_str = zend_string_alloc(tag_len, 0); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, ZSTR_LEN(tag_str), ZSTR_VAL(tag_str)); + ZSTR_VAL(tag_str)[ZSTR_LEN(tag_str)] = 0; + ZEND_TRY_ASSIGN_REF_NEW_STR(tag, tag_str); + } + if (len1 + len2 > 0) { ZEND_TRY_ASSIGN_REF_NEW_STR(sealdata, zend_string_init((char*)buf, len1 + len2, 0)); efree(buf); ekeys = zend_try_array_init(ekeys); if (!ekeys) { - EVP_CIPHER_CTX_free(ctx); goto clean_exit; } @@ -4276,21 +4286,35 @@ PHP_FUNCTION(openssl_seal) } else { efree(buf); } + RETVAL_LONG(len1 + len2); - EVP_CIPHER_CTX_free(ctx); clean_exit: - for (i=0; i 0) { if (!iv) { zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm"); - RETURN_THROWS(); + goto clean_exit; } if ((size_t)cipher_iv_len != iv_len) { php_error_docref(NULL, E_WARNING, "IV length is invalid"); - RETURN_FALSE; + goto clean_exit; } iv_buf = (unsigned char *)iv; } else { @@ -4350,20 +4377,48 @@ PHP_FUNCTION(openssl_open) buf = emalloc(data_len + 1); ctx = EVP_CIPHER_CTX_new(); - if (ctx != NULL && EVP_OpenInit(ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey) && - EVP_OpenUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) && - EVP_OpenFinal(ctx, buf + len1, &len2) && (len1 + len2 > 0)) { + if (ctx == NULL || !EVP_OpenInit(ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey)) { + php_openssl_store_errors(); + goto clean_exit; + } + + tag_len = EVP_CIPHER_CTX_get_tag_length(ctx); + if ((tag != NULL) != (tag_len > 0)) { + const char *imp = tag ? "cannot" : "must"; + zend_argument_value_error(7, "%s be specified for the chosen cipher algorithm", imp); + goto clean_exit; + } + if (tag) { + if (ZSTR_LEN(tag) != tag_len) { + zend_argument_value_error(7, "must be %d bytes long", tag_len); + goto clean_exit; + } + + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, ZSTR_LEN(tag), ZSTR_VAL(tag))) { + php_openssl_store_errors(); + goto clean_exit; + } + } + + if (EVP_OpenUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) && + EVP_OpenFinal(ctx, buf + len1, &len2) && (len1 + len2 > 0)) { buf[len1 + len2] = '\0'; ZEND_TRY_ASSIGN_REF_NEW_STR(opendata, zend_string_init((char*)buf, len1 + len2, 0)); RETVAL_TRUE; } else { php_openssl_store_errors(); - RETVAL_FALSE; } - efree(buf); - EVP_PKEY_free(pkey); - EVP_CIPHER_CTX_free(ctx); +clean_exit: + if (buf) { + efree(buf); + } + if (pkey) { + EVP_PKEY_free(pkey); + } + if (ctx) { + EVP_CIPHER_CTX_free(ctx); + } } /* }}} */ diff --git a/ext/openssl/openssl.stub.php b/ext/openssl/openssl.stub.php index 94902a4acf0da..56e96a27a6ab2 100644 --- a/ext/openssl/openssl.stub.php +++ b/ext/openssl/openssl.stub.php @@ -628,14 +628,15 @@ function openssl_verify(string $data, string $signature, $public_key, string|int * @param string $sealed_data * @param array $encrypted_keys * @param string $iv + * @param string $tag */ -function openssl_seal(#[\SensitiveParameter] string $data, &$sealed_data, &$encrypted_keys, array $public_key, string $cipher_algo, &$iv = null): int|false {} +function openssl_seal(#[\SensitiveParameter] string $data, &$sealed_data, &$encrypted_keys, array $public_key, string $cipher_algo, &$iv = null, &$tag = null): int|false {} /** * @param string $output * @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key */ -function openssl_open(string $data, #[\SensitiveParameter] &$output, string $encrypted_key, #[\SensitiveParameter] $private_key, string $cipher_algo, ?string $iv = null): bool {} +function openssl_open(string $data, #[\SensitiveParameter] &$output, string $encrypted_key, #[\SensitiveParameter] $private_key, string $cipher_algo, ?string $iv = null, ?string $tag = null): bool {} /** * @return array diff --git a/ext/openssl/openssl_arginfo.h b/ext/openssl/openssl_arginfo.h index 796582c185bb6..5ab604828e5cb 100644 --- a/ext/openssl/openssl_arginfo.h +++ b/ext/openssl/openssl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8233a8abc8ab7145d905d0fa51478edfe1e55a06 */ + * Stub hash: 91d576073b79bf4b441e44eccb0deaa2b79a6949 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_TYPE_MASK(0, certificate, OpenSSLCertificate, MAY_BE_STRING, NULL) @@ -302,6 +302,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_openssl_seal, 0, 5, MAY_BE_LONG| ZEND_ARG_TYPE_INFO(0, public_key, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, cipher_algo, IS_STRING, 0) ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, iv, "null") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, tag, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_open, 0, 5, _IS_BOOL, 0) @@ -311,6 +312,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_open, 0, 5, _IS_BOOL, 0) ZEND_ARG_INFO(0, private_key) ZEND_ARG_TYPE_INFO(0, cipher_algo, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, iv, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, tag, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_get_md_methods, 0, 0, IS_ARRAY, 0) diff --git a/ext/openssl/tests/gh7737.phpt b/ext/openssl/tests/gh7737.phpt new file mode 100644 index 0000000000000..5136e3e295c1d --- /dev/null +++ b/ext/openssl/tests/gh7737.phpt @@ -0,0 +1,57 @@ +--TEST-- +GitHub Bug#7737 - openssl_seal/open() does not handle ciphers with Tags (e.g. AES-256-CGM) +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + KEY_TYPE]); + define('KEY_PUBLIC', openssl_pkey_get_details($key)['key']); + define('KEY_PRIVATE', openssl_pkey_get_private($key)); +})(); + +echo 'Plaintext: '; var_dump(PLAINTEXT); + +$sealResult = openssl_seal(PLAINTEXT, + $sealedData, /* out */ + $sealedKeys, /* out */ + [KEY_PUBLIC], + CIPHER_ALGO, + $iv, /* out */ + $tag); /* out */ + +echo 'Seal Result: '; var_dump($sealResult); +echo 'Sealed Data: '; var_dump(strlen($sealedData)); +echo 'IV Length: '; var_dump(strlen($iv)); +echo 'Tag Length: '; var_dump(strlen($tag)); + +$unsealResult = openssl_open($sealedData, + $unsealedData, /* out */ + $sealedKeys[0], + KEY_PRIVATE, + CIPHER_ALGO, + $iv, + $tag); + +echo 'Unseal Result: '; var_dump($unsealResult); +echo 'Unsealed Data: '; var_dump($unsealedData); + +?> +--EXPECT-- +Plaintext: string(16) "Test Data String" +Seal Result: int(16) +Sealed Data: int(16) +IV Length: int(12) +Tag Length: int(16) +Unseal Result: bool(true) +Unsealed Data: string(16) "Test Data String" From 040ea4ab5f9e2390ead553104a3a569768c678a5 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sat, 13 Dec 2025 11:42:10 +0100 Subject: [PATCH 146/252] =?UTF-8?q?Revert=20"Fix=20GH-7737:=20openssl=5Fse?= =?UTF-8?q?al/openssl=5Fopen=20do=20not=20handle=20tagged=20algorithm?= =?UTF-8?q?=E2=80=A6"=20(#20698)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2ee5e6b432c7ed51ff7296522c123551975be95b. --- ext/openssl/openssl.c | 149 +++++++++++----------------------- ext/openssl/openssl.stub.php | 5 +- ext/openssl/openssl_arginfo.h | 4 +- ext/openssl/tests/gh7737.phpt | 57 ------------- 4 files changed, 50 insertions(+), 165 deletions(-) delete mode 100644 ext/openssl/tests/gh7737.phpt diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index f770dc6deac8b..2c09b89e31200 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -4171,24 +4171,22 @@ PHP_FUNCTION(openssl_verify) /* {{{ Seals data */ PHP_FUNCTION(openssl_seal) { - zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL, *tag = NULL; + zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL; HashTable *pubkeysht; - EVP_PKEY **pkeys = NULL; - int i, len1, len2, *eksl = NULL, nkeys = 0, iv_len; - unsigned char iv_buf[EVP_MAX_IV_LENGTH + 1], *buf = NULL, **eks = NULL; + EVP_PKEY **pkeys; + int i, len1, len2, *eksl, nkeys, iv_len; + unsigned char iv_buf[EVP_MAX_IV_LENGTH + 1], *buf = NULL, **eks; char * data; size_t data_len; char *method; size_t method_len; const EVP_CIPHER *cipher; - EVP_CIPHER_CTX *ctx = NULL; - size_t tag_len; + EVP_CIPHER_CTX *ctx; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzas|z!z!", &data, &data_len, - &sealdata, &ekeys, &pubkeys, &method, &method_len, &iv, &tag) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzas|z", &data, &data_len, + &sealdata, &ekeys, &pubkeys, &method, &method_len, &iv) == FAILURE) { RETURN_THROWS(); } - RETVAL_FALSE; PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1); @@ -4196,32 +4194,19 @@ PHP_FUNCTION(openssl_seal) nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0; if (!nkeys) { zend_argument_must_not_be_empty_error(4); - goto clean_exit; + RETURN_THROWS(); } cipher = php_openssl_get_evp_cipher_by_name(method); if (!cipher) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); - goto clean_exit; + RETURN_FALSE; } iv_len = EVP_CIPHER_iv_length(cipher); if (!iv && iv_len > 0) { zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm"); - goto clean_exit; - } - - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) { - php_openssl_store_errors(); - goto clean_exit; - } - - tag_len = EVP_CIPHER_CTX_get_tag_length(ctx); - if ((tag != NULL) != (tag_len > 0)) { - const char *imp = tag ? "cannot" : "must"; - zend_argument_value_error(7, "%s be specified for the chosen cipher algorithm", imp); - goto clean_exit; + RETURN_THROWS(); } pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0); @@ -4238,12 +4223,21 @@ PHP_FUNCTION(openssl_seal) if (!EG(exception)) { php_error_docref(NULL, E_WARNING, "Not a public key (%dth member of pubkeys)", i+1); } + RETVAL_FALSE; goto clean_exit; } eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1); i++; } ZEND_HASH_FOREACH_END(); + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) { + EVP_CIPHER_CTX_free(ctx); + php_openssl_store_errors(); + RETVAL_FALSE; + goto clean_exit; + } + /* allocate one byte extra to make room for \0 */ buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(ctx)); EVP_CIPHER_CTX_reset(ctx); @@ -4252,23 +4246,19 @@ PHP_FUNCTION(openssl_seal) !EVP_SealUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) || !EVP_SealFinal(ctx, buf + len1, &len2)) { efree(buf); + EVP_CIPHER_CTX_free(ctx); php_openssl_store_errors(); + RETVAL_FALSE; goto clean_exit; } - if (tag) { - zend_string *tag_str = zend_string_alloc(tag_len, 0); - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, ZSTR_LEN(tag_str), ZSTR_VAL(tag_str)); - ZSTR_VAL(tag_str)[ZSTR_LEN(tag_str)] = 0; - ZEND_TRY_ASSIGN_REF_NEW_STR(tag, tag_str); - } - if (len1 + len2 > 0) { ZEND_TRY_ASSIGN_REF_NEW_STR(sealdata, zend_string_init((char*)buf, len1 + len2, 0)); efree(buf); ekeys = zend_try_array_init(ekeys); if (!ekeys) { + EVP_CIPHER_CTX_free(ctx); goto clean_exit; } @@ -4286,35 +4276,21 @@ PHP_FUNCTION(openssl_seal) } else { efree(buf); } - RETVAL_LONG(len1 + len2); + EVP_CIPHER_CTX_free(ctx); clean_exit: - if (ctx) { - EVP_CIPHER_CTX_free(ctx); - } - - if (pkeys) { - for (i=0; i 0) { if (!iv) { zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm"); - goto clean_exit; + RETURN_THROWS(); } if ((size_t)cipher_iv_len != iv_len) { php_error_docref(NULL, E_WARNING, "IV length is invalid"); - goto clean_exit; + RETURN_FALSE; } iv_buf = (unsigned char *)iv; } else { @@ -4377,48 +4350,20 @@ PHP_FUNCTION(openssl_open) buf = emalloc(data_len + 1); ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL || !EVP_OpenInit(ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey)) { - php_openssl_store_errors(); - goto clean_exit; - } - - tag_len = EVP_CIPHER_CTX_get_tag_length(ctx); - if ((tag != NULL) != (tag_len > 0)) { - const char *imp = tag ? "cannot" : "must"; - zend_argument_value_error(7, "%s be specified for the chosen cipher algorithm", imp); - goto clean_exit; - } - if (tag) { - if (ZSTR_LEN(tag) != tag_len) { - zend_argument_value_error(7, "must be %d bytes long", tag_len); - goto clean_exit; - } - - if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, ZSTR_LEN(tag), ZSTR_VAL(tag))) { - php_openssl_store_errors(); - goto clean_exit; - } - } - - if (EVP_OpenUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) && - EVP_OpenFinal(ctx, buf + len1, &len2) && (len1 + len2 > 0)) { + if (ctx != NULL && EVP_OpenInit(ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey) && + EVP_OpenUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) && + EVP_OpenFinal(ctx, buf + len1, &len2) && (len1 + len2 > 0)) { buf[len1 + len2] = '\0'; ZEND_TRY_ASSIGN_REF_NEW_STR(opendata, zend_string_init((char*)buf, len1 + len2, 0)); RETVAL_TRUE; } else { php_openssl_store_errors(); + RETVAL_FALSE; } -clean_exit: - if (buf) { - efree(buf); - } - if (pkey) { - EVP_PKEY_free(pkey); - } - if (ctx) { - EVP_CIPHER_CTX_free(ctx); - } + efree(buf); + EVP_PKEY_free(pkey); + EVP_CIPHER_CTX_free(ctx); } /* }}} */ diff --git a/ext/openssl/openssl.stub.php b/ext/openssl/openssl.stub.php index 56e96a27a6ab2..94902a4acf0da 100644 --- a/ext/openssl/openssl.stub.php +++ b/ext/openssl/openssl.stub.php @@ -628,15 +628,14 @@ function openssl_verify(string $data, string $signature, $public_key, string|int * @param string $sealed_data * @param array $encrypted_keys * @param string $iv - * @param string $tag */ -function openssl_seal(#[\SensitiveParameter] string $data, &$sealed_data, &$encrypted_keys, array $public_key, string $cipher_algo, &$iv = null, &$tag = null): int|false {} +function openssl_seal(#[\SensitiveParameter] string $data, &$sealed_data, &$encrypted_keys, array $public_key, string $cipher_algo, &$iv = null): int|false {} /** * @param string $output * @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key */ -function openssl_open(string $data, #[\SensitiveParameter] &$output, string $encrypted_key, #[\SensitiveParameter] $private_key, string $cipher_algo, ?string $iv = null, ?string $tag = null): bool {} +function openssl_open(string $data, #[\SensitiveParameter] &$output, string $encrypted_key, #[\SensitiveParameter] $private_key, string $cipher_algo, ?string $iv = null): bool {} /** * @return array diff --git a/ext/openssl/openssl_arginfo.h b/ext/openssl/openssl_arginfo.h index 5ab604828e5cb..796582c185bb6 100644 --- a/ext/openssl/openssl_arginfo.h +++ b/ext/openssl/openssl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 91d576073b79bf4b441e44eccb0deaa2b79a6949 */ + * Stub hash: 8233a8abc8ab7145d905d0fa51478edfe1e55a06 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_TYPE_MASK(0, certificate, OpenSSLCertificate, MAY_BE_STRING, NULL) @@ -302,7 +302,6 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_openssl_seal, 0, 5, MAY_BE_LONG| ZEND_ARG_TYPE_INFO(0, public_key, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, cipher_algo, IS_STRING, 0) ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, iv, "null") - ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, tag, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_open, 0, 5, _IS_BOOL, 0) @@ -312,7 +311,6 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_open, 0, 5, _IS_BOOL, 0) ZEND_ARG_INFO(0, private_key) ZEND_ARG_TYPE_INFO(0, cipher_algo, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, iv, IS_STRING, 1, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, tag, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_get_md_methods, 0, 0, IS_ARRAY, 0) diff --git a/ext/openssl/tests/gh7737.phpt b/ext/openssl/tests/gh7737.phpt deleted file mode 100644 index 5136e3e295c1d..0000000000000 --- a/ext/openssl/tests/gh7737.phpt +++ /dev/null @@ -1,57 +0,0 @@ ---TEST-- -GitHub Bug#7737 - openssl_seal/open() does not handle ciphers with Tags (e.g. AES-256-CGM) ---EXTENSIONS-- -openssl ---SKIPIF-- - ---FILE-- - KEY_TYPE]); - define('KEY_PUBLIC', openssl_pkey_get_details($key)['key']); - define('KEY_PRIVATE', openssl_pkey_get_private($key)); -})(); - -echo 'Plaintext: '; var_dump(PLAINTEXT); - -$sealResult = openssl_seal(PLAINTEXT, - $sealedData, /* out */ - $sealedKeys, /* out */ - [KEY_PUBLIC], - CIPHER_ALGO, - $iv, /* out */ - $tag); /* out */ - -echo 'Seal Result: '; var_dump($sealResult); -echo 'Sealed Data: '; var_dump(strlen($sealedData)); -echo 'IV Length: '; var_dump(strlen($iv)); -echo 'Tag Length: '; var_dump(strlen($tag)); - -$unsealResult = openssl_open($sealedData, - $unsealedData, /* out */ - $sealedKeys[0], - KEY_PRIVATE, - CIPHER_ALGO, - $iv, - $tag); - -echo 'Unseal Result: '; var_dump($unsealResult); -echo 'Unsealed Data: '; var_dump($unsealedData); - -?> ---EXPECT-- -Plaintext: string(16) "Test Data String" -Seal Result: int(16) -Sealed Data: int(16) -IV Length: int(12) -Tag Length: int(16) -Unseal Result: bool(true) -Unsealed Data: string(16) "Test Data String" From 038e53420b35fe675153aacf43a5f7cefaec911c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 5 Dec 2025 18:51:42 +0100 Subject: [PATCH 147/252] standard: Fix error check for proc_open() command zval_get_string() can never return NULL, you need to use the try version to get NULL. This is observable because the process will still spawn even if an exception had occurred. To fix this, use the try variant. Closes GH-20650. --- NEWS | 3 +++ ext/standard/proc_open.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index be1c55a7c8e89..007faa0249561 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,9 @@ PHP NEWS - LDAP: . Fix memory leak in ldap_set_options(). (ndossche) +- Standard: + . Fix error check for proc_open() command. (ndossche) + 18 Dec 2025, PHP 8.3.29 - Core: diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index ce77109827740..96e5a4ced9915 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -510,7 +510,7 @@ typedef struct _descriptorspec_item { } descriptorspec_item; static zend_string *get_valid_arg_string(zval *zv, int elem_num) { - zend_string *str = zval_get_string(zv); + zend_string *str = zval_try_get_string(zv); if (!str) { return NULL; } From 521c0c5cd233a8626eb62694169453b274cf44eb Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 5 May 2024 15:29:58 +0200 Subject: [PATCH 148/252] Refactor path name processing in phar_build() --- ext/phar/phar_object.c | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 11a0dd17a4e57..9f0d566b42791 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -1411,7 +1411,6 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ goto after_open_fp; case IS_OBJECT: if (instanceof_function(Z_OBJCE_P(value), spl_ce_SplFileInfo)) { - char *test = NULL; spl_filesystem_object *intern = PHAR_FETCH_INTERNAL_EX(value); if (!base_len) { @@ -1421,41 +1420,34 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ switch (intern->type) { case SPL_FS_DIR: { + char *tmp; zend_string *test_str = spl_filesystem_object_get_path(intern); - fname_len = spprintf(&fname, 0, "%s%c%s", ZSTR_VAL(test_str), DEFAULT_SLASH, intern->u.dir.entry.d_name); + fname_len = spprintf(&tmp, 0, "%s%c%s", ZSTR_VAL(test_str), DEFAULT_SLASH, intern->u.dir.entry.d_name); zend_string_release_ex(test_str, /* persistent */ false); - if (php_stream_stat_path(fname, &ssb) == 0 && S_ISDIR(ssb.sb.st_mode)) { + if (php_stream_stat_path(tmp, &ssb) == 0 && S_ISDIR(ssb.sb.st_mode)) { /* ignore directories */ - efree(fname); + efree(tmp); return ZEND_HASH_APPLY_KEEP; } - test = expand_filepath(fname, NULL); - efree(fname); - - if (test) { - fname = test; - fname_len = strlen(fname); - } else { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Could not resolve file path"); - return ZEND_HASH_APPLY_STOP; - } - - save = fname; - goto phar_spl_fileinfo; + fname = expand_filepath(tmp, NULL); + efree(tmp); + break; } case SPL_FS_INFO: case SPL_FS_FILE: fname = expand_filepath(ZSTR_VAL(intern->file_name), NULL); - if (!fname) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Could not resolve file path"); - return ZEND_HASH_APPLY_STOP; - } + break; + } - fname_len = strlen(fname); - save = fname; - goto phar_spl_fileinfo; + if (!fname) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Could not resolve file path"); + return ZEND_HASH_APPLY_STOP; } + + fname_len = strlen(fname); + save = fname; + goto phar_spl_fileinfo; } ZEND_FALLTHROUGH; default: From c9008f6dd867c4d6be9e9f5f6d0bb51fc1e4cbe4 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 5 May 2024 20:58:06 +0200 Subject: [PATCH 149/252] Make buildFromIterator() work with custom SplFileInfo objects While it is possible to return a custom SplFileInfo object in the iterator used by buildFromIterator(), the data is not actually used from that object, instead the data from the underlying internal structure is used. This makes it impossible to override some metadata such as the path name and modification time. The main motivation comes from two reasons: - Consistency. We expect our custom methods to be called when having a custom object. - Support reproducibility. This is the original use case as requested in [1]. Add support for this by calling the getMTime() and getPathname() methods if they're overriden by a user class. [1] https://github.com/theseer/Autoload/issues/114. --- NEWS | 2 + UPGRADING | 5 + ext/phar/phar_internal.h | 3 +- ext/phar/phar_object.c | 103 +++++++++++---- ext/phar/stream.c | 2 +- .../getMTime.phpt | 81 ++++++++++++ .../getMTime_byRef.phpt | 70 +++++++++++ .../getMTime_errors.phpt | 117 ++++++++++++++++++ .../getPathname.phpt | 67 ++++++++++ .../getPathname_byRef.phpt | 58 +++++++++ .../getPathname_exception.phpt | 63 ++++++++++ .../getPathname_wrong_type.phpt | 58 +++++++++ ext/phar/util.c | 4 +- 13 files changed, 608 insertions(+), 25 deletions(-) create mode 100644 ext/phar/tests/buildFromIterator_user_overrides/getMTime.phpt create mode 100644 ext/phar/tests/buildFromIterator_user_overrides/getMTime_byRef.phpt create mode 100644 ext/phar/tests/buildFromIterator_user_overrides/getMTime_errors.phpt create mode 100644 ext/phar/tests/buildFromIterator_user_overrides/getPathname.phpt create mode 100644 ext/phar/tests/buildFromIterator_user_overrides/getPathname_byRef.phpt create mode 100644 ext/phar/tests/buildFromIterator_user_overrides/getPathname_exception.phpt create mode 100644 ext/phar/tests/buildFromIterator_user_overrides/getPathname_wrong_type.phpt diff --git a/NEWS b/NEWS index f2e81284bdf89..671d2b57a6768 100644 --- a/NEWS +++ b/NEWS @@ -39,6 +39,8 @@ PHP NEWS . Support reference values in Phar::mungServer(). (ndossche) . Invalid values now throw in Phar::mungServer() instead of being silently ignored. (ndossche) + . Support overridden methods in SplFileInfo for getMTime() and getPathname() + when building a phar. (ndossche) - Reflection: . Fixed bug GH-20217 (ReflectionClass::isIterable() incorrectly returns true diff --git a/UPGRADING b/UPGRADING index 7f0fcaf8943a8..4e511d094797e 100644 --- a/UPGRADING +++ b/UPGRADING @@ -37,6 +37,11 @@ PHP 8.6 UPGRADE NOTES IntlNumberRangeFormatter::IDENTITY_FALLBACK_RANGE identity fallbacks. It is supported from icu 63. +- Phar: + . Overriding the getMTime() and getPathname() methods of SplFileInfo now + influences the result of the phar buildFrom family of functions. + This makes it possible to override the timestamp and names of files. + - Streams: . Added stream socket context option so_reuseaddr that allows disabling address reuse (SO_REUSEADDR) and explicitly uses SO_EXCLUSIVEADDRUSE on diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 5fc4354545318..144a3c1359f90 100644 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -196,6 +196,7 @@ typedef struct _phar_metadata_tracker { typedef struct _phar_entry_info { /* first bytes are exactly as in file */ uint32_t uncompressed_filesize; + /* modification time */ uint32_t timestamp; uint32_t compressed_filesize; uint32_t crc32; @@ -464,7 +465,7 @@ void phar_entry_delref(phar_entry_data *idata); phar_entry_info *phar_get_entry_info(phar_archive_data *phar, char *path, size_t path_len, char **error, bool security); phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, size_t path_len, char dir, char **error, bool security); -ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security); +ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security, uint32_t timestamp); ZEND_ATTRIBUTE_NONNULL zend_result phar_get_entry_data(phar_entry_data **ret, char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security); ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_flush_ex(phar_archive_data *archive, zend_string *user_stub, bool is_default_stub, char **error); ZEND_ATTRIBUTE_NONNULL int phar_flush(phar_archive_data *archive, char **error); diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 9f0d566b42791..cd00880a0bf01 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -1341,6 +1341,44 @@ struct _phar_t { int count; }; +static zend_always_inline void phar_call_method_with_unwrap(zend_object *obj, const char *name, zval *rv) +{ + zend_call_method_with_0_params(obj, obj->ce, NULL, name, rv); + if (Z_ISREF_P(rv)) { + zend_unwrap_reference(rv); + } +} + +/* This is the same as phar_get_or_create_entry_data(), but allows overriding metadata via SplFileInfo. */ +static phar_entry_data *phar_build_entry_data(char *fname, size_t fname_len, char *path, size_t path_len, char **error, zval *file_info) +{ + uint32_t timestamp; + + /* Expects an instance of SplFileInfo if it is an object, which is verified in phar_build(). */ + if (Z_TYPE_P(file_info) == IS_OBJECT && Z_OBJCE_P(file_info)->type == ZEND_USER_CLASS) { + zval rv; + phar_call_method_with_unwrap(Z_OBJ_P(file_info), "getMTime", &rv); + + if (UNEXPECTED(Z_TYPE(rv) != IS_LONG)) { + /* Either it's a tentative type failure, an exception happened, or the function returned false to indicate failure. */ + *error = estrdup("getMTime() must return an int"); + return NULL; + } + + /* Sanity check bounds. See GH-14141. */ + if (ZEND_LONG_UINT_OVFL(Z_LVAL(rv))) { + *error = estrdup("timestamp is limited to 32-bit"); + return NULL; + } + + timestamp = Z_LVAL(rv); + } else { + timestamp = time(NULL); + } + + return phar_get_or_create_entry_data(fname, fname_len, path, path_len, "w+b", 0, error, true, timestamp); +} + static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ { zval *value; @@ -1351,7 +1389,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ php_stream *fp; size_t fname_len; size_t contents_len; - char *fname, *error = NULL, *base = ZSTR_VAL(p_obj->base), *save = NULL, *temp = NULL; + char *fname = NULL, *error = NULL, *base = ZSTR_VAL(p_obj->base), *save = NULL, *temp = NULL; zend_string *opened; char *str_key; zend_class_entry *ce = p_obj->c; @@ -1418,26 +1456,49 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ return ZEND_HASH_APPLY_STOP; } - switch (intern->type) { - case SPL_FS_DIR: { - char *tmp; - zend_string *test_str = spl_filesystem_object_get_path(intern); - fname_len = spprintf(&tmp, 0, "%s%c%s", ZSTR_VAL(test_str), DEFAULT_SLASH, intern->u.dir.entry.d_name); - zend_string_release_ex(test_str, /* persistent */ false); - if (php_stream_stat_path(tmp, &ssb) == 0 && S_ISDIR(ssb.sb.st_mode)) { - /* ignore directories */ - efree(tmp); - return ZEND_HASH_APPLY_KEEP; + zend_string *tmp_dir_str = NULL; + + /* Take into account that SplFileObject may be overridden. + * The purpose here is to grab the path name of the file to add. */ + if (Z_OBJCE_P(value)->type == ZEND_USER_CLASS) { + zval rv; + phar_call_method_with_unwrap(Z_OBJ_P(value), "getPathname", &rv); + + if (UNEXPECTED(Z_TYPE(rv) != IS_STRING)) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "getPathname() must return a string"); + return ZEND_HASH_APPLY_STOP; + } + tmp_dir_str = Z_STR(rv); + } else { + /* Not a user class, so we can grab the data internally quickly. */ + switch (intern->type) { + case SPL_FS_DIR: { + zend_string *test_str = spl_filesystem_object_get_path(intern); + const char slash = DEFAULT_SLASH; + tmp_dir_str = zend_string_concat3( + ZSTR_VAL(test_str), ZSTR_LEN(test_str), + &slash, 1, + intern->u.dir.entry.d_name, strlen(intern->u.dir.entry.d_name) + ); + zend_string_release_ex(test_str, /* persistent */ false); + break; } + case SPL_FS_INFO: + case SPL_FS_FILE: + fname = expand_filepath(ZSTR_VAL(intern->file_name), NULL); + break; + } + } - fname = expand_filepath(tmp, NULL); - efree(tmp); - break; + if (tmp_dir_str) { + if (php_stream_stat_path(ZSTR_VAL(tmp_dir_str), &ssb) == 0 && S_ISDIR(ssb.sb.st_mode)) { + /* ignore directories */ + zend_string_release_ex(tmp_dir_str, /* persistent */ false); + return ZEND_HASH_APPLY_KEEP; } - case SPL_FS_INFO: - case SPL_FS_FILE: - fname = expand_filepath(ZSTR_VAL(intern->file_name), NULL); - break; + + fname = expand_filepath(ZSTR_VAL(tmp_dir_str), NULL); + zend_string_release_ex(tmp_dir_str, /* persistent */ false); } if (!fname) { @@ -1578,7 +1639,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ return ZEND_HASH_APPLY_KEEP; } - if (!(data = phar_get_or_create_entry_data(phar_obj->archive->fname, phar_obj->archive->fname_len, str_key, str_key_len, "w+b", 0, &error, true))) { + if (!(data = phar_build_entry_data(phar_obj->archive->fname, phar_obj->archive->fname_len, str_key, str_key_len, &error, value))) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s cannot be created: %s", str_key, error); efree(error); @@ -3536,7 +3597,7 @@ static void phar_add_file(phar_archive_data **pphar, zend_string *file_name, con } #endif - if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, filename, filename_len, "w+b", 0, &error, true))) { + if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, filename, filename_len, "w+b", 0, &error, true, time(NULL)))) { if (error) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist and cannot be created: %s", filename, error); efree(error); @@ -3608,7 +3669,7 @@ static void phar_mkdir(phar_archive_data **pphar, zend_string *dir_name) char *error; phar_entry_data *data; - if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, ZSTR_VAL(dir_name), ZSTR_LEN(dir_name), "w+b", 2, &error, true))) { + if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, ZSTR_VAL(dir_name), ZSTR_LEN(dir_name), "w+b", 2, &error, true, time(NULL)))) { if (error) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Directory %s does not exist and cannot be created: %s", ZSTR_VAL(dir_name), error); efree(error); diff --git a/ext/phar/stream.c b/ext/phar/stream.c index bb60f00d8f162..08da1847cd9ce 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -191,7 +191,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* strip leading "/" */ internal_file = estrndup(ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1); if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { - if (NULL == (idata = phar_get_or_create_entry_data(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, strlen(internal_file), mode, 0, &error, true))) { + if (NULL == (idata = phar_get_or_create_entry_data(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, strlen(internal_file), mode, 0, &error, true, time(NULL)))) { if (error) { php_stream_wrapper_log_error(wrapper, options, "%s", error); efree(error); diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getMTime.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getMTime.phpt new file mode 100644 index 0000000000000..eefe352a0cbb6 --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getMTime.phpt @@ -0,0 +1,81 @@ +--TEST-- +buildFromIterator with user overrides - getMTime() +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MySplFileInfo(parent::current()->getPathname()); + } +} + +$workdir = __DIR__.'/getMTime'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world."); + +$phar = new \Phar($workdir . '/test.phar'); +$phar->startBuffering(); +$phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir +); +$phar->stopBuffering(); + + +$result = new \Phar($workdir . '/test.phar', 0, 'test.phar'); +var_dump($result['content/hello.txt']); +var_dump($result['content/hello.txt']->getATime()); +var_dump($result['content/hello.txt']->getMTime()); +var_dump($result['content/hello.txt']->getCTime()); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %shello.txt ] +[MTime] +object(PharFileInfo)#%d (2) { + ["pathName":"SplFileInfo":private]=> + string(%d) "phar://%shello.txt" + ["fileName":"SplFileInfo":private]=> + string(%d) "hello.txt" +} +int(123) +int(123) +int(123) diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getMTime_byRef.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getMTime_byRef.phpt new file mode 100644 index 0000000000000..a7f81709a34e3 --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getMTime_byRef.phpt @@ -0,0 +1,70 @@ +--TEST-- +buildFromIterator with user overrides - getMTime() by ref +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MySplFileInfo(parent::current()->getPathname()); + } +} + +$workdir = __DIR__.'/getMTime_byRef'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world."); + +$phar = new \Phar($workdir . '/test.phar'); +$phar->startBuffering(); +$phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir +); +$phar->stopBuffering(); + + +$result = new \Phar($workdir . '/test.phar', 0, 'test.phar'); +var_dump($result['content/hello.txt']); +var_dump($result['content/hello.txt']->getATime()); +var_dump($result['content/hello.txt']->getMTime()); +var_dump($result['content/hello.txt']->getCTime()); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %shello.txt ] +[MTime] +object(PharFileInfo)#%d (2) { + ["pathName":"SplFileInfo":private]=> + string(%d) "phar://%shello.txt" + ["fileName":"SplFileInfo":private]=> + string(%d) "hello.txt" +} +int(123) +int(123) +int(123) diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getMTime_errors.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getMTime_errors.phpt new file mode 100644 index 0000000000000..f43a7a496a762 --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getMTime_errors.phpt @@ -0,0 +1,117 @@ +--TEST-- +buildFromIterator with user overrides - errors in getMTime() +--EXTENSIONS-- +phar +--SKIPIF-- + +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + if ($counter === 1) { + return new MySplFileInfo1(parent::current()->getPathname()); + } else if ($counter === 2) { + return new MySplFileInfo2(parent::current()->getPathname()); + } else if ($counter === 3) { + return new MySplFileInfo3(parent::current()->getPathname()); + } else if ($counter === 4) { + return new MySplFileInfo4(parent::current()->getPathname()); + } + } +} + +$workdir = __DIR__.'/getMTime_errors'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world."); + +for ($i = 0; $i < 4; $i++) { + echo "--- Iteration $i ---\n"; + try { + $phar = new \Phar($workdir . "/test$i.phar"); + $phar->startBuffering(); + $phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir + ); + $phar->stopBuffering(); + } catch (Throwable $e) { + echo $e->getMessage(), "\n"; + if ($previous = $e->getPrevious()) { + echo "Previous: ", $previous->getMessage(), "\n"; + } + } +} + +?> +--CLEAN-- + +--EXPECTF-- +--- Iteration 0 --- +[ Found: %shello.txt ] +[MTime] +Entry content%chello.txt cannot be created: timestamp is limited to 32-bit +--- Iteration 1 --- +[ Found: %shello.txt ] +[MTime] +Entry content%chello.txt cannot be created: getMTime() must return an int +--- Iteration 2 --- +[ Found: %shello.txt ] +[MTime] +Entry content%chello.txt cannot be created: getMTime() must return an int +Previous: Throwing an exception inside getMTime() +--- Iteration 3 --- +[ Found: %shello.txt ] +[MTime] +Entry content%chello.txt cannot be created: getMTime() must return an int diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getPathname.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getPathname.phpt new file mode 100644 index 0000000000000..144eed1d874a6 --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getPathname.phpt @@ -0,0 +1,67 @@ +--TEST-- +buildFromIterator with user overrides - getPathname() +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MyGlobIterator(parent::current()->getPath() . '/*'); + } +} + +$workdir = __DIR__.'/getPathname'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello1.txt', "Hello world 1."); +file_put_contents($workdir . '/content/hello2.txt', "Hello world 2."); + +$phar = new \Phar($workdir . "/test.phar"); +$phar->startBuffering(); +$phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir +); +$phar->stopBuffering(); + +$result = new \Phar($workdir . '/test.phar', 0, 'test.phar'); +var_dump(isset($result['content/hello1.txt'])); +var_dump(isset($result['content/hello2.txt'])); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %shello%d.txt ] +[getPathname] +string(%d) "%shello1.txt" +[ Found: %shello%d.txt ] +[getPathname] +string(%d) "%shello1.txt" +bool(false) +bool(true) diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getPathname_byRef.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_byRef.phpt new file mode 100644 index 0000000000000..0ae2946ce1ade --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_byRef.phpt @@ -0,0 +1,58 @@ +--TEST-- +buildFromIterator with user overrides - getPathname() by ref +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MyGlobIterator(parent::current()->getPath() . '/*'); + } +} + +$workdir = __DIR__.'/getPathname_byRef'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world 1."); + +$phar = new \Phar($workdir . "/test.phar"); +$phar->startBuffering(); +$phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir +); +$phar->stopBuffering(); + +$result = new \Phar($workdir . '/test.phar', 0, 'test.phar'); +var_dump(isset($result['content/hello.txt'])); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %scontent%chello.txt ] +[getPathname] +bool(true) diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getPathname_exception.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_exception.phpt new file mode 100644 index 0000000000000..86b66050a973f --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_exception.phpt @@ -0,0 +1,63 @@ +--TEST-- +buildFromIterator with user overrides - exception in getPathname() +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MyGlobIterator(parent::current()->getPath() . '/*'); + } +} + +$workdir = __DIR__.'/getPathname_exception'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world."); + +$phar = new \Phar($workdir . "/test.phar"); +$phar->startBuffering(); +try { + $phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir + ); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; + if ($previous = $e->getPrevious()) { + echo "Previous: ", $previous->getMessage(), "\n"; + } +} +$phar->stopBuffering(); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %shello.txt ] +[getPathname] +string(%d) "%shello.txt" +getPathname() must return a string +Previous: exception in getPathname() diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getPathname_wrong_type.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_wrong_type.phpt new file mode 100644 index 0000000000000..dfb3fb5f2c550 --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_wrong_type.phpt @@ -0,0 +1,58 @@ +--TEST-- +buildFromIterator with user overrides - wrong return type in getPathname() +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MyGlobIterator(parent::current()->getPath() . '/*'); + } +} + +$workdir = __DIR__.'/getPathname_wrong_type'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world."); + +$phar = new \Phar($workdir . "/test.phar"); +$phar->startBuffering(); +try { + $phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir + ); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} +$phar->stopBuffering(); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %scontent%chello.txt ] +[getPathname] +getPathname() must return a string diff --git a/ext/phar/util.c b/ext/phar/util.c index 60607006f6712..82c784e621712 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -606,7 +606,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_get_entry_data(phar_entry_data **ret, ch /** * Create a new dummy file slot within a writeable phar for a newly created file */ -ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security) /* {{{ */ +ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security, uint32_t timestamp) /* {{{ */ { phar_archive_data *phar; phar_entry_info *entry, etemp; @@ -668,7 +668,7 @@ ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(char *fnam phar_add_virtual_dirs(phar, path, path_len); etemp.is_modified = 1; - etemp.timestamp = time(0); + etemp.timestamp = timestamp; etemp.is_crc_checked = 1; etemp.phar = phar; etemp.filename = zend_string_init(path, path_len, false); From 6cdb8bf0a29921f8a5630f72d885dcf9c5a450b6 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sat, 13 Dec 2025 20:27:14 +0100 Subject: [PATCH 150/252] Fix xcode-select The old Xcode sticks around, so make sure we sort in reverse to pick the newer version. Technically we should use something like sort -Vr, but -V doesn't exist on macOS. But that won't be a problem until Xcode 100, which my great great grand children can worry about. --- .github/actions/macos-update-clang/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/macos-update-clang/action.yml b/.github/actions/macos-update-clang/action.yml index bceb431aed5a5..916b4dfe41f48 100644 --- a/.github/actions/macos-update-clang/action.yml +++ b/.github/actions/macos-update-clang/action.yml @@ -8,7 +8,7 @@ runs: label=$((softwareupdate -l 2>/dev/null | grep 'Label:' | grep -o 'Command Line Tools for Xcode.*' | head -1) || echo '') if [ -n "$label" ]; then softwareupdate -i "$label" - xcode_path=$(ls -1 '/Applications' | grep 'Xcode_.*\.app' | head -1) + xcode_path=$(ls -1 '/Applications' | grep 'Xcode_.*\.app' | sort -r | head -1) sudo xcode-select -s "/Applications/$xcode_path" else echo "Not found." From 97a90f43617badc7b917d51f6138a7387ab03a46 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 13 Dec 2025 10:20:58 +0000 Subject: [PATCH 151/252] Fix GH-20678: resource created by GlobIterator crashes with fclose(). close GH-20697 --- NEWS | 4 ++++ ext/spl/spl_directory.c | 5 +++++ ext/spl/tests/gh20678.phpt | 14 ++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 ext/spl/tests/gh20678.phpt diff --git a/NEWS b/NEWS index 007faa0249561..ed2c4149a03f0 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,10 @@ PHP NEWS - LDAP: . Fix memory leak in ldap_set_options(). (ndossche) +- SPL: + . Fixed bug GH-20678 (resource created by GlobIterator crashes with fclose()). + (David Carlier) + - Standard: . Fix error check for proc_open() command. (ndossche) diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 0a4d1456d65e9..16bb6a11e5218 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -306,6 +306,11 @@ static void spl_filesystem_dir_open(spl_filesystem_object* intern, zend_string * intern->type = SPL_FS_DIR; intern->u.dir.dirp = php_stream_opendir(ZSTR_VAL(path), REPORT_ERRORS, FG(default_context)); + if (intern->u.dir.dirp) { + /* we prevent potential UAF with conflicting explicit fclose(), relying on the object destructor for this */ + intern->u.dir.dirp->flags |= PHP_STREAM_FLAG_NO_FCLOSE; + } + if (ZSTR_LEN(path) > 1 && IS_SLASH_AT(ZSTR_VAL(path), ZSTR_LEN(path)-1)) { intern->path = zend_string_init(ZSTR_VAL(path), ZSTR_LEN(path)-1, 0); } else { diff --git a/ext/spl/tests/gh20678.phpt b/ext/spl/tests/gh20678.phpt new file mode 100644 index 0000000000000..243f8cef06d16 --- /dev/null +++ b/ext/spl/tests/gh20678.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-20678 (resource created by GlobalIterator crashes when it is called with fclose()) +--CREDITS-- +chongwick +--FILE-- + +--EXPECTF-- + +Warning: fclose(): %d is not a valid stream resource in %s on line %d From 526938648efe12f96f29fef3e346e215810105af Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:35:03 +0100 Subject: [PATCH 152/252] Fix test for 8.5+ --- ext/spl/tests/gh20678.phpt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/spl/tests/gh20678.phpt b/ext/spl/tests/gh20678.phpt index 243f8cef06d16..00364db7f8044 100644 --- a/ext/spl/tests/gh20678.phpt +++ b/ext/spl/tests/gh20678.phpt @@ -10,5 +10,4 @@ $resource = end($resources); fclose($resource); ?> --EXPECTF-- - -Warning: fclose(): %d is not a valid stream resource in %s on line %d +Warning: fclose(): cannot close the provided stream, as it must not be manually closed in %s on line %d From dbf56e0eba68c61385e9a2d15a3e3f5066f80ec4 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Sun, 14 Dec 2025 15:23:43 +0000 Subject: [PATCH 153/252] Squashed commit of the following: commit c4adcbe582faa263f11404ac89eb2cb80f2bdbca Author: Kamil Tekiela Date: Fri Oct 17 15:32:14 2025 +0100 Add NEWS commit 84a6e675af9c1cf74c17f06aeca84cca358e064b Author: Kamil Tekiela Date: Fri Oct 17 14:49:26 2025 +0100 Handle errors in mysqli_begin_transaction --- NEWS | 5 ++- ext/mysqli/mysqli_nonapi.c | 3 ++ .../tests/mysqli_begin_transaction_error.phpt | 42 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 ext/mysqli/tests/mysqli_begin_transaction_error.phpt diff --git a/NEWS b/NEWS index ed2c4149a03f0..13f673a53c023 100644 --- a/NEWS +++ b/NEWS @@ -8,7 +8,7 @@ PHP NEWS - Bz2: . Fixed bug GH-20620 (bzcompress overflow on large source size). - (David Carlier) + (David Carlier) - GD: . Fixed bug GH-20622 (imagestring/imagestringup overflow). (David Carlier) @@ -70,6 +70,9 @@ PHP NEWS . Fixed bug GH-20492 (mbstring compile warning due to non-strings). (ndossche) +- mysqli: + . Make mysqli_begin_transaction() report errors properly. (Kamil Tekiela) + - MySQLnd: . Fixed bug GH-20528 (Regression breaks mysql connexion using an IPv6 address enclosed in square brackets). (Remi) diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index 406d928699784..1f971d000fafa 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -1022,6 +1022,7 @@ PHP_FUNCTION(mysqli_begin_transaction) } if (FAIL == mysqlnd_begin_transaction(mysql->mysql, flags, name)) { + MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } RETURN_TRUE; @@ -1046,6 +1047,7 @@ PHP_FUNCTION(mysqli_savepoint) } if (FAIL == mysqlnd_savepoint(mysql->mysql, name)) { + MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } RETURN_TRUE; @@ -1069,6 +1071,7 @@ PHP_FUNCTION(mysqli_release_savepoint) RETURN_THROWS(); } if (FAIL == mysqlnd_release_savepoint(mysql->mysql, name)) { + MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } RETURN_TRUE; diff --git a/ext/mysqli/tests/mysqli_begin_transaction_error.phpt b/ext/mysqli/tests/mysqli_begin_transaction_error.phpt new file mode 100644 index 0000000000000..e4d63f366e86b --- /dev/null +++ b/ext/mysqli/tests/mysqli_begin_transaction_error.phpt @@ -0,0 +1,42 @@ +--TEST-- +mysqli_begin_transaction() +--EXTENSIONS-- +mysqli +--SKIPIF-- +errno, $link->error)); +?> +--FILE-- + +--CLEAN-- + +--EXPECT-- +Expecting an exception. +done! From ae59c694674aa7e65950d264d2ccaf0d1bedcf5c Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 14 Dec 2025 14:44:04 +0000 Subject: [PATCH 154/252] ext/spl: DirectoryIterator to support modern filesytems. With filesystems with builtin large capacity such as ZFS, Btfrs, NTFS or even ext4 with large_dir feature enabled, an int to represent an entry index is falling short, thus risking overflows. A zend_long however should cover this need without increasing out internal data structure. close GH-20705 --- NEWS | 4 ++++ ext/spl/spl_directory.c | 2 +- ext/spl/spl_directory.h | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 671d2b57a6768..534daec19b37e 100644 --- a/NEWS +++ b/NEWS @@ -50,6 +50,10 @@ PHP NEWS . Soap::__setCookie() when cookie name is a digit is now not stored and represented as a string anymore but a int. (David Carlier) +- SPL: + . DirectoryIterator key can now work better with filesystem supporting larger + directory indexing. (David Carlier) + - Standard: . Fixed bug GH-19926 (reset internal pointer earlier while splicing array while COW violation flag is still set). (alexandre-daubois) diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 6a5fc1757f26d..86e4b11334c81 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -414,7 +414,7 @@ static zend_object *spl_filesystem_object_clone(zend_object *old_object) spl_filesystem_dir_open(intern, source->path); /* read until we hit the position in which we were before */ bool skip_dots = SPL_HAS_FLAG(source->flags, SPL_FILE_DIR_SKIPDOTS); - int index; + zend_long index; for (index = 0; index < source->u.dir.index; ++index) { do { spl_filesystem_dir_read(intern); diff --git a/ext/spl/spl_directory.h b/ext/spl/spl_directory.h index 549dfb1dc4d0f..a2d1d8d854750 100644 --- a/ext/spl/spl_directory.h +++ b/ext/spl/spl_directory.h @@ -62,7 +62,7 @@ struct _spl_filesystem_object { struct { php_stream *dirp; zend_string *sub_path; - int index; + zend_long index; zend_function *func_rewind; zend_function *func_next; zend_function *func_valid; From acd0898bdfb12fb34f422b3fffe54d747f89c235 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:26:27 +0100 Subject: [PATCH 155/252] Fix GH-20699: SQLite3Result fetchArray return array|false, null returned Closes GH-20701. --- NEWS | 4 ++++ ext/sqlite3/sqlite3.c | 1 + ext/sqlite3/tests/gh20699.phpt | 14 ++++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 ext/sqlite3/tests/gh20699.phpt diff --git a/NEWS b/NEWS index 13f673a53c023..4e301bdbc8b0c 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,10 @@ PHP NEWS . Fixed bug GH-20678 (resource created by GlobIterator crashes with fclose()). (David Carlier) +- Sqlite3: + . Fixed bug GH-20699 (SQLite3Result fetchArray return array|false, + null returned). (ndossche, plusminmax) + - Standard: . Fix error check for proc_open() command. (ndossche) diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 9b3286b70220d..b16be6932a07b 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -2011,6 +2011,7 @@ PHP_METHOD(SQLite3Result, fetchArray) default: php_sqlite3_error(result_obj->db_obj, sqlite3_errcode(sqlite3_db_handle(result_obj->stmt_obj->stmt)), "Unable to execute statement: %s", sqlite3_errmsg(sqlite3_db_handle(result_obj->stmt_obj->stmt))); + RETURN_FALSE; } } /* }}} */ diff --git a/ext/sqlite3/tests/gh20699.phpt b/ext/sqlite3/tests/gh20699.phpt new file mode 100644 index 0000000000000..1b53bfc98e39f --- /dev/null +++ b/ext/sqlite3/tests/gh20699.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-20699 (SQLite3Result fetchArray return array|false, null returned) +--EXTENSIONS-- +sqlite3 +--CREDITS-- +plusminmax +--FILE-- +prepare('BEGIN;')->execute()->fetchArray()); +?> +--EXPECTF-- +Warning: SQLite3Result::fetchArray(): Unable to execute statement: cannot start a transaction within a transaction in %s on line %d +bool(false) From 5850c7de7c5032a84edbde17360de3ddc00fce57 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 21 Feb 2023 16:50:31 +0100 Subject: [PATCH 156/252] sapi/fpm: remove use of variable-length arrays (#10645) According to @cmb69, PHP does not require VLA support (https://github.com/php/php-src/pull/10304#discussion_r1069343092). VLAs are a bad idea for several reasons, so let's get rid of them. Two of the VLAs were probably unintended; unlike C++, C doesn't have the concept of "constant expressions", so an array with a "const" length is technically still a VLA. This is fixed by removing the "const" variable, and using sizeof() instead. (cherry picked from commit ff2a211d55650201e5bbe370c319a0c913613eb9) --- sapi/fpm/fpm/fpm_php_trace.c | 11 +++++------ sapi/fpm/fpm/fpm_stdio.c | 5 ++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/sapi/fpm/fpm/fpm_php_trace.c b/sapi/fpm/fpm/fpm_php_trace.c index 0e1d8e3f6cee0..b1535b26e3ef8 100644 --- a/sapi/fpm/fpm/fpm_php_trace.c +++ b/sapi/fpm/fpm/fpm_php_trace.c @@ -39,15 +39,14 @@ static int fpm_php_trace_dump(struct fpm_child_s *child, FILE *slowlog) /* {{{ * int callers_limit = child->wp->config->request_slowlog_trace_depth; pid_t pid = child->pid; struct timeval tv; - static const int buf_size = 1024; - char buf[buf_size]; + char buf[1024]; long execute_data; long path_translated; long l; gettimeofday(&tv, 0); - zlog_print_time(&tv, buf, buf_size); + zlog_print_time(&tv, buf, sizeof(buf)); fprintf(slowlog, "\n%s [pool %s] pid %d\n", buf, child->wp->config->name, (int) pid); @@ -57,7 +56,7 @@ static int fpm_php_trace_dump(struct fpm_child_s *child, FILE *slowlog) /* {{{ * path_translated = l; - if (0 > fpm_trace_get_strz(buf, buf_size, path_translated)) { + if (0 > fpm_trace_get_strz(buf, sizeof(buf), path_translated)) { return -1; } @@ -103,7 +102,7 @@ static int fpm_php_trace_dump(struct fpm_child_s *child, FILE *slowlog) /* {{{ * ZEND_UNREACHABLE(); } } else { - if (0 > fpm_trace_get_strz(buf, buf_size, function_name + offsetof(zend_string, val))) { + if (0 > fpm_trace_get_strz(buf, sizeof(buf), function_name + offsetof(zend_string, val))) { return -1; } @@ -149,7 +148,7 @@ static int fpm_php_trace_dump(struct fpm_child_s *child, FILE *slowlog) /* {{{ * file_name = l; - if (0 > fpm_trace_get_strz(buf, buf_size, file_name + offsetof(zend_string, val))) { + if (0 > fpm_trace_get_strz(buf, sizeof(buf), file_name + offsetof(zend_string, val))) { return -1; } diff --git a/sapi/fpm/fpm/fpm_stdio.c b/sapi/fpm/fpm/fpm_stdio.c index dec540d17aca8..c104ff987f0c5 100644 --- a/sapi/fpm/fpm/fpm_stdio.c +++ b/sapi/fpm/fpm/fpm_stdio.c @@ -168,9 +168,8 @@ int fpm_stdio_flush_child(void) static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) /* {{{ */ { - static const int max_buf_size = 1024; int fd = ev->fd; - char buf[max_buf_size]; + char buf[1024]; struct fpm_child_s *child; int is_stdout; struct fpm_event_s *event; @@ -216,7 +215,7 @@ static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) while (1) { stdio_read: - in_buf = read(fd, buf, max_buf_size - 1); + in_buf = read(fd, buf, sizeof(buf) - 1); if (in_buf <= 0) { /* no data */ if (in_buf == 0 || !PHP_IS_TRANSIENT_ERROR(errno)) { /* pipe is closed or error */ From 85913fc61b57b28b74a9803f3055bdced5c31656 Mon Sep 17 00:00:00 2001 From: Yuya Hamada Date: Wed, 10 Dec 2025 21:16:44 +0900 Subject: [PATCH 157/252] Fix GH-20674 mb_decode_mimeheader does not handle separator `?= =?` is skipped if long term, so skip space character. Add test case from RFC2047 and fix last pattern See: https://www.ietf.org/rfc/rfc2047#section-8 --- NEWS | 2 ++ ext/mbstring/mbstring.c | 4 +++- ext/mbstring/tests/gh20674.phpt | 40 +++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 ext/mbstring/tests/gh20674.phpt diff --git a/NEWS b/NEWS index 534daec19b37e..181c00bd83855 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,8 @@ PHP NEWS . ini_set() with mbstring.detect_order changes the order of mb_detect_order as intended, since mbstring.detect_order is an INI_ALL setting. (tobee94) . Added GB18030-2022 to default encoding list for zh-CN. (HeRaNO) + . Fixed bug GH-20674 (Fix GH-20674 mb_decode_mimeheader does not handle + separator). (Yuya Hamada) - Opcache: . Fixed bug GH-20051 (apache2 shutdowns when restart is requested during diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 7422c600284d5..118986411a8bb 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -6675,13 +6675,15 @@ static zend_string* mb_mime_header_decode(zend_string *input, const mbfl_encodin p = temp; /* Decoding of MIME encoded word was successful; * Try to collapse a run of whitespace */ - if (p < e && (*p == '\n' || *p == '\r')) { + if (p < e && (*p == '\n' || *p == '\r' || *p == '\t' || *p == ' ')) { do { p++; } while (p < e && (*p == '\n' || *p == '\r' || *p == '\t' || *p == ' ')); /* We will only actually output a space if this is not immediately followed * by another valid encoded word */ space_pending = true; + } else { + space_pending = false; } continue; } diff --git a/ext/mbstring/tests/gh20674.phpt b/ext/mbstring/tests/gh20674.phpt new file mode 100644 index 0000000000000..2fb8206037dee --- /dev/null +++ b/ext/mbstring/tests/gh20674.phpt @@ -0,0 +1,40 @@ +--TEST-- +GH-20674 (mb_decode_mimeheader does not handle separator) +--EXTENSIONS-- +mbstring +--FILE-- + +--EXPECTF-- +string(11) "The PHP 8.5" +string(11) "The PHP 8.5" +string(11) "The PHP 8.5" +string(11) "The PHP 8.5" +string(3) "(a)" +string(5) "(a b)" +string(5) "(a b)" +string(4) "(ab)" +string(4) "(ab)" +string(4) "(ab)" From 0056d013bf505302e2477e02d795627709842327 Mon Sep 17 00:00:00 2001 From: Yuya Hamada Date: Wed, 10 Dec 2025 21:16:44 +0900 Subject: [PATCH 158/252] Fix GH-20674 mb_decode_mimeheader does not handle separator `?= =?` is skipped if long term, so skip space character. Add test case from RFC2047 and fix last pattern See: https://www.ietf.org/rfc/rfc2047#section-8 --- NEWS | 4 ++++ ext/mbstring/mbstring.c | 4 +++- ext/mbstring/tests/gh20674.phpt | 40 +++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 ext/mbstring/tests/gh20674.phpt diff --git a/NEWS b/NEWS index 4e301bdbc8b0c..5103b4a96791c 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,10 @@ PHP NEWS - LDAP: . Fix memory leak in ldap_set_options(). (ndossche) +- Mbstring + . Fixed bug GH-20674 (Fix GH-20674 mb_decode_mimeheader does not handle + separator). (Yuya Hamada) + - SPL: . Fixed bug GH-20678 (resource created by GlobIterator crashes with fclose()). (David Carlier) diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 1d5c27a2a3815..5839945d47572 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -6395,13 +6395,15 @@ static zend_string* mb_mime_header_decode(zend_string *input, const mbfl_encodin p = temp; /* Decoding of MIME encoded word was successful; * Try to collapse a run of whitespace */ - if (p < e && (*p == '\n' || *p == '\r')) { + if (p < e && (*p == '\n' || *p == '\r' || *p == '\t' || *p == ' ')) { do { p++; } while (p < e && (*p == '\n' || *p == '\r' || *p == '\t' || *p == ' ')); /* We will only actually output a space if this is not immediately followed * by another valid encoded word */ space_pending = true; + } else { + space_pending = false; } continue; } diff --git a/ext/mbstring/tests/gh20674.phpt b/ext/mbstring/tests/gh20674.phpt new file mode 100644 index 0000000000000..2fb8206037dee --- /dev/null +++ b/ext/mbstring/tests/gh20674.phpt @@ -0,0 +1,40 @@ +--TEST-- +GH-20674 (mb_decode_mimeheader does not handle separator) +--EXTENSIONS-- +mbstring +--FILE-- + +--EXPECTF-- +string(11) "The PHP 8.5" +string(11) "The PHP 8.5" +string(11) "The PHP 8.5" +string(11) "The PHP 8.5" +string(3) "(a)" +string(5) "(a b)" +string(5) "(a b)" +string(4) "(ab)" +string(4) "(ab)" +string(4) "(ab)" From 2bf2411976951c5a316e2c1c3e6e8a6ef85729dc Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 25 Oct 2025 11:15:34 +0200 Subject: [PATCH 159/252] Make bug70417.phpt less flaky Closes GH-20287. (cherry picked from commit ed9529a7d3c1f5dfbf16c2d388d944af8ec86a76) --- ext/phar/tests/tar/bug70417.phpt | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/ext/phar/tests/tar/bug70417.phpt b/ext/phar/tests/tar/bug70417.phpt index 4d98a18954cc3..504d7e1e387b6 100644 --- a/ext/phar/tests/tar/bug70417.phpt +++ b/ext/phar/tests/tar/bug70417.phpt @@ -3,32 +3,17 @@ Bug #70417 (PharData::compress() doesn't close temp file) --EXTENSIONS-- phar zlib ---SKIPIF-- - --FILE-- /dev/null', $out); // Note: valgrind can produce false positives for /usr/bin/lsof - return count($out); -} $filename = __DIR__ . '/bug70417.tar'; @unlink("$filename.gz"); -$openFiles1 = countOpenFiles(); +$resBefore = count(get_resources()); $arch = new PharData($filename); $arch->addFromString('foo', 'bar'); $arch->compress(Phar::GZ); unset($arch); -$openFiles2 = countOpenFiles(); -var_dump($openFiles1 === $openFiles2); +$resAfter = count(get_resources()); +var_dump($resBefore === $resAfter); ?> --CLEAN-- Date: Mon, 15 Dec 2025 16:19:32 +0100 Subject: [PATCH 160/252] uri: Update to uriparser-0.9.9-85-g9a31011 (#20707) This is specifically to import uriparser/uriparser#284 to fix CVE-2025-67899. --- NEWS | 10 +- ext/uri/uriparser/src/UriParse.c | 228 +++++++++++++++---------------- 2 files changed, 117 insertions(+), 121 deletions(-) diff --git a/NEWS b/NEWS index a09a5dfda79d0..3dd85860a2eb2 100644 --- a/NEWS +++ b/NEWS @@ -107,6 +107,12 @@ PHP NEWS . Fixed bug GH-20370 (User stream filters could violate typed property constraints). (alexandre-daubois) +- URI: + . Fixed bug GH-20366 (ext/uri incorrectly throws ValueError when encountering + null byte). (kocsismate) + . Fixed CVE-2025-67899 (uriparser through 0.9.9 allows unbounded recursion + and stack consumption). (Sebastian Pipping) + - XML: . Fixed bug GH-20439 (xml_set_default_handler() does not properly handle special characters in attributes when passing data to callback). (ndossche) @@ -119,10 +125,6 @@ PHP NEWS . Fix assertion failures resulting in crashes with stream filter object parameters. (ndossche) -- URI: - . Fixed bug GH-20366 (ext/uri incorrectly throws ValueError when encountering - null byte). (kocsismate) - 20 Nov 2025, PHP 8.5.0 - Core: diff --git a/ext/uri/uriparser/src/UriParse.c b/ext/uri/uriparser/src/UriParse.c index ed851e94fbd9c..cada02301e00c 100644 --- a/ext/uri/uriparser/src/UriParse.c +++ b/ext/uri/uriparser/src/UriParse.c @@ -81,8 +81,7 @@ static const URI_CHAR * URI_FUNC(ParseAuthority)(URI_TYPE(ParserState) * state, static const URI_CHAR * URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast); -static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, +static const URI_CHAR * URI_FUNC(ParseHexZero)(const URI_CHAR * first, const URI_CHAR * afterLast); static const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState) * state, const URI_CHAR * first, @@ -92,10 +91,6 @@ static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); static const URI_CHAR * URI_FUNC(ParseIpLit2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, @@ -116,10 +111,6 @@ static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory); static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, @@ -160,8 +151,7 @@ static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); -static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, +static const URI_CHAR * URI_FUNC(ParsePort)(const URI_CHAR * first, const URI_CHAR * afterLast); static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state, const URI_CHAR * first, @@ -292,8 +282,7 @@ URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * firs switch (*first) { case _UT(':'): { - const URI_CHAR * const afterPort = - URI_FUNC(ParsePort)(state, first + 1, afterLast); + const URI_CHAR * const afterPort = URI_FUNC(ParsePort)(first + 1, afterLast); if (afterPort == NULL) { return NULL; } @@ -311,16 +300,17 @@ URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * firs * [hexZero]->[HEXDIG][hexZero] * [hexZero]-> */ -static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, +static const URI_CHAR * URI_FUNC(ParseHexZero)(const URI_CHAR * first, const URI_CHAR * afterLast) { +tail_call: if (first >= afterLast) { return afterLast; } switch (*first) { case URI_SET_HEXDIG(_UT): - return URI_FUNC(ParseHexZero)(state, first + 1, afterLast); + first += 1; + goto tail_call; default: return first; @@ -356,49 +346,37 @@ static URI_INLINE const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState) * [ipFutLoop]->[subDelims][ipFutStopGo] * [ipFutLoop]->[unreserved][ipFutStopGo] * [ipFutLoop]-><:>[ipFutStopGo] + * + * [ipFutStopGo]->[ipFutLoop] + * [ipFutStopGo]-> */ static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } + const URI_CHAR * const originalFirst = first; - switch (*first) { - case _UT(':'): - case URI_SET_SUB_DELIMS(_UT): - case URI_SET_UNRESERVED(_UT): - return URI_FUNC(ParseIpFutStopGo)(state, first + 1, afterLast, memory); + while (first < afterLast) { + switch (*first) { + case _UT(':'): + case URI_SET_SUB_DELIMS(_UT): + case URI_SET_UNRESERVED(_UT): + first += 1; + break; - default: - URI_FUNC(StopSyntax)(state, first, memory); - return NULL; + default: + goto done_looping; + break; + } } -} -/* - * [ipFutStopGo]->[ipFutLoop] - * [ipFutStopGo]-> - */ -static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, - const URI_CHAR * afterLast, - UriMemoryManager * memory) { - if (first >= afterLast) { - return afterLast; +done_looping: + if (first == originalFirst) { + URI_FUNC(StopSyntax)(state, first, memory); + return NULL; } - switch (*first) { - case _UT(':'): - case URI_SET_SUB_DELIMS(_UT): - case URI_SET_UNRESERVED(_UT): - return URI_FUNC(ParseIpFutLoop)(state, first, afterLast, memory); - - default: - return first; - } + return first; } /* @@ -430,7 +408,7 @@ static const URI_CHAR * URI_FUNC(ParseIpFuture)(URI_TYPE(ParserState) * state, case URI_SET_HEXDIG(_UT): { const URI_CHAR * afterIpFutLoop; const URI_CHAR * const afterHexZero = - URI_FUNC(ParseHexZero)(state, first + 2, afterLast); + URI_FUNC(ParseHexZero)(first + 2, afterLast); if (afterHexZero == NULL) { return NULL; } @@ -832,6 +810,7 @@ static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) * const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { +tail_call: if (first >= afterLast) { if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first, memory)) { /* SEGMENT BOTH */ @@ -849,14 +828,15 @@ static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) * if (afterPctEncoded == NULL) { return NULL; } - return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast, - memory); + first = afterPctEncoded; + goto tail_call; } case _UT('@'): case URI_SET_SUB_DELIMS(_UT): case URI_SET_UNRESERVED(_UT): - return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast, memory); + first += 1; + goto tail_call; case _UT('/'): { const URI_CHAR * afterZeroMoreSlashSegs; @@ -953,6 +933,7 @@ static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { +tail_call: if (first >= afterLast) { if (!URI_FUNC(OnExitOwnHost2)(state, first, memory)) { URI_FUNC(StopMalloc)(state, memory); @@ -970,7 +951,8 @@ static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state, if (afterPctSubUnres == NULL) { return NULL; } - return URI_FUNC(ParseOwnHost2)(state, afterPctSubUnres, afterLast, memory); + first = afterPctSubUnres; + goto tail_call; } default: @@ -1006,74 +988,69 @@ static URI_INLINE UriBool URI_FUNC(OnExitOwnHostUserInfo)(URI_TYPE(ParserState) return URI_TRUE; /* Success */ } -/* - * [ownHostUserInfo]->[ownHostUserInfoNz] - * [ownHostUserInfo]-> - */ -static URI_INLINE const URI_CHAR * -URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, - const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first, memory)) { - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return afterLast; - } - - switch (*first) { - case URI_SET_PCHAR(_UT): - return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast, memory); - - default: - if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first, memory)) { - URI_FUNC(StopMalloc)(state, memory); - return NULL; - } - return first; - } -} - /* * [ownHostUserInfoNz]->[pctSubUnres][ownHostUserInfo] * [ownHostUserInfoNz]-><:>[ownPortUserInfo] * [ownHostUserInfoNz]-><@>[ownHost] + * + * [ownHostUserInfo]->[ownHostUserInfoNz] + * [ownHostUserInfo]-> */ static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { - if (first >= afterLast) { - URI_FUNC(StopSyntax)(state, afterLast, memory); - return NULL; - } + const URI_CHAR * const originalFirst = first; + + while (first < afterLast) { + switch (*first) { + case _UT('%'): + case URI_SET_SUB_DELIMS(_UT): + case URI_SET_UNRESERVED(_UT): { + const URI_CHAR * const afterPctSubUnres = + URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory); + if (afterPctSubUnres == NULL) { + return NULL; + } + first = afterPctSubUnres; + break; + } - switch (*first) { - case _UT('%'): - case URI_SET_SUB_DELIMS(_UT): - case URI_SET_UNRESERVED(_UT): { - const URI_CHAR * const afterPctSubUnres = - URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory); - if (afterPctSubUnres == NULL) { - return NULL; + default: + goto done_looping; + break; } - return URI_FUNC(ParseOwnHostUserInfo)(state, afterPctSubUnres, afterLast, memory); } - case _UT(':'): - state->uri->hostText.afterLast = first; /* HOST END */ - state->uri->portText.first = first + 1; /* PORT BEGIN */ - return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast, memory); +done_looping: + if (first < afterLast) { + switch (*first) { + case _UT(':'): + state->uri->hostText.afterLast = first; /* HOST END */ + state->uri->portText.first = first + 1; /* PORT BEGIN */ + return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast, memory); - case _UT('@'): - state->uri->userInfo.afterLast = first; /* USERINFO END */ - state->uri->hostText.first = first + 1; /* HOST BEGIN */ - return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast, memory); + case _UT('@'): + state->uri->userInfo.afterLast = first; /* USERINFO END */ + state->uri->hostText.first = first + 1; /* HOST BEGIN */ + return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast, memory); - default: - URI_FUNC(StopSyntax)(state, first, memory); + default: + break; + } + } + + if (first == originalFirst) { + URI_FUNC(StopSyntax)(state, afterLast, memory); return NULL; } + + if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first, memory)) { + URI_FUNC(StopMalloc)(state, memory); + return NULL; + } + + return first; } static URI_INLINE UriBool URI_FUNC(OnExitOwnPortUserInfo)(URI_TYPE(ParserState) * state, @@ -1117,6 +1094,7 @@ static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * s const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { +tail_call: if (first >= afterLast) { if (!URI_FUNC(OnExitOwnPortUserInfo)(state, first, memory)) { URI_FUNC(StopMalloc)(state, memory); @@ -1140,7 +1118,8 @@ static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * s return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast, memory); case URI_SET_DIGIT(_UT): - return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast, memory); + first += 1; + goto tail_call; case _UT('%'): state->uri->portText.first = NULL; /* Not a port, reset */ @@ -1176,6 +1155,7 @@ static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { +tail_call: if (first >= afterLast) { URI_FUNC(StopSyntax)(state, afterLast, memory); return NULL; @@ -1190,11 +1170,13 @@ static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state if (afterPctSubUnres == NULL) { return NULL; } - return URI_FUNC(ParseOwnUserInfo)(state, afterPctSubUnres, afterLast, memory); + first = afterPctSubUnres; + goto tail_call; } case _UT(':'): - return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast, memory); + first += 1; + goto tail_call; case _UT('@'): /* SURE */ @@ -1254,6 +1236,7 @@ static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)(URI_TYPE(ParserState) * stat const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { +tail_call: if (first >= afterLast) { return afterLast; } @@ -1270,7 +1253,8 @@ static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)(URI_TYPE(ParserState) * stat URI_FUNC(StopMalloc)(state, memory); return NULL; } - return URI_FUNC(ParsePathAbsEmpty)(state, afterSegment, afterLast, memory); + first = afterSegment; + goto tail_call; } default: @@ -1443,16 +1427,17 @@ static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state * [port]->[DIGIT][port] * [port]-> */ -static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, - const URI_CHAR * first, +static const URI_CHAR * URI_FUNC(ParsePort)(const URI_CHAR * first, const URI_CHAR * afterLast) { +tail_call: if (first >= afterLast) { return afterLast; } switch (*first) { case URI_SET_DIGIT(_UT): - return URI_FUNC(ParsePort)(state, first + 1, afterLast); + first += 1; + goto tail_call; default: return first; @@ -1469,6 +1454,7 @@ static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { +tail_call: if (first >= afterLast) { return afterLast; } @@ -1480,12 +1466,14 @@ static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state, if (afterPchar == NULL) { return NULL; } - return URI_FUNC(ParseQueryFrag)(state, afterPchar, afterLast, memory); + first = afterPchar; + goto tail_call; } case _UT('/'): case _UT('?'): - return URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast, memory); + first += 1; + goto tail_call; default: return first; @@ -1500,6 +1488,7 @@ static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { +tail_call: if (first >= afterLast) { return afterLast; } @@ -1511,7 +1500,8 @@ static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state, if (afterPchar == NULL) { return NULL; } - return URI_FUNC(ParseSegment)(state, afterPchar, afterLast, memory); + first = afterPchar; + goto tail_call; } default: @@ -1572,6 +1562,7 @@ static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { +tail_call: if (first >= afterLast) { if (!URI_FUNC(OnExitSegmentNzNcOrScheme2)(state, first, memory)) { URI_FUNC(StopMalloc)(state, memory); @@ -1586,7 +1577,8 @@ static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState case _UT('-'): case URI_SET_ALPHA(_UT): case URI_SET_DIGIT(_UT): - return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast, memory); + first += 1; + goto tail_call; case _UT('%'): { const URI_CHAR * const afterPctEncoded = @@ -1796,6 +1788,7 @@ static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)(URI_TYPE(ParserState) * const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { +tail_call: if (first >= afterLast) { return afterLast; } @@ -1812,7 +1805,8 @@ static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)(URI_TYPE(ParserState) * URI_FUNC(StopMalloc)(state, memory); return NULL; } - return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast, memory); + first = afterSegment; + goto tail_call; } default: From 626f3c3c7c6834f884286e454dbe5a870d125299 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 18 Jun 2025 18:01:57 +0200 Subject: [PATCH 161/252] Unify arg info representation for internal and user functions The arg_info member of zend_function is now always a zend_arg_info*. Before, it was a zend_internal_arg_info* on internal functions, unless the ZEND_ACC_USER_ARG_INFO flag was set. Closes GH-19022 --- UPGRADING.INTERNALS | 3 + Zend/tests/closures/gh19653_3.phpt | 21 +++ Zend/tests/unified_arg_infos_001.phpt | 21 +++ Zend/zend.c | 6 + Zend/zend_API.c | 193 ++++++++++++++------------ Zend/zend_API.h | 6 +- Zend/zend_closures.c | 26 ++-- Zend/zend_closures.h | 1 + Zend/zend_compile.c | 22 +-- Zend/zend_compile.h | 5 +- Zend/zend_enum.c | 23 ++- Zend/zend_enum.h | 1 + Zend/zend_execute.c | 38 ++--- Zend/zend_execute_API.c | 6 +- Zend/zend_inheritance.c | 10 +- Zend/zend_object_handlers.c | 13 +- Zend/zend_object_handlers.h | 2 + Zend/zend_opcode.c | 30 ++-- ext/opcache/ZendAccelerator.c | 27 +++- ext/opcache/jit/zend_jit_trace.c | 2 +- ext/pdo/pdo_dbh.c | 16 ++- ext/reflection/php_reflection.c | 58 ++------ ext/zend_test/test.c | 82 ++++++++++- ext/zend_test/tmp_methods.stub.php | 11 ++ ext/zend_test/tmp_methods_arginfo.h | 14 ++ sapi/phpdbg/phpdbg_frame.c | 12 +- 26 files changed, 410 insertions(+), 239 deletions(-) create mode 100644 Zend/tests/closures/gh19653_3.phpt create mode 100644 Zend/tests/unified_arg_infos_001.phpt create mode 100644 ext/zend_test/tmp_methods.stub.php create mode 100644 ext/zend_test/tmp_methods_arginfo.h diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 71b6e1fd01ecc..3ec54f49391b8 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -49,6 +49,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES removed. Call zend_wrong_param_count(); followed by RETURN_THROWS(); instead. . PHP_HAVE_STREAMS macro removed from . + . zend_function.arg_info is now always a zend_arg_info*. Before, it was a + zend_internal_arg_info on internal functions, unless the + ZEND_ACC_USER_ARG_INFO flag was set. ======================== 2. Build system changes diff --git a/Zend/tests/closures/gh19653_3.phpt b/Zend/tests/closures/gh19653_3.phpt new file mode 100644 index 0000000000000..7a9ac589182c3 --- /dev/null +++ b/Zend/tests/closures/gh19653_3.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-19653 (Closure named argument unpacking between temporary closures can cause a crash) - temporary method variation +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: Unknown named parameter $tmpMethodParamName in %s:%d +Stack trace: +#0 %s(%d): usage1(Object(Closure)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/unified_arg_infos_001.phpt b/Zend/tests/unified_arg_infos_001.phpt new file mode 100644 index 0000000000000..d6699aa8d8cb5 --- /dev/null +++ b/Zend/tests/unified_arg_infos_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +Declaring non persistent method with arg info +--EXTENSIONS-- +zend_test +--FILE-- +testTmpMethodWithArgInfo(null); + +echo new ReflectionFunction($o->testTmpMethodWithArgInfo(...)); + +?> +--EXPECT-- +Closure [ public method testTmpMethodWithArgInfo ] { + + - Parameters [2] { + Parameter #0 [ Foo|Bar|null $tmpMethodParamName = null ] + Parameter #1 [ string $tmpMethodParamWithStringDefaultValue = "tmpMethodParamWithStringDefaultValue" ] + } +} diff --git a/Zend/zend.c b/Zend/zend.c index 4d024444a4be9..c46c8e9ada86c 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -38,6 +38,8 @@ #include "zend_call_stack.h" #include "zend_max_execution_timer.h" #include "zend_hrtime.h" +#include "zend_enum.h" +#include "zend_closures.h" #include "Optimizer/zend_optimizer.h" #include "php.h" #include "php_globals.h" @@ -1054,6 +1056,7 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */ ZVAL_UNDEF(&EG(last_fatal_error_backtrace)); zend_interned_strings_init(); + zend_object_handlers_startup(); zend_startup_builtin_functions(); zend_register_standard_constants(); zend_register_auto_global(zend_string_init_interned("GLOBALS", sizeof("GLOBALS") - 1, 1), 1, php_auto_globals_create_globals); @@ -1077,6 +1080,9 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */ tsrm_set_new_thread_end_handler(zend_new_thread_end_handler); tsrm_set_shutdown_handler(zend_interned_strings_dtor); #endif + + zend_enum_startup(); + zend_closure_startup(); } /* }}} */ diff --git a/Zend/zend_API.c b/Zend/zend_API.c index ca001f0e0c434..0881a169dfa7d 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -20,6 +20,7 @@ */ #include "zend.h" +#include "zend_compile.h" #include "zend_execute.h" #include "zend_API.h" #include "zend_hash.h" @@ -2927,6 +2928,80 @@ static zend_always_inline void zend_normalize_internal_type(zend_type *type) { } ZEND_TYPE_FOREACH_END(); } +static void zend_convert_internal_arg_info_type(zend_type *type, bool persistent) +{ + if (ZEND_TYPE_HAS_LITERAL_NAME(*type)) { + // gen_stubs.php does not support codegen for compound types. As a + // temporary workaround, we support union types by splitting + // the type name on `|` characters if necessary. + const char *class_name = ZEND_TYPE_LITERAL_NAME(*type); + type->type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT; + + size_t num_types = 1; + const char *p = class_name; + while ((p = strchr(p, '|'))) { + num_types++; + p++; + } + + if (num_types == 1) { + /* Simple class type */ + zend_string *str = zend_string_init_interned(class_name, strlen(class_name), persistent); + zend_alloc_ce_cache(str); + ZEND_TYPE_SET_PTR(*type, str); + type->type_mask |= _ZEND_TYPE_NAME_BIT; + } else { + /* Union type */ + zend_type_list *list = pemalloc(ZEND_TYPE_LIST_SIZE(num_types), persistent); + list->num_types = num_types; + ZEND_TYPE_SET_LIST(*type, list); + ZEND_TYPE_FULL_MASK(*type) |= _ZEND_TYPE_UNION_BIT; + + const char *start = class_name; + uint32_t j = 0; + while (true) { + const char *end = strchr(start, '|'); + zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), persistent); + zend_alloc_ce_cache(str); + list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0); + if (!end) { + break; + } + start = end + 1; + j++; + } + } + } + if (ZEND_TYPE_IS_ITERABLE_FALLBACK(*type)) { + /* Warning generated an extension load warning which is emitted for every test + zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable," + " regenerate the argument info via the php-src gen_stub build script"); + */ + zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK( + ZSTR_KNOWN(ZEND_STR_TRAVERSABLE), + (type->type_mask | MAY_BE_ARRAY) + ); + *type = legacy_iterable; + } +} + +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) +{ + if (!is_return_info) { + new_arg_info->name = zend_string_init_interned(arg_info->name, strlen(arg_info->name), persistent); + if (arg_info->default_value) { + new_arg_info->default_value = zend_string_init_interned(arg_info->default_value, strlen(arg_info->default_value), persistent); + } else { + new_arg_info->default_value = NULL; + } + } else { + new_arg_info->name = NULL; + new_arg_info->default_value = NULL; + } + new_arg_info->type = arg_info->type; + zend_convert_internal_arg_info_type(&new_arg_info->type, persistent); +} + /* registers all functions in *library_functions in the function hash */ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend_function_entry *functions, HashTable *function_table, int type) /* {{{ */ { @@ -2938,6 +3013,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend int error_type; zend_string *lowercase_name; size_t fname_len; + const zend_internal_arg_info *internal_arg_info; if (type==MODULE_PERSISTENT) { error_type = E_CORE_WARNING; @@ -2994,7 +3070,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend if (ptr->arg_info) { zend_internal_function_info *info = (zend_internal_function_info*)ptr->arg_info; - internal_function->arg_info = (zend_internal_arg_info*)ptr->arg_info+1; + internal_arg_info = ptr->arg_info+1; internal_function->num_args = ptr->num_args; /* Currently you cannot denote that the function can accept less arguments than num_args */ if (info->required_num_args == (uintptr_t)-1) { @@ -3024,7 +3100,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend zend_error(E_CORE_WARNING, "Missing arginfo for %s%s%s()", scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname); - internal_function->arg_info = NULL; + internal_arg_info = NULL; internal_function->num_args = 0; internal_function->required_num_args = 0; } @@ -3035,13 +3111,11 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend !(internal_function->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { zend_error(E_CORE_WARNING, "%s::__toString() implemented without string return type", ZSTR_VAL(scope->name)); - internal_function->arg_info = (zend_internal_arg_info *) arg_info_toString + 1; + internal_arg_info = (zend_internal_arg_info *) arg_info_toString + 1; internal_function->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE; internal_function->num_args = internal_function->required_num_args = 0; } - - zend_set_function_arg_flags((zend_function*)internal_function); if (ptr->flags & ZEND_ACC_ABSTRACT) { if (scope) { /* This is a class that must be abstract itself. Here we set the check info. */ @@ -3106,17 +3180,17 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend } /* If types of arguments have to be checked */ - if (reg_function->arg_info && num_args) { + if (internal_arg_info && num_args) { uint32_t i; for (i = 0; i < num_args; i++) { - zend_internal_arg_info *arg_info = ®_function->arg_info[i]; + const zend_internal_arg_info *arg_info = &internal_arg_info[i]; ZEND_ASSERT(arg_info->name && "Parameter must have a name"); if (ZEND_TYPE_IS_SET(arg_info->type)) { reg_function->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS; } #if ZEND_DEBUG for (uint32_t j = 0; j < i; j++) { - if (!strcmp(arg_info->name, reg_function->arg_info[j].name)) { + if (!strcmp(arg_info->name, internal_arg_info[j].name)) { zend_error_noreturn(E_CORE_ERROR, "Duplicate parameter name $%s for function %s%s%s()", arg_info->name, scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname); @@ -3126,78 +3200,24 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend } } - /* Rebuild arginfos if parameter/property types and/or a return type are used */ - if (reg_function->arg_info && - (reg_function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) { - /* convert "const char*" class type names into "zend_string*" */ + /* Convert zend_internal_arg_info to zend_arg_info */ + if (internal_arg_info) { uint32_t i; - zend_internal_arg_info *arg_info = reg_function->arg_info - 1; - zend_internal_arg_info *new_arg_info; + const zend_internal_arg_info *arg_info = internal_arg_info - 1; + zend_arg_info *new_arg_info; /* Treat return type as an extra argument */ num_args++; - new_arg_info = malloc(sizeof(zend_internal_arg_info) * num_args); - memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args); + new_arg_info = malloc(sizeof(zend_arg_info) * num_args); reg_function->arg_info = new_arg_info + 1; for (i = 0; i < num_args; i++) { - if (ZEND_TYPE_HAS_LITERAL_NAME(new_arg_info[i].type)) { - // gen_stubs.php does not support codegen for DNF types in arg infos. - // As a temporary workaround, we split the type name on `|` characters, - // converting it to an union type if necessary. - const char *class_name = ZEND_TYPE_LITERAL_NAME(new_arg_info[i].type); - new_arg_info[i].type.type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT; - - size_t num_types = 1; - const char *p = class_name; - while ((p = strchr(p, '|'))) { - num_types++; - p++; - } - - if (num_types == 1) { - /* Simple class type */ - zend_string *str = zend_string_init_interned(class_name, strlen(class_name), 1); - zend_alloc_ce_cache(str); - ZEND_TYPE_SET_PTR(new_arg_info[i].type, str); - new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT; - } else { - /* Union type */ - zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types)); - list->num_types = num_types; - ZEND_TYPE_SET_LIST(new_arg_info[i].type, list); - ZEND_TYPE_FULL_MASK(new_arg_info[i].type) |= _ZEND_TYPE_UNION_BIT; - - const char *start = class_name; - uint32_t j = 0; - while (true) { - const char *end = strchr(start, '|'); - zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), 1); - zend_alloc_ce_cache(str); - list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0); - if (!end) { - break; - } - start = end + 1; - j++; - } - } - } - if (ZEND_TYPE_IS_ITERABLE_FALLBACK(new_arg_info[i].type)) { - /* Warning generated an extension load warning which is emitted for every test - zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable," - " regenerate the argument info via the php-src gen_stub build script"); - */ - zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK( - ZSTR_KNOWN(ZEND_STR_TRAVERSABLE), - (new_arg_info[i].type.type_mask | MAY_BE_ARRAY) - ); - new_arg_info[i].type = legacy_iterable; - } - - zend_normalize_internal_type(&new_arg_info[i].type); + zend_convert_internal_arg_info(&new_arg_info[i], &arg_info[i], + i == 0, true); } } + zend_set_function_arg_flags((zend_function*)reg_function); + if (scope) { zend_check_magic_method_implementation( scope, (zend_function *)reg_function, lowercase_name, E_CORE_ERROR); @@ -5242,48 +5262,43 @@ static zend_string *try_parse_string(const char *str, size_t len, char quote) { } ZEND_API zend_result zend_get_default_from_internal_arg_info( - zval *default_value_zval, const zend_internal_arg_info *arg_info) + zval *default_value_zval, const zend_arg_info *arg_info) { - const char *default_value = arg_info->default_value; + const zend_string *default_value = arg_info->default_value; if (!default_value) { return FAILURE; } /* Avoid going through the full AST machinery for some simple and common cases. */ - size_t default_value_len = strlen(default_value); zend_ulong lval; - if (default_value_len == sizeof("null")-1 - && !memcmp(default_value, "null", sizeof("null")-1)) { + if (zend_string_equals_literal(default_value, "null")) { ZVAL_NULL(default_value_zval); return SUCCESS; - } else if (default_value_len == sizeof("true")-1 - && !memcmp(default_value, "true", sizeof("true")-1)) { + } else if (zend_string_equals_literal(default_value, "true")) { ZVAL_TRUE(default_value_zval); return SUCCESS; - } else if (default_value_len == sizeof("false")-1 - && !memcmp(default_value, "false", sizeof("false")-1)) { + } else if (zend_string_equals_literal(default_value, "false")) { ZVAL_FALSE(default_value_zval); return SUCCESS; - } else if (default_value_len >= 2 - && (default_value[0] == '\'' || default_value[0] == '"') - && default_value[default_value_len - 1] == default_value[0]) { + } else if (ZSTR_LEN(default_value) >= 2 + && (ZSTR_VAL(default_value)[0] == '\'' || ZSTR_VAL(default_value)[0] == '"') + && ZSTR_VAL(default_value)[ZSTR_LEN(default_value) - 1] == ZSTR_VAL(default_value)[0]) { zend_string *str = try_parse_string( - default_value + 1, default_value_len - 2, default_value[0]); + ZSTR_VAL(default_value) + 1, ZSTR_LEN(default_value) - 2, ZSTR_VAL(default_value)[0]); if (str) { ZVAL_STR(default_value_zval, str); return SUCCESS; } - } else if (default_value_len == sizeof("[]")-1 - && !memcmp(default_value, "[]", sizeof("[]")-1)) { + } else if (zend_string_equals_literal(default_value, "[]")) { ZVAL_EMPTY_ARRAY(default_value_zval); return SUCCESS; - } else if (ZEND_HANDLE_NUMERIC_STR(default_value, default_value_len, lval)) { + } else if (ZEND_HANDLE_NUMERIC(default_value, lval)) { ZVAL_LONG(default_value_zval, lval); return SUCCESS; } #if 0 - fprintf(stderr, "Evaluating %s via AST\n", default_value); + fprintf(stderr, "Evaluating %s via AST\n", ZSTR_VAL(default_value)); #endif - return get_default_via_ast(default_value_zval, default_value); + return get_default_via_ast(default_value_zval, ZSTR_VAL(default_value)); } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 1bb47a2e8703d..c1ccbf13666a5 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -922,8 +922,12 @@ ZEND_API bool zend_is_iterable(const zval *iterable); ZEND_API bool zend_is_countable(const zval *countable); +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 permanent); + ZEND_API zend_result zend_get_default_from_internal_arg_info( - zval *default_value_zval, const zend_internal_arg_info *arg_info); + zval *default_value_zval, const zend_arg_info *arg_info); END_EXTERN_C() diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 4ecb6b2c493b9..05b6862044816 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -510,7 +510,7 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* { * ZEND_ACC_USER_ARG_INFO flag to prevent invalid usage by Reflection */ invoke->type = ZEND_INTERNAL_FUNCTION; invoke->internal_function.fn_flags = - ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags); + ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | ZEND_ACC_NEVER_CACHE | (closure->func.common.fn_flags & keep_flags); if (closure->func.type != ZEND_INTERNAL_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { invoke->internal_function.fn_flags |= ZEND_ACC_USER_ARG_INFO; @@ -618,7 +618,6 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) zval val; struct _zend_arg_info *arg_info = closure->func.common.arg_info; HashTable *debug_info; - bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO); *is_temp = 1; @@ -694,15 +693,9 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) zend_string *name; zval info; ZEND_ASSERT(arg_info->name && "Argument should have name"); - if (zstr_args) { - name = zend_strpprintf(0, "%s$%s", - ZEND_ARG_SEND_MODE(arg_info) ? "&" : "", - ZSTR_VAL(arg_info->name)); - } else { - name = zend_strpprintf(0, "%s$%s", - ZEND_ARG_SEND_MODE(arg_info) ? "&" : "", - ((zend_internal_arg_info*)arg_info)->name); - } + name = zend_strpprintf(0, "%s$%s", + ZEND_ARG_SEND_MODE(arg_info) ? "&" : "", + ZSTR_VAL(arg_info->name)); ZVAL_NEW_STR(&info, zend_strpprintf(0, "%s", i >= required ? "" : "")); zend_hash_update(Z_ARRVAL(val), name, &info); zend_string_release_ex(name, 0); @@ -885,8 +878,7 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas } /* }}} */ -/* __call and __callStatic name the arguments "$arguments" in the docs. */ -static zend_internal_arg_info trampoline_arg_info[] = {ZEND_ARG_VARIADIC_TYPE_INFO(false, arguments, IS_MIXED, false)}; +static zend_arg_info trampoline_arg_info[1]; void zend_closure_from_frame(zval *return_value, const zend_execute_data *call) { /* {{{ */ zval instance; @@ -951,3 +943,11 @@ void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* { ZVAL_COPY_VALUE(var, val); } /* }}} */ + +void zend_closure_startup(void) +{ + /* __call and __callStatic name the arguments "$arguments" in the docs. */ + trampoline_arg_info[0].name = zend_string_init_interned("arguments", strlen("arguments"), true); + trampoline_arg_info[0].type = (zend_type)ZEND_TYPE_INIT_CODE(IS_MIXED, false, _ZEND_ARG_INFO_FLAGS(false, 1, 0)); + trampoline_arg_info[0].default_value = NULL; +} diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index 8bea4ffb051e8..a118044c6e248 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -28,6 +28,7 @@ BEGIN_EXTERN_C() #define ZEND_CLOSURE_OBJECT(op_array) \ ((zend_object*)((char*)(op_array) - sizeof(zend_object))) +void zend_closure_startup(void); void zend_register_closure_ce(void); void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var); void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 50ba8029873ad..5eba2ec1366fa 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3688,21 +3688,10 @@ static void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */ static uint32_t zend_get_arg_num(const zend_function *fn, const zend_string *arg_name) { // TODO: Caching? - if (fn->type == ZEND_USER_FUNCTION) { - for (uint32_t i = 0; i < fn->common.num_args; i++) { - const zend_arg_info *arg_info = &fn->op_array.arg_info[i]; - if (zend_string_equals(arg_info->name, arg_name)) { - return i + 1; - } - } - } else { - ZEND_ASSERT(fn->common.num_args == 0 || fn->internal_function.arg_info); - for (uint32_t i = 0; i < fn->common.num_args; i++) { - const zend_internal_arg_info *arg_info = &fn->internal_function.arg_info[i]; - size_t len = strlen(arg_info->name); - if (zend_string_equals_cstr(arg_name, arg_info->name, len)) { - return i + 1; - } + for (uint32_t i = 0; i < fn->common.num_args; i++) { + zend_arg_info *arg_info = &fn->op_array.arg_info[i]; + if (zend_string_equals(arg_info->name, arg_name)) { + return i + 1; } } @@ -4702,7 +4691,7 @@ static uint32_t zend_compile_frameless_icall_ex(znode *result, const zend_ast_li if (i < args->children) { zend_compile_expr(&arg_zvs[i], args->child[i]); } else { - const zend_internal_arg_info *arg_info = (const zend_internal_arg_info *)&fbc->common.arg_info[i]; + const zend_arg_info *arg_info = &fbc->common.arg_info[i]; arg_zvs[i].op_type = IS_CONST; if (zend_get_default_from_internal_arg_info(&arg_zvs[i].u.constant, arg_info) == FAILURE) { ZEND_UNREACHABLE(); @@ -7934,6 +7923,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 arg_info = &arg_infos[i]; arg_info->name = zend_string_copy(name); arg_info->type = (zend_type) ZEND_TYPE_INIT_NONE(0); + arg_info->default_value = NULL; if (attributes_ast) { zend_compile_attributes( diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 86fab4b57ded6..d2a3b47bf92f4 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -592,7 +592,7 @@ typedef struct _zend_internal_function { zend_function *prototype; uint32_t num_args; uint32_t required_num_args; - zend_internal_arg_info *arg_info; + zend_arg_info *arg_info; HashTable *attributes; ZEND_MAP_PTR_DEF(void **, run_time_cache); zend_string *doc_comment; @@ -976,7 +976,8 @@ ZEND_API ZEND_COLD void zend_user_exception_handler(void); } \ } while (0) -void zend_free_internal_arg_info(zend_internal_function *function); +ZEND_API void zend_free_internal_arg_info(zend_internal_function *function, + bool permanent); ZEND_API void destroy_zend_function(zend_function *function); ZEND_API void zend_function_dtor(zval *zv); ZEND_API void destroy_zend_class(zval *zv); diff --git a/Zend/zend_enum.c b/Zend/zend_enum.c index ae9e7b701213f..00fbab22a056e 100644 --- a/Zend/zend_enum.c +++ b/Zend/zend_enum.c @@ -36,6 +36,10 @@ ZEND_API zend_class_entry *zend_ce_unit_enum; ZEND_API zend_class_entry *zend_ce_backed_enum; ZEND_API zend_object_handlers zend_enum_object_handlers; +static zend_arg_info zarginfo_class_UnitEnum_cases[sizeof(arginfo_class_UnitEnum_cases)/sizeof(zend_internal_arg_info)]; +static zend_arg_info zarginfo_class_BackedEnum_from[sizeof(arginfo_class_BackedEnum_from)/sizeof(zend_internal_arg_info)]; +static zend_arg_info zarginfo_class_BackedEnum_tryFrom[sizeof(arginfo_class_BackedEnum_tryFrom)/sizeof(zend_internal_arg_info)]; + zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *backing_value_zv) { zend_object *zobj = zend_objects_new(ce); @@ -446,7 +450,7 @@ void zend_enum_register_funcs(zend_class_entry *ce) cases_function->function_name = ZSTR_KNOWN(ZEND_STR_CASES); cases_function->fn_flags = fn_flags; cases_function->doc_comment = NULL; - cases_function->arg_info = (zend_internal_arg_info *) (arginfo_class_UnitEnum_cases + 1); + cases_function->arg_info = zarginfo_class_UnitEnum_cases + 1; zend_enum_register_func(ce, ZEND_STR_CASES, cases_function); if (ce->enum_backing_type != IS_UNDEF) { @@ -457,7 +461,7 @@ void zend_enum_register_funcs(zend_class_entry *ce) from_function->doc_comment = NULL; from_function->num_args = 1; from_function->required_num_args = 1; - from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_from + 1); + from_function->arg_info = zarginfo_class_BackedEnum_from + 1; zend_enum_register_func(ce, ZEND_STR_FROM, from_function); zend_internal_function *try_from_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1); @@ -467,7 +471,7 @@ void zend_enum_register_funcs(zend_class_entry *ce) try_from_function->doc_comment = NULL; try_from_function->num_args = 1; try_from_function->required_num_args = 1; - try_from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_tryFrom + 1); + try_from_function->arg_info = zarginfo_class_BackedEnum_tryFrom + 1; zend_enum_register_func(ce, ZEND_STR_TRYFROM_LOWERCASE, try_from_function); } } @@ -633,3 +637,16 @@ ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char * zend_class_constant *c = zend_hash_str_find_ptr(CE_CONSTANTS_TABLE(ce), name, strlen(name)); return zend_enum_case_from_class_constant(c); } + +void zend_enum_startup(void) +{ + for (size_t i = 0; i < sizeof(zarginfo_class_UnitEnum_cases)/sizeof(zend_arg_info); i++) { + zend_convert_internal_arg_info(&zarginfo_class_UnitEnum_cases[i], &arginfo_class_UnitEnum_cases[i], i == 0, true); + } + for (size_t i = 0; i < sizeof(zarginfo_class_BackedEnum_from)/sizeof(zend_arg_info); i++) { + zend_convert_internal_arg_info(&zarginfo_class_BackedEnum_from[i], &arginfo_class_BackedEnum_from[i], i == 0, true); + } + for (size_t i = 0; i < sizeof(zarginfo_class_BackedEnum_tryFrom)/sizeof(zend_arg_info); i++) { + zend_convert_internal_arg_info(&zarginfo_class_BackedEnum_tryFrom[i], &arginfo_class_BackedEnum_tryFrom[i], i == 0, true); + } +} diff --git a/Zend/zend_enum.h b/Zend/zend_enum.h index 7b3b0184b4eb5..d6c820189475a 100644 --- a/Zend/zend_enum.h +++ b/Zend/zend_enum.h @@ -30,6 +30,7 @@ extern ZEND_API zend_class_entry *zend_ce_unit_enum; extern ZEND_API zend_class_entry *zend_ce_backed_enum; extern ZEND_API zend_object_handlers zend_enum_object_handlers; +void zend_enum_startup(void); void zend_register_enum_ce(void); void zend_enum_add_interfaces(zend_class_entry *ce); zend_result zend_enum_build_backed_enum_table(zend_class_entry *ce); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index d411dcbc3b953..518cbb98fc0f8 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -136,8 +136,7 @@ static ZEND_FUNCTION(pass) { } -ZEND_BEGIN_ARG_INFO_EX(zend_pass_function_arg_info, 0, 0, 0) -ZEND_END_ARG_INFO() +static zend_arg_info zend_pass_function_arg_info[1] = {0}; ZEND_API const zend_internal_function zend_pass_function = { ZEND_INTERNAL_FUNCTION, /* type */ @@ -148,7 +147,7 @@ ZEND_API const zend_internal_function zend_pass_function = { NULL, /* prototype */ 0, /* num_args */ 0, /* required_num_args */ - (zend_internal_arg_info *) zend_pass_function_arg_info + 1, /* arg_info */ + zend_pass_function_arg_info + 1, /* arg_info */ NULL, /* attributes */ NULL, /* run_time_cache */ NULL, /* doc_comment */ @@ -1480,7 +1479,7 @@ static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, con ZEND_API bool zend_verify_internal_return_type(const zend_function *zf, zval *ret) { - const zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1; + const zend_arg_info *ret_info = zf->internal_function.arg_info - 1; if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_VOID) { if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) { @@ -5474,28 +5473,17 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( // TODO: Use a hash table? uint32_t num_args = fbc->common.num_args; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) - || EXPECTED(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { - for (uint32_t i = 0; i < num_args; i++) { - const zend_arg_info *arg_info = &fbc->common.arg_info[i]; - if (zend_string_equals(arg_name, arg_info->name)) { - if (fbc->type == ZEND_USER_FUNCTION && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) { - *cache_slot = unique_id; - *(uintptr_t *)(cache_slot + 1) = i; - } - return i; - } - } - } else { - ZEND_ASSERT(num_args == 0 || fbc->internal_function.arg_info); - for (uint32_t i = 0; i < num_args; i++) { - const zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i]; - size_t len = strlen(arg_info->name); - if (zend_string_equals_cstr(arg_name, arg_info->name, len)) { + for (uint32_t i = 0; i < num_args; i++) { + const zend_arg_info *arg_info = &fbc->common.arg_info[i]; + if (zend_string_equals(arg_name, arg_info->name)) { + if ((fbc->type == ZEND_USER_FUNCTION + && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) + || (fbc->type == ZEND_INTERNAL_FUNCTION + && !(fbc->common.fn_flags & ZEND_ACC_NEVER_CACHE))) { *cache_slot = unique_id; *(uintptr_t *)(cache_slot + 1) = i; - return i; } + return i; } } @@ -5503,7 +5491,7 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( if ((fbc->type == ZEND_USER_FUNCTION && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) || (fbc->type == ZEND_INTERNAL_FUNCTION - && !(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO))) { + && !(fbc->common.fn_flags & ZEND_ACC_NEVER_CACHE))) { *cache_slot = unique_id; *(uintptr_t *)(cache_slot + 1) = fbc->common.num_args; } @@ -5661,7 +5649,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *cal continue; } - zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i]; + zend_arg_info *arg_info = &fbc->internal_function.arg_info[i]; if (i < fbc->common.required_num_args) { zend_execute_data *old = start_fake_frame(call, NULL); zend_argument_error(zend_ce_argument_count_error, i + 1, "not passed"); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 660975f9bc1b5..e134d3d496b6d 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -652,11 +652,7 @@ ZEND_API const char *get_function_arg_name(const zend_function *func, uint32_t a return NULL; } - if (func->type == ZEND_USER_FUNCTION || (func->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { - return ZSTR_VAL(func->common.arg_info[arg_num - 1].name); - } else { - return ((zend_internal_arg_info*) func->common.arg_info)[arg_num - 1].name; - } + return ZSTR_VAL(func->common.arg_info[arg_num - 1].name); } /* }}} */ diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 64448bc535590..3c3931cdca164 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -953,18 +953,14 @@ static ZEND_COLD zend_string *zend_get_function_declaration( } smart_str_appendc(&str, '$'); - if (fptr->type == ZEND_INTERNAL_FUNCTION) { - smart_str_appends(&str, ((zend_internal_arg_info*)arg_info)->name); - } else { - smart_str_appendl(&str, ZSTR_VAL(arg_info->name), ZSTR_LEN(arg_info->name)); - } + smart_str_append(&str, arg_info->name); if (i >= required && !ZEND_ARG_IS_VARIADIC(arg_info)) { smart_str_appends(&str, " = "); if (fptr->type == ZEND_INTERNAL_FUNCTION) { - if (((zend_internal_arg_info*)arg_info)->default_value) { - smart_str_appends(&str, ((zend_internal_arg_info*)arg_info)->default_value); + if (arg_info->default_value) { + smart_str_append(&str, arg_info->default_value); } else { smart_str_appends(&str, ""); } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 470fb76ec14e1..88b7b1112d7b1 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -46,6 +46,9 @@ #define IN_ISSET ZEND_GUARD_PROPERTY_ISSET #define IN_HOOK ZEND_GUARD_PROPERTY_HOOK +static zend_arg_info zend_call_trampoline_arginfo[1] = {{0}}; +static zend_arg_info zend_property_hook_arginfo[1] = {{0}}; + static zend_always_inline bool zend_objects_check_stack_limit(void) { #ifdef ZEND_CHECK_STACK_LIMIT @@ -1782,9 +1785,6 @@ ZEND_API zend_function *zend_get_property_hook_trampoline( const zend_property_info *prop_info, zend_property_hook_kind kind, zend_string *prop_name) { - static const zend_internal_arg_info arg_info[2] = { - { .name = "value" } - }; zend_function *func; if (EXPECTED(EG(trampoline).common.function_name == NULL)) { func = &EG(trampoline); @@ -1810,7 +1810,7 @@ ZEND_API zend_function *zend_get_property_hook_trampoline( func->common.scope = prop_info->ce; func->common.prototype = NULL; func->common.prop_info = prop_info; - func->common.arg_info = (zend_arg_info *) arg_info; + func->common.arg_info = zend_property_hook_arginfo; func->internal_function.handler = kind == ZEND_PROPERTY_HOOK_GET ? ZEND_FN(zend_parent_hook_get_trampoline) : ZEND_FN(zend_parent_hook_set_trampoline); @@ -2574,3 +2574,8 @@ ZEND_API const zend_object_handlers std_object_handlers = { zend_std_compare_objects, /* compare */ NULL, /* get_properties_for */ }; + +void zend_object_handlers_startup(void) { + zend_call_trampoline_arginfo[0].name = ZSTR_KNOWN(ZEND_STR_ARGS); + zend_property_hook_arginfo[0].name = ZSTR_KNOWN(ZEND_STR_VALUE); +} diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index 59277c09d8024..3e922343eb15a 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -334,6 +334,8 @@ ZEND_API zend_function *zend_get_property_hook_trampoline( ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_property_info *prop_info); +void zend_object_handlers_startup(void); + #define zend_release_properties(ht) do { \ if (ht) { \ zend_array_release(ht); \ diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 1962c7b5a56d1..5e9e7b20d869b 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -124,21 +124,32 @@ ZEND_API void zend_type_release(zend_type type, bool persistent) { } } -void zend_free_internal_arg_info(zend_internal_function *function) { - if ((function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) && - function->arg_info) { +ZEND_API void zend_free_internal_arg_info(zend_internal_function *function, + bool persistent) { + if (function->arg_info) { + ZEND_ASSERT((persistent || (function->fn_flags & ZEND_ACC_NEVER_CACHE)) + && "Functions with non-persistent arg_info must be flagged ZEND_ACC_NEVER_CACHE"); uint32_t i; uint32_t num_args = function->num_args + 1; - zend_internal_arg_info *arg_info = function->arg_info - 1; + zend_arg_info *arg_info = function->arg_info - 1; if (function->fn_flags & ZEND_ACC_VARIADIC) { num_args++; } for (i = 0 ; i < num_args; i++) { - zend_type_release(arg_info[i].type, /* persistent */ true); + bool is_return_info = i == 0; + if (!is_return_info) { + zend_string_release_ex(arg_info[i].name, persistent); + if (arg_info[i].default_value) { + zend_string_release_ex(arg_info[i].default_value, + persistent); + } + } + zend_type_release(arg_info[i].type, persistent); } - free(arg_info); + + pefree(arg_info, persistent); } } @@ -157,7 +168,7 @@ ZEND_API void zend_function_dtor(zval *zv) /* For methods this will be called explicitly. */ if (!function->common.scope) { - zend_free_internal_arg_info(&function->internal_function); + zend_free_internal_arg_info(&function->internal_function, true); if (function->common.attributes) { zend_hash_release(function->common.attributes); @@ -474,12 +485,9 @@ ZEND_API void destroy_zend_class(zval *zv) zend_hash_destroy(&ce->properties_info); zend_string_release_ex(ce->name, 1); - /* TODO: eliminate this loop for classes without functions with arg_info / attributes */ ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, fn) { if (fn->common.scope == ce) { - if (fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) { - zend_free_internal_arg_info(&fn->internal_function); - } + zend_free_internal_arg_info(&fn->internal_function, true); if (fn->common.attributes) { zend_hash_release(fn->common.attributes); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index d1d49ca391a69..7acb14b778f8d 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -663,8 +663,7 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int if (Z_FUNC(p->val)->common.function_name) { Z_FUNC(p->val)->common.function_name = new_interned_string(Z_FUNC(p->val)->common.function_name); } - if (Z_FUNC(p->val)->common.arg_info && - (Z_FUNC(p->val)->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) { + if (Z_FUNC(p->val)->common.arg_info) { uint32_t i; uint32_t num_args = Z_FUNC(p->val)->common.num_args + 1; zend_arg_info *arg_info = Z_FUNC(p->val)->common.arg_info - 1; @@ -673,6 +672,12 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int num_args++; } for (i = 0 ; i < num_args; i++) { + if (i > 0) { + arg_info[i].name = new_interned_string(arg_info[i].name); + if (arg_info[i].default_value) { + arg_info[i].default_value = new_interned_string(arg_info[i].default_value); + } + } accel_copy_permanent_list_types(new_interned_string, arg_info[i].type); } } @@ -714,6 +719,24 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int if (Z_FUNC(q->val)->common.function_name) { Z_FUNC(q->val)->common.function_name = new_interned_string(Z_FUNC(q->val)->common.function_name); } + if (Z_FUNC(q->val)->common.scope == ce) { + uint32_t i; + uint32_t num_args = Z_FUNC(q->val)->common.num_args + 1; + zend_arg_info *arg_info = Z_FUNC(q->val)->common.arg_info - 1; + + if (Z_FUNC(q->val)->common.fn_flags & ZEND_ACC_VARIADIC) { + num_args++; + } + for (i = 0 ; i < num_args; i++) { + if (i > 0) { + arg_info[i].name = new_interned_string(arg_info[i].name); + if (arg_info[i].default_value) { + arg_info[i].default_value = new_interned_string(arg_info[i].default_value); + } + } + accel_copy_permanent_list_types(new_interned_string, arg_info[i].type); + } + } } ZEND_HASH_FOREACH_END(); ZEND_HASH_MAP_FOREACH_BUCKET(&ce->constants_table, q) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 9dc6ea1c3b003..10be237d06b8e 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -493,7 +493,7 @@ static bool zend_jit_needs_arg_dtor(const zend_function *func, uint32_t arg_num, && func->type == ZEND_INTERNAL_FUNCTION && (func->internal_function.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0 && arg_num < func->internal_function.num_args) { - const zend_internal_arg_info *arg_info = &func->internal_function.arg_info[arg_num]; + const zend_arg_info *arg_info = &func->internal_function.arg_info[arg_num]; if (ZEND_ARG_SEND_MODE(arg_info) == ZEND_SEND_BY_VAL && ZEND_TYPE_IS_SET(arg_info->type) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index beb3665c4d924..34f19e364faa7 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -1321,6 +1321,7 @@ static void cls_method_dtor(zval *el) /* {{{ */ { if (func->common.attributes) { zend_hash_release(func->common.attributes); } + zend_free_internal_arg_info(&func->internal_function, false); efree(func); } /* }}} */ @@ -1336,6 +1337,7 @@ static void cls_method_pdtor(zval *el) /* {{{ */ { if (func->common.attributes) { zend_hash_release(func->common.attributes); } + zend_free_internal_arg_info(&func->internal_function, true); pefree(func, 1); } /* }}} */ @@ -1408,7 +1410,19 @@ bool pdo_hash_methods(pdo_dbh_object_t *dbh_obj, int kind) if (funcs->arg_info) { zend_internal_function_info *info = (zend_internal_function_info*)funcs->arg_info; - func.arg_info = (zend_internal_arg_info*)funcs->arg_info + 1; + uint32_t num_arg_info = 1 + funcs->num_args; + if (func.fn_flags & ZEND_ACC_VARIADIC) { + num_arg_info++; + } + + zend_arg_info *arg_info = safe_pemalloc(num_arg_info, + sizeof(zend_arg_info), 0, dbh->is_persistent); + for (uint32_t i = 0; i < num_arg_info; i++) { + zend_convert_internal_arg_info(&arg_info[i], + &funcs->arg_info[i], i == 0, dbh->is_persistent); + } + + func.arg_info = arg_info + 1; func.num_args = funcs->num_args; if (info->required_num_args == (uint32_t)-1) { func.required_num_args = funcs->num_args; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index d6e55c982b425..c6f681ee2276c 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -763,11 +763,6 @@ static void format_default_value(smart_str *str, zval *value) { } } -static inline bool has_internal_arg_info(const zend_function *fptr) { - return fptr->type == ZEND_INTERNAL_FUNCTION - && !(fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO); -} - /* {{{ _parameter_string */ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_arg_info *arg_info, uint32_t offset, bool required, char* indent) { @@ -789,17 +784,15 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_ if (ZEND_ARG_IS_VARIADIC(arg_info)) { smart_str_appends(str, "..."); } - smart_str_append_printf(str, "$%s", has_internal_arg_info(fptr) - ? ((zend_internal_arg_info*)arg_info)->name : ZSTR_VAL(arg_info->name)); + smart_str_append_printf(str, "$%s", ZSTR_VAL(arg_info->name)); if (!required && !ZEND_ARG_IS_VARIADIC(arg_info)) { if (fptr->type == ZEND_INTERNAL_FUNCTION) { smart_str_appends(str, " = "); /* TODO: We don't have a way to fetch the default value for an internal function * with userland arg info. */ - if (has_internal_arg_info(fptr) - && ((zend_internal_arg_info*)arg_info)->default_value) { - smart_str_appends(str, ((zend_internal_arg_info*)arg_info)->default_value); + if (arg_info->default_value) { + smart_str_append(str, arg_info->default_value); } else { smart_str_appends(str, ""); } @@ -1463,11 +1456,7 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje } prop_name = reflection_prop_name(object); - if (has_internal_arg_info(fptr)) { - ZVAL_STRING(prop_name, ((zend_internal_arg_info*)arg_info)->name); - } else { - ZVAL_STR_COPY(prop_name, arg_info->name); - } + ZVAL_STR_COPY(prop_name, arg_info->name); } /* }}} */ @@ -1653,8 +1642,7 @@ static zend_result get_parameter_default(zval *result, parameter_reference *para /* We don't have a way to determine the default value for this case right now. */ return FAILURE; } - return zend_get_default_from_internal_arg_info( - result, (zend_internal_arg_info *) param->arg_info); + return zend_get_default_from_internal_arg_info(result, param->arg_info); } else { zval *default_value = get_default_from_recv((zend_op_array *) param->fptr, param->offset); if (!default_value) { @@ -2590,22 +2578,11 @@ ZEND_METHOD(ReflectionParameter, __construct) uint32_t i; position = -1; - if (has_internal_arg_info(fptr)) { - for (i = 0; i < num_args; i++) { - if (arg_info[i].name) { - if (strcmp(((zend_internal_arg_info*)arg_info)[i].name, ZSTR_VAL(arg_name)) == 0) { - position = i; - break; - } - } - } - } else { - for (i = 0; i < num_args; i++) { - if (arg_info[i].name) { - if (zend_string_equals(arg_name, arg_info[i].name)) { - position = i; - break; - } + for (i = 0; i < num_args; i++) { + if (arg_info[i].name) { + if (zend_string_equals(arg_name, arg_info[i].name)) { + position = i; + break; } } } @@ -2646,11 +2623,7 @@ ZEND_METHOD(ReflectionParameter, __construct) prop_name = reflection_prop_name(object); zval_ptr_dtor(prop_name); - if (has_internal_arg_info(fptr)) { - ZVAL_STRING(prop_name, ((zend_internal_arg_info*)arg_info)[position].name); - } else { - ZVAL_STR_COPY(prop_name, arg_info[position].name); - } + ZVAL_STR_COPY(prop_name, arg_info[position].name); return; failure: @@ -2689,11 +2662,7 @@ ZEND_METHOD(ReflectionParameter, getName) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(param); - if (has_internal_arg_info(param->fptr)) { - RETURN_STRING(((zend_internal_arg_info *) param->arg_info)->name); - } else { - RETURN_STR_COPY(param->arg_info->name); - } + RETURN_STR_COPY(param->arg_info->name); } /* }}} */ @@ -2947,8 +2916,7 @@ ZEND_METHOD(ReflectionParameter, isDefaultValueAvailable) GET_REFLECTION_OBJECT_PTR(param); if (param->fptr->type == ZEND_INTERNAL_FUNCTION) { - RETURN_BOOL(!(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO) - && ((zend_internal_arg_info*) (param->arg_info))->default_value); + RETURN_BOOL(param->arg_info->default_value); } else { zval *default_value = get_default_from_recv((zend_op_array *)param->fptr, param->offset); RETURN_BOOL(default_value != NULL); diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 4e06b2106ce59..fd1a51c5776c3 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -37,10 +37,12 @@ #include "Zend/Optimizer/zend_optimizer.h" #include "Zend/zend_alloc.h" #include "test_arginfo.h" +#include "tmp_methods_arginfo.h" #include "zend_call_stack.h" #include "zend_exceptions.h" #include "zend_mm_custom_handlers.h" #include "ext/uri/php_uri.h" +#include "zend_observer.h" #if defined(HAVE_LIBXML) && !defined(PHP_WIN32) # include @@ -1030,16 +1032,38 @@ static ZEND_FUNCTION(zend_test_log_err_debug) php_log_err_with_severity(ZSTR_VAL(str), LOG_DEBUG); } +typedef struct _zend_test_object { + zend_internal_function *tmp_method; + zend_object std; +} zend_test_object; + static zend_object *zend_test_class_new(zend_class_entry *class_type) { - zend_object *obj = zend_objects_new(class_type); - object_properties_init(obj, class_type); - obj->handlers = &zend_test_class_handlers; - return obj; + zend_test_object *intern = zend_object_alloc(sizeof(zend_test_object), class_type); + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + return &intern->std; +} + +static void zend_test_class_free_obj(zend_object *object) +{ + zend_test_object *intern = (zend_test_object*)((char*)object - XtOffsetOf(zend_test_object, std)); + + if (intern->tmp_method) { + zend_internal_function *func = intern->tmp_method; + intern->tmp_method = NULL; + zend_string_release_ex(func->function_name, 0); + zend_free_internal_arg_info(func, false); + efree(func); + } + + zend_object_std_dtor(object); } static zend_function *zend_test_class_method_get(zend_object **object, zend_string *name, const zval *key) { + zend_test_object *intern = (zend_test_object*)((char*)(*object) - XtOffsetOf(zend_test_object, std)); + if (zend_string_equals_literal_ci(name, "test")) { zend_internal_function *fptr; @@ -1058,6 +1082,41 @@ static zend_function *zend_test_class_method_get(zend_object **object, zend_stri fptr->handler = ZEND_FN(zend_test_func); fptr->doc_comment = NULL; + return (zend_function*)fptr; + } else if (zend_string_equals_literal_ci(name, "testTmpMethodWithArgInfo")) { + if (intern->tmp_method) { + return (zend_function*)intern->tmp_method; + } + + const zend_function_entry *entry = &class_ZendTestTmpMethods_methods[0]; + zend_internal_function *fptr = emalloc(sizeof(zend_internal_function)); + memset(fptr, 0, sizeof(zend_internal_function)); + fptr->type = ZEND_INTERNAL_FUNCTION; + fptr->handler = entry->handler; + fptr->function_name = zend_string_init(entry->fname, strlen(entry->fname), false); + fptr->scope = intern->std.ce; + fptr->prototype = NULL; + fptr->T = ZEND_OBSERVER_ENABLED; + fptr->fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_NEVER_CACHE; + + zend_internal_function_info *info = (zend_internal_function_info*)entry->arg_info; + + uint32_t num_arg_info = 1 + entry->num_args; + zend_arg_info *arg_info = safe_emalloc(num_arg_info, sizeof(zend_arg_info), 0); + for (uint32_t i = 0; i < num_arg_info; i++) { + zend_convert_internal_arg_info(&arg_info[i], &entry->arg_info[i], i == 0, false); + } + + fptr->arg_info = arg_info + 1; + fptr->num_args = entry->num_args; + if (info->required_num_args == (uint32_t)-1) { + fptr->required_num_args = entry->num_args; + } else { + fptr->required_num_args = info->required_num_args; + } + + intern->tmp_method = fptr; + return (zend_function*)fptr; } return zend_std_get_method(object, name, key); @@ -1145,6 +1204,18 @@ static ZEND_METHOD(_ZendTestClass, variadicTest) { object_init_ex(return_value, zend_get_called_scope(execute_data)); } +ZEND_METHOD(ZendTestTmpMethods, testTmpMethodWithArgInfo) +{ + zend_object *obj; + zend_string *str; + + ZEND_PARSE_PARAMETERS_START(0, 2); + Z_PARAM_OPTIONAL; + Z_PARAM_OBJ_OR_NULL(obj); + Z_PARAM_STR(str); + ZEND_PARSE_PARAMETERS_END(); +} + static ZEND_METHOD(_ZendTestChildClass, returnsThrowable) { ZEND_PARSE_PARAMETERS_NONE(); @@ -1450,11 +1521,14 @@ PHP_MINIT_FUNCTION(zend_test) register_ZendTestClass_dnf_property(zend_test_class); zend_test_class->create_object = zend_test_class_new; zend_test_class->get_static_method = zend_test_class_static_method_get; + zend_test_class->default_object_handlers = &zend_test_class_handlers; zend_test_child_class = register_class__ZendTestChildClass(zend_test_class); memcpy(&zend_test_class_handlers, &std_object_handlers, sizeof(zend_object_handlers)); zend_test_class_handlers.get_method = zend_test_class_method_get; + zend_test_class_handlers.free_obj = zend_test_class_free_obj; + zend_test_class_handlers.offset = XtOffsetOf(zend_test_object, std); zend_test_gen_stub_flag_compatibility_test = register_class_ZendTestGenStubFlagCompatibilityTest(); diff --git a/ext/zend_test/tmp_methods.stub.php b/ext/zend_test/tmp_methods.stub.php new file mode 100644 index 0000000000000..af479c7d5422f --- /dev/null +++ b/ext/zend_test/tmp_methods.stub.php @@ -0,0 +1,11 @@ +common.num_args) { if (arginfo) { - if (func->type == ZEND_INTERNAL_FUNCTION) { - arg_name = (char *) ((zend_internal_arg_info *) &arginfo[i])->name; - } else { - arg_name = ZSTR_VAL(arginfo[i].name); - } + arg_name = ZSTR_VAL(arginfo[i].name); } smart_str_appends(s, arg_name ? arg_name : "?"); smart_str_appendc(s, '='); @@ -213,11 +209,7 @@ static void phpdbg_dump_prototype(zval *tmp) /* {{{ */ char *arg_name = NULL; if (arginfo) { - if (func->type == ZEND_INTERNAL_FUNCTION) { - arg_name = (char *)((zend_internal_arg_info *)&arginfo[j])->name; - } else { - arg_name = ZSTR_VAL(arginfo[j].name); - } + arg_name = ZSTR_VAL(arginfo[j].name); } if (!is_variadic) { From 886729454f1a551f47a7316d33f36093e71cb43d Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 15 Dec 2025 20:13:03 +0300 Subject: [PATCH 162/252] Update IR (#20710) IR commit: 3d72a7295c77743da22b36bab808ebb5f564488d --- ext/opcache/jit/ir/ir.c | 176 ++++++++++++- ext/opcache/jit/ir/ir.h | 24 +- ext/opcache/jit/ir/ir_aarch64.dasc | 376 ++++++++++++++++++++++------ ext/opcache/jit/ir/ir_builder.h | 16 +- ext/opcache/jit/ir/ir_cfg.c | 11 +- ext/opcache/jit/ir/ir_check.c | 6 + ext/opcache/jit/ir/ir_dump.c | 13 + ext/opcache/jit/ir/ir_emit.c | 33 +-- ext/opcache/jit/ir/ir_fold.h | 122 +++++---- ext/opcache/jit/ir/ir_gcm.c | 38 +-- ext/opcache/jit/ir/ir_private.h | 25 +- ext/opcache/jit/ir/ir_ra.c | 113 ++------- ext/opcache/jit/ir/ir_save.c | 16 ++ ext/opcache/jit/ir/ir_sccp.c | 64 ++--- ext/opcache/jit/ir/ir_x86.dasc | 384 ++++++++++++++++++++++++----- 15 files changed, 1032 insertions(+), 385 deletions(-) diff --git a/ext/opcache/jit/ir/ir.c b/ext/opcache/jit/ir/ir.c index 81621ce11bd36..745a66b2163ae 100644 --- a/ext/opcache/jit/ir/ir.c +++ b/ext/opcache/jit/ir/ir.c @@ -118,7 +118,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted { char buf[128]; - if (insn->op == IR_FUNC || insn->op == IR_SYM) { + if (insn->op == IR_FUNC || insn->op == IR_SYM || insn->op == IR_LABEL) { fprintf(f, "%s", ir_get_str(ctx, insn->val.name)); return; } else if (insn->op == IR_STR) { @@ -290,6 +290,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted #define ir_op_kind_prb IR_OPND_PROB #define ir_op_kind_opt IR_OPND_PROB #define ir_op_kind_pro IR_OPND_PROTO +#define ir_op_kind_lbl IR_OPND_LABEL_REF #define _IR_OP_FLAGS(name, flags, op1, op2, op3) \ IR_OP_FLAGS(ir_op_flag_ ## flags, ir_op_kind_ ## op1, ir_op_kind_ ## op2, ir_op_kind_ ## op3), @@ -689,6 +690,13 @@ ir_ref ir_const_str(ir_ctx *ctx, ir_ref str) return ir_const_ex(ctx, val, IR_ADDR, IR_OPTX(IR_STR, IR_ADDR, 0)); } +ir_ref ir_const_label(ir_ctx *ctx, ir_ref str) +{ + ir_val val; + val.u64 = str; + return ir_const_ex(ctx, val, IR_ADDR, IR_OPTX(IR_LABEL, IR_ADDR, 0)); +} + ir_ref ir_str(ir_ctx *ctx, const char *s) { size_t len; @@ -879,6 +887,17 @@ static ir_ref _ir_fold_cse(ir_ctx *ctx, uint32_t opt, ir_ref op1, ir_ref op2, ir return IR_UNUSED; } +IR_ALWAYS_INLINE ir_ref _ir_fold_cast(ir_ctx *ctx, ir_ref ref, ir_type type) +{ + if (ctx->ir_base[ref].type == type) { + return ref; + } else if (IR_IS_CONST_REF(ref) && !IR_IS_SYM_CONST(ctx->ir_base[ref].op)) { + return ir_const(ctx, ctx->ir_base[ref].val, type); + } else { + return ir_emit1(ctx, IR_OPT(IR_BITCAST, type), ref); + } +} + #define IR_FOLD(X) IR_FOLD1(X, __LINE__) #define IR_FOLD1(X, Y) IR_FOLD2(X, Y) #define IR_FOLD2(X, Y) case IR_RULE_ ## Y: @@ -1158,7 +1177,7 @@ ir_ref ir_bind(ir_ctx *ctx, ir_ref var, ir_ref def) IR_ASSERT(var < 0); if (!ir_hashtab_add(ctx->binding, def, var)) { /* Add a copy with different binding */ - def = ir_emit2(ctx, IR_OPT(IR_COPY, ctx->ir_base[def].type), def, 1); + def = ir_emit2(ctx, IR_OPT(IR_COPY, ctx->ir_base[def].type), def, IR_COPY_HARD); ir_hashtab_add(ctx->binding, def, var); } return def; @@ -1836,8 +1855,49 @@ int ir_mem_flush(void *ptr, size_t size) return 1; } #else + +#if defined(__linux__) && defined(__x86_64__) && defined(PKEY_DISABLE_WRITE) +# define HAVE_PKEY_MPROTECT 1 +#endif + +#ifdef HAVE_PKEY_MPROTECT + +#ifndef PKEY_DISABLE_EXECUTE +# define PKEY_DISABLE_EXECUTE 0 +#endif + +int pkey_mprotect(void* addr, size_t len, int prot, int pkey) __attribute__((weak)); +int pkey_alloc(unsigned int, unsigned int) __attribute__((weak)); +int pkey_free(int) __attribute__((weak)); +int pkey_set(int, unsigned) __attribute__((weak)); + +static int ir_pkey = 0; +#endif + void *ir_mem_mmap(size_t size) { +#ifdef HAVE_PKEY_MPROTECT + if (!ir_pkey && pkey_mprotect) { + int key = pkey_alloc(0, PKEY_DISABLE_WRITE); + if (key > 0) { + ir_pkey = key; + } + } + if (ir_pkey > 0) { + void *ret = mmap(NULL, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ret == MAP_FAILED) { + return NULL; + } + if (pkey_mprotect(ret, size, PROT_EXEC|PROT_READ|PROT_WRITE, ir_pkey) != 0) { +#ifdef IR_DEBUG + fprintf(stderr, "pkey_mprotect() failed\n"); +#endif + munmap(ret, size); + return NULL; + } + return ret; + } +#endif int prot_flags = PROT_EXEC; #if defined(__NetBSD__) prot_flags |= PROT_MPROTECT(PROT_READ|PROT_WRITE); @@ -1852,11 +1912,28 @@ void *ir_mem_mmap(size_t size) int ir_mem_unmap(void *ptr, size_t size) { munmap(ptr, size); +#ifdef HAVE_PKEY_MPROTECT +// if (ir_pkey > 0) { +// pkey_free(ir_pkey); +// ir_pkey = 0; +// } +#endif return 1; } int ir_mem_protect(void *ptr, size_t size) { +#ifdef HAVE_PKEY_MPROTECT + if (ir_pkey > 0) { + if (pkey_set(ir_pkey, PKEY_DISABLE_WRITE)) { +#ifdef IR_DEBUG + fprintf(stderr, "mprotect() failed\n"); +#endif + return 0; + } + return 1; + } +#endif if (mprotect(ptr, size, PROT_READ | PROT_EXEC) != 0) { #ifdef IR_DEBUG fprintf(stderr, "mprotect() failed\n"); @@ -1868,6 +1945,17 @@ int ir_mem_protect(void *ptr, size_t size) int ir_mem_unprotect(void *ptr, size_t size) { +#ifdef HAVE_PKEY_MPROTECT + if (ir_pkey > 0) { + if (pkey_set(ir_pkey, PKEY_DISABLE_EXECUTE)) { +#ifdef IR_DEBUG + fprintf(stderr, "mprotect() failed\n"); +#endif + return 0; + } + return 1; + } +#endif if (mprotect(ptr, size, PROT_READ | PROT_WRITE) != 0) { #ifdef IR_DEBUG fprintf(stderr, "mprotect() failed\n"); @@ -2070,7 +2158,26 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_load_i(ir_ctx *ctx, ir_ref ref, ir_type } } else if (insn->op == IR_RSTORE) { modified_regset |= (1 << insn->op3); - } else if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN || insn->op == IR_CALL || insn->op == IR_VSTORE) { + } else if (insn->op == IR_CALL) { + ir_insn *func = &ctx->ir_base[insn->op2]; + ir_ref func_proto; + const ir_proto_t *proto; + + if (func->op == IR_FUNC || func->op == IR_FUNC_ADDR) { + func_proto = func->proto; + } else if (func->op == IR_PROTO) { + func_proto = func->op2; + } else { + break; + } + if (!func_proto) { + break; + } + proto = (const ir_proto_t *)ir_get_str(ctx, func_proto); + if (!(proto->flags & (IR_CONST_FUNC|IR_PURE_FUNC))) { + break; + } + } else if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN || insn->op == IR_VSTORE) { return IR_UNUSED; } ref = insn->op1; @@ -2116,7 +2223,26 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_vload_i(ir_ctx *ctx, ir_ref ref, ir_typ break; } } - } else if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN || insn->op == IR_CALL || insn->op == IR_STORE) { + } else if (insn->op == IR_CALL) { + ir_insn *func = &ctx->ir_base[insn->op2]; + ir_ref func_proto; + const ir_proto_t *proto; + + if (func->op == IR_FUNC || func->op == IR_FUNC_ADDR) { + func_proto = func->proto; + } else if (func->op == IR_PROTO) { + func_proto = func->op2; + } else { + break; + } + if (!func_proto) { + break; + } + proto = (const ir_proto_t *)ir_get_str(ctx, func_proto); + if (!(proto->flags & (IR_CONST_FUNC|IR_PURE_FUNC))) { + break; + } + } else if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN || insn->op == IR_STORE) { break; } ref = insn->op1; @@ -3013,6 +3139,16 @@ void _ir_IJMP(ir_ctx *ctx, ir_ref addr) ctx->control = IR_UNUSED; } +ir_ref _ir_IGOTO(ir_ctx *ctx, ir_ref addr) +{ + ir_ref ref; + + IR_ASSERT(ctx->control); + ctx->control = ref = ir_emit2(ctx, IR_IGOTO, ctx->control, addr); + ctx->control = IR_UNUSED; + return ref; +} + ir_ref _ir_ADD_OFFSET(ir_ctx *ctx, ir_ref addr, uintptr_t offset) { if (offset) { @@ -3135,6 +3271,18 @@ void _ir_VSTORE(ir_ctx *ctx, ir_ref var, ir_ref val) ctx->control = ir_emit3(ctx, IR_VSTORE, ctx->control, var, val); } +ir_ref _ir_VLOAD_v(ir_ctx *ctx, ir_type type, ir_ref var) +{ + IR_ASSERT(ctx->control); + return ctx->control = ir_emit2(ctx, IR_OPT(IR_VLOAD_v, type), ctx->control, var); +} + +void _ir_VSTORE_v(ir_ctx *ctx, ir_ref var, ir_ref val) +{ + IR_ASSERT(ctx->control); + ctx->control = ir_emit3(ctx, IR_VSTORE_v, ctx->control, var, val); +} + ir_ref _ir_TLS(ir_ctx *ctx, ir_ref index, ir_ref offset) { IR_ASSERT(ctx->control); @@ -3193,6 +3341,18 @@ void _ir_STORE(ir_ctx *ctx, ir_ref addr, ir_ref val) ctx->control = ir_emit3(ctx, IR_STORE, ctx->control, addr, val); } +ir_ref _ir_LOAD_v(ir_ctx *ctx, ir_type type, ir_ref addr) +{ + IR_ASSERT(ctx->control); + return ctx->control = ir_emit2(ctx, IR_OPT(IR_LOAD_v, type), ctx->control, addr); +} + +void _ir_STORE_v(ir_ctx *ctx, ir_ref addr, ir_ref val) +{ + IR_ASSERT(ctx->control); + ctx->control = ir_emit3(ctx, IR_STORE_v, ctx->control, addr, val); +} + void _ir_VA_START(ir_ctx *ctx, ir_ref list) { IR_ASSERT(ctx->control); @@ -3217,11 +3377,13 @@ ir_ref _ir_VA_ARG(ir_ctx *ctx, ir_type type, ir_ref list) return ctx->control = ir_emit2(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list); } -ir_ref _ir_VA_ARG_EX(ir_ctx *ctx, ir_type type, ir_ref list, size_t size) +ir_ref _ir_VA_ARG_EX(ir_ctx *ctx, ir_type type, ir_ref list, size_t size, size_t align) { IR_ASSERT(ctx->control); - IR_ASSERT(size <= 0x7fffffff); - return ctx->control = ir_emit3(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list, (ir_ref)size); + IR_ASSERT(size <= 0x0fffffff); + IR_ASSERT(align != 0 && ((align & (align - 1)) == 0) && align <= 128); + return ctx->control = ir_emit3(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list, + (ir_ref)IR_VA_ARG_OP3(size, align)); } ir_ref _ir_BLOCK_BEGIN(ir_ctx *ctx) diff --git a/ext/opcache/jit/ir/ir.h b/ext/opcache/jit/ir/ir.h index 8fcfbffa7d6bc..a96650597055a 100644 --- a/ext/opcache/jit/ir/ir.h +++ b/ext/opcache/jit/ir/ir.h @@ -216,6 +216,7 @@ typedef enum _ir_type { * prb - branch probability 1-99 (0 - unspecified): (IF_TRUE, IF_FALSE, CASE_VAL, CASE_DEFAULT) * opt - optional number * pro - function prototype + * lbl - label used as value (a reference to constant): (BEGIN) * * The order of IR opcodes is carefully selected for efficient folding. * - foldable instruction go first @@ -322,6 +323,7 @@ typedef enum _ir_type { _(FUNC_ADDR, r0, ___, ___, ___) /* constant func ref */ \ _(FUNC, r0, ___, ___, ___) /* constant func ref */ \ _(SYM, r0, ___, ___, ___) /* constant symbol ref */ \ + _(LABEL, r0, ___, ___, ___) /* label address ref */ \ _(STR, r0, ___, ___, ___) /* constant str ref */ \ \ /* call ops */ \ @@ -334,11 +336,15 @@ typedef enum _ir_type { _(BLOCK_BEGIN, a1, src, ___, ___) /* stacksave */ \ _(BLOCK_END, a2, src, def, ___) /* stackrestore */ \ _(VLOAD, l2, src, var, ___) /* load value of local var */ \ + _(VLOAD_v, l2, src, var, ___) /* volatile variant of VLOAD */ \ _(VSTORE, s3, src, var, def) /* store value to local var */ \ + _(VSTORE_v, s3, src, var, def) /* volatile variant of VSTORE */ \ _(RLOAD, l1X2, src, num, opt) /* load value from register */ \ _(RSTORE, s2X1, src, def, num) /* store value into register */ \ _(LOAD, l2, src, ref, ___) /* load from memory */ \ + _(LOAD_v, l2, src, ref, ___) /* volatile variant of VLOAD */ \ _(STORE, s3, src, ref, def) /* store to memory */ \ + _(STORE_v, s3, src, ref, def) /* volatile variant of VSTORE */ \ _(TLS, l1X2, src, num, num) /* thread local variable */ \ _(TRAP, x1, src, ___, ___) /* DebugBreak */ \ /* memory reference ops (A, H, U, S, TMP, STR, NEW, X, V) ??? */ \ @@ -360,7 +366,7 @@ typedef enum _ir_type { /* control-flow nodes */ \ _(START, S0X1, ret, ___, ___) /* function start */ \ _(ENTRY, S1X1, src, num, ___) /* entry with a fake src edge */ \ - _(BEGIN, S1, src, ___, ___) /* block start */ \ + _(BEGIN, S1X1, src, lbl, ___) /* block start, optional &&lbl */ \ _(IF_TRUE, S1X1, src, prb, ___) /* IF TRUE proj. */ \ _(IF_FALSE, S1X1, src, prb, ___) /* IF FALSE proj. */ \ _(CASE_VAL, S2X1, src, def, prb) /* switch proj. */ \ @@ -372,8 +378,9 @@ typedef enum _ir_type { _(LOOP_END, E1, src, ___, ___) /* loop end */ \ _(IF, E2, src, def, ___) /* conditional control split */ \ _(SWITCH, E2, src, def, ___) /* multi-way control split */ \ + _(IGOTO, E2, src, def, ___) /* computed goto (internal) */ \ + _(IJMP, T2X1, src, def, ret) /* computed goto (terminating) */ \ _(RETURN, T2X1, src, def, ret) /* function return */ \ - _(IJMP, T2X1, src, def, ret) /* computed goto */ \ _(UNREACHABLE, T1X2, src, ___, ret) /* unreachable (tailcall, etc) */ \ \ /* deoptimization helper */ \ @@ -400,6 +407,13 @@ typedef enum _ir_op { #define IR_OPTX(op, type, n) ((uint32_t)(op) | ((uint32_t)(type) << IR_OPT_TYPE_SHIFT) | ((uint32_t)(n) << IR_OPT_INPUTS_SHIFT)) #define IR_OPT_TYPE(opt) (((opt) & IR_OPT_TYPE_MASK) >> IR_OPT_TYPE_SHIFT) +/* "opt" modifiers */ +#define IR_COPY_HARD (1<<0) + +#define IR_VA_ARG_SIZE(op3) (((uint32_t)(op3) >> 3)) +#define IR_VA_ARG_ALIGN(op3) (1U << ((uint32_t)(op3) & 0x7)) +#define IR_VA_ARG_OP3(s, a) (((s) << 3) | ir_ntzl(a)) + /* IR References */ typedef int32_t ir_ref; @@ -533,6 +547,9 @@ void ir_strtab_free(ir_strtab *strtab); #define IR_EXTERN (1<<5) #define IR_CONST (1<<6) +#define IR_CONST_FUNC (1<<6) +#define IR_PURE_FUNC (1<<7) + #define IR_INITIALIZED (1<<7) /* sym data flag: constant or an initialized variable */ #define IR_CONST_STRING (1<<8) /* sym data flag: constant string */ @@ -648,7 +665,6 @@ struct _ir_ctx { ir_ref vars; /* list of VARs (used by register allocator) */ }; ir_snapshot_create_t snapshot_create; - int32_t stack_frame_alignment; int32_t stack_frame_size; /* spill stack frame size (used by register allocator and code generator) */ int32_t call_stack_size; /* stack for parameter passing (used by register allocator and code generator) */ uint64_t used_preserved_regs; @@ -698,6 +714,7 @@ ir_ref ir_const_func_addr(ir_ctx *ctx, uintptr_t c, ir_ref proto); ir_ref ir_const_func(ir_ctx *ctx, ir_ref str, ir_ref proto); ir_ref ir_const_sym(ir_ctx *ctx, ir_ref str); ir_ref ir_const_str(ir_ctx *ctx, ir_ref str); +ir_ref ir_const_label(ir_ctx *ctx, ir_ref str); ir_ref ir_unique_const_addr(ir_ctx *ctx, uintptr_t c); @@ -893,6 +910,7 @@ struct _ir_loader { void*(*resolve_sym_name) (ir_loader *loader, const char *name, uint32_t flags); bool (*has_sym) (ir_loader *loader, const char *name); bool (*add_sym) (ir_loader *loader, const char *name, void *addr); + bool (*add_label) (ir_loader *loader, const char *name, void *addr); }; void ir_loader_init(void); diff --git a/ext/opcache/jit/ir/ir_aarch64.dasc b/ext/opcache/jit/ir/ir_aarch64.dasc index 12c3694d469f0..b553243309f54 100644 --- a/ext/opcache/jit/ir/ir_aarch64.dasc +++ b/ext/opcache/jit/ir/ir_aarch64.dasc @@ -218,6 +218,7 @@ typedef struct _ir_backend_data { dasm_State *dasm_state; ir_bitset emit_constants; int rodata_label, jmp_table_label; + bool resolved_label_syms; } ir_backend_data; #define IR_GP_REG_NAME(code, name64, name32) \ @@ -315,6 +316,7 @@ const char *ir_reg_name(int8_t reg, ir_type type) _(RETURN_VOID) \ _(RETURN_INT) \ _(RETURN_FP) \ + _(IGOTO_DUP) \ #define IR_RULE_ENUM(name) IR_ ## name, @@ -385,7 +387,7 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co n++; break; } - } else if (ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA) { + } else if (!IR_IS_CONST_REF(insn->op2) && ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA) { constraints->tmp_regs[n] = IR_TMP_REG(2, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); n++; } @@ -478,10 +480,16 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co if (IR_IS_CONST_REF(insn->op1)) { constraints->tmp_regs[n] = IR_TMP_REG(1, insn->type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); n++; + } else if (ir_rule(ctx, insn->op1) == IR_STATIC_ALLOCA) { + constraints->tmp_regs[n] = IR_TMP_REG(1, insn->type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); + n++; } if (IR_IS_CONST_REF(insn->op2) && insn->op1 != insn->op2) { constraints->tmp_regs[n] = IR_TMP_REG(2, insn->type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); n++; + } else if (!IR_IS_CONST_REF(insn->op2) && ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA) { + constraints->tmp_regs[n] = IR_TMP_REG(2, insn->type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); + n++; } break; case IR_CMP_INT: @@ -520,6 +528,7 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co } break; case IR_VSTORE: + case IR_VSTORE_v: insn = &ctx->ir_base[ref]; if (IR_IS_CONST_REF(insn->op3)) { insn = &ctx->ir_base[insn->op3]; @@ -596,6 +605,19 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co } flags = IR_USE_SHOULD_BE_IN_REG | IR_OP2_MUST_BE_IN_REG | IR_OP3_SHOULD_BE_IN_REG; break; + case IR_IGOTO: + insn = &ctx->ir_base[ref]; + if (ctx->ir_base[insn->op1].op == IR_MERGE || ctx->ir_base[insn->op1].op == IR_LOOP_BEGIN) { + ir_insn *merge = &ctx->ir_base[insn->op1]; + ir_ref *p, n = merge->inputs_count; + + for (p = merge->ops + 1; n > 0; p++, n--) { + ir_ref input = *p; + IR_ASSERT(ctx->ir_base[input].op == IR_END || ctx->ir_base[input].op == IR_LOOP_END); + ctx->rules[input] = IR_IGOTO_DUP; + } + } + return insn->op; case IR_COND: insn = &ctx->ir_base[ref]; n = 0; @@ -665,7 +687,7 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co } break; case IR_VA_ARG: - flags = IR_USE_MUST_BE_IN_REG | IR_OP2_MUST_BE_IN_REG; + flags = IR_USE_MUST_BE_IN_REG | IR_OP2_MUST_BE_IN_REG | IR_DEF_CONFLICTS_WITH_INPUT_REGS; constraints->tmp_regs[0] = IR_TMP_REG(3, IR_ADDR, IR_LOAD_SUB_REF, IR_SAVE_SUB_REF); n = 1; insn = &ctx->ir_base[ref]; @@ -714,7 +736,8 @@ static void ir_match_fuse_addr(ir_ctx *ctx, ir_ref addr_ref, ir_type type) do { ir_insn *insn = &ctx->ir_base[*p]; - if (insn->op != IR_LOAD && (insn->op != IR_STORE || insn->op3 == addr_ref)) { + if (insn->op != IR_LOAD && insn->op != IR_LOAD_v + && ((insn->op != IR_STORE && insn->op != IR_STORE_v) || insn->op3 == addr_ref)) { return; } p++; @@ -961,7 +984,7 @@ binop_fp: ctx->flags2 |= IR_HAS_CALLS; return IR_CALL; case IR_VAR: - return IR_SKIPPED | IR_VAR; + return IR_STATIC_ALLOCA; case IR_PARAM: return ctx->use_lists[ref].count > 0 ? IR_PARAM : IR_SKIPPED | IR_PARAM; case IR_ALLOCA: @@ -978,6 +1001,7 @@ binop_fp: } return IR_ALLOCA; case IR_LOAD: + case IR_LOAD_v: ir_match_fuse_addr(ctx, insn->op2, insn->type); if (IR_IS_TYPE_INT(insn->type)) { return IR_LOAD_INT; @@ -986,6 +1010,7 @@ binop_fp: } break; case IR_STORE: + case IR_STORE_v: ir_match_fuse_addr(ctx, insn->op2, ctx->ir_base[insn->op3].type); if (IR_IS_TYPE_INT(ctx->ir_base[insn->op3].type)) { return IR_STORE_INT; @@ -1364,7 +1389,7 @@ static void ir_emit_load_imm_fp(ir_ctx *ctx, ir_type type, ir_reg reg, ir_ref sr } else if (type == IR_DOUBLE && insn->val.u64 == 0) { | fmov Rd(reg-IR_REG_FP_FIRST), xzr } else { - label = ir_const_label(ctx, src); + label = ir_get_const_label(ctx, src); if (type == IR_DOUBLE) { | ldr Rd(reg-IR_REG_FP_FIRST), =>label } else { @@ -1441,10 +1466,41 @@ static void ir_load_local_addr(ir_ctx *ctx, ir_reg reg, ir_ref src) | add Rx(reg), Rx(base), #offset } else { ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); - | add Rx(reg), sp, Rx(IR_REG_INT_TMP) + | add Rx(reg), Rx(base), Rx(IR_REG_INT_TMP) } } +static void ir_resolve_label_syms(ir_ctx *ctx) +{ + uint32_t b; + ir_block *bb; + + for (b = 1, bb = &ctx->cfg_blocks[b]; b <= ctx->cfg_blocks_count; bb++, b++) { + ir_insn *insn = &ctx->ir_base[bb->start]; + + if (insn->op == IR_BEGIN && insn->op2) { + IR_ASSERT(ctx->ir_base[insn->op2].op == IR_LABEL); + ctx->ir_base[insn->op2].val.u32_hi = b; + } + } +} + +static void ir_emit_load_label_addr(ir_ctx *ctx, ir_reg reg, ir_insn *label) +{ + ir_backend_data *data = ctx->data; + dasm_State **Dst = &data->dasm_state; + + if (!data->resolved_label_syms) { + data->resolved_label_syms = 1; + ir_resolve_label_syms(ctx); + } + + IR_ASSERT(label->op == IR_LABEL); + int b = label->val.u32_hi; + + b = ir_skip_empty_target_blocks(ctx, b); + | adr Rx(reg), =>b +} static void ir_emit_load(ir_ctx *ctx, ir_type type, ir_reg reg, ir_ref src) { @@ -1459,9 +1515,11 @@ static void ir_emit_load(ir_ctx *ctx, ir_type type, ir_reg reg, ir_ref src) } else if (insn->op == IR_STR) { ir_backend_data *data = ctx->data; dasm_State **Dst = &data->dasm_state; - int label = ir_const_label(ctx, src); + int label = ir_get_const_label(ctx, src); | adr Rx(reg), =>label + } else if (insn->op == IR_LABEL) { + ir_emit_load_label_addr(ctx, reg, insn); } else { ir_emit_load_imm_int(ctx, type, reg, insn->val.i64); } @@ -1697,6 +1755,7 @@ static void ir_emit_prologue(ir_ctx *ctx) | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), #offset] } else { ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + offset -= sizeof(void*); | str Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] | sub Rx(IR_REG_INT_TMP), Rx(IR_REG_INT_TMP), #8 | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] @@ -1795,7 +1854,12 @@ static void ir_emit_prologue(ir_ctx *ctx) offset += 16 * ctx->fp_reg_params; for (i = ctx->fp_reg_params; i < IR_REG_FP_ARGS; i++) { // TODO: Rd->Rq stur->str ??? - | str Rd(fp_reg_params[i]-IR_REG_FP_FIRST), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, 8)) { + | str Rd(fp_reg_params[i]-IR_REG_FP_FIRST), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rd(fp_reg_params[i]-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] + } offset += 16; } } @@ -1828,26 +1892,44 @@ static void ir_emit_epilogue(ir_ctx *ctx) offset -= sizeof(void*) * 2; if (aarch64_may_encode_imm7_addr_offset(offset, 8)) { | ldp Rx(prev), Rx(i), [Rx(fp), #offset] - } else { - IR_ASSERT(aarch64_may_encode_addr_offset(offset, 8)); + } else if (aarch64_may_encode_addr_offset(offset + 8, 8)) { | ldr Rx(prev), [Rx(fp), #offset] | ldr Rx(i), [Rx(fp), #(offset+8)] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | ldr Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + | add Rx(IR_REG_INT_TMP), Rx(IR_REG_INT_TMP), #8 + | ldr Rx(i), [Rx(fp), Rx(IR_REG_INT_TMP)] } prev = IR_REG_NONE; } else { if (prev < IR_REG_FP_FIRST) { offset -= sizeof(void*); - | ldr Rx(prev), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, 8)) { + | ldr Rx(prev), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | ldr Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + } offset -= sizeof(void*); - | ldr Rd(i-IR_REG_FP_FIRST), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, 8)) { + | ldr Rd(i-IR_REG_FP_FIRST), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | ldr Rd(i-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } else { offset -= sizeof(void*) * 2; if (aarch64_may_encode_imm7_addr_offset(offset, 8)) { | ldp Rd(prev-IR_REG_FP_FIRST), Rd(i-IR_REG_FP_FIRST), [Rx(fp), #offset] - } else { - IR_ASSERT(aarch64_may_encode_addr_offset(offset, 8)); + } else if (aarch64_may_encode_addr_offset(offset + 8, 8)) { | ldr Rd(prev-IR_REG_FP_FIRST), [Rx(fp), #offset] | ldr Rd(i-IR_REG_FP_FIRST), [Rx(fp), #(offset+8)] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | ldr Rx(prev-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] + | add Rx(IR_REG_INT_TMP), Rx(IR_REG_INT_TMP), #8 + | ldr Rx(i-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] } } prev = IR_REG_NONE; @@ -1857,10 +1939,20 @@ static void ir_emit_epilogue(ir_ctx *ctx) if (prev != IR_REG_NONE) { if (prev < IR_REG_FP_FIRST) { offset -= sizeof(void*); - | ldr Rx(prev), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, 8)) { + | ldr Rx(prev), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | ldr Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } else { offset -= sizeof(void*); - | ldr Rd(prev-IR_REG_FP_FIRST), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, 8)) { + | ldr Rd(prev-IR_REG_FP_FIRST), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | ldr Rd(prev-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } } } @@ -1909,6 +2001,9 @@ static void ir_emit_binop_int(ir_ctx *ctx, ir_ref def, ir_insn *insn) op1_reg = IR_REG_NUM(op1_reg); ir_emit_load(ctx, type, op1_reg, op1); } + if (op2_reg == IR_REG_NONE && op1 == op2) { + op2_reg = op1_reg; + } if (op2_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op2_reg)) { op2_reg = IR_REG_NUM(op2_reg); @@ -3415,25 +3510,52 @@ static void ir_emit_sext(ir_ctx *ctx, ir_ref def, ir_insn *insn) int32_t offset = ir_ref_spill_slot_offset(ctx, insn->op1, &fp); if (ir_type_size[src_type] == 1) { - if (ir_type_size[dst_type] == 2) { - | ldrsb Rw(def_reg), [Rx(fp), #offset] - } else if (ir_type_size[dst_type] == 4) { - | ldrsb Rw(def_reg), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, ir_type_size[src_type])) { + if (ir_type_size[dst_type] == 2) { + | ldrsb Rw(def_reg), [Rx(fp), #offset] + } else if (ir_type_size[dst_type] == 4) { + | ldrsb Rw(def_reg), [Rx(fp), #offset] + } else { + IR_ASSERT(ir_type_size[dst_type] == 8); + | ldrsb Rx(def_reg), [Rx(fp), #offset] + } } else { - IR_ASSERT(ir_type_size[dst_type] == 8); - | ldrsb Rx(def_reg), [Rx(fp), #offset] + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + if (ir_type_size[dst_type] == 2) { + | ldrsb Rw(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } else if (ir_type_size[dst_type] == 4) { + | ldrsb Rw(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } else { + IR_ASSERT(ir_type_size[dst_type] == 8); + | ldrsb Rx(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } } else if (ir_type_size[src_type] == 2) { - if (ir_type_size[dst_type] == 4) { - | ldrsh Rw(def_reg), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, ir_type_size[src_type])) { + if (ir_type_size[dst_type] == 4) { + | ldrsh Rw(def_reg), [Rx(fp), #offset] + } else { + IR_ASSERT(ir_type_size[dst_type] == 8); + | ldrsh Rx(def_reg), [Rx(fp), #offset] + } } else { - IR_ASSERT(ir_type_size[dst_type] == 8); - | ldrsh Rx(def_reg), [Rx(fp), #offset] + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + if (ir_type_size[dst_type] == 4) { + | ldrsh Rw(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } else { + IR_ASSERT(ir_type_size[dst_type] == 8); + | ldrsh Rx(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } } else { IR_ASSERT(ir_type_size[src_type] == 4); IR_ASSERT(ir_type_size[dst_type] == 8); - | ldrsw Rx(def_reg), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, ir_type_size[src_type])) { + | ldrsw Rx(def_reg), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | ldrsw Rx(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } } if (IR_REG_SPILLED(ctx->regs[def][0])) { @@ -3473,14 +3595,27 @@ static void ir_emit_zext(ir_ctx *ctx, ir_ref def, ir_insn *insn) ir_reg fp; int32_t offset = ir_ref_spill_slot_offset(ctx, insn->op1, &fp); - if (ir_type_size[src_type] == 1) { - | ldrb Rw(def_reg), [Rx(fp), #offset] - } else if (ir_type_size[src_type] == 2) { - | ldrh Rw(def_reg), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, ir_type_size[src_type])) { + if (ir_type_size[src_type] == 1) { + | ldrb Rw(def_reg), [Rx(fp), #offset] + } else if (ir_type_size[src_type] == 2) { + | ldrh Rw(def_reg), [Rx(fp), #offset] + } else { + IR_ASSERT(ir_type_size[src_type] == 4); + IR_ASSERT(ir_type_size[dst_type] == 8); + | ldr Rw(def_reg), [Rx(fp), #offset] + } } else { - IR_ASSERT(ir_type_size[src_type] == 4); - IR_ASSERT(ir_type_size[dst_type] == 8); - | ldr Rw(def_reg), [Rx(fp), #offset] + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + if (ir_type_size[src_type] == 1) { + | ldrb Rw(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } else if (ir_type_size[src_type] == 2) { + | ldrh Rw(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } else { + IR_ASSERT(ir_type_size[src_type] == 4); + IR_ASSERT(ir_type_size[dst_type] == 8); + | ldr Rw(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } } if (IR_REG_SPILLED(ctx->regs[def][0])) { @@ -3579,11 +3714,21 @@ static void ir_emit_bitcast(ir_ctx *ctx, ir_ref def, ir_insn *insn) ir_reg fp; int32_t offset = ir_ref_spill_slot_offset(ctx, insn->op1, &fp); - if (src_type == IR_DOUBLE) { - | ldr Rx(def_reg), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, ir_type_size[src_type])) { + if (src_type == IR_DOUBLE) { + | ldr Rx(def_reg), [Rx(fp), #offset] + } else { + IR_ASSERT(src_type == IR_FLOAT); + | ldr Rw(def_reg), [Rx(fp), #offset] + } } else { - IR_ASSERT(src_type == IR_FLOAT); - | ldr Rw(def_reg), [Rx(fp), #offset] + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + if (src_type == IR_DOUBLE) { + | ldr Rx(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } else { + IR_ASSERT(src_type == IR_FLOAT); + | ldr Rw(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } } } else if (IR_IS_TYPE_FP(dst_type)) { @@ -3605,12 +3750,22 @@ static void ir_emit_bitcast(ir_ctx *ctx, ir_ref def, ir_insn *insn) ir_reg fp; int32_t offset = ir_ref_spill_slot_offset(ctx, insn->op1, &fp); - if (dst_type == IR_DOUBLE) { - | ldr Rd(def_reg), [Rx(fp), #offset] - } else { - IR_ASSERT(src_type == IR_FLOAT); - | ldr Rs(def_reg), [Rx(fp), #offset] - } + if (aarch64_may_encode_addr_offset(offset, ir_type_size[src_type])) { + if (dst_type == IR_DOUBLE) { + | ldr Rd(def_reg), [Rx(fp), #offset] + } else { + IR_ASSERT(dst_type == IR_FLOAT); + | ldr Rs(def_reg), [Rx(fp), #offset] + } + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + if (dst_type == IR_DOUBLE) { + | ldr Rd(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } else { + IR_ASSERT(dst_type == IR_FLOAT); + | ldr Rs(def_reg), [Rx(fp), Rx(IR_REG_INT_TMP)] + } + } } } if (IR_REG_SPILLED(ctx->regs[def][0])) { @@ -3833,7 +3988,12 @@ static void ir_emit_vaddr(ir_ctx *ctx, ir_ref def, ir_insn *insn) IR_ASSERT(def_reg != IR_REG_NONE); offset = ir_var_spill_slot(ctx, insn->op1, &fp); - | add Rx(def_reg), Rx(fp), #offset + if (aarch64_may_encode_imm12(offset)) { + | add Rx(def_reg), Rx(fp), #offset + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | add Rx(def_reg), Rx(fp), Rx(IR_REG_INT_TMP) + } if (IR_REG_SPILLED(ctx->regs[def][0])) { ir_emit_store(ctx, type, def, def_reg); } @@ -4221,7 +4381,12 @@ static void ir_emit_afree(ir_ctx *ctx, ir_ref def, ir_insn *insn) /* Stack must be 16 byte aligned */ size = IR_ALIGNED_SIZE(size, 16); - | add sp, sp, #size + if (aarch64_may_encode_imm12(size)) { + | add sp, sp, #size + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, size); + | add sp, sp, Rx(IR_REG_INT_TMP) + } if (!(ctx->flags & IR_USE_FRAME_POINTER)) { ctx->call_stack_size -= size; } @@ -4283,8 +4448,11 @@ static void ir_emit_frame_addr(ir_ctx *ctx, ir_ref def) if (ctx->flags & IR_USE_FRAME_POINTER) { | mov Rx(def_reg), Rx(IR_REG_X29) - } else { + } else if (aarch64_may_encode_imm12(ctx->stack_frame_size + ctx->call_stack_size)) { | add Rx(def_reg), Rx(IR_REG_X31), #(ctx->stack_frame_size + ctx->call_stack_size) + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, ctx->stack_frame_size + ctx->call_stack_size); + | add Rx(def_reg), Rx(IR_REG_X31), Rx(IR_REG_INT_TMP) } if (IR_REG_SPILLED(ctx->regs[def][0])) { ir_emit_store(ctx, IR_ADDR, def, def_reg); @@ -4377,7 +4545,7 @@ static void ir_emit_va_start(ir_ctx *ctx, ir_ref def, ir_insn *insn) if ((ctx->flags2 & (IR_HAS_VA_ARG_FP|IR_HAS_VA_COPY)) && ctx->fp_reg_params < IR_REG_FP_ARGS) { reg_save_area_offset += 16 * IR_REG_FP_ARGS; /* Set va_list.vr_top */ - if (overflow_arg_area_offset != reg_save_area_offset) { + if (overflow_arg_area_offset != reg_save_area_offset || ctx->gp_reg_params < IR_REG_INT_ARGS) { | add Rx(tmp_reg), Rx(fp), #reg_save_area_offset } | str Rx(tmp_reg), [Rx(op2_reg), #(offset+offsetof(ir_va_list, vr_top))] @@ -5246,6 +5414,19 @@ static void ir_emit_ijmp(ir_ctx *ctx, ir_ref def, ir_insn *insn) } | br Rx(op2_reg) } else if (IR_IS_CONST_REF(insn->op2)) { + if (ctx->ir_base[insn->op2].op == IR_LABEL) { + if (!data->resolved_label_syms) { + data->resolved_label_syms = 1; + ir_resolve_label_syms(ctx); + } + + uint32_t target = ctx->ir_base[insn->op2].val.u32_hi; + target = ir_skip_empty_target_blocks(ctx, target); + + | b =>target + return; + } + void *addr = ir_jmp_addr(ctx, insn, &ctx->ir_base[insn->op2]); if (aarch64_may_use_b(ctx->code_buffer, addr)) { @@ -5636,6 +5817,7 @@ static void ir_emit_param_move(ir_ctx *ctx, uint8_t type, ir_reg from_reg, ir_re { ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; + offset = IR_SPILL_POS_TO_OFFSET(offset); IR_ASSERT(from_reg != IR_REG_NONE || to_reg != IR_REG_NONE); if (IR_IS_TYPE_INT(type)) { @@ -5676,13 +5858,8 @@ static void ir_emit_load_params(ir_ctx *ctx) const int8_t *int_reg_params = _ir_int_reg_params; const int8_t *fp_reg_params = _ir_fp_reg_params; int32_t stack_offset = 0; + int32_t stack_start = ctx->stack_frame_size; - if (ctx->flags & IR_USE_FRAME_POINTER) { - /* skip old frame pointer and return address */ - stack_offset = sizeof(void*) * 2 + ctx->stack_frame_size + ctx->call_stack_size; - } else { - stack_offset = ctx->stack_frame_size + ctx->call_stack_size; - } n = use_list->count; for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) { use = *p; @@ -5706,12 +5883,9 @@ static void ir_emit_load_params(ir_ctx *ctx) if (ctx->vregs[use]) { dst_reg = IR_REG_NUM(ctx->regs[use][0]); IR_ASSERT(src_reg != IR_REG_NONE || dst_reg != IR_REG_NONE || - stack_offset == ctx->live_intervals[ctx->vregs[use]]->stack_spill_pos + - ((ctx->flags & IR_USE_FRAME_POINTER) ? - -(ctx->stack_frame_size - ctx->stack_frame_alignment) : - ctx->call_stack_size)); + stack_start + stack_offset == ctx->live_intervals[ctx->vregs[use]]->stack_spill_pos); if (src_reg != dst_reg) { - ir_emit_param_move(ctx, insn->type, src_reg, dst_reg, use, stack_offset); + ir_emit_param_move(ctx, insn->type, src_reg, dst_reg, use, stack_start + stack_offset); } if (dst_reg != IR_REG_NONE && IR_REG_SPILLED(ctx->regs[use][0])) { ir_emit_store(ctx, insn->type, use, dst_reg); @@ -5785,14 +5959,8 @@ static void ir_fix_param_spills(ir_ctx *ctx) const int8_t *int_reg_params = _ir_int_reg_params; const int8_t *fp_reg_params = _ir_fp_reg_params; int32_t stack_offset = 0; - int32_t param_stack_size = 0; + int32_t stack_start = ctx->stack_frame_size; - if (ctx->flags & IR_USE_FRAME_POINTER) { - /* skip old frame pointer and return address */ - stack_offset = sizeof(void*) * 2 + (ctx->stack_frame_size - ctx->stack_frame_alignment); - } else { - stack_offset = ctx->stack_frame_size; - } n = use_list->count; for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) { use = *p; @@ -5819,15 +5987,13 @@ static void ir_fix_param_spills(ir_ctx *ctx) if ((ival->flags & IR_LIVE_INTERVAL_MEM_PARAM) && ival->stack_spill_pos == -1 && (ival->next || ival->reg == IR_REG_NONE)) { - ival->stack_spill_pos = stack_offset; + ival->stack_spill_pos = stack_start + stack_offset; } } if (sizeof(void*) == 8) { stack_offset += sizeof(void*); - param_stack_size += sizeof(void*); } else { stack_offset += IR_MAX(sizeof(void*), ir_type_size[insn->type]); - param_stack_size += IR_MAX(sizeof(void*), ir_type_size[insn->type]); } } } @@ -5835,7 +6001,7 @@ static void ir_fix_param_spills(ir_ctx *ctx) ctx->gp_reg_params = IR_MIN(int_param_num, int_reg_params_count); ctx->fp_reg_params = IR_MIN(fp_param_num, fp_reg_params_count); - ctx->param_stack_size = param_stack_size; + ctx->param_stack_size = stack_offset; } static void ir_allocate_unique_spill_slots(ir_ctx *ctx) @@ -5876,6 +6042,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) case IR_MERGE: case IR_LOOP_BEGIN: case IR_LOOP_END: + case IR_IGOTO_DUP: break; default: def_flags = ir_get_target_constraints(ctx, i, &constraints); @@ -5892,7 +6059,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) IR_REGSET_EXCL(available, reg); ctx->regs[i][0] = reg | IR_REG_SPILL_STORE; } else if (def_flags & IR_USE_MUST_BE_IN_REG) { - if (insn->op == IR_VLOAD + if ((insn->op == IR_VLOAD || insn->op == IR_VLOAD_v) && ctx->live_intervals[ctx->vregs[i]] && ctx->live_intervals[ctx->vregs[i]]->stack_spill_pos != -1 && ir_is_same_mem_var(ctx, i, ctx->ir_base[insn->op2].op3)) { @@ -5932,7 +6099,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) { use = *p; use_insn = &ctx->ir_base[use]; - if (use_insn->op == IR_VLOAD) { + if (use_insn->op == IR_VLOAD || use_insn->op == IR_VLOAD_v) { if (ctx->vregs[use] && !ctx->live_intervals[ctx->vregs[use]]) { ir_live_interval *ival = ir_arena_alloc(&ctx->arena, sizeof(ir_live_interval)); @@ -5943,7 +6110,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) ival->vreg = ctx->vregs[use]; ival->stack_spill_pos = stack_spill_pos; } - } else if (use_insn->op == IR_VSTORE) { + } else if (use_insn->op == IR_VSTORE || use_insn->op == IR_STORE_v) { if (!IR_IS_CONST_REF(use_insn->op3) && ctx->vregs[use_insn->op3] && !ctx->live_intervals[ctx->vregs[use_insn->op3]]) { @@ -6080,25 +6247,21 @@ void ir_fix_stack_frame(ir_ctx *ctx) ctx->stack_frame_size = IR_ALIGNED_SIZE(ctx->stack_frame_size, sizeof(void*)); ctx->stack_frame_size += additional_size; - ctx->stack_frame_alignment = 0; ctx->call_stack_size = 0; if (!(ctx->flags & IR_FUNCTION)) { while (IR_ALIGNED_SIZE(ctx->stack_frame_size, 16) != ctx->stack_frame_size) { ctx->stack_frame_size += sizeof(void*); - ctx->stack_frame_alignment += sizeof(void*); } } else { /* Stack must be 16 byte aligned */ if (!(ctx->flags & IR_FUNCTION)) { while (IR_ALIGNED_SIZE(ctx->stack_frame_size, 16) != ctx->stack_frame_size) { ctx->stack_frame_size += sizeof(void*); - ctx->stack_frame_alignment += sizeof(void*); } } else if (ctx->flags & IR_USE_FRAME_POINTER) { while (IR_ALIGNED_SIZE(ctx->stack_frame_size + sizeof(void*) * 2, 16) != ctx->stack_frame_size + sizeof(void*) * 2) { ctx->stack_frame_size += sizeof(void*); - ctx->stack_frame_alignment += sizeof(void*); } } else { if (!(ctx->flags & IR_NO_STACK_COMBINE)) { @@ -6107,7 +6270,6 @@ void ir_fix_stack_frame(ir_ctx *ctx) while (IR_ALIGNED_SIZE(ctx->stack_frame_size + ctx->call_stack_size, 16) != ctx->stack_frame_size + ctx->call_stack_size) { ctx->stack_frame_size += sizeof(void*); - ctx->stack_frame_alignment += sizeof(void*); } } } @@ -6143,6 +6305,8 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) int ret; void *entry; size_t size; + ir_ref igoto_dup_ref = IR_UNUSED; + uint32_t igoto_dup_block = 0; data.ra_data.unused_slot_4 = 0; data.ra_data.unused_slot_2 = 0; @@ -6150,11 +6314,11 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) data.ra_data.handled = NULL; data.rodata_label = 0; data.jmp_table_label = 0; + data.resolved_label_syms = 0; ctx->data = &data; if (!ctx->live_intervals) { ctx->stack_frame_size = 0; - ctx->stack_frame_alignment = 0; ctx->call_stack_size = 0; ctx->used_preserved_regs = 0; ir_allocate_unique_spill_slots(ctx); @@ -6176,7 +6340,6 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) } ctx->stack_frame_size = ctx->fixed_stack_frame_size; ctx->call_stack_size = ctx->fixed_call_stack_size; - ctx->stack_frame_alignment = 0; } Dst = &data.dasm_state; @@ -6386,6 +6549,35 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) case IR_TAILCALL: ir_emit_tailcall(ctx, i, insn); break; + case IR_IGOTO_DUP: + if (bb->flags & IR_BB_DESSA_MOVES) { + ir_emit_dessa_moves(ctx, b, bb); + } + IR_ASSERT(!igoto_dup_ref && !igoto_dup_block); + igoto_dup_ref = i; + igoto_dup_block = b; + b = ctx->cfg_edges[bb->successors]; + bb = &ctx->cfg_blocks[b]; + i = bb->start; + insn = &ctx->ir_base[i]; + rule = &ctx->rules[i]; + break; + case IR_IGOTO: + if ((ctx->ir_base[insn->op1].op == IR_MERGE || ctx->ir_base[insn->op1].op == IR_LOOP_BEGIN) + && (ctx->rules[ctx->ir_base[insn->op1].op1] & IR_RULE_MASK) == IR_IGOTO_DUP + && igoto_dup_ref) { + ir_emit_ijmp(ctx, i, insn); + b = igoto_dup_block; + bb = &ctx->cfg_blocks[b]; + i = igoto_dup_ref; + insn = &ctx->ir_base[i]; + rule = &ctx->rules[i]; + igoto_dup_block= 0; + igoto_dup_ref = 0; + break; + } + IR_ASSERT(!igoto_dup_ref && !igoto_dup_block); + IR_FALLTHROUGH; case IR_IJMP: ir_emit_ijmp(ctx, i, insn); break; @@ -6396,9 +6588,11 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) ir_emit_vaddr(ctx, i, insn); break; case IR_VLOAD: + case IR_VLOAD_v: ir_emit_vload(ctx, i, insn); break; case IR_VSTORE: + case IR_VSTORE_v: ir_emit_vstore(ctx, i, insn); break; case IR_RLOAD: @@ -6645,6 +6839,28 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) } while (i != 0); } + if ((ctx->flags2 & IR_HAS_BLOCK_ADDR) && ctx->loader && ctx->loader->add_label) { + for (b = 1, bb = &ctx->cfg_blocks[b]; b <= ctx->cfg_blocks_count; bb++, b++) { + ir_insn *insn = &ctx->ir_base[bb->start]; + + if (insn->op == IR_BEGIN && insn->op2) { + IR_ASSERT(ctx->ir_base[insn->op2].op == IR_LABEL); + ctx->ir_base[insn->op2].val.u32_hi = 0; + ctx->loader->add_label(ctx->loader, ir_get_str(ctx, ctx->ir_base[insn->op2].val.str), + (char*)entry + dasm_getpclabel(&data.dasm_state, ir_skip_empty_target_blocks(ctx, b))); + } + } + } else if (data.resolved_label_syms) { + for (b = 1, bb = &ctx->cfg_blocks[b]; b <= ctx->cfg_blocks_count; bb++, b++) { + ir_insn *insn = &ctx->ir_base[bb->start]; + + if (insn->op == IR_BEGIN && insn->op2) { + IR_ASSERT(ctx->ir_base[insn->op2].op == IR_LABEL); + ctx->ir_base[insn->op2].val.u32_hi = 0; + } + } + } + dasm_free(&data.dasm_state); if (ctx->code_buffer) { diff --git a/ext/opcache/jit/ir/ir_builder.h b/ext/opcache/jit/ir/ir_builder.h index c1dcffdbaa084..03add75906553 100644 --- a/ext/opcache/jit/ir/ir_builder.h +++ b/ext/opcache/jit/ir/ir_builder.h @@ -490,7 +490,7 @@ extern "C" { #define ir_ADD_OFFSET(_addr, _offset) _ir_ADD_OFFSET(_ir_CTX, (_addr), (_offset)) /* Unfoldable variant of COPY */ -#define ir_HARD_COPY(_type, _op1) ir_emit2(_ir_CTX, IR_OPT(IR_COPY, (_type)), (_op1), 1) +#define ir_HARD_COPY(_type, _op1) ir_emit2(_ir_CTX, IR_OPT(IR_COPY, (_type)), (_op1), IR_COPY_HARD) #define ir_HARD_COPY_B(_op1) ir_HARD_COPY(IR_BOOL, _op1) #define ir_HARD_COPY_U8(_op1) ir_HARD_COPY(IR_U8, _op1) #define ir_HARD_COPY_U16(_op1) ir_HARD_COPY(IR_U16, _op1) @@ -544,6 +544,8 @@ extern "C" { #define ir_VLOAD_D(_var) _ir_VLOAD(_ir_CTX, IR_DOUBLE, (_var)) #define ir_VLOAD_F(_var) _ir_VLOAD(_ir_CTX, IR_FLOAT, (_var)) #define ir_VSTORE(_var, _val) _ir_VSTORE(_ir_CTX, (_var), (_val)) +#define ir_VLOAD_v(_type, _var) _ir_VLOAD_v(_ir_CTX, (_type), (_var)) +#define ir_VSTORE_v(_var, _val) _ir_VSTORE_v(_ir_CTX, (_var), (_val)) #define ir_RLOAD(_type, _reg) _ir_RLOAD(_ir_CTX, (_type), (_reg)) #define ir_RLOAD_B(_reg) _ir_RLOAD(_ir_CTX, IR_BOOL, (_reg)) #define ir_RLOAD_U8(_reg) _ir_RLOAD(_ir_CTX, IR_U8, (_reg)) @@ -574,6 +576,8 @@ extern "C" { #define ir_LOAD_D(_addr) _ir_LOAD(_ir_CTX, IR_DOUBLE, (_addr)) #define ir_LOAD_F(_addr) _ir_LOAD(_ir_CTX, IR_FLOAT, (_addr)) #define ir_STORE(_addr, _val) _ir_STORE(_ir_CTX, (_addr), (_val)) +#define ir_LOAD_v(_type, _addr) _ir_LOAD_v(_ir_CTX, (_type), (_addr)) +#define ir_STORE_v(_addr, _val) _ir_STORE_v(_ir_CTX, (_addr), (_val)) #define ir_TLS(_index, _offset) _ir_TLS(_ir_CTX, (_index), (_offset)) #define ir_TRAP() do {_ir_CTX->control = ir_emit1(_ir_CTX, IR_TRAP, _ir_CTX->control);} while (0) @@ -586,7 +590,7 @@ extern "C" { #define ir_VA_END(_list) _ir_VA_END(_ir_CTX, _list) #define ir_VA_COPY(_dst, _src) _ir_VA_COPY(_ir_CTX, _dst, _src) #define ir_VA_ARG(_list, _type) _ir_VA_ARG(_ir_CTX, _type, _list) -#define ir_VA_ARG_EX(_list, _type, size) _ir_VA_ARG_EX(_ir_CTX, _type, _list, size) +#define ir_VA_ARG_EX(_list, _type, s, a) _ir_VA_ARG_EX(_ir_CTX, _type, _list, s, a) #define ir_START() _ir_START(_ir_CTX) #define ir_ENTRY(_src, _num) _ir_ENTRY(_ir_CTX, (_src), (_num)) @@ -607,6 +611,7 @@ extern "C" { #define ir_CASE_RANGE(_switch, _v1, _v2) _ir_CASE_RANGE(_ir_CTX, (_switch), (_v1), (_v2)) #define ir_CASE_DEFAULT(_switch) _ir_CASE_DEFAULT(_ir_CTX, (_switch)) #define ir_RETURN(_val) _ir_RETURN(_ir_CTX, (_val)) +#define ir_IGOTO(_addr) _ir_IGOTO(_ir_CTX, (_addr)) #define ir_IJMP(_addr) _ir_IJMP(_ir_CTX, (_addr)) #define ir_UNREACHABLE() _ir_UNREACHABLE(_ir_CTX) @@ -654,15 +659,19 @@ ir_ref _ir_ALLOCA(ir_ctx *ctx, ir_ref size); void _ir_AFREE(ir_ctx *ctx, ir_ref size); ir_ref _ir_VLOAD(ir_ctx *ctx, ir_type type, ir_ref var); void _ir_VSTORE(ir_ctx *ctx, ir_ref var, ir_ref val); +ir_ref _ir_VLOAD_v(ir_ctx *ctx, ir_type type, ir_ref var); +void _ir_VSTORE_v(ir_ctx *ctx, ir_ref var, ir_ref val); ir_ref _ir_RLOAD(ir_ctx *ctx, ir_type type, ir_ref reg); void _ir_RSTORE(ir_ctx *ctx, ir_ref reg, ir_ref val); ir_ref _ir_LOAD(ir_ctx *ctx, ir_type type, ir_ref addr); void _ir_STORE(ir_ctx *ctx, ir_ref addr, ir_ref val); +ir_ref _ir_LOAD_v(ir_ctx *ctx, ir_type type, ir_ref addr); +void _ir_STORE_v(ir_ctx *ctx, ir_ref addr, ir_ref val); void _ir_VA_START(ir_ctx *ctx, ir_ref list); void _ir_VA_END(ir_ctx *ctx, ir_ref list); void _ir_VA_COPY(ir_ctx *ctx, ir_ref dst, ir_ref src); ir_ref _ir_VA_ARG(ir_ctx *ctx, ir_type type, ir_ref list); -ir_ref _ir_VA_ARG_EX(ir_ctx *ctx, ir_type type, ir_ref list, size_t size); +ir_ref _ir_VA_ARG_EX(ir_ctx *ctx, ir_type type, ir_ref list, size_t size, size_t align); void _ir_START(ir_ctx *ctx); void _ir_ENTRY(ir_ctx *ctx, ir_ref src, ir_ref num); void _ir_BEGIN(ir_ctx *ctx, ir_ref src); @@ -688,6 +697,7 @@ void _ir_CASE_VAL(ir_ctx *ctx, ir_ref switch_ref, ir_ref val); void _ir_CASE_RANGE(ir_ctx *ctx, ir_ref switch_ref, ir_ref v1, ir_ref v2); void _ir_CASE_DEFAULT(ir_ctx *ctx, ir_ref switch_ref); void _ir_RETURN(ir_ctx *ctx, ir_ref val); +ir_ref _ir_IGOTO(ir_ctx *ctx, ir_ref addr); void _ir_IJMP(ir_ctx *ctx, ir_ref addr); void _ir_GUARD(ir_ctx *ctx, ir_ref condition, ir_ref addr); void _ir_GUARD_NOT(ir_ctx *ctx, ir_ref condition, ir_ref addr); diff --git a/ext/opcache/jit/ir/ir_cfg.c b/ext/opcache/jit/ir/ir_cfg.c index 00923387bb21c..46755067b2444 100644 --- a/ext/opcache/jit/ir/ir_cfg.c +++ b/ext/opcache/jit/ir/ir_cfg.c @@ -820,11 +820,14 @@ int ir_build_dominators_tree(ir_ctx *ctx) succ_b = ctx->cfg_edges[bb->successors]; if (bb->successors_count != 1) { /* LOOP_END/END may be linked with the following ENTRY by a fake edge */ - IR_ASSERT(bb->successors_count == 2); - if (blocks[succ_b].flags & IR_BB_ENTRY) { + if (bb->successors_count != 2) { + complete = 0; + break; + } else if (blocks[succ_b].flags & IR_BB_ENTRY) { succ_b = ctx->cfg_edges[bb->successors + 1]; - } else { - IR_ASSERT(blocks[ctx->cfg_edges[bb->successors + 1]].flags & IR_BB_ENTRY); + } else if (!(blocks[ctx->cfg_edges[bb->successors + 1]].flags & IR_BB_ENTRY)) { + complete = 0; + break; } } dom_depth = blocks[succ_b].dom_depth;; diff --git a/ext/opcache/jit/ir/ir_check.c b/ext/opcache/jit/ir/ir_check.c index c25a984aefc1d..ee951291b1b05 100644 --- a/ext/opcache/jit/ir/ir_check.c +++ b/ext/opcache/jit/ir/ir_check.c @@ -328,7 +328,9 @@ bool ir_check(const ir_ctx *ctx) } break; case IR_LOAD: + case IR_LOAD_v: case IR_STORE: + case IR_STORE_v: type = ctx->ir_base[insn->op2].type; if (type != IR_ADDR && (!IR_IS_TYPE_INT(type) || ir_type_size[type] != ir_type_size[IR_ADDR])) { @@ -338,7 +340,9 @@ bool ir_check(const ir_ctx *ctx) } break; case IR_VLOAD: + case IR_VLOAD_v: case IR_VSTORE: + case IR_VSTORE_v: if (ctx->ir_base[insn->op2].op != IR_VAR) { fprintf(stderr, "ir_base[%d].op2 must be 'VAR' (%s)\n", i, ir_op_name[ctx->ir_base[insn->op2].op]); @@ -408,6 +412,8 @@ bool ir_check(const ir_ctx *ctx) ok = 0; } break; + case IR_IGOTO: + break; default: /* skip data references */ count = n = use_list->count; diff --git a/ext/opcache/jit/ir/ir_dump.c b/ext/opcache/jit/ir/ir_dump.c index a501d261f30a7..5cc732927d412 100644 --- a/ext/opcache/jit/ir/ir_dump.c +++ b/ext/opcache/jit/ir/ir_dump.c @@ -129,6 +129,11 @@ void ir_dump_dot(const ir_ctx *ctx, const char *name, FILE *f) case IR_OPND_CONTROL_REF: fprintf(f, "\tn%d -> n%d [style=dashed,dir=back,weight=%d];\n", ref, i, REF_WEIGHT); break; + case IR_OPND_LABEL_REF: + if (ref) { + fprintf(f, "\tc%d -> n%d [color=blue,weight=%d];\n", -ref, i, REF_WEIGHT); + } + break; } } } @@ -491,6 +496,8 @@ void ir_dump_codegen(const ir_ctx *ctx, FILE *f) ir_print_proto(ctx, insn->proto, f); } else if (insn->op == IR_SYM) { fprintf(f, "sym(%s)", ir_get_str(ctx, insn->val.name)); + } else if (insn->op == IR_LABEL) { + fprintf(f, "label(%s)", ir_get_str(ctx, insn->val.name)); } else if (insn->op == IR_FUNC_ADDR) { fprintf(f, "func *"); ir_print_const(ctx, insn, f, true); @@ -648,6 +655,12 @@ void ir_dump_codegen(const ir_ctx *ctx, FILE *f) fprintf(f, "%s%d", first ? "(" : ", ", ref); first = 0; break; + case IR_OPND_LABEL_REF: + if (ref) { + IR_ASSERT(IR_IS_CONST_REF(ref)); + fprintf(f, "%sc_%d", first ? "(" : ", ", -ref); + } + break; } } else if (opnd_kind == IR_OPND_NUM) { fprintf(f, "%s%d", first ? "(" : ", ", ref); diff --git a/ext/opcache/jit/ir/ir_emit.c b/ext/opcache/jit/ir/ir_emit.c index 7a10da1322a72..847ca375b5bd0 100644 --- a/ext/opcache/jit/ir/ir_emit.c +++ b/ext/opcache/jit/ir/ir_emit.c @@ -244,32 +244,30 @@ static int ir_get_args_regs(const ir_ctx *ctx, const ir_insn *insn, int8_t *regs ir_insn *arg = &ctx->ir_base[ir_insn_op(insn, j)]; type = arg->type; if (IR_IS_TYPE_INT(type)) { - if (arg->op == IR_ARGVAL) { - continue; - } else if (int_param < int_reg_params_count) { + if (int_param < int_reg_params_count && arg->op != IR_ARGVAL) { regs[j] = int_reg_params[int_param]; count = j + 1; + int_param++; +#ifdef _WIN64 + /* WIN64 calling convention use common couter for int and fp registers */ + fp_param++; +#endif } else { regs[j] = IR_REG_NONE; } - int_param++; -#ifdef _WIN64 - /* WIN64 calling convention use common couter for int and fp registers */ - fp_param++; -#endif } else { IR_ASSERT(IR_IS_TYPE_FP(type)); if (fp_param < fp_reg_params_count) { regs[j] = fp_reg_params[fp_param]; count = j + 1; + fp_param++; +#ifdef _WIN64 + /* WIN64 calling convention use common couter for int and fp registers */ + int_param++; +#endif } else { regs[j] = IR_REG_NONE; } - fp_param++; -#ifdef _WIN64 - /* WIN64 calling convention use common couter for int and fp registers */ - int_param++; -#endif } } return count; @@ -426,7 +424,7 @@ typedef struct _ir_common_backend_data { ir_bitset emit_constants; } ir_common_backend_data; -static int ir_const_label(ir_ctx *ctx, ir_ref ref) +static int ir_get_const_label(ir_ctx *ctx, ir_ref ref) { ir_common_backend_data *data = ctx->data; int label = ctx->cfg_blocks_count - ref; @@ -1015,11 +1013,16 @@ int ir_match(ir_ctx *ctx) entries_count++; } ctx->rules[start] = IR_SKIPPED | IR_NOP; + if (ctx->ir_base[start].op == IR_BEGIN && ctx->ir_base[start].op2) { + ctx->flags2 |= IR_HAS_BLOCK_ADDR; + } ref = bb->end; if (bb->successors_count == 1) { insn = &ctx->ir_base[ref]; if (insn->op == IR_END || insn->op == IR_LOOP_END) { - ctx->rules[ref] = insn->op; + if (!ctx->rules[ref]) { + ctx->rules[ref] = insn->op; + } ref = prev_ref[ref]; if (ref == start && ctx->cfg_edges[bb->successors] != b) { if (EXPECTED(!(bb->flags & IR_BB_ENTRY))) { diff --git a/ext/opcache/jit/ir/ir_fold.h b/ext/opcache/jit/ir/ir_fold.h index 74f7818d747c4..bab6b2916075f 100644 --- a/ext/opcache/jit/ir/ir_fold.h +++ b/ext/opcache/jit/ir/ir_fold.h @@ -755,8 +755,35 @@ IR_FOLD(NEG(C_FLOAT)) } IR_FOLD(ABS(C_I8)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op1_insn->val.i64 >= 0) { + IR_FOLD_COPY(op1); + } else { + IR_FOLD_CONST_I(-op1_insn->val.i8); + } +} + IR_FOLD(ABS(C_I16)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op1_insn->val.i64 >= 0) { + IR_FOLD_COPY(op1); + } else { + IR_FOLD_CONST_I(-op1_insn->val.i16); + } +} + IR_FOLD(ABS(C_I32)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op1_insn->val.i64 >= 0) { + IR_FOLD_COPY(op1); + } else { + IR_FOLD_CONST_I((int32_t)-op1_insn->val.u32); + } +} + IR_FOLD(ABS(C_I64)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); @@ -847,7 +874,7 @@ IR_FOLD(MUL_OV(C_U64, C_U64)) uint64_t res; IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); res = op1_insn->val.u64 * op2_insn->val.u64; - if (op1_insn->val.u64 != 0 && res / op1_insn->val.u64 != op2_insn->val.u64 && res <= max) { + if ((op1_insn->val.u64 != 0 && res / op1_insn->val.u64 != op2_insn->val.u64) || res > max) { IR_FOLD_NEXT; } IR_FOLD_CONST_U(res); @@ -864,7 +891,7 @@ IR_FOLD(MUL_OV(C_I64, C_I64)) int64_t res; IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); res = op1_insn->val.u64 * op2_insn->val.u64; - if (op1_insn->val.i64 != 0 && res / op1_insn->val.i64 != op2_insn->val.i64 && res >= min && res <= max) { + if ((op1_insn->val.i64 != 0 && res / op1_insn->val.i64 != op2_insn->val.i64) || res < min || res > max) { IR_FOLD_NEXT; } IR_FOLD_CONST_U(res); @@ -1037,220 +1064,220 @@ IR_FOLD(SHL(C_U8, C_U8)) IR_FOLD(SHL(C_CHAR, C_CHAR)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(op1_insn->val.u8 << op2_insn->val.u8); + IR_FOLD_CONST_U(op1_insn->val.u8 << (op2_insn->val.u8 & 0x7)); } IR_FOLD(SHL(C_I8, C_I8)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int8_t)(op1_insn->val.u8 << op2_insn->val.u8)); + IR_FOLD_CONST_I((int8_t)(op1_insn->val.u8 << (op2_insn->val.u8 & 0x7))); } IR_FOLD(SHL(C_U16, C_U16)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(op1_insn->val.u16 << op2_insn->val.u16); + IR_FOLD_CONST_U(op1_insn->val.u16 << (op2_insn->val.u16 & 0xf)); } IR_FOLD(SHL(C_I16, C_I16)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int16_t)(op1_insn->val.u16 << op2_insn->val.u16)); + IR_FOLD_CONST_I((int16_t)(op1_insn->val.u16 << (op2_insn->val.u16 & 0xf))); } IR_FOLD(SHL(C_U32, C_U32)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(op1_insn->val.u32 << op2_insn->val.u32); + IR_FOLD_CONST_U(op1_insn->val.u32 << (op2_insn->val.u32 & 0x1f)); } IR_FOLD(SHL(C_I32, C_I32)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int32_t)(op1_insn->val.u32 << op2_insn->val.u32)); + IR_FOLD_CONST_I((int32_t)(op1_insn->val.u32 << (op2_insn->val.u32 & 0x1f))); } IR_FOLD(SHL(C_U64, C_U64)) IR_FOLD(SHL(C_I64, C_I64)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(op1_insn->val.u64 << op2_insn->val.u64); + IR_FOLD_CONST_U(op1_insn->val.u64 << (op2_insn->val.u64 & 0x3f)); } IR_FOLD(SHR(C_U8, C_U8)) IR_FOLD(SHR(C_CHAR, C_CHAR)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(op1_insn->val.u8 >> op2_insn->val.u8); + IR_FOLD_CONST_U(op1_insn->val.u8 >> (op2_insn->val.u8 & 0x7)); } IR_FOLD(SHR(C_I8, C_I8)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int8_t)(op1_insn->val.u8 >> op2_insn->val.u8)); + IR_FOLD_CONST_I((int8_t)(op1_insn->val.u8 >> (op2_insn->val.u8 & 0x7))); } IR_FOLD(SHR(C_U16, C_U16)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(op1_insn->val.u16 >> op2_insn->val.u16); + IR_FOLD_CONST_U(op1_insn->val.u16 >> (op2_insn->val.u16 & 0xf)); } IR_FOLD(SHR(C_I16, C_I16)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int16_t)(op1_insn->val.u16 >> op2_insn->val.u16)); + IR_FOLD_CONST_I((int16_t)(op1_insn->val.u16 >> (op2_insn->val.u16 & 0xf))); } IR_FOLD(SHR(C_U32, C_U32)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(op1_insn->val.u32 >> op2_insn->val.u32); + IR_FOLD_CONST_U(op1_insn->val.u32 >> (op2_insn->val.u32 & 0x1f)); } IR_FOLD(SHR(C_I32, C_I32)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int32_t)(op1_insn->val.u32 >> op2_insn->val.u32)); + IR_FOLD_CONST_I((int32_t)(op1_insn->val.u32 >> (op2_insn->val.u32 & 0x1f))); } IR_FOLD(SHR(C_U64, C_U64)) IR_FOLD(SHR(C_I64, C_I64)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(op1_insn->val.u64 >> op2_insn->val.u64); + IR_FOLD_CONST_U(op1_insn->val.u64 >> (op2_insn->val.u64 & 0x3f)); } IR_FOLD(SAR(C_U8, C_U8)) IR_FOLD(SAR(C_CHAR, C_CHAR)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U((uint8_t)(op1_insn->val.i8 >> op2_insn->val.i8)); + IR_FOLD_CONST_U((uint8_t)(op1_insn->val.i8 >> (op2_insn->val.i8 & 0x7))); } IR_FOLD(SAR(C_I8, C_I8)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I(op1_insn->val.i8 >> op2_insn->val.i8); + IR_FOLD_CONST_I(op1_insn->val.i8 >> (op2_insn->val.i8 & 0x7)); } IR_FOLD(SAR(C_U16, C_U16)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U((uint16_t)(op1_insn->val.i16 >> op2_insn->val.i16)); + IR_FOLD_CONST_U((uint16_t)(op1_insn->val.i16 >> (op2_insn->val.i16 & 0xf))); } IR_FOLD(SAR(C_I16, C_I16)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I(op1_insn->val.i16 >> op2_insn->val.i16); + IR_FOLD_CONST_I(op1_insn->val.i16 >> (op2_insn->val.i16 & 0xf)); } IR_FOLD(SAR(C_U32, C_U32)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U((uint32_t)(op1_insn->val.i32 >> op2_insn->val.i32)); + IR_FOLD_CONST_U((uint32_t)(op1_insn->val.i32 >> (op2_insn->val.i32 & 0x1f))); } IR_FOLD(SAR(C_I32, C_I32)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I(op1_insn->val.i32 >> op2_insn->val.i32); + IR_FOLD_CONST_I(op1_insn->val.i32 >> (op2_insn->val.i32 & 0x1f)); } IR_FOLD(SAR(C_U64, C_U64)) IR_FOLD(SAR(C_I64, C_I64)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I(op1_insn->val.i64 >> op2_insn->val.i64); + IR_FOLD_CONST_I(op1_insn->val.i64 >> (op2_insn->val.i64 & 0x3f)); } IR_FOLD(ROL(C_U8, C_U8)) IR_FOLD(ROL(C_CHAR, C_CHAR)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(ir_rol8(op1_insn->val.u8, op2_insn->val.u8)); + IR_FOLD_CONST_U(ir_rol8(op1_insn->val.u8, (op2_insn->val.u8 & 0x7))); } IR_FOLD(ROL(C_I8, C_I8)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int8_t)ir_rol8(op1_insn->val.u8, op2_insn->val.u8)); + IR_FOLD_CONST_I((int8_t)ir_rol8(op1_insn->val.u8, (op2_insn->val.u8 & 0x7))); } IR_FOLD(ROL(C_U16, C_U16)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(ir_rol16(op1_insn->val.u16, op2_insn->val.u16)); + IR_FOLD_CONST_U(ir_rol16(op1_insn->val.u16, (op2_insn->val.u16 & 0xf))); } IR_FOLD(ROL(C_I16, C_I16)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int16_t)ir_rol16(op1_insn->val.u16, op2_insn->val.u16)); + IR_FOLD_CONST_I((int16_t)ir_rol16(op1_insn->val.u16, (op2_insn->val.u16 & 0xf))); } IR_FOLD(ROL(C_U32, C_U32)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(ir_rol32(op1_insn->val.u32, op2_insn->val.u32)); + IR_FOLD_CONST_U(ir_rol32(op1_insn->val.u32, (op2_insn->val.u32 & 0x1f))); } IR_FOLD(ROL(C_I32, C_I32)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int32_t)ir_rol32(op1_insn->val.u32, op2_insn->val.u32)); + IR_FOLD_CONST_I((int32_t)ir_rol32(op1_insn->val.u32, (op2_insn->val.u32 & 0x1f))); } IR_FOLD(ROL(C_U64, C_U64)) IR_FOLD(ROL(C_I64, C_I64)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(ir_rol64(op1_insn->val.u64, op2_insn->val.u64)); + IR_FOLD_CONST_U(ir_rol64(op1_insn->val.u64, (op2_insn->val.u64 & 0x3f))); } IR_FOLD(ROR(C_U8, C_U8)) IR_FOLD(ROR(C_CHAR, C_CHAR)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(ir_ror8(op1_insn->val.u8, op2_insn->val.u8)); + IR_FOLD_CONST_U(ir_ror8(op1_insn->val.u8, (op2_insn->val.u8 & 0x7))); } IR_FOLD(ROR(C_I8, C_I8)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int8_t)ir_ror8(op1_insn->val.u8, op2_insn->val.u8)); + IR_FOLD_CONST_I((int8_t)ir_ror8(op1_insn->val.u8, (op2_insn->val.u8 & 0x7))); } IR_FOLD(ROR(C_U16, C_U16)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(ir_ror16(op1_insn->val.u16, op2_insn->val.u16)); + IR_FOLD_CONST_U(ir_ror16(op1_insn->val.u16, (op2_insn->val.u16 & 0xf))); } IR_FOLD(ROR(C_I16, C_I16)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int16_t)ir_ror16(op1_insn->val.u16, op2_insn->val.u16)); + IR_FOLD_CONST_I((int16_t)ir_ror16(op1_insn->val.u16, (op2_insn->val.u16 & 0xf))); } IR_FOLD(ROR(C_U32, C_U32)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(ir_ror32(op1_insn->val.u32, op2_insn->val.u32)); + IR_FOLD_CONST_U(ir_ror32(op1_insn->val.u32, (op2_insn->val.u32 & 0x1f))); } IR_FOLD(ROR(C_I32, C_I32)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_I((int32_t)ir_ror32(op1_insn->val.u32, op2_insn->val.u32)); + IR_FOLD_CONST_I((int32_t)ir_ror32(op1_insn->val.u32, (op2_insn->val.u32 & 0x1f))); } IR_FOLD(ROR(C_U64, C_U64)) IR_FOLD(ROR(C_I64, C_I64)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - IR_FOLD_CONST_U(ir_ror64(op1_insn->val.u64, op2_insn->val.u64)); + IR_FOLD_CONST_U(ir_ror64(op1_insn->val.u64, (op2_insn->val.u64 & 0x3f))); } //IR_FOLD(BSWAP(CONST)) @@ -1392,6 +1419,9 @@ IR_FOLD(TRUNC(C_U64)) IR_FOLD_CONST_U(op1_insn->val.u16); case IR_U32: IR_FOLD_CONST_U(op1_insn->val.u32); + case IR_ADDR: + IR_ASSERT(sizeof(void*) == 4); + IR_FOLD_CONST_U(op1_insn->val.u32); } } @@ -1545,7 +1575,7 @@ IR_FOLD(FP2FP(C_DOUBLE)) IR_FOLD(COPY(_)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); - if (!op2) { + if (!(op2 & IR_COPY_HARD)) { IR_FOLD_COPY(op1); } /* skip CSE */ @@ -2075,23 +2105,23 @@ IR_FOLD(SUB(ADD, ADD)) IR_FOLD_CONST_U(0); } else if (op1_insn->op1 == op2_insn->op1) { /* (a + b) - (a + c) => b - c */ - op1 = op1_insn->op2; - op2 = op2_insn->op2; + op1 = _ir_fold_cast(ctx, op1_insn->op2, IR_OPT_TYPE(opt)); + op2 = _ir_fold_cast(ctx, op2_insn->op2, IR_OPT_TYPE(opt)); IR_FOLD_RESTART; } else if (op1_insn->op1 == op2_insn->op2) { /* (a + b) - (c + a) => b - c */ - op1 = op1_insn->op2; - op2 = op2_insn->op1; + op1 = _ir_fold_cast(ctx, op1_insn->op2, IR_OPT_TYPE(opt)); + op2 = _ir_fold_cast(ctx, op2_insn->op1, IR_OPT_TYPE(opt)); IR_FOLD_RESTART; } else if (op1_insn->op2 == op2_insn->op1) { /* (a + b) - (b + c) => a - c */ - op1 = op1_insn->op1; - op2 = op2_insn->op2; + op1 = _ir_fold_cast(ctx, op1_insn->op1, IR_OPT_TYPE(opt)); + op2 = _ir_fold_cast(ctx, op2_insn->op2, IR_OPT_TYPE(opt)); IR_FOLD_RESTART; } else if (op1_insn->op2 == op2_insn->op2) { /* (a + b) - (c + b) => a - c */ - op1 = op1_insn->op1; - op2 = op2_insn->op1; + op1 = _ir_fold_cast(ctx, op1_insn->op1, IR_OPT_TYPE(opt)); + op2 = _ir_fold_cast(ctx, op2_insn->op1, IR_OPT_TYPE(opt)); IR_FOLD_RESTART; } } diff --git a/ext/opcache/jit/ir/ir_gcm.c b/ext/opcache/jit/ir/ir_gcm.c index 043e1e7bdd853..e6486ba64a1c5 100644 --- a/ext/opcache/jit/ir/ir_gcm.c +++ b/ext/opcache/jit/ir/ir_gcm.c @@ -361,20 +361,20 @@ static bool ir_split_partially_dead_node(ir_ctx *ctx, ir_ref ref, uint32_t b) while (ir_sparse_set_in(&data->totally_useful, ctx->cfg_blocks[j].idom)) { j = ctx->cfg_blocks[j].idom; } + clone = ir_hashtab_find(&hash, j); + if (clone == IR_INVALID_VAL) { + clone = clones_count++; + ir_hashtab_add(&hash, j, clone); + clones[clone].block = j; + clones[clone].use_count = 0; + clones[clone].use = -1; + } + uses[uses_count].ref = use; + uses[uses_count].block = i; + uses[uses_count].next = clones[clone].use; + clones[clone].use_count++; + clones[clone].use = uses_count++; } - clone = ir_hashtab_find(&hash, j); - if (clone == IR_INVALID_VAL) { - clone = clones_count++; - ir_hashtab_add(&hash, j, clone); - clones[clone].block = j; - clones[clone].use_count = 0; - clones[clone].use = -1; - } - uses[uses_count].ref = use; - uses[uses_count].block = i; - uses[uses_count].next = clones[clone].use; - clones[clone].use_count++; - clones[clone].use = uses_count++; } } @@ -1007,7 +1007,11 @@ int ir_schedule(ir_ctx *ctx) start = i = bb->start; _xlat[i] = bb->start = insns_count; insn = &ctx->ir_base[i]; - if (insn->op == IR_CASE_VAL) { + if (insn->op == IR_BEGIN) { + if (insn->op2) { + consts_count += ir_count_constant(_xlat, insn->op2); + } + } else if (insn->op == IR_CASE_VAL) { IR_ASSERT(insn->op2 < IR_TRUE); consts_count += ir_count_constant(_xlat, insn->op2); } else if (insn->op == IR_CASE_RANGE) { @@ -1255,7 +1259,7 @@ int ir_schedule(ir_ctx *ctx) const char *proto = ir_get_strl(ctx, new_insn->proto, &len); new_insn->proto = ir_strl(&new_ctx, proto, len); } - } else if (new_insn->op == IR_SYM || new_insn->op == IR_STR) { + } else if (new_insn->op == IR_SYM || new_insn->op == IR_STR || new_insn->op == IR_LABEL) { size_t len; const char *str = ir_get_strl(ctx, new_insn->val.name, &len); new_insn->val.u64 = ir_strl(&new_ctx, str, len); @@ -1292,7 +1296,7 @@ int ir_schedule(ir_ctx *ctx) } else { new_insn->proto = 0; } - } else if (insn->op == IR_SYM || insn->op == IR_STR) { + } else if (insn->op == IR_SYM || insn->op == IR_STR || insn->op == IR_LABEL) { size_t len; const char *str = ir_get_strl(ctx, insn->val.name, &len); new_insn->val.u64 = ir_strl(&new_ctx, str, len); @@ -1364,6 +1368,8 @@ int ir_schedule(ir_ctx *ctx) size_t len; const char *str = ir_get_strl(ctx, insn->op2, &len); new_insn->op2 = ir_strl(&new_ctx, str, len); + } else if (new_insn->op == IR_BEGIN && insn->op2) { + new_insn->op2 = _xlat[insn->op2]; } else { new_insn->op2 = insn->op2; } diff --git a/ext/opcache/jit/ir/ir_private.h b/ext/opcache/jit/ir/ir_private.h index 2f457cbc99333..dbacc3967d0f7 100644 --- a/ext/opcache/jit/ir/ir_private.h +++ b/ext/opcache/jit/ir/ir_private.h @@ -887,7 +887,7 @@ void ir_print_escaped_str(const char *s, size_t len, FILE *f); #define IR_IS_CONST_OP(op) ((op) > IR_NOP && (op) <= IR_C_FLOAT) #define IR_IS_FOLDABLE_OP(op) ((op) <= IR_LAST_FOLDABLE_OP) -#define IR_IS_SYM_CONST(op) ((op) == IR_STR || (op) == IR_SYM || (op) == IR_FUNC) +#define IR_IS_SYM_CONST(op) ((op) == IR_STR || (op) == IR_SYM || (op) == IR_FUNC || (op) == IR_LABEL) ir_ref ir_const_ex(ir_ctx *ctx, ir_val val, uint8_t type, uint32_t optx); @@ -946,12 +946,13 @@ IR_ALWAYS_INLINE bool ir_ref_is_true(ir_ctx *ctx, ir_ref ref) #define IR_OPND_UNUSED 0x0 #define IR_OPND_DATA 0x1 #define IR_OPND_CONTROL 0x2 -#define IR_OPND_CONTROL_DEP 0x3 -#define IR_OPND_CONTROL_REF 0x4 -#define IR_OPND_STR 0x5 -#define IR_OPND_NUM 0x6 -#define IR_OPND_PROB 0x7 -#define IR_OPND_PROTO 0x8 +#define IR_OPND_LABEL_REF 0x3 +#define IR_OPND_CONTROL_DEP 0x4 +#define IR_OPND_CONTROL_REF 0x5 +#define IR_OPND_STR 0x6 +#define IR_OPND_NUM 0x7 +#define IR_OPND_PROB 0x8 +#define IR_OPND_PROTO 0x9 #define IR_OP_FLAGS(op_flags, op1_flags, op2_flags, op3_flags) \ ((op_flags) | ((op1_flags) << 20) | ((op2_flags) << 24) | ((op3_flags) << 28)) @@ -1013,6 +1014,7 @@ IR_ALWAYS_INLINE uint32_t ir_insn_len(const ir_insn *insn) #define IR_HAS_VA_ARG_FP (1<<9) #define IR_HAS_FP_RET_SLOT (1<<10) #define IR_16B_FRAME_ALIGNMENT (1<<11) +#define IR_HAS_BLOCK_ADDR (1<<12) /* Temporary: MEM2SSA -> SCCP */ #define IR_MEM2SSA_VARS (1<<25) @@ -1248,11 +1250,10 @@ struct _ir_live_range { #define IR_LIVE_INTERVAL_HAS_HINT_REGS (1<<2) #define IR_LIVE_INTERVAL_HAS_HINT_REFS (1<<3) #define IR_LIVE_INTERVAL_MEM_PARAM (1<<4) -#define IR_LIVE_INTERVAL_MEM_LOAD (1<<5) -#define IR_LIVE_INTERVAL_COALESCED (1<<6) -#define IR_LIVE_INTERVAL_SPILL_SPECIAL (1<<7) /* spill slot is pre-allocated in a special area (see ir_ctx.spill_reserved_base) */ -#define IR_LIVE_INTERVAL_SPILLED (1<<8) -#define IR_LIVE_INTERVAL_SPLIT_CHILD (1<<9) +#define IR_LIVE_INTERVAL_COALESCED (1<<5) +#define IR_LIVE_INTERVAL_SPILL_SPECIAL (1<<6) /* spill slot is pre-allocated in a special area (see ir_ctx.spill_reserved_base) */ +#define IR_LIVE_INTERVAL_SPILLED (1<<7) +#define IR_LIVE_INTERVAL_SPLIT_CHILD (1<<8) struct _ir_live_interval { uint8_t type; diff --git a/ext/opcache/jit/ir/ir_ra.c b/ext/opcache/jit/ir/ir_ra.c index 21c7ee3ac64e5..2e8a8e3f34f3f 100644 --- a/ext/opcache/jit/ir/ir_ra.c +++ b/ext/opcache/jit/ir/ir_ra.c @@ -776,9 +776,6 @@ int ir_compute_live_ranges(ir_ctx *ctx) if (insn->op == IR_PARAM) { /* We may reuse parameter stack slot for spilling */ ctx->live_intervals[v]->flags |= IR_LIVE_INTERVAL_MEM_PARAM; - } else if (insn->op == IR_VLOAD) { - /* Load may be fused into the usage instruction */ - ctx->live_intervals[v]->flags |= IR_LIVE_INTERVAL_MEM_LOAD; } def_pos = IR_DEF_LIVE_POS_FROM_REF(ref); } @@ -845,11 +842,17 @@ int ir_compute_live_ranges(ir_ctx *ctx) ival = ctx->live_intervals[v]; } ir_add_use(ctx, ival, j, use_pos, reg, IR_USE_FLAGS(def_flags, j), hint_ref); - } else if (ctx->rules) { - if (ctx->rules[input] & IR_FUSED) { - ir_add_fusion_ranges(ctx, ref, input, bb, live); - } else if (ctx->rules[input] == (IR_SKIPPED|IR_RLOAD)) { - ir_set_alocated_reg(ctx, ref, j, ctx->ir_base[input].op2); + } else { + if (ctx->rules) { + if ((ctx->rules[input] & (IR_FUSED|IR_SKIPPED)) == IR_FUSED) { + ir_add_fusion_ranges(ctx, ref, input, bb, live); + } else if (ctx->rules[input] == (IR_SKIPPED|IR_RLOAD)) { + ir_set_alocated_reg(ctx, ref, j, ctx->ir_base[input].op2); + } + } + if (reg != IR_REG_NONE) { + use_pos = IR_LOAD_LIVE_POS_FROM_REF(ref); + ir_add_fixed_live_range(ctx, reg, use_pos, use_pos + IR_USE_SUB_REF); } } } else if (reg != IR_REG_NONE) { @@ -1396,9 +1399,6 @@ int ir_compute_live_ranges(ir_ctx *ctx) if (insn->op == IR_PARAM) { /* We may reuse parameter stack slot for spilling */ ctx->live_intervals[v]->flags |= IR_LIVE_INTERVAL_MEM_PARAM; - } else if (insn->op == IR_VLOAD) { - /* Load may be fused into the usage instruction */ - ctx->live_intervals[v]->flags |= IR_LIVE_INTERVAL_MEM_LOAD; } def_pos = IR_DEF_LIVE_POS_FROM_REF(ref); } @@ -1465,17 +1465,17 @@ int ir_compute_live_ranges(ir_ctx *ctx) ival = ctx->live_intervals[v]; } ir_add_use(ctx, ival, j, use_pos, reg, IR_USE_FLAGS(def_flags, j), hint_ref); - } else if (ctx->rules) { - if (ctx->rules[input] & IR_FUSED) { - ir_add_fusion_ranges(ctx, ref, input, bb, live_in_block, b); - } else { - if (ctx->rules[input] == (IR_SKIPPED|IR_RLOAD)) { + } else { + if (ctx->rules) { + if ((ctx->rules[input] & (IR_FUSED|IR_SKIPPED)) == IR_FUSED) { + ir_add_fusion_ranges(ctx, ref, input, bb, live_in_block, b); + } else if (ctx->rules[input] == (IR_SKIPPED|IR_RLOAD)) { ir_set_alocated_reg(ctx, ref, j, ctx->ir_base[input].op2); } - if (reg != IR_REG_NONE) { - use_pos = IR_LOAD_LIVE_POS_FROM_REF(ref); - ir_add_fixed_live_range(ctx, reg, use_pos, use_pos + IR_USE_SUB_REF); - } + } + if (reg != IR_REG_NONE) { + use_pos = IR_LOAD_LIVE_POS_FROM_REF(ref); + ir_add_fixed_live_range(ctx, reg, use_pos, use_pos + IR_USE_SUB_REF); } } } else if (reg != IR_REG_NONE) { @@ -1605,7 +1605,7 @@ static void ir_vregs_join(ir_ctx *ctx, uint32_t r1, uint32_t r2) } while (*prev && ((*prev)->pos < use_pos->pos || ((*prev)->pos == use_pos->pos && - (use_pos->op_num == 0 || (*prev)->op_num < use_pos->op_num)))) { + (use_pos->op_num == 0 || ((*prev)->op_num != 0 && (*prev)->op_num < use_pos->op_num))))) { if ((*prev)->hint_ref > 0 && ctx->vregs[(*prev)->hint_ref] == r2) { (*prev)->hint_ref = 0; } @@ -1627,9 +1627,6 @@ static void ir_vregs_join(ir_ctx *ctx, uint32_t r1, uint32_t r2) ctx->live_intervals[r1]->flags |= IR_LIVE_INTERVAL_COALESCED | (ival->flags & (IR_LIVE_INTERVAL_HAS_HINT_REGS|IR_LIVE_INTERVAL_HAS_HINT_REFS)); - if (ctx->ir_base[IR_LIVE_POS_TO_REF(ctx->live_intervals[r1]->use_pos->pos)].op != IR_VLOAD) { - ctx->live_intervals[r1]->flags &= ~IR_LIVE_INTERVAL_MEM_LOAD; - } if (ival->flags & IR_LIVE_INTERVAL_MEM_PARAM) { IR_ASSERT(!(ctx->live_intervals[r1]->flags & IR_LIVE_INTERVAL_MEM_PARAM)); ctx->live_intervals[r1]->flags |= IR_LIVE_INTERVAL_MEM_PARAM; @@ -2343,16 +2340,6 @@ static ir_live_pos ir_first_use_pos_after(ir_live_interval *ival, ir_live_pos po return p ? p->pos : 0x7fffffff; } -static ir_live_pos ir_first_use_pos(ir_live_interval *ival, uint8_t flags) -{ - ir_use_pos *p = ival->use_pos; - - while (p && !(p->flags & flags)) { - p = p->next; - } - return p ? p->pos : 0x7fffffff; -} - static ir_block *ir_block_from_live_pos(ir_ctx *ctx, ir_live_pos pos) { ir_ref ref = IR_LIVE_POS_TO_REF(pos); @@ -3194,7 +3181,6 @@ static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, ir_live_interval *ival, ir_li /* split current before its first use position that requires a register */ ir_live_pos split_pos; -spill_current: if (next_use_pos == ival->range.start) { IR_ASSERT(ival->use_pos && ival->use_pos->op_num == 0); /* split right after definition */ @@ -3228,7 +3214,6 @@ static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, ir_live_interval *ival, ir_li return IR_REG_NONE; } if (split_pos >= blockPos[reg]) { -try_next_available_register: IR_REGSET_EXCL(available, reg); if (IR_REGSET_IS_EMPTY(available)) { fprintf(stderr, "LSRA Internal Error: Unsolvable conflict. Allocation is not possible\n"); @@ -3274,23 +3259,6 @@ static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, ir_live_interval *ival, ir_li } IR_LOG_LSRA(" ---- Finish", other, ""); } else { - if (ir_first_use_pos(other, IR_USE_MUST_BE_IN_REG) <= other->end) { - if (!(ival->flags & IR_LIVE_INTERVAL_TEMP)) { - next_use_pos = ir_first_use_pos(ival, IR_USE_MUST_BE_IN_REG); - if (next_use_pos == ival->range.start) { - IR_ASSERT(ival->use_pos && ival->use_pos->op_num == 0); - /* split right after definition */ - split_pos = next_use_pos + 1; - } else { - split_pos = ir_find_optimal_split_position(ctx, ival, ival->range.start, next_use_pos - 1, 1); - } - - if (split_pos > ival->range.start) { - goto spill_current; - } - } - goto try_next_available_register; - } child = other; other->reg = IR_REG_NONE; if (prev) { @@ -3400,12 +3368,13 @@ static int ir_fix_dessa_tmps(ir_ctx *ctx, uint8_t type, ir_ref from, ir_ref to) static bool ir_ival_spill_for_fuse_load(ir_ctx *ctx, ir_live_interval *ival, ir_reg_alloc_data *data) { ir_use_pos *use_pos = ival->use_pos; - ir_insn *insn; if (ival->flags & IR_LIVE_INTERVAL_MEM_PARAM) { IR_ASSERT(!ival->next && use_pos && use_pos->op_num == 0); - insn = &ctx->ir_base[IR_LIVE_POS_TO_REF(use_pos->pos)]; +#if IR_DEBUG + ir_insn *insn = &ctx->ir_base[IR_LIVE_POS_TO_REF(use_pos->pos)]; IR_ASSERT(insn->op == IR_PARAM); +#endif use_pos = use_pos->next; if (use_pos && (use_pos->next || (use_pos->flags & IR_USE_MUST_BE_IN_REG))) { return 0; @@ -3418,38 +3387,6 @@ static bool ir_ival_spill_for_fuse_load(ir_ctx *ctx, ir_live_interval *ival, ir_ } } - return 1; - } else if (ival->flags & IR_LIVE_INTERVAL_MEM_LOAD) { - insn = &ctx->ir_base[IR_LIVE_POS_TO_REF(use_pos->pos)]; - IR_ASSERT(insn->op == IR_VLOAD); - IR_ASSERT(ctx->ir_base[insn->op2].op == IR_VAR); - use_pos = use_pos->next; - if (use_pos && (use_pos->next || (use_pos->flags & IR_USE_MUST_BE_IN_REG))) { - return 0; - } - - if (use_pos) { - ir_block *bb = ir_block_from_live_pos(ctx, use_pos->pos); - if (bb->loop_depth && bb != ir_block_from_live_pos(ctx, ival->use_pos->pos)) { - return 0; - } - /* check if VAR may be clobbered between VLOAD and use */ - ir_use_list *use_list = &ctx->use_lists[insn->op2]; - ir_ref n = use_list->count; - ir_ref *p = &ctx->use_edges[use_list->refs]; - for (; n > 0; p++, n--) { - ir_ref use = *p; - if (ctx->ir_base[use].op == IR_VSTORE) { - if (use > IR_LIVE_POS_TO_REF(ival->use_pos->pos) && use < IR_LIVE_POS_TO_REF(use_pos->pos)) { - return 0; - } - } else if (ctx->ir_base[use].op == IR_VADDR) { - return 0; - } - } - } - ival->stack_spill_pos = ctx->ir_base[insn->op2].op3; - return 1; } return 0; @@ -3554,7 +3491,7 @@ static int ir_linear_scan(ir_ctx *ctx) for (j = ctx->vregs_count; j != 0; j--) { ival = ctx->live_intervals[j]; if (ival) { - if (!(ival->flags & (IR_LIVE_INTERVAL_MEM_PARAM|IR_LIVE_INTERVAL_MEM_LOAD)) + if (!(ival->flags & IR_LIVE_INTERVAL_MEM_PARAM) || !ir_ival_spill_for_fuse_load(ctx, ival, &data)) { ir_add_to_unhandled(&unhandled, ival); } diff --git a/ext/opcache/jit/ir/ir_save.c b/ext/opcache/jit/ir/ir_save.c index 5ba986fadd481..dd955172950c8 100644 --- a/ext/opcache/jit/ir/ir_save.c +++ b/ext/opcache/jit/ir/ir_save.c @@ -40,6 +40,11 @@ void ir_print_proto_ex(uint8_t flags, ir_type ret_type, uint32_t params_count, c } else if (flags & IR_BUILTIN_FUNC) { fprintf(f, " __builtin"); } + if (flags & IR_CONST_FUNC) { + fprintf(f, " __const"); + } else if (flags & IR_PURE_FUNC) { + fprintf(f, " __pure"); + } } static void ir_save_dessa_moves(const ir_ctx *ctx, int b, ir_block *bb, FILE *f) @@ -109,6 +114,10 @@ void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f) fprintf(f, "sym(%s%s)", (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "", ir_get_str(ctx, insn->val.name)); + } else if (insn->op == IR_LABEL) { + fprintf(f, "label(%s%s)", + (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "", + ir_get_str(ctx, insn->val.name)); } else if (insn->op == IR_FUNC_ADDR) { fprintf(f, "func *"); ir_print_const(ctx, insn, f, true); @@ -272,6 +281,13 @@ void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f) fprintf(f, "%s%d", first ? "(" : ", ", ref); first = 0; break; + case IR_OPND_LABEL_REF: + if (ref) { + IR_ASSERT(IR_IS_CONST_REF(ref)); + fprintf(f, "%sc_%d", first ? "(" : ", ", -ref); + first = 0; + } + break; } } else if (opnd_kind == IR_OPND_NUM) { fprintf(f, "%s%d", first ? "(" : ", ", ref); diff --git a/ext/opcache/jit/ir/ir_sccp.c b/ext/opcache/jit/ir/ir_sccp.c index 45df92ec2be6f..e2f38a058aeba 100644 --- a/ext/opcache/jit/ir/ir_sccp.c +++ b/ext/opcache/jit/ir/ir_sccp.c @@ -1508,8 +1508,8 @@ static bool ir_may_promote_f2d(ir_ctx *ctx, ir_ref ref) switch (insn->op) { case IR_FP2FP: return 1; - case IR_INT2FP: - return ctx->use_lists[ref].count == 1; +// case IR_INT2FP: +// return ctx->use_lists[ref].count == 1; case IR_NEG: case IR_ABS: return ctx->use_lists[ref].count == 1 && @@ -2110,7 +2110,9 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref && !IR_IS_SYM_CONST(ctx->ir_base[use_insn->op1].op)) { ctx->ir_base[use].op1 = ir_ext_const(ctx, &ctx->ir_base[use_insn->op1], op, type); } else { - ctx->ir_base[use].op1 = ir_ext_ref(ctx, use, use_insn->op1, op, type, worklist); + ir_ref tmp = ir_ext_ref(ctx, use, use_insn->op1, op, type, worklist); + use_insn = &ctx->ir_base[use]; + use_insn->op1 = tmp; } ir_bitqueue_add(worklist, use); } @@ -2119,7 +2121,9 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref && !IR_IS_SYM_CONST(ctx->ir_base[use_insn->op2].op)) { ctx->ir_base[use].op2 = ir_ext_const(ctx, &ctx->ir_base[use_insn->op2], op, type); } else { - ctx->ir_base[use].op2 = ir_ext_ref(ctx, use, use_insn->op2, op, type, worklist); + ir_ref tmp = ir_ext_ref(ctx, use, use_insn->op2, op, type, worklist); + use_insn = &ctx->ir_base[use]; + use_insn->op2 = tmp; } ir_bitqueue_add(worklist, use); } @@ -2147,7 +2151,9 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref && !IR_IS_SYM_CONST(ctx->ir_base[use_insn->op1].op)) { ctx->ir_base[use].op1 = ir_ext_const(ctx, &ctx->ir_base[use_insn->op1], op, type); } else { - ctx->ir_base[use].op1 = ir_ext_ref(ctx, use, use_insn->op1, op, type, worklist); + ir_ref tmp = ir_ext_ref(ctx, use, use_insn->op1, op, type, worklist); + use_insn = &ctx->ir_base[use]; + use_insn->op1 = tmp; } ir_bitqueue_add(worklist, use); } @@ -2156,7 +2162,9 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref && !IR_IS_SYM_CONST(ctx->ir_base[use_insn->op2].op)) { ctx->ir_base[use].op2 = ir_ext_const(ctx, &ctx->ir_base[use_insn->op2], op, type); } else { - ctx->ir_base[use].op2 = ir_ext_ref(ctx, use, use_insn->op2, op, type, worklist); + ir_ref tmp = ir_ext_ref(ctx, use, use_insn->op2, op, type, worklist); + use_insn = &ctx->ir_base[use]; + use_insn->op2 = tmp; } ir_bitqueue_add(worklist, use); } @@ -2178,7 +2186,8 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref && !IR_IS_SYM_CONST(ctx->ir_base[phi_insn->op2].op)) { ctx->ir_base[phi_ref].op2 = ir_ext_const(ctx, &ctx->ir_base[phi_insn->op2], op, type); } else { - ctx->ir_base[phi_ref].op2 = ir_ext_ref(ctx, phi_ref, phi_insn->op2, op, type, worklist); + ir_ref tmp = ir_ext_ref(ctx, phi_ref, phi_insn->op2, op, type, worklist); + ctx->ir_base[phi_ref].op2 = tmp; } return 1; @@ -2251,42 +2260,6 @@ static void ir_merge_blocks(ir_ctx *ctx, ir_ref end, ir_ref begin, ir_bitqueue * ir_ref prev, next; ir_use_list *use_list; - if (ctx->use_lists[begin].count > 1) { - ir_ref *p, n, i, use; - ir_insn *use_insn; - ir_ref region = end; - ir_ref next = IR_UNUSED; - - while (!IR_IS_BB_START(ctx->ir_base[region].op)) { - region = ctx->ir_base[region].op1; - } - - use_list = &ctx->use_lists[begin]; - n = use_list->count; - for (p = &ctx->use_edges[use_list->refs], i = 0; i < n; p++, i++) { - use = *p; - use_insn = &ctx->ir_base[use]; - if (ir_op_flags[use_insn->op] & IR_OP_FLAG_CONTROL) { - IR_ASSERT(!next); - next = use; - } else { - IR_ASSERT(use_insn->op == IR_VAR); - IR_ASSERT(use_insn->op1 == begin); - use_insn->op1 = region; - if (ir_use_list_add(ctx, region, use)) { - /* restore after reallocation */ - use_list = &ctx->use_lists[begin]; - n = use_list->count; - p = &ctx->use_edges[use_list->refs + i]; - } - } - } - - IR_ASSERT(next); - ctx->use_edges[use_list->refs] = next; - use_list->count = 1; - } - IR_ASSERT(ctx->ir_base[begin].op == IR_BEGIN); IR_ASSERT(ctx->ir_base[end].op == IR_END); IR_ASSERT(ctx->ir_base[begin].op1 == end); @@ -3595,7 +3568,10 @@ void ir_iter_opt(ir_ctx *ctx, ir_bitqueue *worklist) if (!(ctx->flags & IR_OPT_CFG)) { /* pass */ } else if (insn->op == IR_BEGIN) { - if (insn->op1 && ctx->ir_base[insn->op1].op == IR_END) { + if (insn->op1 + && !insn->op2 /* no computed goto label */ + && ctx->use_lists[i].count == 1 + && ctx->ir_base[insn->op1].op == IR_END) { ir_merge_blocks(ctx, insn->op1, i, worklist); } } else if (insn->op == IR_MERGE) { diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index 42e4eee7da0fc..7f714dd11d27c 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -66,7 +66,7 @@ IR_ALWAYS_INLINE ir_mem IR_MEM(ir_reg base, int32_t offset, ir_reg index, int32_ #define IR_SPILL_POS_TO_OFFSET(offset) \ ((ctx->flags & IR_USE_FRAME_POINTER) ? \ - ((offset) - (ctx->stack_frame_size - ctx->stack_frame_alignment)) : \ + ((offset) - ctx->stack_frame_size) : \ ((offset) + ctx->call_stack_size)) |.macro ASM_EXPAND_OP_MEM, MACRO, op, type, op1 @@ -892,6 +892,9 @@ typedef struct _ir_backend_data { bool double_abs_const; bool float_abs_const; bool double_zero_const; + bool u2d_const; + bool u2f_const; + bool resolved_label_syms; } ir_backend_data; #define IR_GP_REG_NAME(code, name64, name32, name16, name8, name8h) \ @@ -1087,6 +1090,7 @@ const char *ir_reg_name(int8_t reg, ir_type type) _(SSE_TRUNC) \ _(SSE_NEARBYINT) \ _(BIT_OP) \ + _(IGOTO_DUP) \ #define IR_LEA_FIRST IR_LEA_OB #define IR_LEA_LAST IR_LEA_O_SYM @@ -1110,35 +1114,24 @@ const char *ir_rule_name[IR_LAST_OP] = { static bool ir_may_fuse_addr(ir_ctx *ctx, const ir_insn *addr_insn) { - if (sizeof(void*) == 4) { - return 1; + if (addr_insn->op == IR_LABEL) { + return 0; } else if (IR_IS_SYM_CONST(addr_insn->op)) { void *addr = ir_sym_addr(ctx, addr_insn); if (!addr) { return 0; } - return IR_IS_SIGNED_32BIT((int64_t)(intptr_t)addr); + return (sizeof(void*) == 4) || IR_IS_SIGNED_32BIT((int64_t)(intptr_t)addr); } else { - return IR_IS_SIGNED_32BIT(addr_insn->val.i64); + return (sizeof(void*) == 4) || IR_IS_SIGNED_32BIT(addr_insn->val.i64); } } static bool ir_may_fuse_imm(ir_ctx *ctx, const ir_insn *val_insn) { if (val_insn->type == IR_ADDR) { - if (sizeof(void*) == 4) { - return 1; - } else if (IR_IS_SYM_CONST(val_insn->op)) { - void *addr = ir_sym_addr(ctx, val_insn); - - if (!addr) { - return 0; - } - return IR_IS_SIGNED_32BIT((intptr_t)addr); - } else { - return IR_IS_SIGNED_32BIT(val_insn->val.i64); - } + return ir_may_fuse_addr(ctx, val_insn); } else { return (ir_type_size[val_insn->type] <= 4 || IR_IS_SIGNED_32BIT(val_insn->val.i64)); } @@ -1517,6 +1510,11 @@ op2_const: constraints->tmp_regs[0] = IR_TMP_REG(1, ctx->ir_base[insn->op1].type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); n = 1; } + if (IR_IS_TYPE_UNSIGNED(ctx->ir_base[insn->op1].type) + && ir_type_size[ctx->ir_base[insn->op1].type] >= sizeof(void*)) { + constraints->tmp_regs[n] = IR_TMP_REG(2, ctx->ir_base[insn->op1].type, IR_USE_SUB_REF, IR_DEF_SUB_REF); + n++; + } break; case IR_ABS_INT: flags = IR_DEF_CONFLICTS_WITH_INPUT_REGS | IR_USE_MUST_BE_IN_REG | IR_OP1_MUST_BE_IN_REG; @@ -1542,6 +1540,7 @@ op2_const: case IR_GUARD_NOT: flags = IR_OP2_SHOULD_BE_IN_REG; break; + case IR_IGOTO: case IR_IJMP: flags = IR_OP2_SHOULD_BE_IN_REG; break; @@ -1574,7 +1573,7 @@ op2_const: } break; case IR_VA_ARG: - flags = IR_USE_MUST_BE_IN_REG | IR_OP2_MUST_BE_IN_REG; + flags = IR_USE_MUST_BE_IN_REG | IR_OP2_MUST_BE_IN_REG | IR_DEF_CONFLICTS_WITH_INPUT_REGS; constraints->tmp_regs[0] = IR_TMP_REG(3, IR_ADDR, IR_LOAD_SUB_REF, IR_SAVE_SUB_REF); n = 1; insn = &ctx->ir_base[ref]; @@ -1669,7 +1668,9 @@ static void ir_match_fuse_addr(ir_ctx *ctx, ir_ref addr_ref) do { ir_insn *insn = &ctx->ir_base[*p]; - if (insn->op != IR_LOAD && (insn->op != IR_STORE || insn->op3 == addr_ref)) { + if (insn->op != IR_LOAD + && insn->op != IR_LOAD_v + && ((insn->op != IR_STORE && insn->op != IR_STORE_v) || insn->op3 == addr_ref)) { return; } p++; @@ -1752,7 +1753,7 @@ static bool ir_match_has_mem_deps(ir_ctx *ctx, ir_ref ref, ir_ref root) do { ir_insn *insn = &ctx->ir_base[pos]; - if (insn->op == IR_STORE) { + if (insn->op == IR_STORE || insn->op == IR_STORE_v || insn->op == IR_VSTORE || insn->op == IR_VSTORE_v) { // TODO: check if LOAD and STORE addresses may alias return 1; } else if (insn->op == IR_CALL) { @@ -1766,8 +1767,9 @@ static bool ir_match_has_mem_deps(ir_ctx *ctx, ir_ref ref, ir_ref root) static void ir_match_fuse_load(ir_ctx *ctx, ir_ref ref, ir_ref root) { - if (ir_in_same_block(ctx, ref) - && ctx->ir_base[ref].op == IR_LOAD) { + if (ir_in_same_block(ctx, ref) && + (ctx->ir_base[ref].op == IR_LOAD || ctx->ir_base[ref].op == IR_LOAD_v || + ctx->ir_base[ref].op == IR_VLOAD || ctx->ir_base[ref].op == IR_VLOAD_v)) { if (ctx->use_lists[ref].count == 2 && !ir_match_has_mem_deps(ctx, ref, root)) { ir_ref addr_ref = ctx->ir_base[ref].op2; @@ -1792,7 +1794,7 @@ static bool ir_match_try_fuse_load(ir_ctx *ctx, ir_ref ref, ir_ref root) ir_insn *insn = &ctx->ir_base[ref]; if (ir_in_same_block(ctx, ref) - && insn->op == IR_LOAD) { + && (insn->op == IR_LOAD || insn->op == IR_LOAD_v || insn->op == IR_VLOAD || insn->op == IR_VLOAD_v)) { if (ctx->use_lists[ref].count == 2 && !ir_match_has_mem_deps(ctx, ref, root)) { ir_ref addr_ref = ctx->ir_base[ref].op2; @@ -1814,8 +1816,6 @@ static bool ir_match_try_fuse_load(ir_ctx *ctx, ir_ref ref, ir_ref root) && ir_get_param_reg(ctx, ref) == IR_REG_NONE) { return 1; } - } else if (ctx->ir_base[ref].op == IR_VLOAD) { - return 1; } return 0; } @@ -2462,8 +2462,21 @@ binop_fp: case IR_IJMP: ir_match_fuse_load(ctx, insn->op2, ref); return insn->op; + case IR_IGOTO: + if (ctx->ir_base[insn->op1].op == IR_MERGE || ctx->ir_base[insn->op1].op == IR_LOOP_BEGIN) { + ir_insn *merge = &ctx->ir_base[insn->op1]; + ir_ref *p, n = merge->inputs_count; + + for (p = merge->ops + 1; n > 0; p++, n--) { + ir_ref input = *p; + IR_ASSERT(ctx->ir_base[input].op == IR_END || ctx->ir_base[input].op == IR_LOOP_END); + ctx->rules[input] = IR_IGOTO_DUP; + } + } + ir_match_fuse_load(ctx, insn->op2, ref); + return insn->op; case IR_VAR: - return IR_SKIPPED | IR_VAR; + return IR_STATIC_ALLOCA; case IR_PARAM: #ifndef _WIN64 if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) { @@ -2617,7 +2630,15 @@ store_int: return IR_VSTORE_FP; } break; + case IR_VSTORE_v: + if (IR_IS_TYPE_INT(ctx->ir_base[insn->op3].type)) { + return IR_VSTORE_INT; + } else { + return IR_VSTORE_FP; + } + break; case IR_LOAD: + case IR_LOAD_v: ir_match_fuse_addr(ctx, insn->op2); if (IR_IS_TYPE_INT(insn->type)) { return IR_LOAD_INT; @@ -2635,6 +2656,14 @@ store_int: return IR_STORE_FP; } break; + case IR_STORE_v: + ir_match_fuse_addr(ctx, insn->op2); + if (IR_IS_TYPE_INT(ctx->ir_base[insn->op3].type)) { + return IR_STORE_INT; + } else { + return IR_STORE_FP; + } + break; case IR_RLOAD: if (IR_REGSET_IN(IR_REGSET_UNION((ir_regset)ctx->fixed_regset, IR_REGSET_FIXED), insn->op2)) { return IR_SKIPPED | IR_RLOAD; @@ -3175,7 +3204,7 @@ static void ir_emit_load_imm_fp(ir_ctx *ctx, ir_type type, ir_reg reg, ir_ref sr | xorpd xmm(reg-IR_REG_FP_FIRST), xmm(reg-IR_REG_FP_FIRST) } } else { - label = ir_const_label(ctx, src); + label = ir_get_const_label(ctx, src); | ASM_FP_REG_TXT_OP movs, type, reg, [=>label] } } @@ -3229,6 +3258,38 @@ static void ir_load_local_addr(ir_ctx *ctx, ir_reg reg, ir_ref src) } } +static void ir_resolve_label_syms(ir_ctx *ctx) +{ + uint32_t b; + ir_block *bb; + + for (b = 1, bb = &ctx->cfg_blocks[b]; b <= ctx->cfg_blocks_count; bb++, b++) { + ir_insn *insn = &ctx->ir_base[bb->start]; + + if (insn->op == IR_BEGIN && insn->op2) { + IR_ASSERT(ctx->ir_base[insn->op2].op == IR_LABEL); + ctx->ir_base[insn->op2].val.u32_hi = b; + } + } +} + +static void ir_emit_load_label_addr(ir_ctx *ctx, ir_reg reg, ir_insn *label) +{ + ir_backend_data *data = ctx->data; + dasm_State **Dst = &data->dasm_state; + + if (!data->resolved_label_syms) { + data->resolved_label_syms = 1; + ir_resolve_label_syms(ctx); + } + + IR_ASSERT(label->op == IR_LABEL); + int b = label->val.u32_hi; + + b = ir_skip_empty_target_blocks(ctx, b); + | lea Ra(reg), aword [=>b] +} + static void ir_emit_load(ir_ctx *ctx, ir_type type, ir_reg reg, ir_ref src) { if (IR_IS_CONST_REF(src)) { @@ -3241,9 +3302,11 @@ static void ir_emit_load(ir_ctx *ctx, ir_type type, ir_reg reg, ir_ref src) } else if (insn->op == IR_STR) { ir_backend_data *data = ctx->data; dasm_State **Dst = &data->dasm_state; - int label = ir_const_label(ctx, src); + int label = ir_get_const_label(ctx, src); | lea Ra(reg), aword [=>label] + } else if (insn->op == IR_LABEL) { + ir_emit_load_label_addr(ctx, reg, insn); } else { ir_emit_load_imm_int(ctx, type, reg, insn->val.i64); } @@ -3289,7 +3352,7 @@ static void ir_emit_store_mem_int_const(ir_ctx *ctx, ir_type type, ir_mem mem, i IR_ASSERT(IR_IS_CONST_REF(src)); if (val_insn->op == IR_STR) { - int label = ir_const_label(ctx, src); + int label = ir_get_const_label(ctx, src); IR_ASSERT(tmp_reg != IR_REG_NONE); |.if X64 @@ -3298,6 +3361,11 @@ static void ir_emit_store_mem_int_const(ir_ctx *ctx, ir_type type, ir_mem mem, i |.else | ASM_TMEM_TXT_OP mov, aword, mem, =>label |.endif + } else if (val_insn->op == IR_LABEL) { + IR_ASSERT(tmp_reg != IR_REG_NONE); + tmp_reg = IR_REG_NUM(tmp_reg); + ir_emit_load_label_addr(ctx, tmp_reg, val_insn); + ir_emit_store_mem_int(ctx, type, mem, tmp_reg); } else { int64_t val = val_insn->val.i64; @@ -3726,7 +3794,8 @@ static ir_mem ir_fuse_load(ir_ctx *ctx, ir_ref root, ir_ref ref) ir_insn *load_insn = &ctx->ir_base[ref]; ir_reg reg; - IR_ASSERT(load_insn->op == IR_LOAD); + IR_ASSERT(load_insn->op == IR_LOAD || load_insn->op == IR_LOAD_v || + load_insn->op == IR_VLOAD || load_insn->op == IR_VLOAD_v); if (UNEXPECTED(ctx->rules[ref] & IR_FUSED_REG)) { reg = ir_get_fused_reg(ctx, root, ref * sizeof(ir_ref) + 2); } else { @@ -3762,9 +3831,11 @@ static void ir_emit_load_ex(ir_ctx *ctx, ir_type type, ir_reg reg, ir_ref src, i } else if (insn->op == IR_STR) { ir_backend_data *data = ctx->data; dasm_State **Dst = &data->dasm_state; - int label = ir_const_label(ctx, src); + int label = ir_get_const_label(ctx, src); | lea Ra(reg), aword [=>label] + } else if (insn->op == IR_LABEL) { + ir_emit_load_label_addr(ctx, reg, insn); } else { ir_emit_load_imm_int(ctx, type, reg, insn->val.i64); } @@ -3862,7 +3933,7 @@ static void ir_emit_prologue(ir_ctx *ctx) if (ctx->flags & IR_USE_FRAME_POINTER) { fp = IR_REG_FRAME_POINTER; - offset = -(ctx->stack_frame_size - ctx->stack_frame_alignment - ctx->locals_area_size); + offset = -(ctx->stack_frame_size - ctx->locals_area_size); } else { fp = IR_REG_STACK_POINTER; offset = ctx->locals_area_size + ctx->call_stack_size; @@ -5607,7 +5678,7 @@ static void ir_emit_binop_sse2(ir_ctx *ctx, ir_ref def, ir_insn *insn) break; } } else if (IR_IS_CONST_REF(op2)) { - int label = ir_const_label(ctx, op2); + int label = ir_get_const_label(ctx, op2); switch (insn->op) { default: @@ -5714,7 +5785,7 @@ static void ir_emit_binop_avx(ir_ctx *ctx, ir_ref def, ir_insn *insn) break; } } else if (IR_IS_CONST_REF(op2)) { - int label = ir_const_label(ctx, op2); + int label = ir_get_const_label(ctx, op2); switch (insn->op) { default: @@ -6126,7 +6197,7 @@ static ir_op ir_emit_cmp_fp_common(ir_ctx *ctx, ir_ref root, ir_ref cmp_ref, ir_ } | ASM_FP_REG_REG_OP ucomis, type, op1_reg, op2_reg } else if (IR_IS_CONST_REF(op2)) { - int label = ir_const_label(ctx, op2); + int label = ir_get_const_label(ctx, op2); | ASM_FP_REG_TXT_OP ucomis, type, op1_reg, [=>label] } else { @@ -6975,7 +7046,7 @@ static void ir_emit_return_fp(ir_ctx *ctx, ir_ref ref, ir_insn *insn) } else if ((type == IR_FLOAT && value->val.f == 1.0) || (type == IR_DOUBLE && value->val.d == 1.0)) { | fld1 } else { - int label = ir_const_label(ctx, insn->op2); + int label = ir_get_const_label(ctx, insn->op2); if (type == IR_DOUBLE) { | fld qword [=>label] @@ -7260,7 +7331,20 @@ static void ir_emit_trunc(ir_ctx *ctx, ir_ref def, ir_insn *insn) ir_emit_load(ctx, src_type, op1_reg, insn->op1); } if (op1_reg != def_reg) { +#ifdef IR_TARGET_X86 + if (ir_type_size[dst_type] == 1 + && (op1_reg == IR_REG_RBP || op1_reg == IR_REG_RSI || op1_reg == IR_REG_RDI)) { + ir_backend_data *data = ctx->data; + dasm_State **Dst = &data->dasm_state; + + ir_emit_mov(ctx, src_type, def_reg, op1_reg); + | and Rb(def_reg), 0xff + } else { + ir_emit_mov(ctx, dst_type, def_reg, op1_reg); + } +#else ir_emit_mov(ctx, dst_type, def_reg, op1_reg); +#endif } } else { ir_emit_load_ex(ctx, dst_type, def_reg, insn->op1, def); @@ -7385,7 +7469,7 @@ static void ir_emit_bitcast(ir_ctx *ctx, ir_ref def, ir_insn *insn) } } } else if (IR_IS_CONST_REF(insn->op1)) { - int label = ir_const_label(ctx, insn->op1); + int label = ir_get_const_label(ctx, insn->op1); | ASM_FP_REG_TXT_OP movs, dst_type, def_reg, [=>label] } else { @@ -7417,13 +7501,80 @@ static void ir_emit_int2fp(ir_ctx *ctx, ir_ref def, ir_insn *insn) IR_ASSERT(IR_IS_TYPE_INT(src_type)); IR_ASSERT(IR_IS_TYPE_FP(dst_type)); IR_ASSERT(def_reg != IR_REG_NONE); + + if (op1_reg != IR_REG_NONE && IR_REG_SPILLED(op1_reg)) { + op1_reg = IR_REG_NUM(op1_reg); + ir_emit_load(ctx, src_type, op1_reg, insn->op1); + } + + if (IR_IS_TYPE_UNSIGNED(src_type) && ir_type_size[src_type] >= sizeof(void*)) { + ir_reg tmp_reg = ctx->regs[def][2]; + + IR_ASSERT(tmp_reg != IR_REG_NONE); + if (op1_reg == IR_REG_NONE) { + if (IR_IS_CONST_REF(insn->op1)) { + IR_ASSERT(0); + } else { + ir_mem mem; + + if (ir_rule(ctx, insn->op1) & IR_FUSED) { + mem = ir_fuse_load(ctx, def, insn->op1); + } else { + mem = ir_ref_spill_slot(ctx, insn->op1); + } + ir_emit_load_mem_int(ctx, src_type, tmp_reg, mem); + op1_reg = tmp_reg; + } + } + if (sizeof(void*) == 4) { + if (tmp_reg == op1_reg) { + | add Rd(op1_reg), 0x80000000 + } else { + | lea Rd(tmp_reg), dword [Rd(op1_reg)+0x80000000] + op1_reg = tmp_reg; + } + } else { +|.if X64 + | test Rq(op1_reg), Rq(op1_reg) + | js >1 + |.cold_code + |1: + if (tmp_reg != op1_reg) { + | mov Rq(tmp_reg), Rq(op1_reg) + } + | shr Rq(tmp_reg), 1 + | adc Rq(tmp_reg), 0 + if (dst_type == IR_DOUBLE) { + if (ctx->mflags & IR_X86_AVX) { + | vxorps xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST) + | vcvtsi2sd xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST), Rq(tmp_reg) + | vaddsd xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST) + } else { + | pxor xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST) + | cvtsi2sd xmm(def_reg-IR_REG_FP_FIRST), Rq(tmp_reg) + | addsd xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST) + } + } else { + IR_ASSERT(dst_type == IR_FLOAT); + if (ctx->mflags & IR_X86_AVX) { + | vxorps xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST) + | vcvtsi2ss xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST), Rq(tmp_reg) + | vaddss xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST) + } else { + | pxor xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST) + | cvtsi2ss xmm(def_reg-IR_REG_FP_FIRST), Rq(tmp_reg) + | addss xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST) + } + } + | jmp >2 + |.code +|.endif + } + } + if (op1_reg != IR_REG_NONE) { bool src64 = 0; - if (IR_REG_SPILLED(op1_reg)) { - op1_reg = IR_REG_NUM(op1_reg); - ir_emit_load(ctx, src_type, op1_reg, insn->op1); - } if (IR_IS_TYPE_SIGNED(src_type)) { if (ir_type_size[src_type] < 4) { |.if X64 @@ -7462,7 +7613,6 @@ static void ir_emit_int2fp(ir_ctx *ctx, ir_ref def, ir_insn *insn) || } |.endif } else { - // TODO: uint64_t -> double src64 = 1; } } @@ -7508,6 +7658,40 @@ static void ir_emit_int2fp(ir_ctx *ctx, ir_ref def, ir_insn *insn) } |.endif } + |2: + if (sizeof(void*) == 4 && IR_IS_TYPE_UNSIGNED(src_type) && ir_type_size[src_type] >= sizeof(void*)) { + if (dst_type == IR_DOUBLE) { + uint32_t c = (sizeof(void*) == 4) ? 0x41e00000 : 0x43e00000; + if (!data->u2d_const) { + data->u2d_const = 1; + ir_rodata(ctx); + |.align 8 + |->u2d_const: + |.dword 0, c + |.code + } + if (ctx->mflags & IR_X86_AVX) { + | vaddsd xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST), qword [->u2d_const] + } else { + | addsd xmm(def_reg-IR_REG_FP_FIRST), qword [->u2d_const] + } + } else { + uint32_t c = (sizeof(void*) == 4) ? 0x4f000000 : 0x5f000000; + if (!data->u2f_const) { + data->u2f_const = 1; + ir_rodata(ctx); + |.align 4 + |->u2f_const: + |.dword c + |.code + } + if (ctx->mflags & IR_X86_AVX) { + | vaddss xmm(def_reg-IR_REG_FP_FIRST), xmm(def_reg-IR_REG_FP_FIRST), dword [->u2f_const] + } else { + | addss xmm(def_reg-IR_REG_FP_FIRST), dword [->u2f_const] + } + } + } } else if (IR_IS_CONST_REF(insn->op1)) { IR_ASSERT(0); } else { @@ -7625,7 +7809,7 @@ static void ir_emit_fp2int(ir_ctx *ctx, ir_ref def, ir_insn *insn) |.endif } } else if (IR_IS_CONST_REF(insn->op1)) { - int label = ir_const_label(ctx, insn->op1); + int label = ir_get_const_label(ctx, insn->op1); if (!dst64) { if (src_type == IR_DOUBLE) { @@ -7746,7 +7930,7 @@ static void ir_emit_fp2fp(ir_ctx *ctx, ir_ref def, ir_insn *insn) } } } else if (IR_IS_CONST_REF(insn->op1)) { - int label = ir_const_label(ctx, insn->op1); + int label = ir_get_const_label(ctx, insn->op1); if (src_type == IR_DOUBLE) { if (ctx->mflags & IR_X86_AVX) { @@ -8429,7 +8613,7 @@ static void ir_emit_va_start(ir_ctx *ctx, ir_ref def, ir_insn *insn) if (ctx->flags & IR_USE_FRAME_POINTER) { fp = IR_REG_FRAME_POINTER; - reg_save_area_offset = -(ctx->stack_frame_size - ctx->stack_frame_alignment - ctx->locals_area_size); + reg_save_area_offset = -(ctx->stack_frame_size - ctx->locals_area_size); overflow_arg_area_offset = sizeof(void*) * 2 + ctx->param_stack_size; } else { fp = IR_REG_STACK_POINTER; @@ -8588,11 +8772,11 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) } | add Ra(tmp_reg), IR_MAX(ir_type_size[type], sizeof(void*)) } else { - int size = (uint32_t)insn->op3 >> 3; + int size = IR_VA_ARG_SIZE(insn->op3); if (def_reg != IR_REG_NONE) { IR_ASSERT(type == IR_ADDR); - int align = 1U << (insn->op3 & 0x7); + int align = IR_VA_ARG_ALIGN(insn->op3); if (align > (int)sizeof(void*)) { | add Ra(tmp_reg), (align-1) @@ -8604,7 +8788,7 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) } #endif | mov aword [Ra(op2_reg)+offset], Ra(tmp_reg) - if (def_reg && IR_REG_SPILLED(ctx->regs[def][0])) { + if (def_reg != IR_REG_NONE && IR_REG_SPILLED(ctx->regs[def][0])) { ir_emit_store(ctx, type, def, def_reg); } #elif defined(IR_TARGET_X64) @@ -8632,8 +8816,8 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) if (insn->op3) { /* long struct arguemnt */ IR_ASSERT(type == IR_ADDR); - int align = 1U << (insn->op3 & 0x7); - int size = (uint32_t)insn->op3 >> 3; + int align = IR_VA_ARG_ALIGN(insn->op3); + int size = IR_VA_ARG_SIZE(insn->op3); | mov Ra(tmp_reg), aword [Ra(op2_reg)+(offset+offsetof(ir_va_list, overflow_arg_area))] if (align > (int)sizeof(void*)) { @@ -9701,6 +9885,19 @@ static void ir_emit_ijmp(ir_ctx *ctx, ir_ref def, ir_insn *insn) ir_reg op2_reg = ctx->regs[def][2]; if (IR_IS_CONST_REF(insn->op2)) { + if (ctx->ir_base[insn->op2].op == IR_LABEL) { + if (!data->resolved_label_syms) { + data->resolved_label_syms = 1; + ir_resolve_label_syms(ctx); + } + + uint32_t target = ctx->ir_base[insn->op2].val.u32_hi; + target = ir_skip_empty_target_blocks(ctx, target); + + | jmp =>target + return; + } + void *addr = ir_jmp_addr(ctx, insn, &ctx->ir_base[insn->op2]); if (sizeof(void*) == 4 || IR_MAY_USE_32BIT_ADDR(ctx->code_buffer, addr)) { @@ -10478,6 +10675,7 @@ static void ir_emit_param_move(ir_ctx *ctx, uint8_t type, ir_reg from_reg, ir_re { ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; + offset = IR_SPILL_POS_TO_OFFSET(offset); IR_ASSERT(from_reg != IR_REG_NONE || to_reg != IR_REG_NONE); if (IR_IS_TYPE_INT(type)) { @@ -10518,6 +10716,7 @@ static void ir_emit_load_params(ir_ctx *ctx) const int8_t *int_reg_params = _ir_int_reg_params; const int8_t *fp_reg_params = _ir_fp_reg_params; int32_t stack_offset = 0; + int32_t stack_start = 0; #ifdef IR_TARGET_X86 if (sizeof(void*) == 4 && (ctx->flags & IR_FASTCALL_FUNC)) { @@ -10529,9 +10728,11 @@ static void ir_emit_load_params(ir_ctx *ctx) #endif if (ctx->flags & IR_USE_FRAME_POINTER) { - stack_offset = sizeof(void*) * 2; /* skip old frame pointer and return address */ + /* skip old frame pointer and return address */ + stack_start = sizeof(void*) * 2 + ctx->stack_frame_size; } else { - stack_offset = sizeof(void*) + ctx->stack_frame_size + ctx->call_stack_size; /* skip return address */ + /* skip return address */ + stack_start = sizeof(void*) + ctx->stack_frame_size; } n = use_list->count; for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) { @@ -10573,12 +10774,9 @@ static void ir_emit_load_params(ir_ctx *ctx) if (ctx->vregs[use]) { dst_reg = IR_REG_NUM(ctx->regs[use][0]); IR_ASSERT(src_reg != IR_REG_NONE || dst_reg != IR_REG_NONE || - stack_offset == ctx->live_intervals[ctx->vregs[use]]->stack_spill_pos + - ((ctx->flags & IR_USE_FRAME_POINTER) ? - -(ctx->stack_frame_size - ctx->stack_frame_alignment) : - ctx->call_stack_size)); + stack_start + stack_offset == ctx->live_intervals[ctx->vregs[use]]->stack_spill_pos); if (src_reg != dst_reg) { - ir_emit_param_move(ctx, insn->type, src_reg, dst_reg, use, stack_offset); + ir_emit_param_move(ctx, insn->type, src_reg, dst_reg, use, stack_start + stack_offset); } if (dst_reg != IR_REG_NONE && IR_REG_SPILLED(ctx->regs[use][0])) { ir_emit_store(ctx, insn->type, use, dst_reg); @@ -10665,7 +10863,7 @@ static void ir_fix_param_spills(ir_ctx *ctx) if (ctx->flags & IR_USE_FRAME_POINTER) { /* skip old frame pointer and return address */ - stack_start = sizeof(void*) * 2 + (ctx->stack_frame_size - ctx->stack_frame_alignment); + stack_start = sizeof(void*) * 2 + ctx->stack_frame_size; } else { /* skip return address */ stack_start = sizeof(void*) + ctx->stack_frame_size; @@ -10786,6 +10984,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) case IR_MERGE: case IR_LOOP_BEGIN: case IR_LOOP_END: + case IR_IGOTO_DUP: break; #ifndef IR_REG_FP_RET1 case IR_CALL: @@ -10810,7 +11009,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) IR_REGSET_EXCL(available, reg); ctx->regs[i][0] = reg | IR_REG_SPILL_STORE; } else if (def_flags & IR_USE_MUST_BE_IN_REG) { - if (insn->op == IR_VLOAD + if ((insn->op == IR_VLOAD || insn->op == IR_VLOAD_v) && ctx->live_intervals[ctx->vregs[i]] && ctx->live_intervals[ctx->vregs[i]]->stack_spill_pos != -1 && ir_is_same_mem_var(ctx, i, ctx->ir_base[insn->op2].op3)) { @@ -10850,7 +11049,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) { use = *p; use_insn = &ctx->ir_base[use]; - if (use_insn->op == IR_VLOAD) { + if (use_insn->op == IR_VLOAD || use_insn->op == IR_VLOAD_v) { if (ctx->vregs[use] && !ctx->live_intervals[ctx->vregs[use]]) { ir_live_interval *ival = ir_arena_alloc(&ctx->arena, sizeof(ir_live_interval)); @@ -10861,7 +11060,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) ival->vreg = ctx->vregs[use]; ival->stack_spill_pos = stack_spill_pos; } - } else if (use_insn->op == IR_VSTORE) { + } else if (use_insn->op == IR_VSTORE || use_insn->op == IR_VSTORE_v) { if (!IR_IS_CONST_REF(use_insn->op3) && ctx->vregs[use_insn->op3] && !ctx->live_intervals[ctx->vregs[use_insn->op3]]) { @@ -11006,7 +11205,6 @@ void ir_fix_stack_frame(ir_ctx *ctx) ctx->stack_frame_size = IR_ALIGNED_SIZE(ctx->stack_frame_size, sizeof(void*)); ctx->stack_frame_size += additional_size; - ctx->stack_frame_alignment = 0; ctx->call_stack_size = 0; if (ctx->flags2 & IR_16B_FRAME_ALIGNMENT) { @@ -11014,12 +11212,10 @@ void ir_fix_stack_frame(ir_ctx *ctx) if (!(ctx->flags & IR_FUNCTION)) { while (IR_ALIGNED_SIZE(ctx->stack_frame_size, 16) != ctx->stack_frame_size) { ctx->stack_frame_size += sizeof(void*); - ctx->stack_frame_alignment += sizeof(void*); } } else if (ctx->flags & IR_USE_FRAME_POINTER) { while (IR_ALIGNED_SIZE(ctx->stack_frame_size + sizeof(void*) * 2, 16) != ctx->stack_frame_size + sizeof(void*) * 2) { ctx->stack_frame_size += sizeof(void*); - ctx->stack_frame_alignment += sizeof(void*); } } else { if (!(ctx->flags & IR_NO_STACK_COMBINE)) { @@ -11028,7 +11224,6 @@ void ir_fix_stack_frame(ir_ctx *ctx) while (IR_ALIGNED_SIZE(ctx->stack_frame_size + ctx->call_stack_size + sizeof(void*), 16) != ctx->stack_frame_size + ctx->call_stack_size + sizeof(void*)) { ctx->stack_frame_size += sizeof(void*); - ctx->stack_frame_alignment += sizeof(void*); } } } @@ -11061,6 +11256,8 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) int ret; void *entry; size_t size; + ir_ref igoto_dup_ref = IR_UNUSED; + uint32_t igoto_dup_block = 0; data.ra_data.unused_slot_4 = 0; data.ra_data.unused_slot_2 = 0; @@ -11073,11 +11270,13 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) data.double_abs_const = 0; data.float_abs_const = 0; data.double_zero_const = 0; + data.u2d_const = 0; + data.u2f_const = 0; + data.resolved_label_syms = 0; ctx->data = &data; if (!ctx->live_intervals) { ctx->stack_frame_size = 0; - ctx->stack_frame_alignment = 0; ctx->call_stack_size = 0; ctx->used_preserved_regs = 0; ir_allocate_unique_spill_slots(ctx); @@ -11099,7 +11298,6 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) } ctx->stack_frame_size = ctx->fixed_stack_frame_size; ctx->call_stack_size = ctx->fixed_call_stack_size; - ctx->stack_frame_alignment = 0; } Dst = &data.dasm_state; @@ -11420,6 +11618,35 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) case IR_TAILCALL: ir_emit_tailcall(ctx, i, insn); break; + case IR_IGOTO_DUP: + if (bb->flags & IR_BB_DESSA_MOVES) { + ir_emit_dessa_moves(ctx, b, bb); + } + IR_ASSERT(!igoto_dup_ref && !igoto_dup_block); + igoto_dup_ref = i; + igoto_dup_block = b; + b = ctx->cfg_edges[bb->successors]; + bb = &ctx->cfg_blocks[b]; + i = bb->start; + insn = &ctx->ir_base[i]; + rule = &ctx->rules[i]; + break; + case IR_IGOTO: + if ((ctx->ir_base[insn->op1].op == IR_MERGE || ctx->ir_base[insn->op1].op == IR_LOOP_BEGIN) + && (ctx->rules[ctx->ir_base[insn->op1].op1] & IR_RULE_MASK) == IR_IGOTO_DUP + && igoto_dup_ref) { + ir_emit_ijmp(ctx, i, insn); + b = igoto_dup_block; + bb = &ctx->cfg_blocks[b]; + i = igoto_dup_ref; + insn = &ctx->ir_base[i]; + rule = &ctx->rules[i]; + igoto_dup_block= 0; + igoto_dup_ref = 0; + break; + } + IR_ASSERT(!igoto_dup_ref && !igoto_dup_block); + IR_FALLTHROUGH; case IR_IJMP: ir_emit_ijmp(ctx, i, insn); break; @@ -11449,6 +11676,7 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) ir_emit_vaddr(ctx, i, insn); break; case IR_VLOAD: + case IR_VLOAD_v: ir_emit_vload(ctx, i, insn); break; case IR_VSTORE_INT: @@ -11691,6 +11919,28 @@ next_block:; } while (i != 0); } + if ((ctx->flags2 & IR_HAS_BLOCK_ADDR) && ctx->loader && ctx->loader->add_label) { + for (b = 1, bb = &ctx->cfg_blocks[b]; b <= ctx->cfg_blocks_count; bb++, b++) { + ir_insn *insn = &ctx->ir_base[bb->start]; + + if (insn->op == IR_BEGIN && insn->op2) { + IR_ASSERT(ctx->ir_base[insn->op2].op == IR_LABEL); + ctx->ir_base[insn->op2].val.u32_hi = 0; + ctx->loader->add_label(ctx->loader, ir_get_str(ctx, ctx->ir_base[insn->op2].val.str), + (char*)entry + dasm_getpclabel(&data.dasm_state, ir_skip_empty_target_blocks(ctx, b))); + } + } + } else if (data.resolved_label_syms) { + for (b = 1, bb = &ctx->cfg_blocks[b]; b <= ctx->cfg_blocks_count; bb++, b++) { + ir_insn *insn = &ctx->ir_base[bb->start]; + + if (insn->op == IR_BEGIN && insn->op2) { + IR_ASSERT(ctx->ir_base[insn->op2].op == IR_LABEL); + ctx->ir_base[insn->op2].val.u32_hi = 0; + } + } + } + dasm_free(&data.dasm_state); ir_mem_flush(entry, size); From 85cb6e421a0bdb68e47b174255f98e1d192b8785 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:08:45 +0100 Subject: [PATCH 163/252] Fix GH-20695: Assertion failure in normalize_value() when parsing malformed INI input via parse_ini_string() I think there's simply a reasoning error about when which scanner state can cause which parser component to invoke later on. Closes GH-20702. --- NEWS | 2 ++ Zend/zend_ini_scanner.l | 2 +- ext/standard/tests/gh20695.phpt | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/gh20695.phpt diff --git a/NEWS b/NEWS index 5103b4a96791c..5cbee8cb2c534 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ PHP NEWS - Core: . Fix OSS-Fuzz #465488618 (Wrong assumptions when dumping function signature with dynamic class const lookup default argument). (ilutov) + . Fixed bug GH-20695 (Assertion failure in normalize_value() when parsing + malformed INI input via parse_ini_string()). (ndossche) - Bz2: . Fixed bug GH-20620 (bzcompress overflow on large source size). diff --git a/Zend/zend_ini_scanner.l b/Zend/zend_ini_scanner.l index b87f4e33cc8f8..b4013e8334f67 100644 --- a/Zend/zend_ini_scanner.l +++ b/Zend/zend_ini_scanner.l @@ -145,10 +145,10 @@ ZEND_API zend_ini_scanner_globals ini_scanner_globals; if (SCNG(scanner_mode) == ZEND_INI_SCANNER_TYPED && \ (YYSTATE == STATE(ST_VALUE) || YYSTATE == STATE(ST_RAW))) {\ zend_ini_copy_typed_value(ini_lval, type, str, len); \ - Z_EXTRA_P(ini_lval) = 0; \ } else { \ zend_ini_copy_value(ini_lval, str, len); \ } \ + Z_EXTRA_P(ini_lval) = 0; \ return type; \ } diff --git a/ext/standard/tests/gh20695.phpt b/ext/standard/tests/gh20695.phpt new file mode 100644 index 0000000000000..64c81ab9fdb54 --- /dev/null +++ b/ext/standard/tests/gh20695.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-20695 (Assertion failure in normalize_value() when parsing malformed INI input via parse_ini_string()) +--FILE-- + +--EXPECT-- +array(1) { + [8]=> + array(1) { + ["["]=> + int(0) + } +} From 0d7e53535b9b0d23f7233c9d5e6562376b29926e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 14 Dec 2025 00:31:19 +0100 Subject: [PATCH 164/252] Fix NUL byte truncation in sqlite3 TEXT column handling As a bonus, this should probably also be a tad faster. Closes GH-20704. --- NEWS | 3 ++ ext/sqlite3/sqlite3.c | 2 +- ext/sqlite3/tests/text_column_NUL_bytes.phpt | 38 ++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 ext/sqlite3/tests/text_column_NUL_bytes.phpt diff --git a/NEWS b/NEWS index e3c32a4e1648b..9fa63137d8851 100644 --- a/NEWS +++ b/NEWS @@ -60,6 +60,9 @@ PHP NEWS . DirectoryIterator key can now work better with filesystem supporting larger directory indexing. (David Carlier) +- Sqlite3: + . Fix NUL byte truncation in sqlite3 TEXT column handling. (ndossche) + - Standard: . Fixed bug GH-19926 (reset internal pointer earlier while splicing array while COW violation flag is still set). (alexandre-daubois) diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 0ef207d76fd6a..da24b037861a8 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -648,7 +648,7 @@ static void sqlite_value_to_zval(sqlite3_stmt *stmt, int column, zval *data) /* break; case SQLITE3_TEXT: - ZVAL_STRING(data, (char*)sqlite3_column_text(stmt, column)); + ZVAL_STRINGL(data, (const char *) sqlite3_column_text(stmt, column), sqlite3_column_bytes(stmt, column)); break; case SQLITE_BLOB: diff --git a/ext/sqlite3/tests/text_column_NUL_bytes.phpt b/ext/sqlite3/tests/text_column_NUL_bytes.phpt new file mode 100644 index 0000000000000..cf9403d91302d --- /dev/null +++ b/ext/sqlite3/tests/text_column_NUL_bytes.phpt @@ -0,0 +1,38 @@ +--TEST-- +Text column with NUL bytes +--EXTENSIONS-- +sqlite3 +--FILE-- +exec( + 'CREATE TABLE messages ( + content TEXT + )' +); + +$insert = $db->prepare( + 'INSERT INTO messages (content) VALUES (:content)' +); + +$insert->bindValue(':content', "with\0null", SQLITE3_TEXT); +$insert->execute(); +$insert->bindValue(':content', "\0", SQLITE3_TEXT); +$insert->execute(); + +$result = $db->query('SELECT * FROM messages'); +while ($row = $result->fetchArray(SQLITE3_ASSOC)) { + var_dump($row); +} + +?> +--EXPECTF-- +array(1) { + ["content"]=> + string(9) "with%0null" +} +array(1) { + ["content"]=> + string(1) "%0" +} From 4a5a328f26594759ba5a02aaf13b607812ef85da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 15 Dec 2025 23:06:17 +0100 Subject: [PATCH 165/252] uri: Update to uriparser-1.0.0 (#20715) We're now cleanly back on a released version. No functional changes since the last import of the library. Version 0.9.10 never existed, the minimum version in config.m4 was already increased in anticipation of a new release that contained necessary bugfixes to prevent building against a uriparser without these fixes. It therefore also is adjusted to 1.0.0 for correctness without having an impact. --- ext/uri/config.m4 | 2 +- ext/uri/uriparser/include/uriparser/Uri.h | 2 +- ext/uri/uriparser/include/uriparser/UriBase.h | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index 99e0d6b476779..390d8eb223cb9 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -33,7 +33,7 @@ if test "$PHP_EXTERNAL_URIPARSER" = "no"; then $URIPARSER_DIR/src/UriSetScheme.c $URIPARSER_DIR/src/UriSetUserInfo.c $URIPARSER_DIR/src/UriShorten.c $URIPARSER_DIR/src/UriVersion.c" URI_CFLAGS="-DURI_STATIC_BUILD" else - PKG_CHECK_MODULES([LIBURIPARSER], [liburiparser >= 0.9.10]) + PKG_CHECK_MODULES([LIBURIPARSER], [liburiparser >= 1.0.0]) PHP_EVAL_LIBLINE([$LIBURIPARSER_LIBS], [URI_SHARED_LIBADD]) PHP_EVAL_INCLINE([$LIBURIPARSER_CFLAGS]) fi diff --git a/ext/uri/uriparser/include/uriparser/Uri.h b/ext/uri/uriparser/include/uriparser/Uri.h index ea52097d6de58..88976a4846271 100644 --- a/ext/uri/uriparser/include/uriparser/Uri.h +++ b/ext/uri/uriparser/include/uriparser/Uri.h @@ -1,4 +1,4 @@ -/* 207ee4485d5a4690064bec14d369884451a49ae32e907b5bc6502c2bfa338ca1 (0.9.9+) +/* 5abed1007be99942f49ffe603a894d277066b79b9cb824547af0f3b9481cb9ca (1.0.0+) * * uriparser - RFC 3986 URI parsing library * diff --git a/ext/uri/uriparser/include/uriparser/UriBase.h b/ext/uri/uriparser/include/uriparser/UriBase.h index f8256951fe34a..3a9a868e3bb18 100644 --- a/ext/uri/uriparser/include/uriparser/UriBase.h +++ b/ext/uri/uriparser/include/uriparser/UriBase.h @@ -50,9 +50,9 @@ # define URI_ANSI_TO_UNICODE(x) URI_ANSI_TO_UNICODE_HELPER(x) /* Version */ -# define URI_VER_MAJOR 0 -# define URI_VER_MINOR 9 -# define URI_VER_RELEASE 9 +# define URI_VER_MAJOR 1 +# define URI_VER_MINOR 0 +# define URI_VER_RELEASE 0 # define URI_VER_SUFFIX_ANSI "" # define URI_VER_SUFFIX_UNICODE URI_ANSI_TO_UNICODE(URI_VER_SUFFIX_ANSI) @@ -394,7 +394,7 @@ URI_PUBLIC int uriTestMemoryManager(UriMemoryManager * memory); * @see uriEmulateReallocarray * @see UriMemoryManager * @see uriTestMemoryManager - * @since 0.9.10 + * @since 1.0.0 */ URI_PUBLIC int uriTestMemoryManagerEx(UriMemoryManager * memory, UriBool challengeAlignment); From 06b8b75d2e24a78bc190ba2b8a2c016e552054d0 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 14 Feb 2025 11:57:20 +0100 Subject: [PATCH 166/252] Fix curl protocols test expectation Closes GH-17803 (cherry picked from commit 5b87faaaa79c68be6ccf68974ee0454878806f79) --- ext/curl/tests/check_win_config.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/curl/tests/check_win_config.phpt b/ext/curl/tests/check_win_config.phpt index b3beb044a7cfc..fc29e37281970 100644 --- a/ext/curl/tests/check_win_config.phpt +++ b/ext/curl/tests/check_win_config.phpt @@ -54,7 +54,7 @@ UNICODE => No ZSTD => No HSTS => Yes GSASL => No -Protocols => dict, file, ftp, ftps, gopher, %r(gophers, )?%rhttp, https, imap, imaps, ldap, ldaps, %r(mqtt, )?%rpop3, pop3s, rtsp, scp, sftp, smb, smbs, smtp, smtps, telnet, tftp +Protocols => dict, file, ftp, ftps, gopher, %r(gophers, )?%rhttp, https, imap, imaps, ldap, ldaps, %r(mqtt, )?%rpop3, pop3s, rtsp, scp, sftp, smb, smbs, smtp, smtps, telnet, tftp%r(, ws)?(, wss)?%r Host => %s-pc-win32 SSL Version => OpenSSL/%s ZLib Version => %s From c5f28c7cf0a052f48e47877c7aa5c5bcc54f1cfc Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:11:38 +0100 Subject: [PATCH 167/252] Fix GH-20584: Information Leak of Memory The string added had uninitialized memory due to php_read_stream_all_chunks() not moving the buffer position, resulting in the same data always being overwritten instead of new data being added to the end of the buffer. This is backport as there is a security impact as described in GHSA-3237-qqm7-mfv7 . --- ext/standard/image.c | 1 + ext/standard/tests/image/gh20584.phpt | 39 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 ext/standard/tests/image/gh20584.phpt diff --git a/ext/standard/image.c b/ext/standard/image.c index 5200295f3af28..269117318c4b9 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -434,6 +434,7 @@ static size_t php_read_stream_all_chunks(php_stream *stream, char *buffer, size_ if (read_now < stream->chunk_size && read_total != length) { return 0; } + buffer += read_now; } while (read_total < length); return read_total; diff --git a/ext/standard/tests/image/gh20584.phpt b/ext/standard/tests/image/gh20584.phpt new file mode 100644 index 0000000000000..d117f21820264 --- /dev/null +++ b/ext/standard/tests/image/gh20584.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-20584 (Information Leak of Memory) +--CREDITS-- +Nikita Sveshnikov (Positive Technologies) +--FILE-- + +--CLEAN-- + +--EXPECT-- +bool(true) From 727a4ddc39ca9b78131e06b5537fe8b6c3dd6fe7 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sat, 11 Oct 2025 19:37:26 +0200 Subject: [PATCH 168/252] Fix GHSA-8xr5-qppj-gvwj: PDO quoting result null deref --- ext/pdo/pdo_sql_parser.re | 6 +++++ ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt | 28 ++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt diff --git a/ext/pdo/pdo_sql_parser.re b/ext/pdo/pdo_sql_parser.re index 6bb0837fb31f9..7f4721d12a630 100644 --- a/ext/pdo/pdo_sql_parser.re +++ b/ext/pdo/pdo_sql_parser.re @@ -287,6 +287,12 @@ safe: } plc->quoted = stmt->dbh->methods->quoter(stmt->dbh, buf, param_type); + if (plc->quoted == NULL) { + /* bork */ + ret = -1; + strncpy(stmt->error_code, stmt->dbh->error_code, 6); + goto clean_up; + } } } diff --git a/ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt b/ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt new file mode 100644 index 0000000000000..736354cab13cb --- /dev/null +++ b/ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt @@ -0,0 +1,28 @@ +--TEST-- +#GHSA-8xr5-qppj-gvwj: NULL Pointer Derefernce for failed user input quoting +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + +$sql = "SELECT * FROM users where username = :username"; +$stmt = $db->prepare($sql); + +$p1 = "alice\x99"; +var_dump($stmt->execute(['username' => $p1])); + +?> +--EXPECT-- +bool(false) From 8b801151bd54b36aae4593ed6cfc096e8122b415 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:23:11 +0100 Subject: [PATCH 169/252] Fix GHSA-h96m-rvf9-jgm2 --- ext/standard/array.c | 7 ++++++- .../tests/array/GHSA-h96m-rvf9-jgm2.phpt | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt diff --git a/ext/standard/array.c b/ext/standard/array.c index 86d70a8844e30..e3474c7c8fda2 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3771,7 +3771,7 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET int argc, i; zval *src_entry; HashTable *src, *dest; - uint32_t count = 0; + uint64_t count = 0; ZEND_PARSE_PARAMETERS_START(0, -1) Z_PARAM_VARIADIC('+', args, argc) @@ -3791,6 +3791,11 @@ static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMET count += zend_hash_num_elements(Z_ARRVAL_P(arg)); } + if (UNEXPECTED(count >= HT_MAX_SIZE)) { + zend_throw_error(NULL, "The total number of elements must be lower than %u", HT_MAX_SIZE); + RETURN_THROWS(); + } + if (argc == 2) { zval *ret = NULL; diff --git a/ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt b/ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt new file mode 100644 index 0000000000000..2e3e85357e13c --- /dev/null +++ b/ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt @@ -0,0 +1,16 @@ +--TEST-- +GHSA-h96m-rvf9-jgm2 +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +The total number of elements must be lower than %d From ed70b1ea43a9b7ffa2f53b3e5d6ba403f37ae81c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 6 Sep 2025 21:55:13 +0200 Subject: [PATCH 170/252] Fix GHSA-www2-q4fc-65wf --- ext/standard/basic_functions.c | 12 ++-- ext/standard/dns.c | 6 +- ext/standard/dns_win32.c | 6 +- .../tests/network/ghsa-www2-q4fc-65wf.phpt | 62 +++++++++++++++++++ 4 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 ext/standard/tests/network/ghsa-www2-q4fc-65wf.phpt diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index ddaf1368410bc..18db1604678ca 100755 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -638,7 +638,7 @@ PHP_FUNCTION(inet_pton) char buffer[17]; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(address, address_len) + Z_PARAM_PATH(address, address_len) ZEND_PARSE_PARAMETERS_END(); memset(buffer, 0, sizeof(buffer)); @@ -675,7 +675,7 @@ PHP_FUNCTION(ip2long) #endif ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(addr, addr_len) + Z_PARAM_PATH(addr, addr_len) ZEND_PARSE_PARAMETERS_END(); #ifdef HAVE_INET_PTON @@ -2249,8 +2249,8 @@ PHP_FUNCTION(getservbyname) struct servent *serv; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STR(name) - Z_PARAM_STRING(proto, proto_len) + Z_PARAM_PATH_STR(name) + Z_PARAM_PATH(proto, proto_len) ZEND_PARSE_PARAMETERS_END(); @@ -2293,7 +2293,7 @@ PHP_FUNCTION(getservbyport) ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_LONG(port) - Z_PARAM_STRING(proto, proto_len) + Z_PARAM_PATH(proto, proto_len) ZEND_PARSE_PARAMETERS_END(); serv = getservbyport(htons((unsigned short) port), proto); @@ -2316,7 +2316,7 @@ PHP_FUNCTION(getprotobyname) struct protoent *ent; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(name, name_len) + Z_PARAM_PATH(name, name_len) ZEND_PARSE_PARAMETERS_END(); ent = getprotobyname(name); diff --git a/ext/standard/dns.c b/ext/standard/dns.c index 6d22e644a8eff..73a60f2a8259b 100644 --- a/ext/standard/dns.c +++ b/ext/standard/dns.c @@ -399,7 +399,7 @@ PHP_FUNCTION(dns_check_record) #endif ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(hostname, hostname_len) + Z_PARAM_PATH(hostname, hostname_len) Z_PARAM_OPTIONAL Z_PARAM_STR(rectype) ZEND_PARSE_PARAMETERS_END(); @@ -846,7 +846,7 @@ PHP_FUNCTION(dns_get_record) bool raw = 0; ZEND_PARSE_PARAMETERS_START(1, 5) - Z_PARAM_STRING(hostname, hostname_len) + Z_PARAM_PATH(hostname, hostname_len) Z_PARAM_OPTIONAL Z_PARAM_LONG(type_param) Z_PARAM_ZVAL(authns) @@ -1084,7 +1084,7 @@ PHP_FUNCTION(dns_get_mx) #endif ZEND_PARSE_PARAMETERS_START(2, 3) - Z_PARAM_STRING(hostname, hostname_len) + Z_PARAM_PATH(hostname, hostname_len) Z_PARAM_ZVAL(mx_list) Z_PARAM_OPTIONAL Z_PARAM_ZVAL(weight_list) diff --git a/ext/standard/dns_win32.c b/ext/standard/dns_win32.c index bef90cd61f283..0302b8542a0de 100644 --- a/ext/standard/dns_win32.c +++ b/ext/standard/dns_win32.c @@ -48,7 +48,7 @@ PHP_FUNCTION(dns_get_mx) /* {{{ */ DNS_STATUS status; /* Return value of DnsQuery_A() function */ PDNS_RECORD pResult, pRec; /* Pointer to DNS_RECORD structure */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|z", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "pz|z", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) { RETURN_THROWS(); } @@ -102,7 +102,7 @@ PHP_FUNCTION(dns_check_record) DNS_STATUS status; /* Return value of DnsQuery_A() function */ PDNS_RECORD pResult; /* Pointer to DNS_RECORD structure */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|S", &hostname, &hostname_len, &rectype) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|S", &hostname, &hostname_len, &rectype) == FAILURE) { RETURN_THROWS(); } @@ -354,7 +354,7 @@ PHP_FUNCTION(dns_get_record) int type, type_to_fetch, first_query = 1, store_results = 1; bool raw = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lz!z!b", + if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|lz!z!b", &hostname, &hostname_len, &type_param, &authns, &addtl, &raw) == FAILURE) { RETURN_THROWS(); } diff --git a/ext/standard/tests/network/ghsa-www2-q4fc-65wf.phpt b/ext/standard/tests/network/ghsa-www2-q4fc-65wf.phpt new file mode 100644 index 0000000000000..3d082c8e95226 --- /dev/null +++ b/ext/standard/tests/network/ghsa-www2-q4fc-65wf.phpt @@ -0,0 +1,62 @@ +--TEST-- +GHSA-www2-q4fc-65wf +--DESCRIPTION-- +This is a ZPP test but *keep* this as it is security-sensitive! +--FILE-- +getMessage(), "\n"; +} +try { + dns_get_mx("\0", $out); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + dns_get_record("\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + getprotobyname("\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + getservbyname("\0", "tcp"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + getservbyname("x", "tcp\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + getservbyport(0, "tcp\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + inet_pton("\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +try { + ip2long("\0"); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +dns_check_record(): Argument #1 ($hostname) must not contain any null bytes +dns_get_mx(): Argument #1 ($hostname) must not contain any null bytes +dns_get_record(): Argument #1 ($hostname) must not contain any null bytes +getprotobyname(): Argument #1 ($protocol) must not contain any null bytes +getservbyname(): Argument #1 ($service) must not contain any null bytes +getservbyname(): Argument #2 ($protocol) must not contain any null bytes +getservbyport(): Argument #2 ($protocol) must not contain any null bytes +inet_pton(): Argument #1 ($ip) must not contain any null bytes +ip2long(): Argument #1 ($ip) must not contain any null bytes From c48a9f42d336dc22e72141775022f4588ab4b2dd Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 12 Dec 2025 13:49:02 +0100 Subject: [PATCH 171/252] Update NEWS with info about security issues --- NEWS | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/NEWS b/NEWS index 998b5d97d6351..6ac638073fae6 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,18 @@ PHP NEWS . Reset global pointers to prevent use-after-free in zend_jit_status(). (Florian Engelhardt) +- PDO: + . Fixed GHSA-8xr5-qppj-gvwj (PDO quoting result null deref). (CVE-2025-14180) + (Jakub Zelenka) + +- Standard: + . Fixed GHSA-www2-q4fc-65wf (Null byte termination in dns_get_record()). + (ndossche) + . Fixed GHSA-h96m-rvf9-jgm2 (Heap buffer overflow in array_merge()). + (CVE-2025-14178) (ndossche) + . Fixed GHSA-3237-qqm7-mfv7 (Information Leak of Memory in getimagesize). + (CVE-2025-14177) (ndossche) + 03 Jul 2025, PHP 8.1.33 - PGSQL: From fb1ec9a5a7c35e0bdb4aa5b793d4aac7bb765a9c Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 15 Dec 2025 23:39:07 +0100 Subject: [PATCH 172/252] Fix uncatchable exception thrown in generator This procedure may be called during i_free_compiled_variables(), when EG(current_execute_data) is unfortunately already reset to the parent frame. EG(opline_before_exception) does not actually belong to this frame. Furthermore, setting opline to EG(exception_op) early will miss a later zend_rethrow_exception(), which will also miss installation of the correct EG(opline_before_exception). Fixes GH-20714 Closes GH-20716 --- NEWS | 1 + Zend/tests/gh20714.phpt | 29 +++++++++++++++++++++++++++++ Zend/zend_generators.c | 6 ++++-- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/gh20714.phpt diff --git a/NEWS b/NEWS index beff3f224ce2f..11ae976b3238f 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ PHP NEWS with dynamic class const lookup default argument). (ilutov) . Fixed bug GH-20695 (Assertion failure in normalize_value() when parsing malformed INI input via parse_ini_string()). (ndossche) + . Fixed bug GH-20714 (Uncatchable exception thrown in generator). (ilutov) - Bz2: . Fixed bug GH-20620 (bzcompress overflow on large source size). diff --git a/Zend/tests/gh20714.phpt b/Zend/tests/gh20714.phpt new file mode 100644 index 0000000000000..10ffde555f896 --- /dev/null +++ b/Zend/tests/gh20714.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-20714: Uncatchable exception thrown in generator +--CREDITS-- +Grégoire Paris (greg0ire) +--FILE-- + +--EXPECT-- +Caught diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 5ba215f788ce9..cfadf06b46f0b 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -321,7 +321,9 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ zend_object *old_exception = NULL; const zend_op *old_opline_before_exception = NULL; if (EG(exception)) { - if (EG(current_execute_data)) { + if (EG(current_execute_data) + && EG(current_execute_data)->opline + && EG(current_execute_data)->opline->opcode == ZEND_HANDLE_EXCEPTION) { EG(current_execute_data)->opline = EG(opline_before_exception); old_opline_before_exception = EG(opline_before_exception); } @@ -337,7 +339,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ zend_generator_resume(generator); if (old_exception) { - if (EG(current_execute_data)) { + if (old_opline_before_exception) { EG(current_execute_data)->opline = EG(exception_op); EG(opline_before_exception) = old_opline_before_exception; } From 6836c230ed665116a41afd06f184d0937f6b16d3 Mon Sep 17 00:00:00 2001 From: Pierrick Charron Date: Tue, 16 Dec 2025 12:42:07 -0500 Subject: [PATCH 173/252] PHP-8.2 is now for PHP 8.2.31-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 117481ff89549..21a709ab4212d 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.2.30 +?? ??? ????, PHP 8.2.31 + + +18 Dec 2025, PHP 8.2.30 - Curl: . Fix curl build and test failures with version 8.16. diff --git a/Zend/zend.h b/Zend/zend.h index e3a416096f2af..f43d20d59e472 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.2.30-dev" +#define ZEND_VERSION "4.2.31-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 4c43bd7d330f7..1c8244701a68a 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.2.30-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.2.31-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 6251ec54ebe62..5fa68abe48894 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 2 -#define PHP_RELEASE_VERSION 30 +#define PHP_RELEASE_VERSION 31 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.2.30-dev" -#define PHP_VERSION_ID 80230 +#define PHP_VERSION "8.2.31-dev" +#define PHP_VERSION_ID 80231 From b19a3536e83a58a0f5319703c9e34f18c9153b5f Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Tue, 16 Dec 2025 15:45:08 -0300 Subject: [PATCH 174/252] [ci skip] PHP 8.5 | UPGRADING: document IMAGETYPE_HEIF constant (#20713) --- UPGRADING | 1 + 1 file changed, 1 insertion(+) diff --git a/UPGRADING b/UPGRADING index 94b272c69c70f..cc1ff9938ad53 100644 --- a/UPGRADING +++ b/UPGRADING @@ -935,6 +935,7 @@ PHP 8.5 UPGRADE NOTES . T_PIPE. - Standard: + . IMAGETYPE_HEIF. . IMAGETYPE_SVG when libxml is loaded. ======================================== From bb70c5589ab76399e5b090200dc1a485014ac3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 17 Dec 2025 13:58:51 +0100 Subject: [PATCH 175/252] Update GitHub Action workflows to `actions/checkout@v6` Keep this up to date in all nonbranches, because the node.js runtime for older versions might get deprecated in the future and fixing this for all branches at once is easier. see 2650248a927224aa5b76130d406a691ad774fd38 --- .github/workflows/nightly.yml | 38 +++++++++++++++++------------------ .github/workflows/push.yml | 8 ++++---- .github/workflows/root.yml | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index e67deb8ffe4ff..00e2cbb43c233 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -57,7 +57,7 @@ jobs: runs-on: [self-hosted, gentoo, ppc64] steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: System info @@ -98,7 +98,7 @@ jobs: image: 'alpine:3.22' steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: apk @@ -200,7 +200,7 @@ jobs: runs-on: ubuntu-${{ matrix.asan && inputs.asan_ubuntu_version || inputs.ubuntu_version }} steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: Create MSSQL container @@ -298,7 +298,7 @@ jobs: FIREBIRD_PASSWORD: test steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: apt @@ -364,7 +364,7 @@ jobs: runs-on: macos-${{ matrix.os }} steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: Update clang @@ -439,7 +439,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: Create MSSQL container @@ -488,7 +488,7 @@ jobs: USE_TRACKED_ALLOC: 1 steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: apt @@ -688,7 +688,7 @@ jobs: runs-on: ubuntu-${{ inputs.ubuntu_version }} steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: Create MSSQL container @@ -751,7 +751,7 @@ jobs: runs-on: ubuntu-${{ inputs.ubuntu_version }} steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: apt @@ -838,7 +838,7 @@ jobs: runs-on: ubuntu-${{ inputs.ubuntu_version }} steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: apt @@ -883,38 +883,38 @@ jobs: CXX: ccache g++ steps: - name: git checkout PHP - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: path: php ref: ${{ inputs.branch }} - name: git checkout apcu - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: krakjoe/apcu path: apcu - name: git checkout imagick - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: Imagick/imagick path: imagick - name: git checkout memcached - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: php-memcached-dev/php-memcached path: memcached - name: git checkout redis - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: phpredis/phpredis path: redis - name: git checkout xdebug if: false - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: xdebug/xdebug path: xdebug - name: git checkout yaml - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: php/pecl-file_formats-yaml path: yaml @@ -1019,7 +1019,7 @@ jobs: - name: git config run: git config --global core.autocrlf false && git config --global core.eol lf - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: Setup @@ -1040,7 +1040,7 @@ jobs: timeout-minutes: 50 steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.branch }} - name: FreeBSD diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 054f4a15286d2..bedf186bfefef 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -71,7 +71,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Create MSSQL container uses: ./.github/actions/setup-mssql - name: Create Oracle container @@ -116,7 +116,7 @@ jobs: runs-on: macos-14 steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Update clang uses: ./.github/actions/macos-update-clang - name: brew @@ -165,7 +165,7 @@ jobs: - name: git config run: git config --global core.autocrlf false && git config --global core.eol lf - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup uses: ./.github/actions/setup-windows - name: Build @@ -179,6 +179,6 @@ jobs: timeout-minutes: 50 steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: FreeBSD uses: ./.github/actions/freebsd diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index ea23349fd6928..68e43772d89c6 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -13,7 +13,7 @@ jobs: outputs: branches: ${{ steps.set-matrix.outputs.branches }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: # Set fetch-depth to 0 to clone the full repository # including all branches. This is required to find From 6f61244a03e5a1b0de70181de627296e28287eeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 17 Dec 2025 14:00:56 +0100 Subject: [PATCH 176/252] Update GitHub Action workflows to `actions/cache@v5` Keep this up to date in all nonbranches, because the node.js runtime for older versions might get deprecated in the future and fixing this for all branches at once is easier. see 2650248a927224aa5b76130d406a691ad774fd38 --- .github/workflows/root.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index 68e43772d89c6..55bfe2ebeb9fc 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -20,7 +20,7 @@ jobs: # the correct commit hashes. fetch-depth: 0 - name: Grab the commit mapping - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: branch-commit-cache.json # The cache key needs to change every time for the From 61c35928fac708021bac50c575ffbb0735690d31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 17 Dec 2025 15:19:38 +0100 Subject: [PATCH 177/252] Update GitHub Action workflows to `actions/checkout@v6` (8.2) --- .github/workflows/push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a3e95afedc614..9a7a0ea4adba5 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -132,7 +132,7 @@ jobs: MYSQL_ROOT_PASSWORD: root steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: apt uses: ./.github/actions/apt-x32 - name: ccache From 7c1830b056fc2d2549eede4d8423a1aa3178663f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 17 Dec 2025 15:21:31 +0100 Subject: [PATCH 178/252] Update GitHub Action workflows to `actions/checkout@v6` (8.3) --- .github/workflows/push.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index daddc6adec0bf..aa5270c86cc4f 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -248,7 +248,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: apt @@ -308,7 +308,7 @@ jobs: mysql -uroot -proot -e "CREATE USER 'wordpress'@'localhost' IDENTIFIED BY 'wordpress'; FLUSH PRIVILEGES;" mysql -uroot -proot -e "GRANT ALL PRIVILEGES ON *.* TO 'wordpress'@'localhost' WITH GRANT OPTION;" - name: git checkout benchmarking-data - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: php/benchmarking-data ssh-key: ${{ secrets.BENCHMARKING_DATA_DEPLOY_KEY }} From 302aed78ee6dda46ae6f82252db155b75c8222de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 17 Dec 2025 15:23:35 +0100 Subject: [PATCH 179/252] Update GitHub Action workflows to `actions/checkout@v6` (8.4) --- .github/workflows/docs.yml | 2 +- .github/workflows/push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 36e8cd144a0c2..5c1c642a85630 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,7 +17,7 @@ jobs: if: github.repository == 'php/php-src' steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install dependencies run: pip install sphinx-design sphinxawesome-theme rstfmt - name: Check formatting diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index f803d2dd14be2..98beaeebe4070 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -48,7 +48,7 @@ jobs: image: 'alpine:3.22' steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: apk uses: ./.github/actions/apk - name: System info From 034ee3f47b0efe06f3dd49d1b57421ca57e585fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 17 Dec 2025 15:24:55 +0100 Subject: [PATCH 180/252] Update GitHub Action workflows to `actions/labeler@v6` --- .github/workflows/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index dbddbdc5d89b3..4cf6357c491fd 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -12,6 +12,6 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v5 + - uses: actions/labeler@v6 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" From eb1c0177fd679157fb27abf940b5a3be02a3fec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 17 Dec 2025 15:30:00 +0100 Subject: [PATCH 181/252] Update GitHub Action workflows to `actions/upload-artifact@v6` --- .github/workflows/push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 98beaeebe4070..ffe53afbf7c59 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -391,7 +391,7 @@ jobs: ${{ github.sha }} \ $(git merge-base ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) \ > $GITHUB_STEP_SUMMARY - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 with: name: profiles path: ${{ github.workspace }}/benchmark/profiles From 872c9bd41db5b5b483dc23b05933f54334a7e700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 17 Dec 2025 15:26:05 +0100 Subject: [PATCH 182/252] Update GitHub Action workflows to `actions/checkout@v6` (8.5) --- .github/workflows/real-time-benchmark.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/real-time-benchmark.yml b/.github/workflows/real-time-benchmark.yml index 539e9768f4ac1..0ace7d72e067d 100644 --- a/.github/workflows/real-time-benchmark.yml +++ b/.github/workflows/real-time-benchmark.yml @@ -118,21 +118,21 @@ jobs: sudo apt-get update -y sudo apt-get install -y terraform=1.5.7-* - name: Checkout benchmark suite - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: 'kocsismate/php-version-benchmarks' ref: 'main' fetch-depth: 1 path: 'php-version-benchmarks' - name: Checkout php-src (benchmarked version) - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: '${{ env.REPOSITORY }}' ref: '${{ env.COMMIT }}' fetch-depth: 100 path: 'php-version-benchmarks/tmp/php_${{ env.ID }}' - name: Checkout php-src (baseline version) - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: '${{ env.REPOSITORY }}' ref: '${{ env.BASELINE_COMMIT }}' @@ -146,7 +146,7 @@ jobs: rm -rf ./php-version-benchmarks/docs/results - name: Checkout benchmark data if: github.event_name != 'workflow_dispatch' - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: php/real-time-benchmark-data ssh-key: ${{ secrets.PHP_VERSION_BENCHMARK_RESULTS_DEPLOY_KEY }} From 874727b41f37df379f62e0368523d4f3ee121329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 17 Dec 2025 15:31:27 +0100 Subject: [PATCH 183/252] Update GitHub Action workflows to `actions/upload-artifact@v6` (8.5) --- .github/workflows/real-time-benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/real-time-benchmark.yml b/.github/workflows/real-time-benchmark.yml index 0ace7d72e067d..9276539841e72 100644 --- a/.github/workflows/real-time-benchmark.yml +++ b/.github/workflows/real-time-benchmark.yml @@ -290,7 +290,7 @@ jobs: git push - name: Upload artifact if: github.event_name == 'workflow_dispatch' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: results path: ./php-version-benchmarks/docs/results/${{ env.YEAR }} From dc045c0a6da3c0ebd65c718f4304bfa112e11351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 17 Dec 2025 15:32:24 +0100 Subject: [PATCH 184/252] Update GitHub Action workflows to `actions/checkout@v6` (master) --- .github/workflows/unit-tests.yml | 2 +- .github/workflows/verify-bundled-files.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index dc52a152f7abd..6338a1cb945db 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -35,7 +35,7 @@ jobs: timeout-minutes: 20 steps: - name: git checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install dependencies run: | diff --git a/.github/workflows/verify-bundled-files.yml b/.github/workflows/verify-bundled-files.yml index e15fcb36a0e7a..6cce1a14cf7f3 100644 --- a/.github/workflows/verify-bundled-files.yml +++ b/.github/workflows/verify-bundled-files.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: git checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Detect changed files uses: dorny/paths-filter@v3 From b010aa851a6cc8b02a491d1d6f8d81782d073eee Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 17 Dec 2025 07:17:17 -0800 Subject: [PATCH 185/252] ext/intl/ERROR_CONVENTIONS.md: fix typo (not -> note) [skip ci] (#20696) --- ext/intl/ERROR_CONVENTIONS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/intl/ERROR_CONVENTIONS.md b/ext/intl/ERROR_CONVENTIONS.md index af2450c8186a0..432862afd6855 100644 --- a/ext/intl/ERROR_CONVENTIONS.md +++ b/ext/intl/ERROR_CONVENTIONS.md @@ -75,7 +75,7 @@ and `intl_get_error_message()`. parsing errors), not `NULL`. Constructors and factory methods are the exception; these should return `NULL`, not `FALSE`. -Not that constructors in Intl generally (always?) don't throws exceptions. They +Note that constructors in Intl generally (always?) don't throws exceptions. They instead destroy the object to that the result of new `IntlClass()` can be `NULL`. This may be surprising. From 3502c6500c22f4064c289f21a36152ea3c2503dc Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 12 Dec 2025 03:49:34 +0000 Subject: [PATCH 186/252] ext/standard: refactor Windows codepage tests Most of the tests do not require the iconv extension. Moreover, due to them being compiled as shared libraries and loaded via the --EXTENSIONS-- section of the test it is highly likely that none of these tests were being run. --- .../tests/file/windows_mb_path/bug54028.phpt | 6 +----- .../file/windows_mb_path/bug54028_2.phpt | 6 +----- .../tests/file/windows_mb_path/bug54977.phpt | 6 +----- .../tests/file/windows_mb_path/bug61315.phpt | 6 +----- .../tests/file/windows_mb_path/bug64506.phpt | 6 +----- .../tests/file/windows_mb_path/bug64699.phpt | 6 +----- .../tests/file/windows_mb_path/bug70903.phpt | 6 +----- .../tests/file/windows_mb_path/bug71509.phpt | 8 +++----- .../tests/file/windows_mb_path/bug74923.phpt | 5 +---- .../file/windows_mb_path/bug75063_cp1251.phpt | 6 +----- .../file/windows_mb_path/bug75063_utf8.phpt | 6 +----- .../file/windows_mb_path/recursive_it.phpt | 5 +---- .../file/windows_mb_path/test_big5_0.phpt | 3 +-- .../file/windows_mb_path/test_big5_1.phpt | 3 +-- .../file/windows_mb_path/test_big5_2.phpt | 3 +-- .../windows_mb_path/test_big5_to_utf8_0.phpt | 6 +----- .../windows_mb_path/test_big5_to_utf8_1.phpt | 6 +----- .../windows_mb_path/test_big5_to_utf8_2.phpt | 6 +----- .../test_cp1250_to_utf8_0.phpt | 6 +----- .../test_cp1250_to_utf8_1.phpt | 6 +----- .../test_cp1250_to_utf8_2.phpt | 6 +----- .../test_cp1250_to_utf8_3.phpt | 6 +----- .../test_cp1250_to_utf8_4.phpt | 6 +----- .../test_cp1250_to_utf8_5.phpt | 6 +----- .../file/windows_mb_path/test_cp1251_0.phpt | 3 +-- .../file/windows_mb_path/test_cp1251_1.phpt | 3 +-- .../file/windows_mb_path/test_cp1251_2.phpt | 3 +-- .../test_cp1251_to_utf8_0.phpt | 8 +++----- .../test_cp1251_to_utf8_1.phpt | 8 +++----- .../test_cp1251_to_utf8_2.phpt | 8 +++----- .../test_cp1251_zend_multibyte_0.phpt | 6 +----- .../test_cp1251_zend_multibyte_1.phpt | 6 +----- .../test_cp1251_zend_multibyte_2.phpt | 6 +----- .../file/windows_mb_path/test_cp1252_0.phpt | 3 +-- .../test_cp1252_to_utf8_0.phpt | 8 +++----- .../test_cp1252_to_utf8_1.phpt | 8 +++----- .../test_cp1252_to_utf8_2.phpt | 8 +++----- .../test_cp1252_to_utf8_3.phpt | 8 +++----- .../test_cp1252_to_utf8_4.phpt | 8 +++----- .../test_cp1252_to_utf8_5.phpt | 8 +++----- .../file/windows_mb_path/test_cp1253_0.phpt | 3 +-- .../file/windows_mb_path/test_cp1253_1.phpt | 3 +-- .../file/windows_mb_path/test_cp1253_2.phpt | 3 +-- .../test_cp1253_to_utf8_0.phpt | 6 +----- .../test_cp1253_to_utf8_1.phpt | 6 +----- .../test_cp1253_to_utf8_2.phpt | 6 +----- .../file/windows_mb_path/test_cp1254_0.phpt | 3 +-- .../file/windows_mb_path/test_cp1254_1.phpt | 3 +-- .../file/windows_mb_path/test_cp1254_2.phpt | 3 +-- .../file/windows_mb_path/test_cp1254_3.phpt | 3 +-- .../test_cp1254_to_utf8_0.phpt | 6 +----- .../test_cp1254_to_utf8_1.phpt | 6 +----- .../test_cp1254_to_utf8_2.phpt | 6 +----- .../test_cp1254_to_utf8_3.phpt | 6 +----- .../file/windows_mb_path/test_cp1255_0.phpt | 3 +-- .../file/windows_mb_path/test_cp1255_1.phpt | 3 +-- .../file/windows_mb_path/test_cp1255_2.phpt | 3 +-- .../test_cp1255_to_utf8_0.phpt | 6 +----- .../test_cp1255_to_utf8_1.phpt | 6 +----- .../test_cp1255_to_utf8_2.phpt | 6 +----- .../file/windows_mb_path/test_cp1256_0.phpt | 3 +-- .../file/windows_mb_path/test_cp1256_1.phpt | 3 +-- .../file/windows_mb_path/test_cp1256_2.phpt | 3 +-- .../test_cp1256_to_utf8_0.phpt | 6 +----- .../test_cp1256_to_utf8_1.phpt | 6 +----- .../test_cp1256_to_utf8_2.phpt | 6 +----- .../file/windows_mb_path/test_cp874_0.phpt | 3 +-- .../file/windows_mb_path/test_cp874_1.phpt | 3 +-- .../windows_mb_path/test_cp874_to_utf8_0.phpt | 6 +----- .../windows_mb_path/test_cp874_to_utf8_1.phpt | 6 +----- .../file/windows_mb_path/test_cp932_0.phpt | 3 +-- .../file/windows_mb_path/test_cp932_1.phpt | 3 +-- .../file/windows_mb_path/test_cp932_2.phpt | 3 +-- .../file/windows_mb_path/test_cp932_3.phpt | 3 +-- .../windows_mb_path/test_cp932_to_utf8_0.phpt | 8 +++----- .../windows_mb_path/test_cp932_to_utf8_1.phpt | 8 +++----- .../windows_mb_path/test_cp932_to_utf8_2.phpt | 8 +++----- .../file/windows_mb_path/test_cp936_0.phpt | 3 +-- .../file/windows_mb_path/test_cp936_1.phpt | 3 +-- .../file/windows_mb_path/test_cp936_2.phpt | 3 +-- .../windows_mb_path/test_cp936_to_utf8_0.phpt | 8 +++----- .../windows_mb_path/test_cp936_to_utf8_1.phpt | 8 +++----- .../windows_mb_path/test_cp936_to_utf8_2.phpt | 8 +++----- .../windows_mb_path/test_cwd_mb_names.phpt | 9 +-------- .../windows_mb_path/test_eucjp_to_utf8_0.phpt | 8 +++----- .../windows_mb_path/test_eucjp_to_utf8_1.phpt | 8 +++----- .../windows_mb_path/test_eucjp_to_utf8_2.phpt | 8 +++----- .../windows_mb_path/test_kartuli_utf8_0.phpt | 6 +----- .../windows_mb_path/test_kartuli_utf8_1.phpt | 6 +----- .../windows_mb_path/test_kartuli_utf8_2.phpt | 6 +----- .../windows_mb_path/test_kartuli_utf8_3.phpt | 6 +----- .../windows_mb_path/test_long_path_0.phpt | 6 +----- .../windows_mb_path/test_long_path_1.phpt | 5 +---- .../windows_mb_path/test_long_path_2.phpt | 6 +----- .../test_long_path_bug30730.phpt | 5 +---- .../test_long_path_bug70943.phpt | 5 +---- .../test_long_path_bug71103.phpt | 5 +---- .../windows_mb_path/test_long_path_mkdir.phpt | 6 ++---- .../test_readdir_mb_names.phpt | 6 +----- .../windows_mb_path/test_rename_mb_names.phpt | 9 +-------- .../tests/file/windows_mb_path/util.inc | 19 ------------------- 101 files changed, 139 insertions(+), 431 deletions(-) diff --git a/ext/standard/tests/file/windows_mb_path/bug54028.phpt b/ext/standard/tests/file/windows_mb_path/bug54028.phpt index 61913a1a7218f..3603bced34d8a 100644 --- a/ext/standard/tests/file/windows_mb_path/bug54028.phpt +++ b/ext/standard/tests/file/windows_mb_path/bug54028.phpt @@ -4,12 +4,8 @@ Bug #54028 Directory::read() cannot handle non-unicode chars properly mbstring --SKIPIF-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --CONFLICTS-- bug71509 diff --git a/ext/standard/tests/file/windows_mb_path/bug74923.phpt b/ext/standard/tests/file/windows_mb_path/bug74923.phpt index 9cffd5860f601..fb87f2b266d54 100644 --- a/ext/standard/tests/file/windows_mb_path/bug74923.phpt +++ b/ext/standard/tests/file/windows_mb_path/bug74923.phpt @@ -2,11 +2,8 @@ Bug #74923 Crash when crawling through network share --SKIPIF-- --FILE-- --INI-- default_charset=cp1251 diff --git a/ext/standard/tests/file/windows_mb_path/bug75063_utf8.phpt b/ext/standard/tests/file/windows_mb_path/bug75063_utf8.phpt index a1878bb88a37c..615b76d196fb6 100644 --- a/ext/standard/tests/file/windows_mb_path/bug75063_utf8.phpt +++ b/ext/standard/tests/file/windows_mb_path/bug75063_utf8.phpt @@ -2,12 +2,8 @@ Bug #75063 Many filesystem-related functions do not work with multibyte file names, UTF-8 --SKIPIF-- --FILE-- 259) die("skip Unsuitable starting path length"); ?> --FILE-- diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_0.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_0.phpt index 794c1a2f7b878..7dc11a6167b5d 100644 --- a/ext/standard/tests/file/windows_mb_path/test_big5_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_big5_0.phpt @@ -4,9 +4,8 @@ Test fopen() for reading big5 path diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_1.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_1.phpt index d4821b23be0e4..7a88f57b7cf0d 100644 --- a/ext/standard/tests/file/windows_mb_path/test_big5_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_big5_1.phpt @@ -4,9 +4,8 @@ Test mkdir/rmdir big5 path diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_2.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_2.phpt index d7652615dea4e..89a8970344814 100644 --- a/ext/standard/tests/file/windows_mb_path/test_big5_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_big5_2.phpt @@ -4,9 +4,8 @@ Test fopen() for write big5 to UTF-8 path diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_0.phpt index b355267f851d9..c68bd58be0fc6 100644 --- a/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_0.phpt @@ -2,12 +2,8 @@ Test fopen() for reading big5 to UTF-8 path --SKIPIF-- --CONFLICTS-- file_big5 diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_1.phpt index 5076768c955b3..33573f04ec39e 100644 --- a/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_1.phpt @@ -2,12 +2,8 @@ Test mkdir/rmdir big5 to UTF-8 path --SKIPIF-- --CONFLICTS-- dir_big5 diff --git a/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_2.phpt index 4f94f898796f3..2451ebca18e95 100644 --- a/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_big5_to_utf8_2.phpt @@ -2,12 +2,8 @@ Test fopen() for write big5 to UTF-8 path --SKIPIF-- --CONFLICTS-- file_big5 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_0.phpt index 98c716678a6d7..20e5de98e07f9 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_0.phpt @@ -2,12 +2,8 @@ Test fopen() for reading UTF-8 path with cp1250 specific symbols --SKIPIF-- --CONFLICTS-- file_cp1250 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_1.phpt index ad09ff1967caf..1411911f6d54c 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_1.phpt @@ -2,12 +2,8 @@ Test mkdir/rmdir UTF-8 path with cp1250 specific symbols --SKIPIF-- --CONFLICTS-- dir_cp1250 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_2.phpt index 61e1b4eaa598b..72cf60068d246 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_2.phpt @@ -2,12 +2,8 @@ Test fopen() for write to UTF-8 path with cp1250 specific symbols --SKIPIF-- --CONFLICTS-- dir_cp1250 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_3.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_3.phpt index 2f3ae9afd608d..4c07d5a6de656 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_3.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_3.phpt @@ -2,12 +2,8 @@ Test fopen() for reading UTF-8 path with cp1250 specific symbols --SKIPIF-- --CONFLICTS-- file_cp1250 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_4.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_4.phpt index 8992a847eb507..9d2a13de2b949 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_4.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_4.phpt @@ -2,12 +2,8 @@ Test mkdir/rmdir UTF-8 path with cp1250 specific symbols --SKIPIF-- --CONFLICTS-- dir_cp1250 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_5.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_5.phpt index 392c486a767f7..4ed862f08b086 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_5.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1250_to_utf8_5.phpt @@ -2,12 +2,8 @@ Test fopen() for write to UTF-8 path with cp1250 specific symbols --SKIPIF-- --CONFLICTS-- dir_cp1250 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_0.phpt index bf30b4baeb040..7766d6dfd327e 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1251_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_0.phpt @@ -4,9 +4,8 @@ Test fopen() for reading CP1251 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_1.phpt index 0ea67f575e563..1ea343ae30ce1 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1251_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_1.phpt @@ -4,9 +4,8 @@ Test mkdir/rmdir CP1251 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_2.phpt index f65b38eafdad0..60a7dbf07fd89 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1251_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_2.phpt @@ -4,9 +4,8 @@ Test fopen() for write CP1251 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_0.phpt index 0cd657e94c596..6cb74e468eaeb 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_0.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for reading CP1251 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- file_cp1251 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_1.phpt index 6e53ff48c99d9..7776b908a8de9 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_1.phpt @@ -1,13 +1,11 @@ --TEST-- Test mkdir/rmdir CP1251 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- dir_cp1251 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_2.phpt index 95b75985c6188..90c9ddf01254c 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_to_utf8_2.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for write CP1251 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- file_cp1251 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_0.phpt index 8b800ed3bdeab..f962c5c0b0240 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_0.phpt @@ -7,12 +7,8 @@ zend.multibyte=1 zend.script_encoding=cp1251 --SKIPIF-- --CONFLICTS-- file_cp1251 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_1.phpt index 31db615f4d2c8..6d8a1f5d0050f 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_1.phpt @@ -7,12 +7,8 @@ zend.multibyte=1 zend.script_encoding=cp1251 --SKIPIF-- --CONFLICTS-- dir_cp1251 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_2.phpt index d6075da559c92..63745231771de 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1251_zend_multibyte_2.phpt @@ -7,12 +7,8 @@ zend.multibyte=1 zend.script_encoding=cp1251 --SKIPIF-- --CONFLICTS-- file_cp1251 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_0.phpt index f2671aa6054a5..aa3bc38dded21 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1252_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_0.phpt @@ -4,9 +4,8 @@ cp1252 cmd test diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_0.phpt index 45234243cee44..c7631eb601538 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_0.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for reading cp1252 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- file_cp1252 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_1.phpt index 5f5a6a78385a7..ea14608ad4c03 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_1.phpt @@ -1,13 +1,11 @@ --TEST-- Test mkdir/rmdir cp1252 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- dir_cp1252 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_2.phpt index 04baef95b0252..381b64eda6045 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_2.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for write cp1252 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- dir_cp1252 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_3.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_3.phpt index f6559253d2de4..ef441bcbbfddf 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_3.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_3.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for reading cp1252 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- file2_cp1252 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_4.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_4.phpt index a3359f686c1b3..739413dd74848 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_4.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_4.phpt @@ -1,13 +1,11 @@ --TEST-- Test mkdir/rmdir cp1252 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- dir2_cp1252 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_5.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_5.phpt index 42212d027126c..9cf7a31feea1b 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_5.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1252_to_utf8_5.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for write cp1252 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- dir2_cp1252 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_0.phpt index 93b63a536e839..11116849036b1 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1253_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_0.phpt @@ -4,9 +4,8 @@ Test fopen() for reading cp1253 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_1.phpt index 9ffdb11b2b83e..998ab348a0dc3 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1253_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_1.phpt @@ -4,9 +4,8 @@ Test mkdir/rmdir cp1253 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_2.phpt index 642e4aaabd194..01177620bd549 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1253_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_2.phpt @@ -4,9 +4,8 @@ Test fopen() for write cp1253 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_0.phpt index d12a174a7feaf..a592ed1f85bc6 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_0.phpt @@ -2,12 +2,8 @@ Test fopen() for reading cp1253 to UTF-8 path --SKIPIF-- --CONFLICTS-- file_cp1253 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_1.phpt index c84e9c95b3694..addc407f3e976 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_1.phpt @@ -2,12 +2,8 @@ Test mkdir/rmdir cp1253 to UTF-8 path --SKIPIF-- --CONFLICTS-- dir_cp1253 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_2.phpt index c0723d18b1228..cf88d95a4c696 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1253_to_utf8_2.phpt @@ -2,12 +2,8 @@ Test fopen() for write cp1253 to UTF-8 path --SKIPIF-- --CONFLICTS-- dir_cp1253 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_0.phpt index 77b52e0f1a2d6..83a413549ab31 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1254_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_0.phpt @@ -4,9 +4,8 @@ Test fopen() for reading cp1254 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_1.phpt index 82d74c750961b..9dc8225598358 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1254_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_1.phpt @@ -4,9 +4,8 @@ Test mkdir/rmdir cp1254 to UTF-8 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_2.phpt index f14a4c3de086e..2e386b3af45b9 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1254_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_2.phpt @@ -4,9 +4,8 @@ Test fopen() for write cp1254 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_3.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_3.phpt index 0e0e83d285a3b..076bf2eba50ea 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1254_3.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_3.phpt @@ -4,9 +4,8 @@ cp1254 cmd test diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_0.phpt index 211233de1c8e4..8dff7615dbfb5 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_0.phpt @@ -2,12 +2,8 @@ Test fopen() for reading cp1254 to UTF-8 path --SKIPIF-- --CONFLICTS-- file_cp1254 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_1.phpt index 42b241176ffb6..7b83cdbb395b3 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_1.phpt @@ -2,12 +2,8 @@ Test mkdir/rmdir cp1254 to UTF-8 path --SKIPIF-- --CONFLICTS-- dir_cp1254 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_2.phpt index b0c4f5ec83f0e..5ec63de4bc6e8 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_2.phpt @@ -2,12 +2,8 @@ Test fopen() for write cp1254 to UTF-8 path --SKIPIF-- --CONFLICTS-- dir_cp1254 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_3.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_3.phpt index 67549daf601d2..1e70dc8fe5b14 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_3.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1254_to_utf8_3.phpt @@ -2,12 +2,8 @@ cp1254 cmd test --SKIPIF-- --CONFLICTS-- file_cp1254 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_0.phpt index e9adaf843fb6a..61bb6b4155dc0 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1255_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_0.phpt @@ -4,9 +4,8 @@ Test fopen() for reading cp1255 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_1.phpt index 2cf42239267ad..a539ce8fd34e3 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1255_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_1.phpt @@ -4,9 +4,8 @@ Test mkdir/rmdir cp1255 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_2.phpt index e4779b721ba92..7fe2fecb7ea39 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1255_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_2.phpt @@ -4,9 +4,8 @@ Test fopen() for write cp1255 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_0.phpt index 1b65143553425..e8b35687e2fa8 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_0.phpt @@ -2,12 +2,8 @@ Test fopen() for reading cp1255 to UTF-8 path --SKIPIF-- --CONFLICTS-- file_cp1255 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_1.phpt index 356143ca7876a..48fa4f26a4b73 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_1.phpt @@ -2,12 +2,8 @@ Test mkdir/rmdir cp1255 to UTF-8 path --SKIPIF-- --CONFLICTS-- dir_cp1255 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_2.phpt index b378a7231d15e..358908ae56bd2 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1255_to_utf8_2.phpt @@ -2,12 +2,8 @@ Test fopen() for write cp1255 to UTF-8 path --SKIPIF-- --CONFLICTS-- dir_cp1255 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_0.phpt index 40910894b8ee7..9c0abe4c83269 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1256_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_0.phpt @@ -4,9 +4,8 @@ Test fopen() for reading cp1256 to UTF-8 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_1.phpt index 244758fe7bd67..f19bd04e9da98 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1256_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_1.phpt @@ -4,9 +4,8 @@ Test mkdir/rmdir cp1256 to UTF-8 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_2.phpt index f5b5db8935277..c734e24205d6a 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1256_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_2.phpt @@ -4,9 +4,8 @@ Test fopen() for write cp1256 to UTF-8 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_0.phpt index 901026f3899b0..9af7d83df2ad9 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_0.phpt @@ -2,12 +2,8 @@ Test fopen() for reading cp1256 to UTF-8 path --SKIPIF-- --CONFLICTS-- file_cp1256 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_1.phpt index b630f37648bc4..6da6d789e45ee 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_1.phpt @@ -2,12 +2,8 @@ Test mkdir/rmdir cp1256 to UTF-8 path --SKIPIF-- --CONFLICTS-- dir_cp1256 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_2.phpt index 6aa29f43c4268..2cce0b5411169 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp1256_to_utf8_2.phpt @@ -2,12 +2,8 @@ Test fopen() for write cp1256 to UTF-8 path --SKIPIF-- --CONFLICTS-- dir_cp1256 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp874_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp874_0.phpt index e8bc394a5d0d1..cbab58e84855c 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp874_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp874_0.phpt @@ -4,9 +4,8 @@ Thai cp874 basic test diff --git a/ext/standard/tests/file/windows_mb_path/test_cp874_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp874_1.phpt index 9d948d4023aa6..cd438c52f361e 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp874_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp874_1.phpt @@ -4,9 +4,8 @@ Thai cp874 cmd test diff --git a/ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_0.phpt index c21f124b6e195..b8ed1c23758a3 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp874_to_utf8_0.phpt @@ -2,12 +2,8 @@ Thai UTF-8 basic test --SKIPIF-- --FILE-- --CONFLICTS-- file_cp874 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_0.phpt index ccb273a94ce0f..d2a8e06a61cb0 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp932_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp932_0.phpt @@ -4,9 +4,8 @@ Test fopen() for reading cp932 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_1.phpt index 744d1a592be95..3f58aa5614f5c 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp932_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp932_1.phpt @@ -4,9 +4,8 @@ Test mkdir/rmdir cp932 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_2.phpt index 737a4d3eb869b..dc474cf9dff38 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp932_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp932_2.phpt @@ -4,9 +4,8 @@ Test fopen() for write to cp932 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_3.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_3.phpt index 4fdfb925a82db..d54d90e17beed 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp932_3.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp932_3.phpt @@ -4,9 +4,8 @@ cp932 cmd test diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_0.phpt index df855aaca4e3b..925e0558ab26a 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_0.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for reading cp932 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- file_cp932 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_1.phpt index b9575302c8abc..8346c86a4d195 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_1.phpt @@ -1,13 +1,11 @@ --TEST-- Test mkdir/rmdir cp932 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- dir_cp932 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_2.phpt index 7c3870593692e..5d0f10f647acd 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp932_to_utf8_2.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for write cp932 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- dir_cp932 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_0.phpt index b1a4f8744f94c..3199aa979355e 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp936_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp936_0.phpt @@ -4,9 +4,8 @@ Test fopen() for reading cp936 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_1.phpt index 529808aad5fb2..8ee68392ff318 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp936_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp936_1.phpt @@ -4,9 +4,8 @@ Test mkdir/rmdir cp936 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_2.phpt index e872c05942a68..024e84d4317fc 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp936_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp936_2.phpt @@ -4,9 +4,8 @@ Test fopen() for write cp936 path diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_0.phpt index 2797d25904f7f..39f989371968d 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_0.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for reading cp936 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- file_cp936 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_1.phpt index f9730b6fe9a08..7321ea0ed9d59 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_1.phpt @@ -1,13 +1,11 @@ --TEST-- Test mkdir/rmdir cp936 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- dir_cp936 diff --git a/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_2.phpt index 31ff1db318b84..8b3eb208f12fe 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cp936_to_utf8_2.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for write cp936 to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- file_cp936 diff --git a/ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt b/ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt index 3cb09f777d37f..622c38272e847 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt @@ -2,15 +2,8 @@ Test chdir()/getcwd() with a dir for multibyte filenames --SKIPIF-- --CONFLICTS-- dir_mb diff --git a/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_0.phpt index 7a06f53b7dd0c..48f2443017d6c 100644 --- a/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_0.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for reading eucjp to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- file_eucjp diff --git a/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_1.phpt index e3111ba33af7e..922f9e8290644 100644 --- a/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_1.phpt @@ -1,13 +1,11 @@ --TEST-- Test mkdir/rmdir eucjp to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- dir_eucjp diff --git a/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_2.phpt index 9425a1cc1cea8..b4a9895428d53 100644 --- a/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_eucjp_to_utf8_2.phpt @@ -1,13 +1,11 @@ --TEST-- Test fopen() for write eucjp to UTF-8 path +--EXTENSIONS-- +iconv --SKIPIF-- --CONFLICTS-- dir_eucjp diff --git a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_0.phpt b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_0.phpt index 0840610c2058e..f3c4f18131fa7 100644 --- a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_0.phpt @@ -2,12 +2,8 @@ Test fopen() for reading Kartuli UTF-8 path --SKIPIF-- --CONFLICTS-- file_kartuli diff --git a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_1.phpt b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_1.phpt index d348ccfd3c8fd..16f68f0f0ecb8 100644 --- a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_1.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_1.phpt @@ -2,12 +2,8 @@ Test mkdir/rmdir Kartuli UTF-8 path --SKIPIF-- --CONFLICTS-- dir_kartuli diff --git a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_2.phpt b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_2.phpt index 2e57dc0c91fa0..ca6c887a24ed9 100644 --- a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_2.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_2.phpt @@ -2,12 +2,8 @@ Test fopen() for write Kartuli UTF-8 path --SKIPIF-- --CONFLICTS-- dir_kartuli diff --git a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_3.phpt b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_3.phpt index 8d498d19907b1..e0984b0dd73bd 100644 --- a/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_3.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_kartuli_utf8_3.phpt @@ -2,12 +2,8 @@ Kartuli UTF-8 cmd test --SKIPIF-- --CONFLICTS-- file_kartuli diff --git a/ext/standard/tests/file/windows_mb_path/test_long_path_0.phpt b/ext/standard/tests/file/windows_mb_path/test_long_path_0.phpt index 6a5b60f08bbeb..daabfaad17fe4 100644 --- a/ext/standard/tests/file/windows_mb_path/test_long_path_0.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_long_path_0.phpt @@ -4,12 +4,8 @@ Basic long path test mbstring --SKIPIF-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- 248 has be a long path --SKIPIF-- 260 || strlen($start) > 248) { +if (strlen($start) > 260 || strlen($start) < 248) { die("skip the starting path length is unsuitable for this test"); } diff --git a/ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt b/ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt index 84fe5e327335b..c04bdbdcb468a 100644 --- a/ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt @@ -2,12 +2,8 @@ Test readdir() with a dir for multibyte filenames --SKIPIF-- --CONFLICTS-- mb_names diff --git a/ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt b/ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt index 53dd1bd3ea862..a04aa72fb45b5 100644 --- a/ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt @@ -2,15 +2,8 @@ Test rename() with a dir for multibyte filenames --SKIPIF-- --CONFLICTS-- file2_mb diff --git a/ext/standard/tests/file/windows_mb_path/util.inc b/ext/standard/tests/file/windows_mb_path/util.inc index a26a66c01751b..65a2b78404af4 100644 --- a/ext/standard/tests/file/windows_mb_path/util.inc +++ b/ext/standard/tests/file/windows_mb_path/util.inc @@ -51,25 +51,6 @@ function skip_if_wrong_cp($cp, $kind = "") } } -function skip_if_no_required_exts() -{ - $exts = func_get_args(); - $exts[] = "iconv"; - - foreach ($exts as $ext) { - if (!extension_loaded($ext)) { - die("skip $ext is not loaded"); - } - } -} - -function skip_if_not_win() -{ - if(substr(PHP_OS, 0, 3) != 'WIN' ) { - die('skip windows only test'); - } -} - function create_verify_file($prefix, $basename, $content = "", $cp = 65001) { $full = $prefix . DIRECTORY_SEPARATOR . $basename; From 4ca4be5dfea819638f1e399893768c6a1d3d1be1 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 12 Dec 2025 18:07:45 +0000 Subject: [PATCH 187/252] ext/standard: use sapi_windows_cp_*() functions directly --- .../tests/file/windows_mb_path/bug64699.phpt | 10 ++-- .../windows_mb_path/test_cwd_mb_names.phpt | 8 +-- .../test_readdir_mb_names.phpt | 10 ++-- .../windows_mb_path/test_rename_mb_names.phpt | 28 +++-------- .../tests/file/windows_mb_path/util.inc | 49 ++++++------------- 5 files changed, 37 insertions(+), 68 deletions(-) diff --git a/ext/standard/tests/file/windows_mb_path/bug64699.phpt b/ext/standard/tests/file/windows_mb_path/bug64699.phpt index 8dc9b31114a8c..0bde1ea1bfa58 100644 --- a/ext/standard/tests/file/windows_mb_path/bug64699.phpt +++ b/ext/standard/tests/file/windows_mb_path/bug64699.phpt @@ -12,8 +12,9 @@ if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); include __DIR__ . DIRECTORY_SEPARATOR . "util.inc"; -$old_cp = get_active_cp(); -set_active_cp(65001); +$old_cp = sapi_windows_cp_get(); +sapi_windows_cp_set(65001); +echo "Active code page: ", sapi_windows_cp_get(), "\n"; $prefix = __DIR__ . DIRECTORY_SEPARATOR . "testBug64699" . DIRECTORY_SEPARATOR; @@ -41,10 +42,10 @@ foreach ($dirs as $d) { } rmdir($prefix); -set_active_cp($old_cp); +sapi_windows_cp_set($old_cp); ?> ---EXPECTF-- +--EXPECT-- Active code page: 65001 filetype()[dir ] == is_dir()[dir ] -> OK: . filetype()[dir ] == is_dir()[dir ] -> OK: .. @@ -54,4 +55,3 @@ filetype()[dir ] == is_dir()[dir ] -> OK: ソ filetype()[dir ] == is_dir()[dir ] -> OK: ゾ filetype()[dir ] == is_dir()[dir ] -> OK: 多国語 filetype()[dir ] == is_dir()[dir ] -> OK: 表 -Active code page: %d diff --git a/ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt b/ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt index 622c38272e847..9f9d1f24e6cdc 100644 --- a/ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_cwd_mb_names.phpt @@ -17,8 +17,9 @@ $prefix = create_data("dir_mb"); $dirw = $prefix . DIRECTORY_SEPARATOR . "テストマルチバイト・パス42"; touch($dirw . DIRECTORY_SEPARATOR . "dummy.txt"); -$old_cp = get_active_cp(); -set_active_cp(65001); +$old_cp = sapi_windows_cp_get(); +sapi_windows_cp_set(65001); +echo "Active code page: ", sapi_windows_cp_get(), "\n"; $oldcwd = getcwd(); var_dump(chdir($dirw)); @@ -26,7 +27,7 @@ var_dump(getcwd()); var_dump(file_exists("dummy.txt")); -set_active_cp($old_cp); +sapi_windows_cp_set($old_cp); chdir($oldcwd); remove_data("dir_mb"); @@ -37,4 +38,3 @@ Active code page: 65001 bool(true) string(%d) "%s\テストマルチバイト・パス42" bool(true) -Active code page: %d diff --git a/ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt b/ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt index c04bdbdcb468a..65a0dada9f4c4 100644 --- a/ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_readdir_mb_names.phpt @@ -34,8 +34,9 @@ create_verify_dir($prefix, "żółć"); $dirw = $prefix . DIRECTORY_SEPARATOR; -$old_cp = get_active_cp(); -set_active_cp(65001); +$old_cp = sapi_windows_cp_get(); +sapi_windows_cp_set(65001); +echo "Active code page: ", sapi_windows_cp_get(), "\n"; if (is_dir($dirw)) { if ($dh = opendir($dirw)) { @@ -47,12 +48,12 @@ if (is_dir($dirw)) { } else { echo "is_dir failed\n"; } -set_active_cp($old_cp); +sapi_windows_cp_set($old_cp); remove_data("mb_names"); ?> ---EXPECTF-- +--EXPECT-- Active code page: 65001 filename: . : filetype: dir filename: .. : filetype: dir @@ -72,4 +73,3 @@ filename: テストマルチバイト・パス : filetype: file filename: テストマルチバイト・パス42 : filetype: dir filename: 測試多字節路徑 : filetype: file filename: 測試多字節路徑5 : filetype: dir -Active code page: %d diff --git a/ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt b/ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt index a04aa72fb45b5..ce040d1eda545 100644 --- a/ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt +++ b/ext/standard/tests/file/windows_mb_path/test_rename_mb_names.phpt @@ -10,47 +10,34 @@ file2_mb --FILE-- ---EXPECTF-- +--EXPECT-- Active code page: 65001 bool(true) string(21) "Ελλάδα_copy.txt" @@ -59,4 +46,3 @@ bool(true) string(27) "測試多字節路徑17.txt" bool(true) bool(true) -Active code page: %d diff --git a/ext/standard/tests/file/windows_mb_path/util.inc b/ext/standard/tests/file/windows_mb_path/util.inc index 65a2b78404af4..cec79aba56c15 100644 --- a/ext/standard/tests/file/windows_mb_path/util.inc +++ b/ext/standard/tests/file/windows_mb_path/util.inc @@ -1,52 +1,35 @@ Date: Fri, 12 Dec 2025 18:13:42 +0000 Subject: [PATCH 188/252] ext/standard: create_data() is never called with a 4th argument --- .../tests/file/windows_mb_path/util.inc | 28 +++---------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/ext/standard/tests/file/windows_mb_path/util.inc b/ext/standard/tests/file/windows_mb_path/util.inc index cec79aba56c15..a94e2d75c5104 100644 --- a/ext/standard/tests/file/windows_mb_path/util.inc +++ b/ext/standard/tests/file/windows_mb_path/util.inc @@ -90,29 +90,9 @@ function remove_data($id, $dir = NULL) } } -function create_data($id, $item = "", $cp = 65001, $utf8 = true) +/* Keep this file ASCII, so zend.multibyte related stuff can be tasted as well. */ +include __DIR__ . DIRECTORY_SEPARATOR . "util_utf8.inc"; +function create_data($id, $item = "", $cp = 65001) { - if ($utf8) { - /* Keep this file ASCII, so zend.multibyte related stuff can be tasted as well. */ - include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util_utf8.inc"; - return create_data_from_utf8($id, $item, $cp); - } else { - - $prefix = dirname(__FILE__) . DIRECTORY_SEPARATOR . $id; - - if (!is_dir($prefix)) { - mkdir($prefix); - } - - if (str_starts_with($id, "dir")) { - create_verify_dir($prefix, $item, $cp); - } else if (str_starts_with($id, "file")) { - /* a bit unhandy, but content can be put from outside, if needed */ - create_verify_file($prefix, $item, "dummy content", $cp); - } else { - echo "Item has either to start with \"dir\" or \"file\""; - } - } - - return $prefix; + return create_data_from_utf8($id, $item, $cp); } From 65c367f98285090a9863732f83dfab098e95cebc Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 17 Dec 2025 21:14:15 -0800 Subject: [PATCH 189/252] pdo_mysql___construct_options.phpt: remove unexpected Cedilla (#20692) [skip ci] --- ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt b/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt index 2437e7ca12d16..910afceb27f08 100644 --- a/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt @@ -73,7 +73,7 @@ MySQLPDOTest::skip(); } if (!is_object($db = new PDO($dsn, $user, $pass, array()))) - printf("[002] Expecting object got %s/%s¸\n", gettype($db), $db); + printf("[002] Expecting object got %s/%s\n", gettype($db), $db); $invalid = 999; if (is_object($db = new PDO($dsn, $user, $pass, array($invalid => true)))) From c0b16e080b6e7bcb9be17bb71efe199d9cfcf18d Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 17 Dec 2025 21:14:57 -0800 Subject: [PATCH 190/252] reflection: set `key_initialized` global to proper booleans (#20691) Instead of using `1` and `0`, use `true` and `false` --- ext/reflection/php_reflection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index c6f681ee2276c..64e79651913fb 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -7112,7 +7112,7 @@ ZEND_METHOD(ReflectionReference, getId) RETURN_THROWS(); } - REFLECTION_G(key_initialized) = 1; + REFLECTION_G(key_initialized) = true; } /* SHA1(ref || key) to avoid directly exposing memory addresses. */ @@ -7970,7 +7970,7 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ reflection_property_hook_type_ptr = register_class_PropertyHookType(); - REFLECTION_G(key_initialized) = 0; + REFLECTION_G(key_initialized) = false; return SUCCESS; } /* }}} */ From 833120eb72295fd9a6d0cacc68e85cbbeaa6a535 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 18 Dec 2025 10:55:05 +0100 Subject: [PATCH 191/252] Trim WS --- Zend/zend_ast.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 9774cce39db2b..30bd4a9c05dd0 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1124,12 +1124,12 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner( if (!(fptr->common.fn_flags & ZEND_ACC_STATIC)) { zend_non_static_method_call(fptr); - + return FAILURE; } if ((fptr->common.fn_flags & ZEND_ACC_ABSTRACT)) { zend_abstract_method_call(fptr); - + return FAILURE; } else if (fptr->common.scope->ce_flags & ZEND_ACC_TRAIT) { zend_error(E_DEPRECATED, From 06ea0e5df1d63f1dbd7bf93295f2ff64d1855075 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 18 Dec 2025 20:27:30 +0100 Subject: [PATCH 192/252] [RFC] Add clamp function (#19434) * Implement clamp function Co-authored-by: thinkverse * - Use a common function for normal and frameless implementations - Add tests for null and not-comparable cases - Fix object support for frameless clamp function - Improve NAN handling * Create tests triggering both frameless and dynamic variants * Add changelog * [Review] rephrase error messages to use "must not" * Enable assert() --------- Co-authored-by: thinkverse --- NEWS | 1 + UPGRADING | 4 + ext/standard/basic_functions.stub.php | 6 ++ ext/standard/basic_functions_arginfo.h | 16 +++- ext/standard/math.c | 56 +++++++++++++ ext/standard/tests/math/clamp.phpt | 104 +++++++++++++++++++++++++ 6 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/math/clamp.phpt diff --git a/NEWS b/NEWS index 9fa63137d8851..cea609f1d7229 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ PHP NEWS request. (ilutov) . It is now possible to use reference assign on WeakMap without the key needing to be present beforehand. (ndossche) + . Added `clamp()`. (kylekatarnls, thinkverse) - Hash: . Upgrade xxHash to 0.8.2. (timwolla) diff --git a/UPGRADING b/UPGRADING index 4e511d094797e..9a2159a33e7ea 100644 --- a/UPGRADING +++ b/UPGRADING @@ -71,6 +71,10 @@ PHP 8.6 UPGRADE NOTES 6. New Functions ======================================== +- Standard: + . `clamp()` returns the given value if in range, else return the nearest bound. + RFC: https://wiki.php.net/rfc/clamp_v2 + ======================================== 7. New Classes and Interfaces ======================================== diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 7913ca0e00194..e27dca069c55b 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -1606,6 +1606,12 @@ function min(mixed $value, mixed ...$values): mixed {} */ function max(mixed $value, mixed ...$values): mixed {} +/** + * @compile-time-eval + * @frameless-function {"arity": 3} + */ +function clamp(mixed $value, mixed $min, mixed $max): mixed {} + function array_walk(array|object &$array, callable $callback, mixed $arg = UNKNOWN): true {} function array_walk_recursive(array|object &$array, callable $callback, mixed $arg = UNKNOWN): true {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 0a21d7d76426c..6f202c01463fd 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: f6bf6cdd07080c01d3a0cb08d71409d05b1084f9 */ + * Stub hash: 1a1667a5c59111f096a758d5bb4aa7cf3ec09cfe */ 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) @@ -138,6 +138,12 @@ ZEND_END_ARG_INFO() #define arginfo_max arginfo_min +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_clamp, 0, 3, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, min, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, max, IS_MIXED, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_walk, 0, 2, IS_TRUE, 0) ZEND_ARG_TYPE_MASK(1, array, MAY_BE_ARRAY|MAY_BE_OBJECT, NULL) ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) @@ -2197,6 +2203,12 @@ static const zend_frameless_function_info frameless_function_infos_max[] = { { 0 }, }; +ZEND_FRAMELESS_FUNCTION(clamp, 3); +static const zend_frameless_function_info frameless_function_infos_clamp[] = { + { ZEND_FRAMELESS_FUNCTION_NAME(clamp, 3), 3 }, + { 0 }, +}; + ZEND_FRAMELESS_FUNCTION(in_array, 2); ZEND_FRAMELESS_FUNCTION(in_array, 3); static const zend_frameless_function_info frameless_function_infos_in_array[] = { @@ -2332,6 +2344,7 @@ ZEND_FUNCTION(current); ZEND_FUNCTION(key); ZEND_FUNCTION(min); ZEND_FUNCTION(max); +ZEND_FUNCTION(clamp); ZEND_FUNCTION(array_walk); ZEND_FUNCTION(array_walk_recursive); ZEND_FUNCTION(in_array); @@ -2925,6 +2938,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(key, arginfo_key) ZEND_RAW_FENTRY("min", zif_min, arginfo_min, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_min, NULL) ZEND_RAW_FENTRY("max", zif_max, arginfo_max, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_max, NULL) + ZEND_RAW_FENTRY("clamp", zif_clamp, arginfo_clamp, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_clamp, NULL) ZEND_FE(array_walk, arginfo_array_walk) 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) diff --git a/ext/standard/math.c b/ext/standard/math.c index 142d473864f75..95384c06588ac 100644 --- a/ext/standard/math.c +++ b/ext/standard/math.c @@ -389,6 +389,62 @@ PHP_FUNCTION(round) } /* }}} */ +/* Return the given value if in range of min and max */ +static void php_math_clamp(zval *return_value, zval *value, zval *min, zval *max) +{ + if (Z_TYPE_P(min) == IS_DOUBLE && UNEXPECTED(zend_isnan(Z_DVAL_P(min)))) { + zend_argument_value_error(2, "must not be NAN"); + RETURN_THROWS(); + } + + if (Z_TYPE_P(max) == IS_DOUBLE && UNEXPECTED(zend_isnan(Z_DVAL_P(max)))) { + zend_argument_value_error(3, "must not be NAN"); + RETURN_THROWS(); + } + + if (zend_compare(max, min) == -1) { + zend_argument_value_error(2, "must be smaller than or equal to argument #3 ($max)"); + RETURN_THROWS(); + } + + if (zend_compare(max, value) == -1) { + RETURN_COPY(max); + } + + if (zend_compare(value, min) == -1) { + RETURN_COPY(min); + } + + RETURN_COPY(value); +} + +/* {{{ Return the given value if in range of min and max */ +PHP_FUNCTION(clamp) +{ + zval *zvalue, *zmin, *zmax; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_ZVAL(zvalue) + Z_PARAM_ZVAL(zmin) + Z_PARAM_ZVAL(zmax) + ZEND_PARSE_PARAMETERS_END(); + + php_math_clamp(return_value, zvalue, zmin, zmax); +} +/* }}} */ + +/* {{{ Return the given value if in range of min and max */ +ZEND_FRAMELESS_FUNCTION(clamp, 3) +{ + zval *zvalue, *zmin, *zmax; + Z_FLF_PARAM_ZVAL(1, zvalue); + Z_FLF_PARAM_ZVAL(2, zmin); + Z_FLF_PARAM_ZVAL(3, zmax); + + php_math_clamp(return_value, zvalue, zmin, zmax); +} +/* }}} */ + /* {{{ Returns the sine of the number in radians */ PHP_FUNCTION(sin) { diff --git a/ext/standard/tests/math/clamp.phpt b/ext/standard/tests/math/clamp.phpt new file mode 100644 index 0000000000000..beb4c8d53148d --- /dev/null +++ b/ext/standard/tests/math/clamp.phpt @@ -0,0 +1,104 @@ +--TEST-- +clamp() tests +--INI-- +precision=14 +date.timezone=UTC +zend.assertions=1 +--FILE-- +getMessage(), "\n"; + } + + try { + var_dump(make_clamp_fcc()($value, $min, $max)); + } catch (ValueError $error) { + echo $error->getMessage(), "\n"; + } +} + +var_dump(check_clamp_result(2, 1, 3)); +var_dump(check_clamp_result(0, 1, 3)); +var_dump(check_clamp_result(6, 1, 3)); +var_dump(check_clamp_result(2, 1.3, 3.4)); +var_dump(check_clamp_result(2.5, 1, 3)); +var_dump(check_clamp_result(2.5, 1.3, 3.4)); +var_dump(check_clamp_result(0, 1.3, 3.4)); +var_dump(check_clamp_result(M_PI, -INF, INF)); +var_dump(check_clamp_result(NAN, 4, 6)); +var_dump(check_clamp_result("a", "c", "g")); +var_dump(check_clamp_result("d", "c", "g")); +echo check_clamp_result('2025-08-01', '2025-08-15', '2025-09-15'), "\n"; +echo check_clamp_result('2025-08-20', '2025-08-15', '2025-09-15'), "\n"; +echo check_clamp_result(new \DateTimeImmutable('2025-08-01'), new \DateTimeImmutable('2025-08-15'), new \DateTimeImmutable('2025-09-15'))->format('Y-m-d'), "\n"; +echo check_clamp_result(new \DateTimeImmutable('2025-08-20'), new \DateTimeImmutable('2025-08-15'), new \DateTimeImmutable('2025-09-15'))->format('Y-m-d'), "\n"; +var_dump(check_clamp_result(null, -1, 1)); +var_dump(check_clamp_result(null, 1, 3)); +var_dump(check_clamp_result(null, -3, -1)); +var_dump(check_clamp_result(-9999, null, 10)); +var_dump(check_clamp_result(12, null, 10)); + +$a = new \InvalidArgumentException('a'); +$b = new \RuntimeException('b'); +$c = new \LogicException('c'); +echo check_clamp_result($a, $b, $c)::class, "\n"; +echo check_clamp_result($b, $a, $c)::class, "\n"; +echo check_clamp_result($c, $a, $b)::class, "\n"; + +check_clamp_exception(4, NAN, 6); +check_clamp_exception(7, 6, NAN); +check_clamp_exception(1, 3, 2); +check_clamp_exception(-9999, 5, null); +check_clamp_exception(12, -5, null); + +?> +--EXPECT-- +int(2) +int(1) +int(3) +int(2) +float(2.5) +float(2.5) +float(1.3) +float(3.141592653589793) +float(NAN) +string(1) "c" +string(1) "d" +2025-08-15 +2025-08-20 +2025-08-15 +2025-08-20 +int(-1) +int(1) +int(-3) +int(-9999) +int(10) +InvalidArgumentException +RuntimeException +LogicException +clamp(): Argument #2 ($min) must not be NAN +clamp(): Argument #2 ($min) must not be NAN +clamp(): Argument #3 ($max) must not be NAN +clamp(): Argument #3 ($max) must not be NAN +clamp(): Argument #2 ($min) must be smaller than or equal to argument #3 ($max) +clamp(): Argument #2 ($min) must be smaller than or equal to argument #3 ($max) +clamp(): Argument #2 ($min) must be smaller than or equal to argument #3 ($max) +clamp(): Argument #2 ($min) must be smaller than or equal to argument #3 ($max) +clamp(): Argument #2 ($min) must be smaller than or equal to argument #3 ($max) +clamp(): Argument #2 ($min) must be smaller than or equal to argument #3 ($max) From f4538c67c3de9e8d34f55293d34947e2ac80cb46 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Thu, 18 Dec 2025 16:20:59 +0000 Subject: [PATCH 193/252] Remove unused macro --- ext/mysqli/mysqli_api.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 65c423990bc9d..1bf74dd77eeab 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -1343,9 +1343,6 @@ PHP_FUNCTION(mysqli_real_query) } /* }}} */ -# define mysql_real_escape_string_quote(mysql, to, from, length, quote) \ - mysql_real_escape_string(mysql, to, from, length) - PHP_FUNCTION(mysqli_real_escape_string) { MY_MYSQL *mysql; zval *mysql_link = NULL; @@ -1359,7 +1356,7 @@ PHP_FUNCTION(mysqli_real_escape_string) { MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID); newstr = zend_string_safe_alloc(2, escapestr_len, 0, 0); - ZSTR_LEN(newstr) = mysql_real_escape_string_quote(mysql->mysql, ZSTR_VAL(newstr), escapestr, escapestr_len, '\''); + ZSTR_LEN(newstr) = mysql_real_escape_string(mysql->mysql, ZSTR_VAL(newstr), escapestr, escapestr_len); newstr = zend_string_truncate(newstr, ZSTR_LEN(newstr), 0); RETURN_NEW_STR(newstr); From cf62b6c0588a637a7155bec92b949dbf9e23916d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 18 Dec 2025 22:38:21 +0100 Subject: [PATCH 194/252] [ci skip] dom: Remove outdated comment --- ext/dom/notation.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ext/dom/notation.c b/ext/dom/notation.c index a15fae9ee9d8e..f83b31428e16c 100644 --- a/ext/dom/notation.c +++ b/ext/dom/notation.c @@ -31,8 +31,6 @@ * Since: */ -/* {{{ attribute protos, not implemented yet */ - /* {{{ publicId string readonly=yes URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-54F2B4D0 @@ -73,6 +71,4 @@ zend_result dom_notation_system_id_read(dom_object *obj, zval *retval) /* }}} */ -/* }}} */ - #endif From 983be089c0c800dfae9a3b9bdb974e35aceeeabd Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:37:04 +0100 Subject: [PATCH 195/252] Fix GH-20722: Null pointer dereference in DOM namespace node cloning via clone on malformed objects Closes GH-20730. --- NEWS | 4 ++++ ext/dom/php_dom.c | 12 +++++++----- ext/dom/tests/gh20722.phpt | 13 +++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 ext/dom/tests/gh20722.phpt diff --git a/NEWS b/NEWS index 11ae976b3238f..d7fd92cf997fd 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,10 @@ PHP NEWS . Fixed bug GH-20620 (bzcompress overflow on large source size). (David Carlier) +- DOM: + . Fixed bug GH-20722 (Null pointer dereference in DOM namespace node cloning + via clone on malformed objects). (ndossche) + - GD: . Fixed bug GH-20622 (imagestring/imagestringup overflow). (David Carlier) diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index d097081b0bdeb..a1eb8fb8d7864 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -541,15 +541,17 @@ static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject) zend_object *clone = dom_objects_namespace_node_new(intern->dom.std.ce); dom_object_namespace_node *clone_intern = php_dom_namespace_node_obj_from_obj(clone); - xmlNodePtr original_node = dom_object_get_node(&intern->dom); - ZEND_ASSERT(original_node->type == XML_NAMESPACE_DECL); - xmlNodePtr cloned_node = php_dom_create_fake_namespace_decl_node_ptr(original_node->parent, original_node->ns); - if (intern->parent_intern) { clone_intern->parent_intern = intern->parent_intern; GC_ADDREF(&clone_intern->parent_intern->std); } - dom_update_refcount_after_clone(&intern->dom, original_node, &clone_intern->dom, cloned_node); + + xmlNodePtr original_node = dom_object_get_node(&intern->dom); + if (original_node != NULL) { + ZEND_ASSERT(original_node->type == XML_NAMESPACE_DECL); + xmlNodePtr cloned_node = php_dom_create_fake_namespace_decl_node_ptr(original_node->parent, original_node->ns); + dom_update_refcount_after_clone(&intern->dom, original_node, &clone_intern->dom, cloned_node); + } zend_objects_clone_members(clone, &intern->dom.std); return clone; diff --git a/ext/dom/tests/gh20722.phpt b/ext/dom/tests/gh20722.phpt new file mode 100644 index 0000000000000..38d3314618f3e --- /dev/null +++ b/ext/dom/tests/gh20722.phpt @@ -0,0 +1,13 @@ +--TEST-- +GH-20722 (Null pointer dereference in DOM namespace node cloning via clone on malformed objects) +--EXTENSIONS-- +dom +--FILE-- + +--EXPECT-- +Done From 46e55dd97ceea10267fe935c2c5abc2a05c8130b Mon Sep 17 00:00:00 2001 From: James Lucas Date: Mon, 15 Dec 2025 21:23:44 +1100 Subject: [PATCH 196/252] ext/sockets: adding Linux's TCP_USER_TIMEOUT constant. Set TCP_USER_TIMEOUT to cap (ms) how long TCP will wait for ACKs on in-flight data before aborting the connection; prevents stuck/half-open sessions and enables faster failover vs default retransmission timeouts. Co-authored-by: David Carlier close GH-20708 --- NEWS | 4 ++ UPGRADING | 3 ++ ext/sockets/sockets.c | 24 ++++++++++- ext/sockets/sockets.stub.php | 7 ++++ ext/sockets/sockets_arginfo.h | 5 ++- ...socket_setoption_tcpusertimeout_32bit.phpt | 34 +++++++++++++++ ...socket_setoption_tcpusertimeout_64bit.phpt | 41 +++++++++++++++++++ 7 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 ext/sockets/tests/socket_setoption_tcpusertimeout_32bit.phpt create mode 100644 ext/sockets/tests/socket_setoption_tcpusertimeout_64bit.phpt diff --git a/NEWS b/NEWS index cea609f1d7229..12740c65c3c89 100644 --- a/NEWS +++ b/NEWS @@ -57,6 +57,10 @@ PHP NEWS . Soap::__setCookie() when cookie name is a digit is now not stored and represented as a string anymore but a int. (David Carlier) +- Sockets: + . Added the TCP_USER_TIMEOUT constant for Linux to set the maximum time in milliseconds + transmitted data can remain unacknowledged. (James Lucas) + - SPL: . DirectoryIterator key can now work better with filesystem supporting larger directory indexing. (David Carlier) diff --git a/UPGRADING b/UPGRADING index 9a2159a33e7ea..d52827bf96154 100644 --- a/UPGRADING +++ b/UPGRADING @@ -94,6 +94,9 @@ PHP 8.6 UPGRADE NOTES 10. New Global Constants ======================================== +- Sockets: + . TCP_USER_TIMEOUT (Linux only). + ======================================== 11. Changes to INI File Handling ======================================== diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 4a9332498c3cc..b76818830fc06 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1773,7 +1773,7 @@ PHP_FUNCTION(socket_sendto) RETURN_THROWS(); } - memset(&sll, 0, sizeof(sll)); + memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_ifindex = port; @@ -2130,6 +2130,28 @@ PHP_FUNCTION(socket_set_option) } #endif +#if defined(TCP_USER_TIMEOUT) + case TCP_USER_TIMEOUT: { + zend_long timeout = zval_get_long(arg4); + + // TCP_USER_TIMEOUT unsigned int + if (timeout < 0 || timeout > UINT_MAX) { + zend_argument_value_error(4, "must be of between 0 and %u", UINT_MAX); + RETURN_THROWS(); + } + + unsigned int val = (unsigned int)timeout; + optlen = sizeof(val); + opt_ptr = &val; + if (setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen) != 0) { + PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno); + RETURN_FALSE; + } + + RETURN_TRUE; + } +#endif + } } diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index 3df9b598a1e8f..04fb702807e0a 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -602,6 +602,13 @@ */ const TCP_SYNCNT = UNKNOWN; #endif +#ifdef TCP_USER_TIMEOUT +/** + * @var int + * @cvalue TCP_USER_TIMEOUT + */ +const TCP_USER_TIMEOUT = UNKNOWN; +#endif #ifdef SO_ZEROCOPY /** * @var int diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index edfc344ff8cc0..0145d01252881 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f754368e28f6e45bf3a63a403e49f5659c29d2c6 */ + * Stub hash: 038081ca7bb98076d4b559d93b4c9300acc47160 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1) @@ -527,6 +527,9 @@ static void register_sockets_symbols(int module_number) #if defined(TCP_SYNCNT) REGISTER_LONG_CONSTANT("TCP_SYNCNT", TCP_SYNCNT, CONST_PERSISTENT); #endif +#if defined(TCP_USER_TIMEOUT) + REGISTER_LONG_CONSTANT("TCP_USER_TIMEOUT", TCP_USER_TIMEOUT, CONST_PERSISTENT); +#endif #if defined(SO_ZEROCOPY) REGISTER_LONG_CONSTANT("SO_ZEROCOPY", SO_ZEROCOPY, CONST_PERSISTENT); #endif diff --git a/ext/sockets/tests/socket_setoption_tcpusertimeout_32bit.phpt b/ext/sockets/tests/socket_setoption_tcpusertimeout_32bit.phpt new file mode 100644 index 0000000000000..3f12120e54d89 --- /dev/null +++ b/ext/sockets/tests/socket_setoption_tcpusertimeout_32bit.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test if socket_set_option() works, option:TCP_USER_TIMEOUT +--EXTENSIONS-- +sockets +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +$timeout = 200; +$retval_2 = socket_set_option($socket, SOL_TCP, TCP_USER_TIMEOUT, $timeout); +$retval_3 = socket_get_option($socket, SOL_TCP, TCP_USER_TIMEOUT); +var_dump($retval_2); +var_dump($retval_3 === $timeout); +socket_close($socket); +?> +--EXPECTF-- +socket_setopt(): Argument #4 ($value) must be of between 0 and %d +bool(true) +bool(true) diff --git a/ext/sockets/tests/socket_setoption_tcpusertimeout_64bit.phpt b/ext/sockets/tests/socket_setoption_tcpusertimeout_64bit.phpt new file mode 100644 index 0000000000000..8dbb7f80e48b1 --- /dev/null +++ b/ext/sockets/tests/socket_setoption_tcpusertimeout_64bit.phpt @@ -0,0 +1,41 @@ +--TEST-- +Test if socket_set_option() works, option:TCP_USER_TIMEOUT +--EXTENSIONS-- +sockets +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +try { + socket_setopt($socket, SOL_TCP, TCP_USER_TIMEOUT, PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +$timeout = 200; +$retval_2 = socket_set_option($socket, SOL_TCP, TCP_USER_TIMEOUT, $timeout); +$retval_3 = socket_get_option($socket, SOL_TCP, TCP_USER_TIMEOUT); +var_dump($retval_2); +var_dump($retval_3 === $timeout); +socket_close($socket); +?> +--EXPECTF-- +socket_setopt(): Argument #4 ($value) must be of between 0 and %d +socket_setopt(): Argument #4 ($value) must be of between 0 and %d +bool(true) +bool(true) From ee0143887dcdf971b27a5193e8f140c150dc268c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 2 Nov 2025 00:07:47 +0100 Subject: [PATCH 197/252] Fix GH-20352: UAF in php_output_handler_free via re-entrant ob_start() during error deactivation The problem is that the code is doing `php_output_handler_free` in a loop on the output stack, but prior to freeing the pointer on the stack in `php_output_handler_free` it calls `php_output_handler_dtor` which can run user code that reallocates the stack, resulting in a dangling pointer freed by php_output_handler_free. Furthermore, OG(active) is set when creating a new output handler, but the loop is supposed to clean up all handlers, so OG(active) must be reset as well. Closes GH-20356. --- NEWS | 2 ++ main/output.c | 13 +++++++++---- tests/output/gh20352.phpt | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 tests/output/gh20352.phpt diff --git a/NEWS b/NEWS index d7fd92cf997fd..2c6bab631e615 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,8 @@ PHP NEWS . Fixed bug GH-20695 (Assertion failure in normalize_value() when parsing malformed INI input via parse_ini_string()). (ndossche) . Fixed bug GH-20714 (Uncatchable exception thrown in generator). (ilutov) + . Fixed bug GH-20352 (UAF in php_output_handler_free via re-entrant + ob_start() during error deactivation). (ndossche) - Bz2: . Fixed bug GH-20620 (bzcompress overflow on large source size). diff --git a/main/output.c b/main/output.c index c2cb0ceab0112..a650f116c7e1c 100644 --- a/main/output.c +++ b/main/output.c @@ -188,8 +188,12 @@ PHPAPI void php_output_deactivate(void) /* release all output handlers */ if (OG(handlers).elements) { while ((handler = zend_stack_top(&OG(handlers)))) { - php_output_handler_free(handler); zend_stack_del_top(&OG(handlers)); + /* It's possible to start a new output handler and mark it as active, + * however this loop will destroy all active handlers. */ + OG(active) = NULL; + ZEND_ASSERT(OG(running) == NULL && "output is deactivated therefore running should stay NULL"); + php_output_handler_free(handler); } } zend_stack_destroy(&OG(handlers)); @@ -719,10 +723,11 @@ PHPAPI void php_output_handler_dtor(php_output_handler *handler) * Destroy and free an output handler */ PHPAPI void php_output_handler_free(php_output_handler **h) { - if (*h) { - php_output_handler_dtor(*h); - efree(*h); + php_output_handler *handler = *h; + if (handler) { *h = NULL; + php_output_handler_dtor(handler); + efree(handler); } } /* }}} */ diff --git a/tests/output/gh20352.phpt b/tests/output/gh20352.phpt new file mode 100644 index 0000000000000..16be0b920e80f --- /dev/null +++ b/tests/output/gh20352.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-20352 (UAF in php_output_handler_free via re-entrant ob_start() during error deactivation) +--FILE-- + +--EXPECTF-- +Fatal error: ob_start(): Cannot use output buffering in output buffering display handlers in %s on line %d From 4315c3a5aa1380ebf874daa9a2629e83ad4afa46 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 19 Dec 2025 12:11:17 -0800 Subject: [PATCH 198/252] standard: Remove nonsensical dtor of return value in fread() (#20739) It was never set to a string at this point, so why dtor it? --- ext/standard/file.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/standard/file.c b/ext/standard/file.c index fdeabd1872d20..4168cd0f84b79 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -1555,7 +1555,6 @@ PHPAPI PHP_FUNCTION(fread) str = php_stream_read_to_str(stream, len); if (!str) { - zval_ptr_dtor_str(return_value); RETURN_FALSE; } From 420f3dce529e6505d132eab2cf31ab4662c00daf Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:19:21 -0800 Subject: [PATCH 199/252] standard: Move freeing of temp buffer and use efree variant (#20738) --- ext/standard/file.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/standard/file.c b/ext/standard/file.c index 4168cd0f84b79..bd6ee252875e5 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -663,11 +663,10 @@ PHP_FUNCTION(file) p = e; goto parse_eol; } - } - if (target_buf) { - zend_string_free(target_buf); + zend_string_efree(target_buf); } + php_stream_close(stream); } /* }}} */ From cec087e02f199470eb2ff3edb7e63701f67041b8 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 19 Dec 2025 19:05:52 -0800 Subject: [PATCH 200/252] ext/standard/tests/file/windows_mb_path/util.inc: fix typo (#20723) "zend.multibyte related stuff" probably doesn't taste too good [skip ci] --- ext/standard/tests/file/windows_mb_path/util.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/tests/file/windows_mb_path/util.inc b/ext/standard/tests/file/windows_mb_path/util.inc index a94e2d75c5104..c8f0f6c586cb3 100644 --- a/ext/standard/tests/file/windows_mb_path/util.inc +++ b/ext/standard/tests/file/windows_mb_path/util.inc @@ -90,7 +90,7 @@ function remove_data($id, $dir = NULL) } } -/* Keep this file ASCII, so zend.multibyte related stuff can be tasted as well. */ +/* Keep this file ASCII, so zend.multibyte related stuff can be tested as well. */ include __DIR__ . DIRECTORY_SEPARATOR . "util_utf8.inc"; function create_data($id, $item = "", $cp = 65001) { From 22aaa20dab657f43c309dbfbad0123674477c2ee Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 19 Dec 2025 18:21:45 +0100 Subject: [PATCH 201/252] Fix GH-20732: Phar::LoadPhar undefined behavior when loading directory The size of `got` was incorrect: it being unsigned means that the error return codes are converted from -1 to SIZE_MAX. We should use ssize_t instead. Closes GH-20735. --- NEWS | 4 ++++ ext/phar/phar.c | 4 ++-- ext/phar/tests/gh20732.phpt | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 ext/phar/tests/gh20732.phpt diff --git a/NEWS b/NEWS index 2c6bab631e615..d96969b286f9f 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,10 @@ PHP NEWS . Fixed bug GH-20674 (Fix GH-20674 mb_decode_mimeheader does not handle separator). (Yuya Hamada) +- Phar: + . Fixed bug GH-20732 (Phar::LoadPhar undefined behavior when reading fails). + (ndossche) + - SPL: . Fixed bug GH-20678 (resource created by GlobIterator crashes with fclose()). (David Carlier) diff --git a/ext/phar/phar.c b/ext/phar/phar.c index a63a8dd8eb6d6..4c2cab27dcea4 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1609,7 +1609,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char const zend_long readsize = sizeof(buffer) - sizeof(token); const zend_long tokenlen = sizeof(token) - 1; zend_long halt_offset; - size_t got; + ssize_t got; uint32_t compression = PHAR_FILE_COMPRESSED_NONE; if (error) { @@ -1627,7 +1627,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char /* Maybe it's better to compile the file instead of just searching, */ /* but we only want the offset. So we want a .re scanner to find it. */ while(!php_stream_eof(fp)) { - if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) { + if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < tokenlen) { MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)") } diff --git a/ext/phar/tests/gh20732.phpt b/ext/phar/tests/gh20732.phpt new file mode 100644 index 0000000000000..b938d16d42caf --- /dev/null +++ b/ext/phar/tests/gh20732.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-20732 (Phar::LoadPhar undefined behavior when loading directory) +--EXTENSIONS-- +phar +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECTF-- +%r(internal corruption of phar "%s" \(truncated entry\)|unable to open phar for reading ".")%r From 5dd5a4267f84971025bf8b316fd11910b4a1e03e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 20 Dec 2025 15:59:07 -0800 Subject: [PATCH 202/252] enchant: Use an array of the correct size before filling it (#20743) This avoids the need to do resizes. --- ext/enchant/enchant.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/enchant/enchant.c b/ext/enchant/enchant.c index eedb49b69e8f9..534908f9d1cc5 100644 --- a/ext/enchant/enchant.c +++ b/ext/enchant/enchant.c @@ -671,17 +671,18 @@ PHP_FUNCTION(enchant_dict_suggest) } PHP_ENCHANT_GET_DICT; - array_init(return_value); suggs = enchant_dict_suggest(pdict->pdict, word, wordlen, &n_sugg); if (suggs && n_sugg) { - size_t i; + array_init_size(return_value, n_sugg); - for (i = 0; i < n_sugg; i++) { + for (size_t i = 0; i < n_sugg; i++) { add_next_index_string(return_value, suggs[i]); } enchant_dict_free_string_list(pdict->pdict, suggs); + } else { + RETURN_EMPTY_ARRAY(); } } /* }}} */ From e35fefbfdb83f08f6d4007b404261c17faf6e940 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Sat, 20 Dec 2025 08:18:43 +0100 Subject: [PATCH 203/252] Fix header for uriBaseRuntimeVersionA prototype --- ext/uri/php_uri.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 29b26f7b8ce27..77fc627b6a99d 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -30,7 +30,7 @@ #include "uri_parser_rfc3986.h" #include "uri_parser_php_parse_url.h" #include "php_uri_arginfo.h" -#include "uriparser/UriBase.h" +#include "uriparser/Uri.h" zend_class_entry *php_uri_ce_rfc3986_uri; zend_class_entry *php_uri_ce_whatwg_url; From 42f994cd05da353c93cd65775026859f664289a3 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sun, 21 Dec 2025 06:57:10 +0000 Subject: [PATCH 204/252] ext/zlib: minor internal changes. (#20654) consolidate encoding error exception b/w inflate_init()/deflate_init(). --- ext/zlib/tests/inflate_init_error.phpt | 2 +- ext/zlib/tests/leak_invalid_encoding_with_dict.phpt | 2 +- ext/zlib/zlib.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/zlib/tests/inflate_init_error.phpt b/ext/zlib/tests/inflate_init_error.phpt index 8faed763be4e5..9854f7453909b 100644 --- a/ext/zlib/tests/inflate_init_error.phpt +++ b/ext/zlib/tests/inflate_init_error.phpt @@ -13,4 +13,4 @@ try { ?> --EXPECT-- -Encoding mode must be ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE +inflate_init(): Argument #1 ($encoding) must be one of ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP, or ZLIB_ENCODING_DEFLATE diff --git a/ext/zlib/tests/leak_invalid_encoding_with_dict.phpt b/ext/zlib/tests/leak_invalid_encoding_with_dict.phpt index da2a11849c0c2..507e6842cb587 100644 --- a/ext/zlib/tests/leak_invalid_encoding_with_dict.phpt +++ b/ext/zlib/tests/leak_invalid_encoding_with_dict.phpt @@ -16,5 +16,5 @@ try { } ?> --EXPECT-- -Encoding mode must be ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE +inflate_init(): Argument #1 ($encoding) must be one of ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP, or ZLIB_ENCODING_DEFLATE deflate_init(): Argument #1 ($encoding) must be one of ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP, or ZLIB_ENCODING_DEFLATE diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 0b08cea7d69ec..68c5572931b66 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -887,7 +887,7 @@ PHP_FUNCTION(inflate_init) case PHP_ZLIB_ENCODING_DEFLATE: break; default: - zend_value_error("Encoding mode must be ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE"); + zend_argument_value_error(1, "must be one of ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP, or ZLIB_ENCODING_DEFLATE"); RETURN_THROWS(); } From efde160ef4db5e87879117624b600608102eeb6a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 13 Nov 2025 22:54:44 +0100 Subject: [PATCH 205/252] phar: Fix SplFileInfo::openFile() in write mode This stopped working after e735d2bc3b because fp_refcount is increased, making phar think that the file has open read pointers. To fix this, the refcount shouldn't be increased but that would re-introduce the previous bug. Instead, we need to add a field that "locks" the existence of the internal entry separate from the refcount. Closes GH-20473. --- NEWS | 1 + ext/phar/phar.c | 2 +- ext/phar/phar_internal.h | 1 + ext/phar/phar_object.c | 4 +-- ext/phar/tar.c | 2 +- .../tests/SplFileInfo_openFile_write.phpt | 31 +++++++++++++++++++ ext/phar/tests/gh17808.phpt | 20 ++++++++---- ext/phar/zip.c | 2 +- 8 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 ext/phar/tests/SplFileInfo_openFile_write.phpt diff --git a/NEWS b/NEWS index d96969b286f9f..ad754d97bdb2e 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,7 @@ PHP NEWS - Phar: . Fixed bug GH-20732 (Phar::LoadPhar undefined behavior when reading fails). (ndossche) + . Fix SplFileInfo::openFile() in write mode. (ndossche) - SPL: . Fixed bug GH-20678 (resource created by GlobIterator crashes with fclose()). diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 4c2cab27dcea4..6e06ca5e08247 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -418,7 +418,7 @@ void phar_entry_remove(phar_entry_data *idata, char **error) /* {{{ */ phar = idata->phar; - if (idata->internal_file->fp_refcount < 2) { + if (idata->internal_file->fp_refcount < 2 && idata->internal_file->fileinfo_lock_count == 0) { if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) { php_stream_close(idata->fp); } diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 9f8a46b65ec60..4afa72db231aa 100644 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -249,6 +249,7 @@ typedef struct _phar_entry_info { php_stream *fp; php_stream *cfp; int fp_refcount; + unsigned int fileinfo_lock_count; char *tmp; phar_archive_data *phar; char *link; /* symbolic link to another file */ diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 7c1bb6293782e..6d61adee776e4 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -4492,7 +4492,7 @@ PHP_METHOD(PharFileInfo, __construct) entry_obj->entry = entry_info; if (!entry_info->is_persistent && !entry_info->is_temp_dir) { - ++entry_info->fp_refcount; + ++entry_info->fileinfo_lock_count; /* The phar data must exist to keep the alias locked. */ ZEND_ASSERT(!phar_data->is_persistent); ++phar_data->refcount; @@ -4540,7 +4540,7 @@ PHP_METHOD(PharFileInfo, __destruct) efree(entry); entry_obj->entry = NULL; } else if (!entry->is_persistent) { - --entry->fp_refcount; + --entry->fileinfo_lock_count; /* The entry itself still lives in the manifest, * which will either be freed here if the file info was the last reference; or freed later. */ entry_obj->entry = NULL; diff --git a/ext/phar/tar.c b/ext/phar/tar.c index 3bbaad5968260..4424ad43d8954 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -721,7 +721,7 @@ static int phar_tar_writeheaders_int(phar_entry_info *entry, void *argument) /* } if (entry->is_deleted) { - if (entry->fp_refcount <= 0) { + if (entry->fp_refcount <= 0 && entry->fileinfo_lock_count == 0) { return ZEND_HASH_APPLY_REMOVE; } else { /* we can't delete this in-memory until it is closed */ diff --git a/ext/phar/tests/SplFileInfo_openFile_write.phpt b/ext/phar/tests/SplFileInfo_openFile_write.phpt new file mode 100644 index 0000000000000..f63baf5c7ad10 --- /dev/null +++ b/ext/phar/tests/SplFileInfo_openFile_write.phpt @@ -0,0 +1,31 @@ +--TEST-- +SplFileInfo::openFile() in write mode +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +--FILE-- +addFromString('test', 'contents'); +var_dump($phar['test']->openFile('w')); + +?> +--CLEAN-- + +--EXPECTF-- +object(SplFileObject)#%d (%d) { + ["pathName":"SplFileInfo":private]=> + string(%d) "phar://%stest" + ["fileName":"SplFileInfo":private]=> + string(4) "test" + ["openMode":"SplFileObject":private]=> + string(1) "w" + ["delimiter":"SplFileObject":private]=> + string(1) "," + ["enclosure":"SplFileObject":private]=> + string(1) """ +} diff --git a/ext/phar/tests/gh17808.phpt b/ext/phar/tests/gh17808.phpt index 03e54ff264bfa..a5c13a5405e24 100644 --- a/ext/phar/tests/gh17808.phpt +++ b/ext/phar/tests/gh17808.phpt @@ -5,18 +5,26 @@ phar zlib --FILE-- getContent())); -unlink("$file"); +unlink($file); var_dump($file->getATime()); ?> +--CLEAN-- + --EXPECTF-- -string(%d) "phar://%spackage.xml" +object(PharFileInfo)#%d (%d) { + ["pathName":"SplFileInfo":private]=> + string(%d) "phar://%spackage.xml" + ["fileName":"SplFileInfo":private]=> + string(11) "package.xml" +} int(6747) - -Warning: unlink(): phar error: "package.xml" in phar %s, has open file pointers, cannot unlink in %s on line %d int(33188) diff --git a/ext/phar/zip.c b/ext/phar/zip.c index b5133063e4460..d168c2a73ff2f 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -833,7 +833,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ } if (entry->is_deleted) { - if (entry->fp_refcount <= 0) { + if (entry->fp_refcount <= 0 && entry->fileinfo_lock_count == 0) { return ZEND_HASH_APPLY_REMOVE; } else { /* we can't delete this in-memory until it is closed */ From f89a3503bf9435bb4a3d08360eeb89607e219f82 Mon Sep 17 00:00:00 2001 From: Giovanni Giacobbi Date: Sun, 21 Dec 2025 12:17:50 +0100 Subject: [PATCH 206/252] Use EVP_MD_CTX_destroy() instead of EVP_MD_CTX_free() for compatibility and consistency Closes GH-20748. --- NEWS | 1 + ext/phar/util.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index ad754d97bdb2e..ecdd051c5d6c2 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,7 @@ PHP NEWS . Fixed bug GH-20732 (Phar::LoadPhar undefined behavior when reading fails). (ndossche) . Fix SplFileInfo::openFile() in write mode. (ndossche) + . Fix build on legacy OpenSSL 1.1.0 systems. (Giovanni Giacobbi) - SPL: . Fixed bug GH-20678 (resource created by GlobIterator crashes with fclose()). diff --git a/ext/phar/util.c b/ext/phar/util.c index c69f830e62805..4b9ff1cb58ba4 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -1905,7 +1905,7 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat if (!EVP_SignInit(md_ctx, mdtype)) { EVP_PKEY_free(key); - EVP_MD_CTX_free(md_ctx); + EVP_MD_CTX_destroy(md_ctx); efree(sigbuf); if (error) { spprintf(error, 0, "unable to initialize openssl signature for phar \"%s\"", phar->fname); @@ -1916,7 +1916,7 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { if (!EVP_SignUpdate(md_ctx, buf, sig_len)) { EVP_PKEY_free(key); - EVP_MD_CTX_free(md_ctx); + EVP_MD_CTX_destroy(md_ctx); efree(sigbuf); if (error) { spprintf(error, 0, "unable to update the openssl signature for phar \"%s\"", phar->fname); @@ -1927,7 +1927,7 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat if (!EVP_SignFinal (md_ctx, sigbuf, &siglen, key)) { EVP_PKEY_free(key); - EVP_MD_CTX_free(md_ctx); + EVP_MD_CTX_destroy(md_ctx); efree(sigbuf); if (error) { spprintf(error, 0, "unable to write phar \"%s\" with requested openssl signature", phar->fname); @@ -1937,7 +1937,7 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat sigbuf[siglen] = '\0'; EVP_PKEY_free(key); - EVP_MD_CTX_free(md_ctx); + EVP_MD_CTX_destroy(md_ctx); #else size_t siglen; sigbuf = NULL; From 1faf17b0177fcb4572e1618475f7570110d884c3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 21 Dec 2025 13:30:20 -0800 Subject: [PATCH 207/252] Use packed fill in scandir() (#20737) By preallocating the array to the right size and using a packed fill, we can avoid reallocations and use the fastest way to fill the array. --- ext/standard/dir.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ext/standard/dir.c b/ext/standard/dir.c index 918b92fab456f..7c1f8efe68875 100644 --- a/ext/standard/dir.c +++ b/ext/standard/dir.c @@ -558,11 +558,15 @@ PHP_FUNCTION(scandir) RETURN_FALSE; } - array_init(return_value); + array_init_size(return_value, n); + zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); - for (i = 0; i < n; i++) { - add_next_index_str(return_value, namelist[i]); - } + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + for (i = 0; i < n; i++) { + ZEND_HASH_FILL_SET_STR(namelist[i]); + ZEND_HASH_FILL_NEXT(); + } + } ZEND_HASH_FILL_END(); if (n) { efree(namelist); From 4f3c28aaac7ab4e5e66e7ef9018600d648ef29d2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Mon, 22 Dec 2025 02:51:31 -0800 Subject: [PATCH 208/252] phar: Simplify phar_open_archive_fp() (#20753) By returning the stream directly, we avoid calling some helpers functions and it becomes more clear on what stream the code actually acts upon. --- ext/phar/phar.c | 11 ++++++----- ext/phar/phar_internal.h | 2 +- ext/phar/stream.c | 4 ++-- ext/phar/util.c | 30 +++++++++++++++--------------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/ext/phar/phar.c b/ext/phar/phar.c index a4464444a02b5..b57f5596b37e3 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -2310,15 +2310,16 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_postprocess_file(phar_entry_data *idata, /* verify local file header */ phar_zip_file_header local; phar_zip_data_desc desc; + php_stream *stream = phar_open_archive_fp(idata->phar); - if (SUCCESS != phar_open_archive_fp(idata->phar)) { + if (!stream) { spprintf(error, 0, "phar error: unable to open zip-based phar archive \"%s\" to verify local file header for file \"%s\"", idata->phar->fname, ZSTR_VAL(entry->filename)); return FAILURE; } - php_stream_seek(phar_get_entrypfp(idata->internal_file), entry->header_offset, SEEK_SET); + php_stream_seek(stream, entry->header_offset, SEEK_SET); - if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file), (char *) &local, sizeof(local))) { + if (sizeof(local) != php_stream_read(stream, (char *) &local, sizeof(local))) { spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local file header for file \"%s\")", idata->phar->fname, ZSTR_VAL(entry->filename)); return FAILURE; @@ -2326,12 +2327,12 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_postprocess_file(phar_entry_data *idata, /* check for data descriptor */ if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) { - php_stream_seek(phar_get_entrypfp(idata->internal_file), + php_stream_seek(stream, entry->header_offset + sizeof(local) + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len) + entry->compressed_filesize, SEEK_SET); - if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file), + if (sizeof(desc) != php_stream_read(stream, (char *) &desc, sizeof(desc))) { spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local data descriptor for file \"%s\")", idata->phar->fname, ZSTR_VAL(entry->filename)); diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index abdbb9a9816f5..5ed213fb03e16 100644 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -440,7 +440,7 @@ php_stream *phar_get_efp(phar_entry_info *entry, bool follow_links); ZEND_ATTRIBUTE_NONNULL zend_result phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **error); ZEND_ATTRIBUTE_NONNULL zend_result phar_open_entry_fp(phar_entry_info *entry, char **error, bool follow_links); phar_entry_info *phar_get_link_source(phar_entry_info *entry); -zend_result phar_open_archive_fp(phar_archive_data *phar); +php_stream *phar_open_archive_fp(phar_archive_data *phar); zend_result phar_copy_on_write(phar_archive_data **pphar); /* tar functions in tar.c */ diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 08da1847cd9ce..4bd1e85666b85 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -254,13 +254,13 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha } else { php_stream *stream = phar_get_pharfp(phar); if (stream == NULL) { - if (UNEXPECTED(FAILURE == phar_open_archive_fp(phar))) { + stream = phar_open_archive_fp(phar); + if (UNEXPECTED(!stream)) { php_stream_wrapper_log_error(wrapper, options, "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); return NULL; } - stream = phar_get_pharfp(phar); } phar_entry_info *entry; diff --git a/ext/phar/util.c b/ext/phar/util.c index 5e5495d8d96af..fdadc4d9b6bf7 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -107,11 +107,12 @@ php_stream *phar_get_efp(phar_entry_info *entry, bool follow_links) /* {{{ */ } if (phar_get_fp_type(entry) == PHAR_FP) { - if (!phar_get_entrypfp(entry)) { + php_stream *stream = phar_get_entrypfp(entry); + if (!stream) { /* re-open just in time for cases where our refcount reached 0 on the phar archive */ - phar_open_archive_fp(entry->phar); + stream = phar_open_archive_fp(entry->phar); } - return phar_get_entrypfp(entry); + return stream; } else if (phar_get_fp_type(entry) == PHAR_UFP) { return phar_get_entrypufp(entry); } else if (entry->fp_type == PHAR_MOD) { @@ -708,24 +709,23 @@ static inline void phar_set_pharfp(phar_archive_data *phar, php_stream *fp) PHAR_G(cached_fp)[phar->phar_pos].fp = fp; } -/* initialize a phar_archive_data's read-only fp for existing phar data */ -zend_result phar_open_archive_fp(phar_archive_data *phar) /* {{{ */ +/* Initialize a phar_archive_data's read-only fp for existing phar data. + * The stream is owned by the `phar` object and must not be closed manually. */ +php_stream *phar_open_archive_fp(phar_archive_data *phar) /* {{{ */ { - if (phar_get_pharfp(phar)) { - return SUCCESS; + php_stream *stream = phar_get_pharfp(phar); + if (stream) { + return stream; } if (php_check_open_basedir(phar->fname)) { - return FAILURE; + return NULL; } - phar_set_pharfp(phar, php_stream_open_wrapper(phar->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL)); - - if (!phar_get_pharfp(phar)) { - return FAILURE; - } + stream = php_stream_open_wrapper(phar->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, NULL); + phar_set_pharfp(phar, stream); - return SUCCESS; + return stream; } /* }}} */ @@ -829,7 +829,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_open_entry_fp(phar_entry_info *entry, ch } if (!phar_get_pharfp(phar)) { - if (FAILURE == phar_open_archive_fp(phar)) { + if (!phar_open_archive_fp(phar)) { spprintf(error, 4096, "phar error: Cannot open phar archive \"%s\" for reading", phar->fname); return FAILURE; } From 9a7c09c9639afd333d9c01894972988fb9d58d70 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 21 Dec 2025 23:37:02 +0100 Subject: [PATCH 209/252] intl: Fix leak in umsg_format_helper() Closes GH-20756. --- NEWS | 3 +++ ext/intl/msgformat/msgformat_helpers.cpp | 1 + ext/intl/tests/msgfmt_format_error4.phpt | 9 +++++++++ 3 files changed, 13 insertions(+) diff --git a/NEWS b/NEWS index ecdd051c5d6c2..b52e9b1a1366d 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,9 @@ PHP NEWS - GD: . Fixed bug GH-20622 (imagestring/imagestringup overflow). (David Carlier) +- Intl: + . Fix leak in umsg_format_helper(). (ndossche) + - LDAP: . Fix memory leak in ldap_set_options(). (ndossche) diff --git a/ext/intl/msgformat/msgformat_helpers.cpp b/ext/intl/msgformat/msgformat_helpers.cpp index fbd85b857f3bc..58e2a96bf23b9 100644 --- a/ext/intl/msgformat/msgformat_helpers.cpp +++ b/ext/intl/msgformat/msgformat_helpers.cpp @@ -457,6 +457,7 @@ U_CFUNC void umsg_format_helper(MessageFormatter_object *mfo, char *message; spprintf(&message, 0, "Invalid UTF-8 data in string argument: " "'%s'", ZSTR_VAL(str)); + zend_tmp_string_release(tmp_str); intl_errors_set(&err, err.code, message, 1); efree(message); delete text; diff --git a/ext/intl/tests/msgfmt_format_error4.phpt b/ext/intl/tests/msgfmt_format_error4.phpt index 08b52d8ba421f..ee84388f4a7db 100644 --- a/ext/intl/tests/msgfmt_format_error4.phpt +++ b/ext/intl/tests/msgfmt_format_error4.phpt @@ -14,9 +14,18 @@ $mf = new MessageFormatter('en_US', $fmt); var_dump($mf->format(array("foo" => 7, "\x80" => "bar"))); var_dump($mf->format(array("foo" => "\x80"))); + +var_dump($mf->format(array("foo" => new class { + function __toString(): string { + return str_repeat("\x80", random_int(1, 1)); + } +}))); --EXPECTF-- Warning: MessageFormatter::format(): Invalid UTF-8 data in argument key: '�' in %s on line %d bool(false) Warning: MessageFormatter::format(): Invalid UTF-8 data in string argument: '�' in %s on line %d bool(false) + +Warning: MessageFormatter::format(): Invalid UTF-8 data in string argument: '�' in %s on line %d +bool(false) From e90b48c8e5dd9f007aeb25f28d266cf588e975fa Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 21 Dec 2025 15:46:47 +0100 Subject: [PATCH 210/252] Fix bug #74154: Phar extractTo creates empty files The current code causes the phar entry to remain in the fname cache. This would be fine for uncompressed phars, but is a problem for compressed phars when they try to reopen the file pointer. The reopen code will try to use the compressed file pointer as if it were an uncompressed file pointer. In that case, for the given test, the file offsets are out of bounds for the compressed file pointer because they are the uncompressed offsets. This results in empty files. In other cases, it's possible to read compressed parts of the file that don't belong to that particular file. To solve this, we simply remove the phar entry from the fname cache if the file pointer was closed but the phar is compressed. This will make sure that reopening the phar will not go through the cache and instead opens up a fresh file pointer with the right decompression settings. Closes GH-20754. --- NEWS | 1 + ext/phar/phar.c | 20 ++++++++++------ ext/phar/tests/bug74154.phpt | 40 ++++++++++++++++++++++++++++++++ ext/phar/tests/tar/bug70417.phpt | 5 ++-- 4 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 ext/phar/tests/bug74154.phpt diff --git a/NEWS b/NEWS index 23ff95cee78b3..223373fdc7442 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,7 @@ PHP NEWS (ndossche) . Fix SplFileInfo::openFile() in write mode. (ndossche) . Fix build on legacy OpenSSL 1.1.0 systems. (Giovanni Giacobbi) + . Fixed bug #74154 (Phar extractTo creates empty files). (ndossche) - SPL: . Fixed bug GH-20678 (resource created by GlobIterator crashes with fclose()). diff --git a/ext/phar/phar.c b/ext/phar/phar.c index fb80075a140dc..7e74de782ccbf 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -260,20 +260,26 @@ bool phar_archive_delref(phar_archive_data *phar) /* {{{ */ PHAR_G(last_phar) = NULL; PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + /* This is a new phar that has perhaps had an alias/metadata set, but has never been flushed. */ + bool remove_fname_cache = !zend_hash_num_elements(&phar->manifest); + if (phar->fp && (!(phar->flags & PHAR_FILE_COMPRESSION_MASK) || !phar->alias)) { /* close open file handle - allows removal or rename of the file on windows, which has greedy locking - only close if the archive was not already compressed. If it - was compressed, then the fp does not refer to the original file. - We're also closing compressed files to save resources, - but only if the archive isn't aliased. */ + only close if the archive was not already compressed. + We're also closing compressed files to save resources, but only if the archive isn't aliased. + If it was compressed, then the fp does not refer to the original compressed file: + it refers to the **uncompressed** filtered file stream. + Therefore, upon closing a compressed file we need to invalidate the phar archive such + that the code that reopens the phar will not try to use the **compressed** file as if it was uncompressed. + That would result in treating compressed file data as if it were compressed and using uncompressed file offsets + on the compressed file. */ php_stream_close(phar->fp); phar->fp = NULL; + remove_fname_cache |= phar->flags & PHAR_FILE_COMPRESSION_MASK; } - if (!zend_hash_num_elements(&phar->manifest)) { - /* this is a new phar that has perhaps had an alias/metadata set, but has never - been flushed */ + if (remove_fname_cache) { if (zend_hash_str_del(&(PHAR_G(phar_fname_map)), phar->fname, phar->fname_len) != SUCCESS) { phar_destroy_phar_data(phar); } diff --git a/ext/phar/tests/bug74154.phpt b/ext/phar/tests/bug74154.phpt new file mode 100644 index 0000000000000..4aecc76e6dc18 --- /dev/null +++ b/ext/phar/tests/bug74154.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #74154 (Phar extractTo creates empty files) +--EXTENSIONS-- +phar +--FILE-- +buildFromDirectory($dir); + +$compPhar = $phar->compress(Phar::GZ); +unset($phar); //make sure that test.tar is closed +unlink(__DIR__.'/bug74154.tar'); +unset($compPhar); //make sure that test.tar.gz is closed +$extractingPhar = new PharData(__DIR__.'/bug74154.tar.gz'); +$extractingPhar->extractTo($dir.'_out'); + +var_dump(file_get_contents($dir.'_out/1.txt')); +var_dump(file_get_contents($dir.'_out/2.txt')); + +?> +--CLEAN-- + +--EXPECT-- +string(64) "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" +string(64) "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" diff --git a/ext/phar/tests/tar/bug70417.phpt b/ext/phar/tests/tar/bug70417.phpt index 504d7e1e387b6..7bb6bbafbcfa4 100644 --- a/ext/phar/tests/tar/bug70417.phpt +++ b/ext/phar/tests/tar/bug70417.phpt @@ -10,10 +10,11 @@ $filename = __DIR__ . '/bug70417.tar'; $resBefore = count(get_resources()); $arch = new PharData($filename); $arch->addFromString('foo', 'bar'); +$arch->addFromString('foo2', 'baz'); $arch->compress(Phar::GZ); unset($arch); $resAfter = count(get_resources()); -var_dump($resBefore === $resAfter); +var_dump($resAfter - $resBefore); ?> --CLEAN-- --EXPECT-- -bool(true) +int(0) From d3ef80649e66066fc675d707d0783b167aee19e0 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Sun, 21 Dec 2025 13:35:54 +0100 Subject: [PATCH 211/252] Fix zend_vm_gen.php when executed with PHP 8.5 PHP 8.5 defines constant ZEND_VM_KIND since GH-19574, but this name is also used by zend_vm_gen.php. This causes zend_vm_gen.php to generate invalid code when executed with PHP 8.5 in an older branch. Here I rename the constant in zend_vm_gen.php. --- Zend/zend_vm_gen.php | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index 64105f5659f17..362d8554d54c3 100755 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -841,7 +841,7 @@ function($matches) use ($spec, $prefix, $op1, $op2, $extra_spec, $name) { $handler = $matches[1]; $opcode = $opcodes[$opnames[$handler]]; $inline = - ZEND_VM_KIND == ZEND_VM_KIND_HYBRID && + ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID && isset($opcode["use"]) && is_hot_handler($opcode["hot"], $op1, $op2, $extra_spec) && is_hot_handler($opcodes[$opnames[$name]]["hot"], $op1, $op2, $extra_spec) ? @@ -1077,7 +1077,7 @@ function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno, } return; case ZEND_VM_KIND_CALL: - if ($opcode["hot"] && ZEND_VM_KIND == ZEND_VM_KIND_HYBRID && is_hot_handler($opcode["hot"], $op1, $op2, $extra_spec)) { + if ($opcode["hot"] && ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID && is_hot_handler($opcode["hot"], $op1, $op2, $extra_spec)) { if (isset($opcode["use"])) { out($f,"static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL {$spec_name}_INLINE_HANDLER(ZEND_OPCODE_HANDLER_ARGS)\n"); $additional_func = true; @@ -2326,7 +2326,7 @@ function gen_vm_opcodes_header( $str .= "#define ZEND_VM_KIND_SWITCH\t" . ZEND_VM_KIND_SWITCH . "\n"; $str .= "#define ZEND_VM_KIND_GOTO\t" . ZEND_VM_KIND_GOTO . "\n"; $str .= "#define ZEND_VM_KIND_HYBRID\t" . ZEND_VM_KIND_HYBRID . "\n"; - if ($GLOBALS["vm_kind_name"][ZEND_VM_KIND] === "ZEND_VM_KIND_HYBRID") { + if ($GLOBALS["vm_kind_name"][ZEND_VM_GEN_KIND] === "ZEND_VM_KIND_HYBRID") { $str .= "/* HYBRID requires support for computed GOTO and global register variables*/\n"; $str .= "#if (defined(__GNUC__) && defined(HAVE_GCC_GLOBAL_REGS))\n"; $str .= "# define ZEND_VM_KIND\t\tZEND_VM_KIND_HYBRID\n"; @@ -2334,7 +2334,7 @@ function gen_vm_opcodes_header( $str .= "# define ZEND_VM_KIND\t\tZEND_VM_KIND_CALL\n"; $str .= "#endif\n"; } else { - $str .= "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_KIND] . "\n"; + $str .= "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_GEN_KIND] . "\n"; } $str .= "\n"; $str .= "#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) && !defined(__SANITIZE_ADDRESS__)\n"; @@ -2536,9 +2536,9 @@ function gen_vm($def, $skel) { } // Store parameters - if ((ZEND_VM_KIND == ZEND_VM_KIND_GOTO - || ZEND_VM_KIND == ZEND_VM_KIND_SWITCH - || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID && $hot)) + if ((ZEND_VM_GEN_KIND == ZEND_VM_KIND_GOTO + || ZEND_VM_GEN_KIND == ZEND_VM_KIND_SWITCH + || (ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID && $hot)) && $param) { foreach (explode(",", $param ) as $p) { $p = trim($p); @@ -2600,7 +2600,7 @@ function gen_vm($def, $skel) { die("ERROR ($def:$lineno): Opcode with name '$op' is not defined.\n"); } $opcodes[$opnames[$dsc['op']]]['alias'] = $op; - if (!ZEND_VM_SPEC && ZEND_VM_KIND == ZEND_VM_KIND_SWITCH) { + if (!ZEND_VM_SPEC && ZEND_VM_GEN_KIND == ZEND_VM_KIND_SWITCH) { $code = $opnames[$op]; $opcodes[$code]['use'] = 1; } @@ -2713,7 +2713,7 @@ function gen_vm($def, $skel) { out($f, "255\n};\n\n"); // Generate specialized executor - gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_KIND, "execute", "zend_vm_init"); + gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_GEN_KIND, "execute", "zend_vm_init"); out($f, "\n"); // Generate zend_vm_get_opcode_handler() function @@ -2807,7 +2807,7 @@ function gen_vm($def, $skel) { out($f, "}\n"); out($f, "#endif\n\n"); - if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID) { // Generate zend_vm_get_opcode_handler_func() function out($f, "#if ZEND_VM_KIND == ZEND_VM_KIND_HYBRID\n"); out($f,"static const void *zend_vm_get_opcode_handler_func(uint8_t opcode, const zend_op* op)\n"); @@ -2918,10 +2918,10 @@ function gen_vm($def, $skel) { out($f, "}\n\n"); // Generate zend_vm_call_opcode_handler() function - if (ZEND_VM_KIND == ZEND_VM_KIND_CALL || ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_GEN_KIND == ZEND_VM_KIND_CALL || ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID) { out($f, "ZEND_API int ZEND_FASTCALL zend_vm_call_opcode_handler(zend_execute_data* ex)\n"); out($f, "{\n"); - if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID) { out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n"); out($f, "\topcode_handler_t handler;\n"); out($f,"#endif\n"); @@ -2939,7 +2939,7 @@ function gen_vm($def, $skel) { out($f, "\n"); out($f, "\tLOAD_OPLINE();\n"); out($f,"#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)\n"); - if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID) { out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n"); out($f, "\thandler = (opcode_handler_t)zend_vm_get_opcode_handler_func(zend_user_opcodes[opline->opcode], opline);\n"); out($f, "\thandler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); @@ -2947,7 +2947,7 @@ function gen_vm($def, $skel) { out($f,"#else\n"); } out($f, "\t((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); - if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID) { out($f, "\tif (EXPECTED(opline)) {\n"); out($f,"#endif\n"); } else { @@ -3009,16 +3009,16 @@ function usage() { $kind = substr($argv[$i], strlen("--with-vm-kind=")); switch ($kind) { case "CALL": - define("ZEND_VM_KIND", ZEND_VM_KIND_CALL); + define("ZEND_VM_GEN_KIND", ZEND_VM_KIND_CALL); break; case "SWITCH": - define("ZEND_VM_KIND", ZEND_VM_KIND_SWITCH); + define("ZEND_VM_GEN_KIND", ZEND_VM_KIND_SWITCH); break; case "GOTO": - define("ZEND_VM_KIND", ZEND_VM_KIND_GOTO); + define("ZEND_VM_GEN_KIND", ZEND_VM_KIND_GOTO); break; case "HYBRID": - define("ZEND_VM_KIND", ZEND_VM_KIND_HYBRID); + define("ZEND_VM_GEN_KIND", ZEND_VM_KIND_HYBRID); break; default: echo("ERROR: Invalid vm kind '$kind'\n"); @@ -3042,9 +3042,9 @@ function usage() { } // Using defaults -if (!defined("ZEND_VM_KIND")) { +if (!defined("ZEND_VM_GEN_KIND")) { // Using CALL threading by default - define("ZEND_VM_KIND", ZEND_VM_KIND_HYBRID); + define("ZEND_VM_GEN_KIND", ZEND_VM_KIND_HYBRID); } if (!defined("ZEND_VM_SPEC")) { // Using specialized executor by default From e63dae2941959c832b5488fc66b1b7375d25efcb Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 20 Dec 2025 22:15:12 +0000 Subject: [PATCH 212/252] ext/posix: (Further) fix groups array creation on macos. With macos Tahoe and clang "17.0.0" (Xcode) the ext/posix/tests/posix_getgrgid_macosx.phpt test crashes as follow: ext/posix/posix.c:681:19: runtime error: load of misaligned address 0x60800000e972 for type 'char **', which requires 8 byte alignment 0x60800000e972: note: pointer points here 70 00 2a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 seems memcpy had been translated to a load instruction ? anyhow, we force to copy a "proper" char * source. close GH-20744 --- NEWS | 4 ++++ ext/posix/posix.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index b52e9b1a1366d..44da45b565b04 100644 --- a/NEWS +++ b/NEWS @@ -38,6 +38,10 @@ PHP NEWS . Fix SplFileInfo::openFile() in write mode. (ndossche) . Fix build on legacy OpenSSL 1.1.0 systems. (Giovanni Giacobbi) +- POSIX: + . Fixed crash on posix groups to php array creation on macos. + (David Carlier) + - SPL: . Fixed bug GH-20678 (resource created by GlobIterator crashes with fclose()). (David Carlier) diff --git a/ext/posix/posix.c b/ext/posix/posix.c index 2c87fbd28d981..85ea03d8cc0cb 100644 --- a/ext/posix/posix.c +++ b/ext/posix/posix.c @@ -678,7 +678,8 @@ int php_posix_group_to_array(struct group *g, zval *array_group) /* {{{ */ for (count = 0;; count++) { /* gr_mem entries may be misaligned on macos. */ char *gr_mem; - memcpy(&gr_mem, &g->gr_mem[count], sizeof(char *)); + char *entry = (char *)g->gr_mem + (count * sizeof (char *)); + memcpy(&gr_mem, entry, sizeof(char *)); if (!gr_mem) { break; } From 4d9a038fd0b38c5eea13b1872e53789fc2c82a0c Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 22 Dec 2025 17:56:03 +0100 Subject: [PATCH 213/252] [skip ci] Drop CI for 8.1 --- .github/nightly_matrix.php | 1 - .github/workflows/root.yml | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/nightly_matrix.php b/.github/nightly_matrix.php index c1f5627542684..0032da7dbcea8 100644 --- a/.github/nightly_matrix.php +++ b/.github/nightly_matrix.php @@ -6,7 +6,6 @@ ['ref' => 'PHP-8.4', 'version' => [8, 4]], ['ref' => 'PHP-8.3', 'version' => [8, 3]], ['ref' => 'PHP-8.2', 'version' => [8, 2]], - ['ref' => 'PHP-8.1', 'version' => [8, 1]], ]; function get_branch_commit_cache_file_path(): string { diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index 55bfe2ebeb9fc..767a853e7c8ff 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -57,9 +57,7 @@ jobs: windows_version: '2022' vs_crt_version: ${{ ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) && 'vs17') || 'vs16' }} skip_laravel: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} - symfony_version: ${{ (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9) && '8.1') - || ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 2) && '7.4') - || '' }} + symfony_version: ${{ (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9) && '8.1') || '7.4' }} skip_wordpress: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} variation_enable_zend_max_execution_timers: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 3) || matrix.branch.version[0] >= 9 }} secrets: inherit From ef87a146fb8a12398bc0ffc58b3493991f818167 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Mon, 22 Dec 2025 15:39:09 -0800 Subject: [PATCH 214/252] pgsql: Don't allocate memory for default arguments (#20757) --- ext/pgsql/pgsql.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 77425b25aee17..e3d8cb82ece91 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3350,9 +3350,8 @@ PHP_FUNCTION(pg_copy_to) pgsql_link_handle *link; zend_string *table_name; zend_string *pg_delimiter = NULL; - char *pg_null_as = NULL; + char *pg_null_as = "\\\\N"; size_t pg_null_as_len = 0; - bool free_pg_null = false; char *query; PGconn *pgsql; PGresult *pgsql_result; @@ -3377,10 +3376,6 @@ PHP_FUNCTION(pg_copy_to) zend_argument_value_error(3, "must be one character"); RETURN_THROWS(); } - if (!pg_null_as) { - pg_null_as = estrdup("\\\\N"); - free_pg_null = true; - } spprintf(&query, 0, "COPY %s TO STDOUT DELIMITER E'%c' NULL AS E'%s'", ZSTR_VAL(table_name), *ZSTR_VAL(pg_delimiter), pg_null_as); @@ -3388,9 +3383,6 @@ PHP_FUNCTION(pg_copy_to) PQclear(pgsql_result); } pgsql_result = PQexec(pgsql, query); - if (free_pg_null) { - efree(pg_null_as); - } efree(query); if (pgsql_result) { @@ -3475,9 +3467,8 @@ PHP_FUNCTION(pg_copy_from) zval *value; zend_string *table_name; zend_string *pg_delimiter = NULL; - char *pg_null_as = NULL; + char *pg_null_as = "\\\\N"; size_t pg_null_as_len; - bool pg_null_as_free = false; char *query; PGconn *pgsql; PGresult *pgsql_result; @@ -3502,10 +3493,6 @@ PHP_FUNCTION(pg_copy_from) zend_argument_value_error(4, "must be one character"); RETURN_THROWS(); } - if (!pg_null_as) { - pg_null_as = estrdup("\\\\N"); - pg_null_as_free = true; - } spprintf(&query, 0, "COPY %s FROM STDIN DELIMITER E'%c' NULL AS E'%s'", ZSTR_VAL(table_name), *ZSTR_VAL(pg_delimiter), pg_null_as); while ((pgsql_result = PQgetResult(pgsql))) { @@ -3513,9 +3500,6 @@ PHP_FUNCTION(pg_copy_from) } pgsql_result = PQexec(pgsql, query); - if (pg_null_as_free) { - efree(pg_null_as); - } efree(query); if (pgsql_result) { From 8c860ce66e3b16b42cb809c05edb87c329438094 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 23 Dec 2025 11:27:34 +0100 Subject: [PATCH 215/252] [ci skip] Add missing EXTENSIONS dependency to test --- ext/phar/tests/bug74154.phpt | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/phar/tests/bug74154.phpt b/ext/phar/tests/bug74154.phpt index 4aecc76e6dc18..ab3836ce87d46 100644 --- a/ext/phar/tests/bug74154.phpt +++ b/ext/phar/tests/bug74154.phpt @@ -2,6 +2,7 @@ Bug #74154 (Phar extractTo creates empty files) --EXTENSIONS-- phar +zlib --FILE-- Date: Tue, 23 Dec 2025 12:46:06 -0500 Subject: [PATCH 216/252] ext/session/mod_mm.c: add a few missing ZSTR macros In eaee504c the session's save_path global was changed to a zend_string pointer, but there are a few direct char-pointer accesses in ext/session/mod_mm.c that slipped through the cracks. GCC-15 notices them and fails to build due to the incompatible pointer types. Three ZSTR_* wrappers are all that is needed. Gentoo-Bug: https://bugs.gentoo.org/967862 Closes GH-20772. --- NEWS | 3 +++ ext/session/mod_mm.c | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 676e7bac4e557..a28ef5f450d32 100644 --- a/NEWS +++ b/NEWS @@ -40,6 +40,9 @@ PHP NEWS . Fix build on legacy OpenSSL 1.1.0 systems. (Giovanni Giacobbi) . Fixed bug #74154 (Phar extractTo creates empty files). (ndossche) +- Session: + . Fix support for MM module. (Michael Orlitzky) + - Sqlite3: . Fixed bug GH-20699 (SQLite3Result fetchArray return array|false, null returned). (ndossche, plusminmax) diff --git a/ext/session/mod_mm.c b/ext/session/mod_mm.c index b997a2bdcff54..b794be646961a 100644 --- a/ext/session/mod_mm.c +++ b/ext/session/mod_mm.c @@ -264,7 +264,7 @@ static void ps_mm_destroy(ps_mm *data) PHP_MINIT_FUNCTION(ps_mm) { - size_t save_path_len = strlen(PS(save_path)); + size_t save_path_len = ZSTR_LEN(PS(save_path)); size_t mod_name_len = strlen(sapi_module.name); size_t euid_len; char *ps_mm_path, euid[30]; @@ -284,8 +284,8 @@ PHP_MINIT_FUNCTION(ps_mm) /* Directory + '/' + File + Module Name + Effective UID + \0 */ ps_mm_path = emalloc(save_path_len + 1 + (sizeof(PS_MM_FILE) - 1) + mod_name_len + euid_len + 1); - memcpy(ps_mm_path, PS(save_path), save_path_len); - if (save_path_len && PS(save_path)[save_path_len - 1] != DEFAULT_SLASH) { + memcpy(ps_mm_path, ZSTR_VAL(PS(save_path)), save_path_len); + if (save_path_len && ZSTR_VAL(PS(save_path))[save_path_len - 1] != DEFAULT_SLASH) { ps_mm_path[save_path_len] = DEFAULT_SLASH; save_path_len++; } From 13d63d610551431077e090da0e42694e93fc57c1 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 23 Dec 2025 19:14:28 +0100 Subject: [PATCH 217/252] Fix GH-20771: Assertion failure when getUnicodeHost() returns empty string If nothing was added to a smart_str, the interned empty string is returned, and therefore ZVAL_NEW_STR is wrong as it'll set the REFCOUNTED flag. Closes GH-20773. --- NEWS | 4 ++++ ext/uri/tests/whatwg/getters/gh20771.phpt | 14 ++++++++++++++ ext/uri/uri_parser_whatwg.c | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 ext/uri/tests/whatwg/getters/gh20771.phpt diff --git a/NEWS b/NEWS index a28ef5f450d32..9ea0ffa9bbe2d 100644 --- a/NEWS +++ b/NEWS @@ -50,6 +50,10 @@ PHP NEWS - Standard: . Fix error check for proc_open() command. (ndossche) +- URI: + . Fixed bug GH-20771 (Assertion failure when getUnicodeHost() returns + empty string). (ndossche) + 18 Dec 2025, PHP 8.5.1 - Core: diff --git a/ext/uri/tests/whatwg/getters/gh20771.phpt b/ext/uri/tests/whatwg/getters/gh20771.phpt new file mode 100644 index 0000000000000..19f1d1fb0f500 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/gh20771.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-20771 (Assertion failure when getUnicodeHost() returns empty string) +--EXTENSIONS-- +uri +--FILE-- +getUnicodeHost()); + +?> +--EXPECT-- +string(0) "" diff --git a/ext/uri/uri_parser_whatwg.c b/ext/uri/uri_parser_whatwg.c index 2e9ffad22d463..055f130af7c65 100644 --- a/ext/uri/uri_parser_whatwg.c +++ b/ext/uri/uri_parser_whatwg.c @@ -365,7 +365,7 @@ static zend_result php_uri_parser_whatwg_host_read(void *uri, php_uri_component_ lxb_url_serialize_host_unicode(&lexbor_idna, &lexbor_uri->host, serialize_to_smart_str_callback, &host_str); lxb_unicode_idna_clean(&lexbor_idna); - ZVAL_NEW_STR(retval, smart_str_extract(&host_str)); + ZVAL_STR(retval, smart_str_extract(&host_str)); break; } case PHP_URI_COMPONENT_READ_MODE_NORMALIZED_ASCII: From 494dd9752144413078962fb0d428b0f64449d8ca Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Wed, 24 Dec 2025 13:29:23 +0100 Subject: [PATCH 218/252] Fix NEWS formatting --- NEWS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 44da45b565b04..a2690fa84d3ef 100644 --- a/NEWS +++ b/NEWS @@ -28,9 +28,9 @@ PHP NEWS - LDAP: . Fix memory leak in ldap_set_options(). (ndossche) -- Mbstring - . Fixed bug GH-20674 (Fix GH-20674 mb_decode_mimeheader does not handle - separator). (Yuya Hamada) +- Mbstring: + . Fixed bug GH-20674 (mb_decode_mimeheader does not handle separator). + (Yuya Hamada) - Phar: . Fixed bug GH-20732 (Phar::LoadPhar undefined behavior when reading fails). From 5faa54d93bd5b30397352a9534b4df964a1d75e0 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 19 Dec 2025 12:49:17 +0000 Subject: [PATCH 219/252] ext/pcntl: fix pcntl_getcpuaffinity() for solaris. trusting the call to handle invalid process id via errnos. see https://github.com/php/php-src/pull/20709#discussion_r2630221301 for rationale. close GH-20731 --- NEWS | 4 ++++ ext/pcntl/pcntl.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 7dca021c37623..2fb8cd3620fec 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,10 @@ PHP NEWS . Fixed bug GH-20674 (mb_decode_mimeheader does not handle separator). (Yuya Hamada) +- PCNTL: + . Fixed bug with pcntl_getcpuaffinity() on solaris regarding invalid + process ids handling. (David Carlier) + - Phar: . Fixed bug GH-20732 (Phar::LoadPhar undefined behavior when reading fails). (ndossche) diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 27e0c90dc96f0..2034ca80b05b8 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -93,7 +93,7 @@ typedef cpuset_t *cpu_set_t; #elif defined(HAVE_PSET_BIND) #include typedef psetid_t cpu_set_t; - #define sched_getaffinity(p, c, m) pset_bind(PS_QUERY, P_PID, (p <= 0 ? getpid() : p), &m) + #define sched_getaffinity(p, c, m) pset_bind(PS_QUERY, P_PID, p, &m) #define sched_setaffinity(p, c, m) pset_bind(m, P_PID, (p <= 0 ? getpid() : p), NULL) #define PCNTL_CPUSET(mask) mask #define PCNTL_CPU_ISSET(i, mask) (pset_assign(PS_QUERY, (processorid_t)i, &query) == 0 && query == mask) From 20f9772063cc3eb1e021508adc1433b62b6e5a25 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 17:48:18 +0100 Subject: [PATCH 220/252] ext/standard: Fix memory leak in mail() when header key is numeric Closes GH-20776 --- NEWS | 1 + ext/standard/mail.c | 3 ++- ext/standard/tests/mail/gh20776.phpt | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/mail/gh20776.phpt diff --git a/NEWS b/NEWS index 2fb8cd3620fec..604f81d3078b8 100644 --- a/NEWS +++ b/NEWS @@ -57,6 +57,7 @@ PHP NEWS - Standard: . Fix error check for proc_open() command. (ndossche) + . Fix memory leak in mail() when header key is numeric. (Girgias) 18 Dec 2025, PHP 8.4.16 diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 35c23a0be76c0..c9b34fbdfc92d 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -214,7 +214,8 @@ PHPAPI zend_string *php_mail_build_headers(HashTable *headers) ZEND_HASH_FOREACH_KEY_VAL(headers, idx, key, val) { if (!key) { zend_type_error("Header name cannot be numeric, " ZEND_LONG_FMT " given", idx); - break; + smart_str_free(&s); + return NULL; } ZVAL_DEREF(val); /* https://tools.ietf.org/html/rfc2822#section-3.6 */ diff --git a/ext/standard/tests/mail/gh20776.phpt b/ext/standard/tests/mail/gh20776.phpt new file mode 100644 index 0000000000000..aec68c4719202 --- /dev/null +++ b/ext/standard/tests/mail/gh20776.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-20776: mail() memory leak when header array contains numeric keys +--FILE-- + 'Value', 5 => 'invalid key'])); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +TypeError: Header name cannot be numeric, 5 given From 7d4ba807054425074641f9e63a9445cbed12caca Mon Sep 17 00:00:00 2001 From: Giovanni Giacobbi Date: Thu, 25 Dec 2025 21:10:29 +0100 Subject: [PATCH 221/252] gen_stub: Fix php-parser package download (#20775) If the system wgetrc has the `content-disposition = on` option, the file is actually saved as `PHP-Parser-5.0.0.tar.gz`, causing a subsequent failure. Even with `content-disposition = off`, if for any reason the download file already exists and is corrupted, it won't be overwritten, and a new file such as `v5.0.0.tar.gz.1` is saved instead. We solve both problems by enforcing the name of the downloaded file. Also, if for any other reason the unpacking should fail, remove the created directory to allow further attempts. --- build/gen_stub.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index f1d8b43862e62..a2f488a6a226d 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -5970,9 +5970,10 @@ function installPhpParser(string $version, string $phpParserDir) { chdir(__DIR__); $tarName = "v$version.tar.gz"; - passthru("wget https://github.com/nikic/PHP-Parser/archive/$tarName", $exit); + $downloadUrl = "https://github.com/nikic/PHP-Parser/archive/$tarName"; + passthru("wget -O $tarName $downloadUrl", $exit); if ($exit !== 0) { - passthru("curl -LO https://github.com/nikic/PHP-Parser/archive/$tarName", $exit); + passthru("curl -LO $downloadUrl", $exit); } if ($exit !== 0) { throw new Exception("Failed to download PHP-Parser tarball"); @@ -5982,6 +5983,7 @@ function installPhpParser(string $version, string $phpParserDir) { } passthru("tar xvzf $tarName -C PHP-Parser-$version --strip-components 1", $exit); if ($exit !== 0) { + rmdir($phpParserDir); throw new Exception("Failed to extract PHP-Parser tarball"); } unlink(__DIR__ . "/$tarName"); From f20701416decead028551cb14cfc052a6b4750d0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 26 Dec 2025 03:15:25 -0800 Subject: [PATCH 222/252] mbstring: Transform RETURN_STR(zend_string_init_fast(...)) to RETURN_STRINGL_FAST(...) (#20779) This is a dedicated API which is cleaner. --- ext/mbstring/mbstring.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 118986411a8bb..12c366c33d5e2 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -2448,7 +2448,7 @@ PHP_FUNCTION(mb_strcut) if (len > string.len - from) { len = string.len - from; } - RETURN_STR(zend_string_init_fast((const char*)(string.val + from), len & -char_len)); + RETURN_STRINGL_FAST((const char*)(string.val + from), len & -char_len); } if (enc->mblen_table) { @@ -2471,7 +2471,7 @@ PHP_FUNCTION(mb_strcut) } end = p; } - RETURN_STR(zend_string_init_fast((const char*)start, end - start)); + RETURN_STRINGL_FAST((const char*)start, end - start); } ret = mbfl_strcut(&string, &result, from, len); From 913d3084528c1b2e6cfe9a9c6d9cfe334d78af63 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 26 Dec 2025 15:43:15 +0100 Subject: [PATCH 223/252] [ci skip] Remove useless test --- ext/ffi/tests/001.phpt | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 ext/ffi/tests/001.phpt diff --git a/ext/ffi/tests/001.phpt b/ext/ffi/tests/001.phpt deleted file mode 100644 index 3196498aba0db..0000000000000 --- a/ext/ffi/tests/001.phpt +++ /dev/null @@ -1,12 +0,0 @@ ---TEST-- -FFI 001: Check if FFI is loaded ---EXTENSIONS-- -ffi ---INI-- -ffi.enable=1 ---FILE-- - ---EXPECT-- -The extension "FFI" is available From 40c291cf932c31a302fa584bb50b6edb199fb07e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 23:31:27 +0100 Subject: [PATCH 224/252] Fix GH-20444: Dom\XMLDocument::C14N() seems broken compared to DOMDocument::C14N() C14N code expects namespace to be in-tree, but we store namespaces in a different way out-of-tree to avoid reconciliations that break the tree structure in a way unexpected by the DOM spec. In the DOM spec, namespace nodes don't exist; they're regular attributes. To solve this, we temporarily make fake namespace nodes that we later remove. Closes GH-20457. --- NEWS | 2 + ext/dom/node.c | 121 +++++++++++++++++- ext/dom/tests/canonicalization.phpt | 30 +++-- .../modern/xml/canonicalize_unattached.phpt | 20 +++ ext/dom/tests/modern/xml/gh20444.phpt | 43 +++++++ 5 files changed, 204 insertions(+), 12 deletions(-) create mode 100644 ext/dom/tests/modern/xml/canonicalize_unattached.phpt create mode 100644 ext/dom/tests/modern/xml/gh20444.phpt diff --git a/NEWS b/NEWS index 604f81d3078b8..e586e31998f6b 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,8 @@ PHP NEWS - DOM: . Fixed bug GH-20722 (Null pointer dereference in DOM namespace node cloning via clone on malformed objects). (ndossche) + . Fixed bug GH-20444 (Dom\XMLDocument::C14N() seems broken compared + to DOMDocument::C14N()). (ndossche) - GD: . Fixed bug GH-20622 (imagestring/imagestringup overflow). (David Carlier) diff --git a/ext/dom/node.c b/ext/dom/node.c index 3ec1db841571a..0dd1b959752ef 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -2081,6 +2081,97 @@ PHP_METHOD(DOMNode, lookupNamespaceURI) } /* }}} end dom_node_lookup_namespace_uri */ +static void dom_relink_ns_decls_element(HashTable *links, xmlNodePtr node) +{ + if (node->type == XML_ELEMENT_NODE) { + for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) { + if (php_dom_ns_is_fast((const xmlNode *) attr, php_dom_ns_is_xmlns_magic_token)) { + xmlNsPtr ns = xmlMalloc(sizeof(*ns)); + if (!ns) { + return; + } + + zval *zv = zend_hash_index_lookup(links, (zend_ulong) node); + if (Z_ISNULL_P(zv)) { + ZVAL_LONG(zv, 1); + } else { + Z_LVAL_P(zv)++; + } + + bool should_free; + xmlChar *attr_value = php_libxml_attr_value(attr, &should_free); + + memset(ns, 0, sizeof(*ns)); + ns->type = XML_LOCAL_NAMESPACE; + ns->href = should_free ? attr_value : xmlStrdup(attr_value); + ns->prefix = attr->ns->prefix ? xmlStrdup(attr->name) : NULL; + ns->next = node->nsDef; + node->nsDef = ns; + + ns->_private = attr; + if (attr->prev) { + attr->prev = attr->next; + } else { + node->properties = attr->next; + } + if (attr->next) { + attr->next->prev = attr->prev; + } + } + } + + /* The default namespace is handled separately from the other namespaces in C14N. + * The default namespace is explicitly looked up while the other namespaces are + * deduplicated and compared to a list of visible namespaces. */ + if (node->ns && !node->ns->prefix) { + /* Workaround for the behaviour where the xmlSearchNs() call inside c14n.c + * can return the current namespace. */ + zend_hash_index_add_new_ptr(links, (zend_ulong) node | 1, node->ns); + node->ns = xmlSearchNs(node->doc, node, NULL); + } + } +} + +static void dom_relink_ns_decls(HashTable *links, xmlNodePtr root) +{ + dom_relink_ns_decls_element(links, root); + + xmlNodePtr base = root; + xmlNodePtr node = base->children; + while (node != NULL) { + dom_relink_ns_decls_element(links, node); + node = php_dom_next_in_tree_order(node, base); + } +} + +static void dom_unlink_ns_decls(HashTable *links) +{ + ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL(links, zend_ulong h, zval *data) { + if (h & 1) { + xmlNodePtr node = (xmlNodePtr) (h ^ 1); + node->ns = Z_PTR_P(data); + } else { + xmlNodePtr node = (xmlNodePtr) h; + while (Z_LVAL_P(data)-- > 0) { + xmlNsPtr ns = node->nsDef; + node->nsDef = ns->next; + + xmlAttrPtr attr = ns->_private; + if (attr->prev) { + attr->prev->next = attr; + } else { + node->properties = attr; + } + if (attr->next) { + attr->next->prev = attr; + } + + xmlFreeNs(ns); + } + } + } ZEND_HASH_FOREACH_END(); +} + static int dom_canonicalize_node_parent_lookup_cb(void *user_data, xmlNodePtr node, xmlNodePtr parent) { xmlNodePtr root = user_data; @@ -2136,7 +2227,23 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ docp = nodep->doc; - if (! docp) { + HashTable links; + bool modern = php_dom_follow_spec_node(nodep); + if (modern) { + xmlNodePtr root = nodep; + while (root->parent) { + root = root->parent; + } + + if (UNEXPECTED(root->type != XML_DOCUMENT_NODE && root->type != XML_HTML_DOCUMENT_NODE)) { + php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "Canonicalization can only happen on nodes attached to a document.", /* strict */ true); + RETURN_THROWS(); + } + + zend_hash_init(&links, 0, NULL, NULL, false); + dom_relink_ns_decls(&links, xmlDocGetRootElement(docp)); + } else if (!docp) { + /* Note: not triggerable with modern DOM */ zend_throw_error(NULL, "Node must be associated with a document"); RETURN_THROWS(); } @@ -2158,12 +2265,12 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ if (!tmp) { /* if mode == 0 then $xpath arg is 3, if mode == 1 then $xpath is 4 */ zend_argument_value_error(3 + mode, "must have a \"query\" key"); - RETURN_THROWS(); + goto clean_links; } if (Z_TYPE_P(tmp) != IS_STRING) { /* if mode == 0 then $xpath arg is 3, if mode == 1 then $xpath is 4 */ zend_argument_type_error(3 + mode, "\"query\" option must be a string, %s given", zend_zval_value_name(tmp)); - RETURN_THROWS(); + goto clean_links; } xquery = Z_STRVAL_P(tmp); @@ -2195,7 +2302,7 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ } xmlXPathFreeContext(ctxp); zend_throw_error(NULL, "XPath query did not return a nodeset"); - RETURN_THROWS(); + goto clean_links; } } @@ -2264,6 +2371,12 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ RETURN_LONG(bytes); } } + +clean_links: + if (modern) { + dom_unlink_ns_decls(&links); + zend_hash_destroy(&links); + } } /* }}} */ diff --git a/ext/dom/tests/canonicalization.phpt b/ext/dom/tests/canonicalization.phpt index 4183b7cd41edc..fcd9b207bc24b 100644 --- a/ext/dom/tests/canonicalization.phpt +++ b/ext/dom/tests/canonicalization.phpt @@ -21,32 +21,46 @@ $dom = new DOMDocument(); $dom->loadXML($xml); $doc = $dom->documentElement->firstChild; +$newDom = Dom\XMLDocument::createFromString($xml); +$newDoc = $newDom->documentElement->firstChild; +$counter = 0; + +function check($doc, $newDoc, ...$args) { + global $counter; + $counter++; + echo $doc->C14N(...$args)."\n\n"; + if ($doc->C14N(...$args) !== $newDoc->C14N(...$args)) { + var_dump($doc->C14N(...$args), $newDoc->C14N(...$args)); + throw new Error("mismatch: $counter"); + } +} + /* inclusive/without comments first child element of doc element is context. */ -echo $doc->C14N()."\n\n"; +check($doc, $newDoc); /* exclusive/without comments first child element of doc element is context. */ -echo $doc->c14N(TRUE)."\n\n"; +check($doc, $newDoc, TRUE); /* inclusive/with comments first child element of doc element is context. */ -echo $doc->C14N(FALSE, TRUE)."\n\n"; +check($doc, $newDoc, FALSE, TRUE); /* exclusive/with comments first child element of doc element is context. */ -echo $doc->C14N(TRUE, TRUE)."\n\n"; +check($doc, $newDoc, TRUE, TRUE); /* exclusive/without comments using xpath query. */ -echo $doc->c14N(TRUE, FALSE, array('query'=>'(//. | //@* | //namespace::*)'))."\n\n"; +check($doc, $newDoc, TRUE, FALSE, array('query'=>'(//. | //@* | //namespace::*)'))."\n\n"; /* exclusive/without comments first child element of doc element is context. using xpath query with registered namespace. test namespace prefix is also included. */ -echo $doc->c14N(TRUE, FALSE, +check($doc, $newDoc, TRUE, FALSE, array('query'=>'(//a:contain | //a:bar | .//namespace::*)', 'namespaces'=>array('a'=>'http://www.example.com/ns/foo')), - array('test'))."\n\n"; + array('test')); /* exclusive/without comments first child element of doc element is context. test namespace prefix is also included */ -echo $doc->C14N(TRUE, FALSE, NULL, array('test')); +check($doc, $newDoc, TRUE, FALSE, NULL, array('test')); ?> --EXPECT-- diff --git a/ext/dom/tests/modern/xml/canonicalize_unattached.phpt b/ext/dom/tests/modern/xml/canonicalize_unattached.phpt new file mode 100644 index 0000000000000..cec5f1085757b --- /dev/null +++ b/ext/dom/tests/modern/xml/canonicalize_unattached.phpt @@ -0,0 +1,20 @@ +--TEST-- +Canonicalize unattached node should fail +--EXTENSIONS-- +dom +--FILE-- +'); +$child = $d->documentElement->firstChild; +$child->remove(); + +try { + $child->C14N(); +} catch (Dom\DOMException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Canonicalization can only happen on nodes attached to a document. diff --git a/ext/dom/tests/modern/xml/gh20444.phpt b/ext/dom/tests/modern/xml/gh20444.phpt new file mode 100644 index 0000000000000..b3a77e3f13edb --- /dev/null +++ b/ext/dom/tests/modern/xml/gh20444.phpt @@ -0,0 +1,43 @@ +--TEST-- +GH-20444 (Dom\XMLDocument::C14N() seems broken compared to DOMDocument::C14N()) +--EXTENSIONS-- +dom +--FILE-- + + + + abc + +EOF; + +$d = \Dom\XMLDocument::createFromString($xml); +var_dump($d->C14N(true)); + +$xml = << + + + + 123 + + +EOF; + +$d = \Dom\XMLDocument::createFromString($xml); +var_dump($d->C14N()); + +?> +--EXPECT-- +string(128) " + + abc +" +string(134) " + + + 123 + +" From 99ed66b49fa59fcb5a7a4f2b460d4155027d44d0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 25 Nov 2025 22:58:09 +0100 Subject: [PATCH 225/252] Fix GH-20582: Heap Buffer Overflow in iptcembed If you can extend the file between the file size gathering (resulting in a buffer allocation), and reading / writing to the file you can trigger a TOC-TOU where you write out of bounds. To solve this, add extra bound checks and make sure that write actions always fail when going out of bounds. The easiest way to trigger this is via a pipe, which is used in the test, but it should be possible with a regular file and a quick race condition as well. Closes GH-20591. --- NEWS | 1 + ext/standard/iptc.c | 80 +++++++++++++++++---------- ext/standard/tests/image/gh20582.phpt | 52 +++++++++++++++++ 3 files changed, 105 insertions(+), 28 deletions(-) create mode 100644 ext/standard/tests/image/gh20582.phpt diff --git a/NEWS b/NEWS index a2690fa84d3ef..77119e29fcfc4 100644 --- a/NEWS +++ b/NEWS @@ -52,6 +52,7 @@ PHP NEWS - Standard: . Fix error check for proc_open() command. (ndossche) + . Fixed bug GH-20582 (Heap Buffer Overflow in iptcembed). (ndossche) 18 Dec 2025, PHP 8.3.29 diff --git a/ext/standard/iptc.c b/ext/standard/iptc.c index 44dd33bab10ac..0f46ecd19bc7c 100644 --- a/ext/standard/iptc.c +++ b/ext/standard/iptc.c @@ -73,19 +73,24 @@ #define M_APP15 0xef /* {{{ php_iptc_put1 */ -static int php_iptc_put1(FILE *fp, int spool, unsigned char c, unsigned char **spoolbuf) +static int php_iptc_put1(FILE *fp, int spool, unsigned char c, unsigned char **spoolbuf, const unsigned char *spoolbuf_end) { if (spool > 0) PUTC(c); - if (spoolbuf) *(*spoolbuf)++ = c; + if (spoolbuf) { + if (UNEXPECTED(*spoolbuf >= spoolbuf_end)) { + return EOF; + } + *(*spoolbuf)++ = c; + } return c; } /* }}} */ /* {{{ php_iptc_get1 */ -static int php_iptc_get1(FILE *fp, int spool, unsigned char **spoolbuf) +static int php_iptc_get1(FILE *fp, int spool, unsigned char **spoolbuf, const unsigned char *spoolbuf_end) { int c; char cc; @@ -99,66 +104,71 @@ static int php_iptc_get1(FILE *fp, int spool, unsigned char **spoolbuf) PUTC(cc); } - if (spoolbuf) *(*spoolbuf)++ = c; + if (spoolbuf) { + if (UNEXPECTED(*spoolbuf >= spoolbuf_end)) { + return EOF; + } + *(*spoolbuf)++ = c; + } return c; } /* }}} */ /* {{{ php_iptc_read_remaining */ -static int php_iptc_read_remaining(FILE *fp, int spool, unsigned char **spoolbuf) +static int php_iptc_read_remaining(FILE *fp, int spool, unsigned char **spoolbuf, const unsigned char *spoolbuf_end) { - while (php_iptc_get1(fp, spool, spoolbuf) != EOF) continue; + while (php_iptc_get1(fp, spool, spoolbuf, spoolbuf_end) != EOF) continue; return M_EOI; } /* }}} */ /* {{{ php_iptc_skip_variable */ -static int php_iptc_skip_variable(FILE *fp, int spool, unsigned char **spoolbuf) +static int php_iptc_skip_variable(FILE *fp, int spool, unsigned char **spoolbuf, const unsigned char *spoolbuf_end) { unsigned int length; int c1, c2; - if ((c1 = php_iptc_get1(fp, spool, spoolbuf)) == EOF) return M_EOI; + if ((c1 = php_iptc_get1(fp, spool, spoolbuf, spoolbuf_end)) == EOF) return M_EOI; - if ((c2 = php_iptc_get1(fp, spool, spoolbuf)) == EOF) return M_EOI; + if ((c2 = php_iptc_get1(fp, spool, spoolbuf, spoolbuf_end)) == EOF) return M_EOI; length = (((unsigned char) c1) << 8) + ((unsigned char) c2); length -= 2; while (length--) - if (php_iptc_get1(fp, spool, spoolbuf) == EOF) return M_EOI; + if (php_iptc_get1(fp, spool, spoolbuf, spoolbuf_end) == EOF) return M_EOI; return 0; } /* }}} */ /* {{{ php_iptc_next_marker */ -static int php_iptc_next_marker(FILE *fp, int spool, unsigned char **spoolbuf) +static int php_iptc_next_marker(FILE *fp, int spool, unsigned char **spoolbuf, const unsigned char *spoolbuf_end) { int c; /* skip unimportant stuff */ - c = php_iptc_get1(fp, spool, spoolbuf); + c = php_iptc_get1(fp, spool, spoolbuf, spoolbuf_end); if (c == EOF) return M_EOI; while (c != 0xff) { - if ((c = php_iptc_get1(fp, spool, spoolbuf)) == EOF) + if ((c = php_iptc_get1(fp, spool, spoolbuf, spoolbuf_end)) == EOF) return M_EOI; /* we hit EOF */ } /* get marker byte, swallowing possible padding */ do { - c = php_iptc_get1(fp, 0, 0); + c = php_iptc_get1(fp, 0, 0, NULL); if (c == EOF) return M_EOI; /* we hit EOF */ else if (c == 0xff) - php_iptc_put1(fp, spool, (unsigned char)c, spoolbuf); + php_iptc_put1(fp, spool, (unsigned char)c, spoolbuf, spoolbuf_end); } while (c == 0xff); return (unsigned int) c; @@ -178,6 +188,7 @@ PHP_FUNCTION(iptcembed) size_t inx; zend_string *spoolbuf = NULL; unsigned char *poi = NULL; + unsigned char *spoolbuf_end = NULL; zend_stat_t sb = {0}; bool written = 0; @@ -210,10 +221,11 @@ PHP_FUNCTION(iptcembed) spoolbuf = zend_string_safe_alloc(1, iptcdata_len + sizeof(psheader) + 1024 + 1, sb.st_size, 0); poi = (unsigned char*)ZSTR_VAL(spoolbuf); + spoolbuf_end = poi + ZSTR_LEN(spoolbuf); memset(poi, 0, iptcdata_len + sizeof(psheader) + sb.st_size + 1024 + 1); } - if (php_iptc_get1(fp, spool, poi?&poi:0) != 0xFF) { + if (php_iptc_get1(fp, spool, poi?&poi:0, spoolbuf_end) != 0xFF) { fclose(fp); if (spoolbuf) { zend_string_efree(spoolbuf); @@ -221,7 +233,8 @@ PHP_FUNCTION(iptcembed) RETURN_FALSE; } - if (php_iptc_get1(fp, spool, poi?&poi:0) != 0xD8) { + if (php_iptc_get1(fp, spool, poi?&poi:0, spoolbuf_end) != 0xD8) { +err: fclose(fp); if (spoolbuf) { zend_string_efree(spoolbuf); @@ -230,20 +243,22 @@ PHP_FUNCTION(iptcembed) } while (!done) { - marker = php_iptc_next_marker(fp, spool, poi?&poi:0); + marker = php_iptc_next_marker(fp, spool, poi?&poi:0, spoolbuf_end); if (marker == M_EOI) { /* EOF */ break; } else if (marker != M_APP13) { - php_iptc_put1(fp, spool, (unsigned char)marker, poi?&poi:0); + if (php_iptc_put1(fp, spool, (unsigned char)marker, poi?&poi:0, spoolbuf_end) < 0) { + goto err; + } } switch (marker) { case M_APP13: /* we are going to write a new APP13 marker, so don't output the old one */ - php_iptc_skip_variable(fp, 0, 0); + php_iptc_skip_variable(fp, 0, 0, spoolbuf_end); fgetc(fp); /* skip already copied 0xFF byte */ - php_iptc_read_remaining(fp, spool, poi?&poi:0); + php_iptc_read_remaining(fp, spool, poi?&poi:0, spoolbuf_end); done = 1; break; @@ -256,7 +271,7 @@ PHP_FUNCTION(iptcembed) } written = 1; - php_iptc_skip_variable(fp, spool, poi?&poi:0); + php_iptc_skip_variable(fp, spool, poi?&poi:0, spoolbuf_end); if (iptcdata_len & 1) { iptcdata_len++; /* make the length even */ @@ -266,25 +281,33 @@ PHP_FUNCTION(iptcembed) psheader[ 3 ] = (iptcdata_len+28)&0xff; for (inx = 0; inx < 28; inx++) { - php_iptc_put1(fp, spool, psheader[inx], poi?&poi:0); + if (php_iptc_put1(fp, spool, psheader[inx], poi?&poi:0, spoolbuf_end) < 0) { + goto err; + } } - php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len>>8), poi?&poi:0); - php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len&0xff), poi?&poi:0); + if (php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len>>8), poi?&poi:0, spoolbuf_end) < 0) { + goto err; + } + if (php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len&0xff), poi?&poi:0, spoolbuf_end) < 0) { + goto err; + } for (inx = 0; inx < iptcdata_len; inx++) { - php_iptc_put1(fp, spool, iptcdata[inx], poi?&poi:0); + if (php_iptc_put1(fp, spool, iptcdata[inx], poi?&poi:0, spoolbuf_end) < 0) { + goto err; + } } break; case M_SOS: /* we hit data, no more marker-inserting can be done! */ - php_iptc_read_remaining(fp, spool, poi?&poi:0); + php_iptc_read_remaining(fp, spool, poi?&poi:0, spoolbuf_end); done = 1; break; default: - php_iptc_skip_variable(fp, spool, poi?&poi:0); + php_iptc_skip_variable(fp, spool, poi?&poi:0, spoolbuf_end); break; } } @@ -292,6 +315,7 @@ PHP_FUNCTION(iptcembed) fclose(fp); if (spool < 2) { + *poi = '\0'; spoolbuf = zend_string_truncate(spoolbuf, poi - (unsigned char*)ZSTR_VAL(spoolbuf), 0); RETURN_NEW_STR(spoolbuf); } else { diff --git a/ext/standard/tests/image/gh20582.phpt b/ext/standard/tests/image/gh20582.phpt new file mode 100644 index 0000000000000..63561534b2fd0 --- /dev/null +++ b/ext/standard/tests/image/gh20582.phpt @@ -0,0 +1,52 @@ +--TEST-- +GH-20582 (Heap Buffer Overflow in iptcembed) +--CREDITS-- +Nikita Sveshnikov (Positive Technologies) +ndossche +--SKIPIF-- + +--FILE-- + STDIN, + 1 => STDOUT, + 2 => STDOUT, +); +$pipes = []; +$proc = proc_open([PHP_BINARY, '-n', '-r', "var_dump(iptcembed('A', '$pipe'));"], $descriptorspec, $pipes); + +// Blocks until a reader opens it +$fp = fopen($pipe, 'wb') or die("Failed to open FIFO"); + +// Write header +$data = "\xFF\xD8"; // SOI marker +$data .= "\xFF\xE0\x00\x10"; // APP0 marker (JFIF) +$data .= "JFIF" . str_repeat("\x00", 9); +$data .= "\xFF\xDA\x00\x08"; // SOS marker +$data .= str_repeat("\x00", 6); +fwrite($fp, $data); + +// Write garbage +fwrite($fp, str_repeat("A", 5120)); + +fclose($fp); + +?> +--CLEAN-- + +--EXPECTF-- +string(1055) "����%0JFIF%0%0%0%0%0%0%0%0%0���%0Photoshop 3.0%08BIM%0%0%0%0%0A%0�%0%0%0%0%0%0%0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" From 7e8c636b3084c6eb545bb8abfbefce5940d90178 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 18:14:31 +0100 Subject: [PATCH 226/252] win32/sendmail.c: mark error messages array as const --- win32/sendmail.c | 4 ++-- win32/sendmail.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/win32/sendmail.c b/win32/sendmail.c index 80dff3f390772..fbb2ae0298971 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -63,7 +63,7 @@ char seps[] = " ,\t\n"; char *php_mailer = "PHP 7 WIN32"; /* Error messages */ -static char *ErrorMessages[] = +static const char *ErrorMessages[] = { "Success", /* 0 */ "Bad arguments from form", /* 1 */ @@ -328,7 +328,7 @@ PHPAPI void TSMClose(void) // Author/Date: jcar 20/9/96 // History: //********************************************************************* -PHPAPI char *GetSMErrorText(int index) +PHPAPI const char *GetSMErrorText(int index) { if (MIN_ERROR_INDEX <= index && index < MAX_ERROR_INDEX) { return (ErrorMessages[index]); diff --git a/win32/sendmail.h b/win32/sendmail.h index e6096f789eb78..6cfdc5706f1e6 100644 --- a/win32/sendmail.h +++ b/win32/sendmail.h @@ -36,5 +36,5 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message, const char *headers, const char *Subject, const char *mailTo, const char *data, char *mailCc, char *mailBcc, char *mailRPath); PHPAPI void TSMClose(void); -PHPAPI char *GetSMErrorText(int index); +PHPAPI const char *GetSMErrorText(int index); #endif /* sendmail_h */ From 4dad723c413f6ec4e156c51a707285066f71bb5d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 18:23:52 +0100 Subject: [PATCH 227/252] win32/sendmail.c/Post(): refactor function Change return type to bool as it only ever returns two values --- win32/sendmail.c | 64 ++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/win32/sendmail.c b/win32/sendmail.c index fbb2ae0298971..719175bf74b09 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -115,7 +115,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * const char *headers, char *headers_lc, char **error_message); static int MailConnect(); static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char *xheaders); -static int Post(LPCSTR msg); +static bool Post(LPCSTR msg); static int Ack(char **server_response); static unsigned long GetAddr(LPSTR szHost); static int FormatEmailAddress(char* Buf, char* EmailAddress, char* FormatString); @@ -421,14 +421,14 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * /* in the beginning of the dialog */ /* attempt reconnect if the first Post fail */ - if ((res = Post(PW32G(mail_buffer))) != SUCCESS) { + if (!Post(PW32G(mail_buffer))) { int err = MailConnect(); if (0 != err) { return (FAILED_TO_SEND); } - if ((res = Post(PW32G(mail_buffer))) != SUCCESS) { - return (res); + if (!Post(PW32G(mail_buffer))) { + return (FAILED_TO_SEND); } } if ((res = Ack(&server_response)) != SUCCESS) { @@ -438,8 +438,8 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * SMTP_SKIP_SPACE(RPath); FormatEmailAddress(PW32G(mail_buffer), RPath, "MAIL FROM:<%s>\r\n"); - if ((res = Post(PW32G(mail_buffer))) != SUCCESS) { - return (res); + if (!Post(PW32G(mail_buffer))) { + return (FAILED_TO_SEND); } if ((res = Ack(&server_response)) != SUCCESS) { SMTP_ERROR_RESPONSE(server_response); @@ -453,9 +453,9 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * { SMTP_SKIP_SPACE(token); FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n"); - if ((res = Post(PW32G(mail_buffer))) != SUCCESS) { + if (!Post(PW32G(mail_buffer))) { efree(tempMailTo); - return (res); + return (FAILED_TO_SEND); } if ((res = Ack(&server_response)) != SUCCESS) { SMTP_ERROR_RESPONSE(server_response); @@ -474,9 +474,9 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * { SMTP_SKIP_SPACE(token); FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n"); - if ((res = Post(PW32G(mail_buffer))) != SUCCESS) { + if (!Post(PW32G(mail_buffer))) { efree(tempMailTo); - return (res); + return (FAILED_TO_SEND); } if ((res = Ack(&server_response)) != SUCCESS) { SMTP_ERROR_RESPONSE(server_response); @@ -514,9 +514,9 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * { SMTP_SKIP_SPACE(token); FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n"); - if ((res = Post(PW32G(mail_buffer))) != SUCCESS) { + if (!Post(PW32G(mail_buffer))) { efree(tempMailTo); - return (res); + return (FAILED_TO_SEND); } if ((res = Ack(&server_response)) != SUCCESS) { SMTP_ERROR_RESPONSE(server_response); @@ -539,9 +539,9 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * { SMTP_SKIP_SPACE(token); FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n"); - if ((res = Post(PW32G(mail_buffer))) != SUCCESS) { + if (!Post(PW32G(mail_buffer))) { efree(tempMailTo); - return (res); + return (FAILED_TO_SEND); } if ((res = Ack(&server_response)) != SUCCESS) { SMTP_ERROR_RESPONSE(server_response); @@ -587,9 +587,9 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * { SMTP_SKIP_SPACE(token); FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n"); - if ((res = Post(PW32G(mail_buffer))) != SUCCESS) { + if (!Post(PW32G(mail_buffer))) { efree(tempMailTo); - return (res); + return (FAILED_TO_SEND); } if ((res = Ack(&server_response)) != SUCCESS) { SMTP_ERROR_RESPONSE(server_response); @@ -625,11 +625,11 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * stripped_header = estrndup(headers, strlen(headers)); } - if ((res = Post("DATA\r\n")) != SUCCESS) { + if (!Post("DATA\r\n")) { if (stripped_header) { efree(stripped_header); } - return (res); + return (FAILED_TO_SEND); } if ((res = Ack(&server_response)) != SUCCESS) { SMTP_ERROR_RESPONSE(server_response); @@ -670,24 +670,24 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * e2 = p + 1024; c = *e2; *e2 = '\0'; - if ((res = Post(p)) != SUCCESS) { + if (!Post(p)) { zend_string_free(data_cln); - return(res); + return(FAILED_TO_SEND); } *e2 = c; p = e2; } - if ((res = Post(p)) != SUCCESS) { + if (!Post(p)) { zend_string_free(data_cln); - return(res); + return(FAILED_TO_SEND); } } zend_string_free(data_cln); /*send termination dot */ - if ((res = Post("\r\n.\r\n")) != SUCCESS) - return (res); + if (!Post("\r\n.\r\n")) + return (FAILED_TO_SEND); if ((res = Ack(&server_response)) != SUCCESS) { SMTP_ERROR_RESPONSE(server_response); return (res); @@ -771,14 +771,14 @@ static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char if (headers_lc) { efree(headers_lc); } - if ((res = Post(header_buffer)) != SUCCESS) { + if (!Post(header_buffer)) { efree(header_buffer); - return (res); + return (FAILED_TO_SEND); } efree(header_buffer); - if ((res = Post("\r\n")) != SUCCESS) { - return (res); + if (!Post("\r\n")) { + return (FAILED_TO_SEND); } return (SUCCESS); @@ -896,7 +896,7 @@ return 0; // Author/Date: jcar 20/9/96 // History: //********************************************************************* -static int Post(LPCSTR msg) +static bool Post(LPCSTR msg) { int len = (int)strlen(msg); int slen; @@ -905,16 +905,16 @@ static int Post(LPCSTR msg) #if SENDMAIL_DEBUG if (msg) printf("POST: '%s'\n", msg); - return (SUCCESS); + return true; #endif while (len > 0) { if ((slen = send(PW32G(mail_socket), msg + index, len, 0)) < 1) - return (FAILED_TO_SEND); + return false; len -= slen; index += slen; } - return (SUCCESS); + return true; } From 6f28370d2fdf9f4e20e22a2efc61059b2e66d1db Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 18:35:15 +0100 Subject: [PATCH 228/252] win32/sendmail.c: remove mailRPath parameter that is always NULL --- ext/standard/mail.c | 2 +- win32/sendmail.c | 6 ++---- win32/sendmail.h | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ext/standard/mail.c b/ext/standard/mail.c index b631d12e4c7e2..e1d514f9e0b40 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -543,7 +543,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c char *tsm_errmsg = NULL; /* handle old style win smtp sending */ - if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, hdr, subject, to, message, NULL, NULL, NULL) == FAILURE) { + if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, hdr, subject, to, message, NULL, NULL) == FAILURE) { if (tsm_errmsg) { php_error_docref(NULL, E_WARNING, "%s", tsm_errmsg); efree(tsm_errmsg); diff --git a/win32/sendmail.c b/win32/sendmail.c index 719175bf74b09..f336cabae774b 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -186,7 +186,7 @@ static zend_string *php_win32_mail_trim_header(const char *header) //********************************************************************* PHPAPI int TSendMail(const char *host, int *error, char **error_message, const char *headers, const char *Subject, const char *mailTo, const char *data, - char *mailCc, char *mailBcc, char *mailRPath) + char *mailCc, char *mailBcc) { int ret; char *RPath = NULL; @@ -216,9 +216,7 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message, } /* Fall back to sendmail_from php.ini setting */ - if (mailRPath && *mailRPath) { - RPath = estrdup(mailRPath); - } else if (INI_STR("sendmail_from")) { + if (INI_STR("sendmail_from")) { RPath = estrdup(INI_STR("sendmail_from")); } else if (headers_lc) { int found = 0; diff --git a/win32/sendmail.h b/win32/sendmail.h index 6cfdc5706f1e6..cf38d1dc281a2 100644 --- a/win32/sendmail.h +++ b/win32/sendmail.h @@ -34,7 +34,7 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message, const char *headers, const char *Subject, const char *mailTo, const char *data, - char *mailCc, char *mailBcc, char *mailRPath); + char *mailCc, char *mailBcc); PHPAPI void TSMClose(void); PHPAPI const char *GetSMErrorText(int index); #endif /* sendmail_h */ From 0e19bc1bfd17c67a2e1fdd1a27cd5318740dfeac Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 18:36:55 +0100 Subject: [PATCH 229/252] win32/sendmail.c: remove mailBbc parameter that is always NULL --- ext/standard/mail.c | 2 +- win32/sendmail.c | 31 +++++-------------------------- win32/sendmail.h | 2 +- 3 files changed, 7 insertions(+), 28 deletions(-) diff --git a/ext/standard/mail.c b/ext/standard/mail.c index e1d514f9e0b40..42dc40b90d828 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -543,7 +543,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c char *tsm_errmsg = NULL; /* handle old style win smtp sending */ - if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, hdr, subject, to, message, NULL, NULL) == FAILURE) { + if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, hdr, subject, to, message, NULL) == FAILURE) { if (tsm_errmsg) { php_error_docref(NULL, E_WARNING, "%s", tsm_errmsg); efree(tsm_errmsg); diff --git a/win32/sendmail.c b/win32/sendmail.c index f336cabae774b..6d6866e55223a 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -111,7 +111,7 @@ static const char *ErrorMessages[] = #define PHP_WIN32_MAIL_DOT_PATTERN "\n." #define PHP_WIN32_MAIL_DOT_REPLACE "\n.." -static int SendText(char *RPath, const char *Subject, const char *mailTo, char *mailCc, char *mailBcc, const char *data, +static int SendText(char *RPath, const char *Subject, const char *mailTo, char *mailCc, const char *data, const char *headers, char *headers_lc, char **error_message); static int MailConnect(); static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char *xheaders); @@ -186,7 +186,7 @@ static zend_string *php_win32_mail_trim_header(const char *header) //********************************************************************* PHPAPI int TSendMail(const char *host, int *error, char **error_message, const char *headers, const char *Subject, const char *mailTo, const char *data, - char *mailCc, char *mailBcc) + char *mailCc) { int ret; char *RPath = NULL; @@ -279,7 +279,7 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message, PW32G(mail_host), !INI_INT("smtp_port") ? 25 : INI_INT("smtp_port")); return FAILURE; } else { - ret = SendText(RPath, Subject, mailTo, mailCc, mailBcc, data, headers ? ZSTR_VAL(headers_trim) : NULL, headers ? ZSTR_VAL(headers_lc) : NULL, error_message); + ret = SendText(RPath, Subject, mailTo, mailCc, data, headers ? ZSTR_VAL(headers_trim) : NULL, headers ? ZSTR_VAL(headers_lc) : NULL, error_message); TSMClose(); if (RPath) { efree(RPath); @@ -387,7 +387,7 @@ static char *find_address(char *list, char **state) // Author/Date: jcar 20/9/96 // History: //********************************************************************* -static int SendText(char *RPath, const char *Subject, const char *mailTo, char *mailCc, char *mailBcc, const char *data, +static int SendText(char *RPath, const char *Subject, const char *mailTo, char *mailCc, const char *data, const char *headers, char *headers_lc, char **error_message) { int res; @@ -529,28 +529,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * /* Send mail to all Bcc rcpt's This is basically a rip of the Cc code above. Just don't forget to remove the Bcc: from the header afterwards. */ - if (mailBcc && *mailBcc) { - tempMailTo = estrdup(mailBcc); - /* Send mail to all rcpt's */ - token = find_address(tempMailTo, &token_state); - while (token != NULL) - { - SMTP_SKIP_SPACE(token); - FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n"); - if (!Post(PW32G(mail_buffer))) { - efree(tempMailTo); - return (FAILED_TO_SEND); - } - if ((res = Ack(&server_response)) != SUCCESS) { - SMTP_ERROR_RESPONSE(server_response); - efree(tempMailTo); - return (res); - } - token = find_address(NULL, &token_state); - } - efree(tempMailTo); - } - else if (headers) { + if (headers) { if ((pos1 = strstr(headers_lc, "bcc:")) && (pos1 == headers_lc || *(pos1-1) == '\n')) { /* Real offset is memaddress from the original headers + difference of * string found in the lowercase headers + 4 characters to jump over diff --git a/win32/sendmail.h b/win32/sendmail.h index cf38d1dc281a2..7eeb3d1825e32 100644 --- a/win32/sendmail.h +++ b/win32/sendmail.h @@ -34,7 +34,7 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message, const char *headers, const char *Subject, const char *mailTo, const char *data, - char *mailCc, char *mailBcc); + char *mailCc); PHPAPI void TSMClose(void); PHPAPI const char *GetSMErrorText(int index); #endif /* sendmail_h */ From 4431aa94b7fa050dca724467514306f7257ae53c Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 18:39:11 +0100 Subject: [PATCH 230/252] win32/sendmail.c: remove mailCc parameter that is always NULL --- ext/standard/mail.c | 2 +- win32/sendmail.c | 32 +++++--------------------------- win32/sendmail.h | 3 +-- 3 files changed, 7 insertions(+), 30 deletions(-) diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 42dc40b90d828..3ffb7d05bb4b0 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -543,7 +543,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c char *tsm_errmsg = NULL; /* handle old style win smtp sending */ - if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, hdr, subject, to, message, NULL) == FAILURE) { + if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, hdr, subject, to, message) == FAILURE) { if (tsm_errmsg) { php_error_docref(NULL, E_WARNING, "%s", tsm_errmsg); efree(tsm_errmsg); diff --git a/win32/sendmail.c b/win32/sendmail.c index 6d6866e55223a..fef37e1b897b2 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -111,7 +111,7 @@ static const char *ErrorMessages[] = #define PHP_WIN32_MAIL_DOT_PATTERN "\n." #define PHP_WIN32_MAIL_DOT_REPLACE "\n.." -static int SendText(char *RPath, const char *Subject, const char *mailTo, char *mailCc, const char *data, +static int SendText(char *RPath, const char *Subject, const char *mailTo, const char *data, const char *headers, char *headers_lc, char **error_message); static int MailConnect(); static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char *xheaders); @@ -185,8 +185,7 @@ static zend_string *php_win32_mail_trim_header(const char *header) // See SendText() for additional args! //********************************************************************* PHPAPI int TSendMail(const char *host, int *error, char **error_message, - const char *headers, const char *Subject, const char *mailTo, const char *data, - char *mailCc) + const char *headers, const char *Subject, const char *mailTo, const char *data) { int ret; char *RPath = NULL; @@ -279,7 +278,7 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message, PW32G(mail_host), !INI_INT("smtp_port") ? 25 : INI_INT("smtp_port")); return FAILURE; } else { - ret = SendText(RPath, Subject, mailTo, mailCc, data, headers ? ZSTR_VAL(headers_trim) : NULL, headers ? ZSTR_VAL(headers_lc) : NULL, error_message); + ret = SendText(RPath, Subject, mailTo, data, headers ? ZSTR_VAL(headers_trim) : NULL, headers ? ZSTR_VAL(headers_lc) : NULL, error_message); TSMClose(); if (RPath) { efree(RPath); @@ -387,7 +386,7 @@ static char *find_address(char *list, char **state) // Author/Date: jcar 20/9/96 // History: //********************************************************************* -static int SendText(char *RPath, const char *Subject, const char *mailTo, char *mailCc, const char *data, +static int SendText(char *RPath, const char *Subject, const char *mailTo, const char *data, const char *headers, char *headers_lc, char **error_message) { int res; @@ -464,29 +463,8 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, char * } efree(tempMailTo); - if (mailCc && *mailCc) { - tempMailTo = estrdup(mailCc); - /* Send mail to all rcpt's */ - token = find_address(tempMailTo, &token_state); - while (token != NULL) - { - SMTP_SKIP_SPACE(token); - FormatEmailAddress(PW32G(mail_buffer), token, "RCPT TO:<%s>\r\n"); - if (!Post(PW32G(mail_buffer))) { - efree(tempMailTo); - return (FAILED_TO_SEND); - } - if ((res = Ack(&server_response)) != SUCCESS) { - SMTP_ERROR_RESPONSE(server_response); - efree(tempMailTo); - return (res); - } - token = find_address(NULL, &token_state); - } - efree(tempMailTo); - } /* Send mail to all Cc rcpt's */ - else if (headers && (pos1 = strstr(headers_lc, "cc:")) && ((pos1 == headers_lc) || (*(pos1-1) == '\n'))) { + if (headers && (pos1 = strstr(headers_lc, "cc:")) && ((pos1 == headers_lc) || (*(pos1-1) == '\n'))) { /* Real offset is memaddress from the original headers + difference of * string found in the lowercase headers + 3 characters to jump over * the cc: */ diff --git a/win32/sendmail.h b/win32/sendmail.h index 7eeb3d1825e32..9999f9a6dfa58 100644 --- a/win32/sendmail.h +++ b/win32/sendmail.h @@ -33,8 +33,7 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message, - const char *headers, const char *Subject, const char *mailTo, const char *data, - char *mailCc); + const char *headers, const char *Subject, const char *mailTo, const char *data); PHPAPI void TSMClose(void); PHPAPI const char *GetSMErrorText(int index); #endif /* sendmail_h */ From 84e63bfd9b62a812b26479fa824046e3afb225e4 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 18:43:59 +0100 Subject: [PATCH 231/252] win32/sendmail.c/php_win32_mail_trim_header(): use ZSTR_INIT_LITERAL() --- win32/sendmail.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/win32/sendmail.c b/win32/sendmail.c index fef37e1b897b2..14ad6d6a6c422 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -140,8 +140,8 @@ static zend_string *php_win32_mail_trim_header(const char *header) return NULL; } - replace = zend_string_init(PHP_WIN32_MAIL_UNIFY_REPLACE, strlen(PHP_WIN32_MAIL_UNIFY_REPLACE), 0); - regex = zend_string_init(PHP_WIN32_MAIL_UNIFY_PATTERN, sizeof(PHP_WIN32_MAIL_UNIFY_PATTERN)-1, 0); + replace = ZSTR_INIT_LITERAL(PHP_WIN32_MAIL_UNIFY_REPLACE, false); + regex = ZSTR_INIT_LITERAL(PHP_WIN32_MAIL_UNIFY_PATTERN, false); result = php_pcre_replace(regex, NULL, header, strlen(header), @@ -149,24 +149,24 @@ static zend_string *php_win32_mail_trim_header(const char *header) -1, NULL); - zend_string_release_ex(replace, 0); - zend_string_release_ex(regex, 0); + zend_string_release_ex(replace, false); + zend_string_release_ex(regex, false); if (NULL == result) { return NULL; } - replace = zend_string_init(PHP_WIN32_MAIL_RMVDBL_PATTERN, strlen(PHP_WIN32_MAIL_RMVDBL_PATTERN), 0); - regex = zend_string_init(PHP_WIN32_MAIL_RMVDBL_PATTERN, sizeof(PHP_WIN32_MAIL_RMVDBL_PATTERN)-1, 0); + replace = ZSTR_INIT_LITERAL(PHP_WIN32_MAIL_RMVDBL_PATTERN, false); + regex = ZSTR_INIT_LITERAL(PHP_WIN32_MAIL_RMVDBL_PATTERN, false); result2 = php_pcre_replace(regex, result, ZSTR_VAL(result), ZSTR_LEN(result), replace, -1, NULL); - zend_string_release_ex(replace, 0); - zend_string_release_ex(regex, 0); - zend_string_release_ex(result, 0); + zend_string_release_ex(replace, false); + zend_string_release_ex(regex, false); + zend_string_release_ex(result, false); return result2; } From f1a8944fcde384ce0b5842b6869535306f889ea5 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 18:50:21 +0100 Subject: [PATCH 232/252] win32/sendmail.c/SendText(): use zend_string* for headers{_lc} parameters This prevents some strlen() reconputations --- win32/sendmail.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/win32/sendmail.c b/win32/sendmail.c index 14ad6d6a6c422..3a32f3e4f0df8 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -112,7 +112,7 @@ static const char *ErrorMessages[] = #define PHP_WIN32_MAIL_DOT_REPLACE "\n.." static int SendText(char *RPath, const char *Subject, const char *mailTo, const char *data, - const char *headers, char *headers_lc, char **error_message); + const zend_string *headers, zend_string *headers_lc, char **error_message); static int MailConnect(); static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char *xheaders); static bool Post(LPCSTR msg); @@ -278,7 +278,7 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message, PW32G(mail_host), !INI_INT("smtp_port") ? 25 : INI_INT("smtp_port")); return FAILURE; } else { - ret = SendText(RPath, Subject, mailTo, data, headers ? ZSTR_VAL(headers_trim) : NULL, headers ? ZSTR_VAL(headers_lc) : NULL, error_message); + ret = SendText(RPath, Subject, mailTo, data, headers_trim, headers_lc, error_message); TSMClose(); if (RPath) { efree(RPath); @@ -387,7 +387,7 @@ static char *find_address(char *list, char **state) // History: //********************************************************************* static int SendText(char *RPath, const char *Subject, const char *mailTo, const char *data, - const char *headers, char *headers_lc, char **error_message) + const zend_string *headers, zend_string *headers_lc, char **error_message) { int res; char *p; @@ -464,11 +464,11 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, const efree(tempMailTo); /* Send mail to all Cc rcpt's */ - if (headers && (pos1 = strstr(headers_lc, "cc:")) && ((pos1 == headers_lc) || (*(pos1-1) == '\n'))) { + if (headers && (pos1 = strstr(ZSTR_VAL(headers_lc), "cc:")) && ((pos1 == ZSTR_VAL(headers_lc)) || (*(pos1-1) == '\n'))) { /* Real offset is memaddress from the original headers + difference of * string found in the lowercase headers + 3 characters to jump over * the cc: */ - pos1 = headers + (pos1 - headers_lc) + 3; + pos1 = ZSTR_VAL(headers) + (pos1 - ZSTR_VAL(headers_lc)) + 3; if (NULL == (pos2 = strstr(pos1, "\r\n"))) { tempMailTo = estrndup(pos1, strlen(pos1)); } else { @@ -508,11 +508,11 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, const This is basically a rip of the Cc code above. Just don't forget to remove the Bcc: from the header afterwards. */ if (headers) { - if ((pos1 = strstr(headers_lc, "bcc:")) && (pos1 == headers_lc || *(pos1-1) == '\n')) { + if ((pos1 = strstr(ZSTR_VAL(headers_lc), "bcc:")) && (pos1 == ZSTR_VAL(headers_lc) || *(pos1-1) == '\n')) { /* Real offset is memaddress from the original headers + difference of * string found in the lowercase headers + 4 characters to jump over * the bcc: */ - pos1 = headers + (pos1 - headers_lc) + 4; + pos1 = ZSTR_VAL(headers) + (pos1 - ZSTR_VAL(headers_lc)) + 4; if (NULL == (pos2 = strstr(pos1, "\r\n"))) { tempMailTo = estrndup(pos1, strlen(pos1)); /* Later, when we remove the Bcc: out of the @@ -557,19 +557,19 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, const /* Now that we've identified that we've a Bcc list, remove it from the current header. */ - stripped_header = ecalloc(1, strlen(headers)); + stripped_header = ecalloc(1, ZSTR_LEN(headers)); /* headers = point to string start of header pos1 = pointer IN headers where the Bcc starts '4' = Length of the characters 'bcc:' Because we've added +4 above for parsing the Emails we've to subtract them here. */ - memcpy(stripped_header, headers, pos1 - headers - 4); + memcpy(stripped_header, ZSTR_VAL(headers), pos1 - ZSTR_VAL(headers) - 4); if (pos1 != pos2) { /* if pos1 != pos2 , pos2 points to the rest of the headers. Since pos1 != pos2 if "\r\n" was found, we know those characters are there and so we jump over them (else we would generate a new header which would look like "\r\n\r\n". */ - memcpy(stripped_header + (pos1 - headers - 4), pos2 + 2, strlen(pos2) - 2); + memcpy(stripped_header + (pos1 - ZSTR_VAL(headers) - 4), pos2 + 2, strlen(pos2) - 2); } } } @@ -577,7 +577,7 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, const /* Simplify the code that we create a copy of stripped_header no matter if we actually strip something or not. So we've a single efree() later. */ if (headers && !stripped_header) { - stripped_header = estrndup(headers, strlen(headers)); + stripped_header = estrndup(ZSTR_VAL(headers), ZSTR_LEN(headers)); } if (!Post("DATA\r\n")) { From 6bd9c555eed1a1e5963a0b5144f83ef3878f9d2d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 26 Dec 2025 00:58:51 +0100 Subject: [PATCH 233/252] win32/sendmail.c/SendText(): move string duplication code to a more logical place --- win32/sendmail.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/win32/sendmail.c b/win32/sendmail.c index 3a32f3e4f0df8..2b72ae328e0ac 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -571,15 +571,13 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, const which would look like "\r\n\r\n". */ memcpy(stripped_header + (pos1 - ZSTR_VAL(headers) - 4), pos2 + 2, strlen(pos2) - 2); } + } else { + /* Simplify the code that we create a copy of stripped_header no matter if + we actually strip something or not. So we've a single efree() later. */ + stripped_header = estrndup(ZSTR_VAL(headers), ZSTR_LEN(headers)); } } - /* Simplify the code that we create a copy of stripped_header no matter if - we actually strip something or not. So we've a single efree() later. */ - if (headers && !stripped_header) { - stripped_header = estrndup(ZSTR_VAL(headers), ZSTR_LEN(headers)); - } - if (!Post("DATA\r\n")) { if (stripped_header) { efree(stripped_header); From 004c630106cc36049b5b3e5751b654fa7656a8a5 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 26 Dec 2025 01:03:43 +0100 Subject: [PATCH 234/252] win32/sendmail.c/addToHeader(): voidify function It always returns 1 and thus a bunch of error handling code is useless --- win32/sendmail.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/win32/sendmail.c b/win32/sendmail.c index 2b72ae328e0ac..ea8deadeba549 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -649,13 +649,12 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, const return (SUCCESS); } -static int addToHeader(char **header_buffer, const char *specifier, const char *string) +static void addToHeader(char **header_buffer, const char *specifier, const char *string) { size_t header_buffer_size = strlen(*header_buffer); size_t total_size = header_buffer_size + strlen(specifier) + strlen(string) + 1; *header_buffer = erealloc(*header_buffer, total_size); snprintf(*header_buffer + header_buffer_size, total_size - header_buffer_size, specifier, string); - return 1; } //********************************************************************* @@ -701,24 +700,16 @@ static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char } if (!headers_lc || !strstr(headers_lc, "from:")) { - if (!addToHeader(&header_buffer, "From: %s\r\n", RPath)) { - goto PostHeader_outofmem; - } - } - if (!addToHeader(&header_buffer, "Subject: %s\r\n", Subject)) { - goto PostHeader_outofmem; + addToHeader(&header_buffer, "From: %s\r\n", RPath); } + addToHeader(&header_buffer, "Subject: %s\r\n", Subject); /* Only add the To: field from the $to parameter if isn't in the custom headers */ if ((headers_lc && (!strstr(headers_lc, "\r\nto:") && (strncmp(headers_lc, "to:", 3) != 0))) || !headers_lc) { - if (!addToHeader(&header_buffer, "To: %s\r\n", mailTo)) { - goto PostHeader_outofmem; - } + addToHeader(&header_buffer, "To: %s\r\n", mailTo); } if (xheaders) { - if (!addToHeader(&header_buffer, "%s\r\n", xheaders)) { - goto PostHeader_outofmem; - } + addToHeader(&header_buffer, "%s\r\n", xheaders); } if (headers_lc) { @@ -735,12 +726,6 @@ static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char } return (SUCCESS); - -PostHeader_outofmem: - if (headers_lc) { - efree(headers_lc); - } - return OUT_OF_MEMORY; } From 8aa64bb976583a67556c1605b3749e8cdfd6da63 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 26 Dec 2025 01:21:56 +0100 Subject: [PATCH 235/252] win32/sendmail.c/SendText(): move posting of DATA prior to stripped header computation Removes some error handling and work if it fails --- win32/sendmail.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/win32/sendmail.c b/win32/sendmail.c index ea8deadeba549..f8b22897a2c6a 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -504,6 +504,14 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, const efree(tempMailTo); } + if (!Post("DATA\r\n")) { + return (FAILED_TO_SEND); + } + if ((res = Ack(&server_response)) != SUCCESS) { + SMTP_ERROR_RESPONSE(server_response); + return (res); + } + /* Send mail to all Bcc rcpt's This is basically a rip of the Cc code above. Just don't forget to remove the Bcc: from the header afterwards. */ @@ -578,20 +586,6 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, const } } - if (!Post("DATA\r\n")) { - if (stripped_header) { - efree(stripped_header); - } - return (FAILED_TO_SEND); - } - if ((res = Ack(&server_response)) != SUCCESS) { - SMTP_ERROR_RESPONSE(server_response); - if (stripped_header) { - efree(stripped_header); - } - return (res); - } - /* send message header */ if (Subject == NULL) { res = PostHeader(RPath, "No Subject", mailTo, stripped_header); From fc276aee681cf2405b0fcbc33039586555476939 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 26 Dec 2025 01:20:22 +0100 Subject: [PATCH 236/252] win32/sendmail.c/SendText(): use zend_string for stripped_headers This prevents some copying and is in preparation of other refactoring preventing strlen() recomputations --- win32/sendmail.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/win32/sendmail.c b/win32/sendmail.c index f8b22897a2c6a..5f79f1ada79f0 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -112,7 +112,7 @@ static const char *ErrorMessages[] = #define PHP_WIN32_MAIL_DOT_REPLACE "\n.." static int SendText(char *RPath, const char *Subject, const char *mailTo, const char *data, - const zend_string *headers, zend_string *headers_lc, char **error_message); + zend_string *headers, zend_string *headers_lc, char **error_message); static int MailConnect(); static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char *xheaders); static bool Post(LPCSTR msg); @@ -387,14 +387,14 @@ static char *find_address(char *list, char **state) // History: //********************************************************************* static int SendText(char *RPath, const char *Subject, const char *mailTo, const char *data, - const zend_string *headers, zend_string *headers_lc, char **error_message) + zend_string *headers, zend_string *headers_lc, char **error_message) { int res; char *p; char *tempMailTo, *token, *token_state; const char *pos1, *pos2; char *server_response = NULL; - char *stripped_header = NULL; + zend_string *stripped_header = NULL; zend_string *data_cln; /* check for NULL parameters */ @@ -565,35 +565,37 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, const /* Now that we've identified that we've a Bcc list, remove it from the current header. */ - stripped_header = ecalloc(1, ZSTR_LEN(headers)); /* headers = point to string start of header pos1 = pointer IN headers where the Bcc starts '4' = Length of the characters 'bcc:' Because we've added +4 above for parsing the Emails we've to subtract them here. */ - memcpy(stripped_header, ZSTR_VAL(headers), pos1 - ZSTR_VAL(headers) - 4); + size_t header_length_prior_to_bcc = pos1 - ZSTR_VAL(headers) - 4; if (pos1 != pos2) { /* if pos1 != pos2 , pos2 points to the rest of the headers. Since pos1 != pos2 if "\r\n" was found, we know those characters are there and so we jump over them (else we would generate a new header which would look like "\r\n\r\n". */ - memcpy(stripped_header + (pos1 - ZSTR_VAL(headers) - 4), pos2 + 2, strlen(pos2) - 2); + stripped_header = zend_string_concat2(ZSTR_VAL(headers), header_length_prior_to_bcc, pos2 + 2, strlen(pos2) - 2); + } else { + stripped_header = zend_string_truncate(headers, header_length_prior_to_bcc, false); + ZSTR_VAL(stripped_header)[ZSTR_LEN(stripped_header)] = '\0'; } } else { /* Simplify the code that we create a copy of stripped_header no matter if - we actually strip something or not. So we've a single efree() later. */ - stripped_header = estrndup(ZSTR_VAL(headers), ZSTR_LEN(headers)); + we actually strip something or not. So we've a single zend_string_release() later. */ + stripped_header = zend_string_copy(headers); } } /* send message header */ if (Subject == NULL) { - res = PostHeader(RPath, "No Subject", mailTo, stripped_header); + res = PostHeader(RPath, "No Subject", mailTo, stripped_header ? ZSTR_VAL(stripped_header) : NULL); } else { - res = PostHeader(RPath, Subject, mailTo, stripped_header); + res = PostHeader(RPath, Subject, mailTo, stripped_header ? ZSTR_VAL(stripped_header) : NULL); } if (stripped_header) { - efree(stripped_header); + zend_string_release_ex(stripped_header, false); } if (res != SUCCESS) { return (res); From 77d306ef385e4f1108efab6f3f7a4ee28386d950 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 26 Dec 2025 01:45:47 +0100 Subject: [PATCH 237/252] win32/sendmail.c/PostHeader(): refactor function Input as zend_string Use smart_str to concatenate strings Change return type to bool --- win32/sendmail.c | 82 +++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 47 deletions(-) diff --git a/win32/sendmail.c b/win32/sendmail.c index 5f79f1ada79f0..394676f30316d 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -32,6 +32,7 @@ #include "php_win32_globals.h" +#include "Zend/zend_smart_str.h" #include "ext/pcre/php_pcre.h" #include "ext/standard/php_string.h" #include "ext/date/php_date.h" @@ -114,7 +115,7 @@ static const char *ErrorMessages[] = static int SendText(char *RPath, const char *Subject, const char *mailTo, const char *data, zend_string *headers, zend_string *headers_lc, char **error_message); static int MailConnect(); -static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char *xheaders); +static bool PostHeader(char *RPath, const char *Subject, const char *mailTo, zend_string *xheaders); static bool Post(LPCSTR msg); static int Ack(char **server_response); static unsigned long GetAddr(LPSTR szHost); @@ -589,16 +590,17 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, const } /* send message header */ + bool PostHeaderIsSuccessful = false; if (Subject == NULL) { - res = PostHeader(RPath, "No Subject", mailTo, stripped_header ? ZSTR_VAL(stripped_header) : NULL); + PostHeaderIsSuccessful = PostHeader(RPath, "No Subject", mailTo, stripped_header); } else { - res = PostHeader(RPath, Subject, mailTo, stripped_header ? ZSTR_VAL(stripped_header) : NULL); + PostHeaderIsSuccessful = PostHeader(RPath, Subject, mailTo, stripped_header); } if (stripped_header) { zend_string_release_ex(stripped_header, false); } - if (res != SUCCESS) { - return (res); + if (!PostHeaderIsSuccessful) { + return FAILED_TO_SEND; } /* Escape \n. sequences @@ -645,14 +647,6 @@ static int SendText(char *RPath, const char *Subject, const char *mailTo, const return (SUCCESS); } -static void addToHeader(char **header_buffer, const char *specifier, const char *string) -{ - size_t header_buffer_size = strlen(*header_buffer); - size_t total_size = header_buffer_size + strlen(specifier) + strlen(string) + 1; - *header_buffer = erealloc(*header_buffer, total_size); - snprintf(*header_buffer + header_buffer_size, total_size - header_buffer_size, specifier, string); -} - //********************************************************************* // Name: PostHeader // Input: 1) return path @@ -664,64 +658,58 @@ static void addToHeader(char **header_buffer, const char *specifier, const char // Author/Date: jcar 20/9/96 // History: //********************************************************************* -static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char *xheaders) +static bool PostHeader(char *RPath, const char *Subject, const char *mailTo, zend_string *xheaders) { /* Print message header according to RFC 822 */ /* Return-path, Received, Date, From, Subject, Sender, To, cc */ - int res; - char *header_buffer; - char *headers_lc = NULL; - size_t i; + zend_string *headers_lc = NULL; + smart_str combined_headers = {0}; if (xheaders) { - size_t headers_lc_len; - - headers_lc = estrdup(xheaders); - headers_lc_len = strlen(headers_lc); - - for (i = 0; i < headers_lc_len; i++) { - headers_lc[i] = tolower(headers_lc[i]); - } + headers_lc = zend_string_tolower(xheaders); } - header_buffer = ecalloc(1, MAIL_BUFFER_SIZE); - - if (!xheaders || !strstr(headers_lc, "date:")) { + if (!xheaders || !strstr(ZSTR_VAL(headers_lc), "date:")) { time_t tNow = time(NULL); zend_string *dt = php_format_date("r", 1, tNow, 1); - snprintf(header_buffer, MAIL_BUFFER_SIZE, "Date: %s\r\n", ZSTR_VAL(dt)); + smart_str_appends(&combined_headers, "Date: "); + smart_str_append(&combined_headers, dt); + smart_str_appends(&combined_headers, "\r\n"); zend_string_free(dt); } - if (!headers_lc || !strstr(headers_lc, "from:")) { - addToHeader(&header_buffer, "From: %s\r\n", RPath); + if (!headers_lc || !strstr(ZSTR_VAL(headers_lc), "from:")) { + smart_str_appends(&combined_headers, "From: "); + smart_str_appends(&combined_headers, RPath); + smart_str_appends(&combined_headers, "\r\n"); } - addToHeader(&header_buffer, "Subject: %s\r\n", Subject); + smart_str_appends(&combined_headers, "Subject: "); + smart_str_appends(&combined_headers, Subject); + smart_str_appends(&combined_headers, "\r\n"); /* Only add the To: field from the $to parameter if isn't in the custom headers */ - if ((headers_lc && (!strstr(headers_lc, "\r\nto:") && (strncmp(headers_lc, "to:", 3) != 0))) || !headers_lc) { - addToHeader(&header_buffer, "To: %s\r\n", mailTo); + if (!headers_lc || (!strstr(ZSTR_VAL(headers_lc), "\r\nto:") && (strncmp(ZSTR_VAL(headers_lc), "to:", 3) != 0))) { + smart_str_appends(&combined_headers, "To: "); + smart_str_appends(&combined_headers, mailTo); + smart_str_appends(&combined_headers, "\r\n"); } if (xheaders) { - addToHeader(&header_buffer, "%s\r\n", xheaders); + smart_str_append(&combined_headers, xheaders); + smart_str_appends(&combined_headers, "\r\n"); } + /* End of headers */ + smart_str_appends(&combined_headers, "\r\n"); + zend_string *combined_headers_str = smart_str_extract(&combined_headers); if (headers_lc) { - efree(headers_lc); - } - if (!Post(header_buffer)) { - efree(header_buffer); - return (FAILED_TO_SEND); + zend_string_release_ex(headers_lc, false); } - efree(header_buffer); - if (!Post("\r\n")) { - return (FAILED_TO_SEND); - } - - return (SUCCESS); + bool header_post_status = Post(ZSTR_VAL(combined_headers_str)); + zend_string_efree(combined_headers_str); + return header_post_status; } From 09cb5ad4429913e80b9762aaf39a28a362a424c2 Mon Sep 17 00:00:00 2001 From: Michael Telgmann Date: Sat, 27 Dec 2025 12:53:15 +0100 Subject: [PATCH 238/252] fix: Allow variadic syntax in PHPDoc parameter annotation in `gen_stub.php` (#20342) Closes #20277 Co-authored-by: Ilija Tovilo --- build/gen_stub.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index a83c6c13bf8c1..9d6bd2ce3935a 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3323,7 +3323,7 @@ public function getType(): string { $matches = []; if ($this->name === "param") { - preg_match('/^\s*([\w\|\\\\\[\]<>, ]+)\s*(?:[{(]|\$\w+).*$/', $value, $matches); + preg_match('/^\s*([\w\|\\\\\[\]<>, ]+)\s*(?:[{(]|(\.\.\.)?\$\w+).*$/', $value, $matches); } elseif ($this->name === "return" || $this->name === "var") { preg_match('/^\s*([\w\|\\\\\[\]<>, ]+)/', $value, $matches); } @@ -3345,7 +3345,7 @@ public function getVariableName(): string { if ($this->name === "param") { // Allow for parsing extended types like callable(string):mixed in docblocks - preg_match('/^\s*(?[\w\|\\\\]+(?\((?(?:(?&parens)|[^(){}[\]]*+))++\)|\{(?&inparens)\}|\[(?&inparens)\])*+(?::(?&type))?)\s*\$(?\w+).*$/', $value, $matches); + preg_match('/^\s*(?[\w\|\\\\]+(?\((?(?:(?&parens)|[^(){}[\]]*+))++\)|\{(?&inparens)\}|\[(?&inparens)\])*+(?::(?&type))?)\s*(\.\.\.)?\$(?\w+).*$/', $value, $matches); } elseif ($this->name === "prefer-ref") { preg_match('/^\s*\$(?\w+).*$/', $value, $matches); } From 2709ebc0ce64fb016be2c4647d979061677e0fdb Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Fri, 26 Dec 2025 13:58:01 +0100 Subject: [PATCH 239/252] Fix OOB gzseek() causing assertion failure Closes GH-20785. --- NEWS | 3 +++ ext/zlib/tests/gzseek_seek_oob.phpt | 19 +++++++++++++++++++ ext/zlib/zlib_fopen_wrapper.c | 9 +++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 ext/zlib/tests/gzseek_seek_oob.phpt diff --git a/NEWS b/NEWS index 77119e29fcfc4..0b5020c6f0c80 100644 --- a/NEWS +++ b/NEWS @@ -54,6 +54,9 @@ PHP NEWS . Fix error check for proc_open() command. (ndossche) . Fixed bug GH-20582 (Heap Buffer Overflow in iptcembed). (ndossche) +- Zlib: + . Fix OOB gzseek() causing assertion failure. (ndossche) + 18 Dec 2025, PHP 8.3.29 - Core: diff --git a/ext/zlib/tests/gzseek_seek_oob.phpt b/ext/zlib/tests/gzseek_seek_oob.phpt new file mode 100644 index 0000000000000..021f8cb174dab --- /dev/null +++ b/ext/zlib/tests/gzseek_seek_oob.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test function gzseek() by seeking out of bounds +--EXTENSIONS-- +zlib +--FILE-- + +--EXPECT-- +int(-1) +int(0) +int(0) diff --git a/ext/zlib/zlib_fopen_wrapper.c b/ext/zlib/zlib_fopen_wrapper.c index 31b5212a720ac..da948af37ffc6 100644 --- a/ext/zlib/zlib_fopen_wrapper.c +++ b/ext/zlib/zlib_fopen_wrapper.c @@ -94,9 +94,14 @@ static int php_gziop_seek(php_stream *stream, zend_off_t offset, int whence, zen php_error_docref(NULL, E_WARNING, "SEEK_END is not supported"); return -1; } - *newoffs = gzseek(self->gz_file, offset, whence); - return (*newoffs < 0) ? -1 : 0; + z_off_t new_offset = gzseek(self->gz_file, offset, whence); + if (new_offset < 0) { + return -1; + } + + *newoffs = new_offset; + return 0; } static int php_gziop_close(php_stream *stream, int close_handle) From b86f107076cc328eb777355163a5880713ef337d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 16:46:09 +0100 Subject: [PATCH 240/252] ext/standard: refactor _php_error_log() In preparation for php_mail() refactoring --- UPGRADING.INTERNALS | 5 +++++ ext/standard/basic_functions.c | 32 ++++++++++++-------------------- ext/standard/basic_functions.h | 4 +--- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 3ec54f49391b8..9b30458392a87 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -70,6 +70,11 @@ PHP 8.6 INTERNALS UPGRADE NOTES - ext/mbstring: . Added GB18030-2022 to default encoding list for zh-CN. +- ext/standard: + . _php_error_log() now has a formal return type of zend_result. + . _php_error_log() now accepts zend_string* values instead of char*. + . _php_error_log_ex() has been removed. + ======================== 4. OpCode changes ======================== diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 8b72ffffc8379..b87eacfdfda32 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1330,19 +1330,18 @@ error options: /* {{{ Send an error message somewhere */ PHP_FUNCTION(error_log) { - char *message, *opt = NULL, *headers = NULL; - size_t message_len, opt_len = 0, headers_len = 0; + zend_string *message, *opt = NULL, *headers = NULL; zend_long erropt = 0; ZEND_PARSE_PARAMETERS_START(1, 4) - Z_PARAM_STRING(message, message_len) + Z_PARAM_STR(message) Z_PARAM_OPTIONAL Z_PARAM_LONG(erropt) - Z_PARAM_PATH_OR_NULL(opt, opt_len) - Z_PARAM_STRING_OR_NULL(headers, headers_len) + Z_PARAM_PATH_STR_OR_NULL(opt) + Z_PARAM_STR_OR_NULL(headers) ZEND_PARSE_PARAMETERS_END(); - if (_php_error_log_ex((int) erropt, message, message_len, opt, headers) == FAILURE) { + if (_php_error_log((int) erropt, message, opt, headers) == FAILURE) { RETURN_FALSE; } @@ -1350,14 +1349,7 @@ PHP_FUNCTION(error_log) } /* }}} */ -/* For BC (not binary-safe!) */ -PHPAPI int _php_error_log(int opt_err, const char *message, const char *opt, const char *headers) /* {{{ */ -{ - return _php_error_log_ex(opt_err, message, (opt_err == 3) ? strlen(message) : 0, opt, headers); -} -/* }}} */ - -PHPAPI int _php_error_log_ex(int opt_err, const char *message, size_t message_len, const char *opt, const char *headers) /* {{{ */ +PHPAPI zend_result _php_error_log(int opt_err, const zend_string *message, const zend_string *opt, const zend_string *headers) /* {{{ */ { php_stream *stream = NULL; size_t nbytes; @@ -1365,7 +1357,7 @@ PHPAPI int _php_error_log_ex(int opt_err, const char *message, size_t message_le switch (opt_err) { case 1: /*send an email */ - if (!php_mail(opt, "PHP error_log message", message, headers, NULL)) { + if (!php_mail(ZSTR_VAL(opt), "PHP error_log message", ZSTR_VAL(message), ZSTR_VAL(headers), NULL)) { return FAILURE; } break; @@ -1375,27 +1367,27 @@ PHPAPI int _php_error_log_ex(int opt_err, const char *message, size_t message_le return FAILURE; case 3: /*save to a file */ - stream = php_stream_open_wrapper(opt, "a", REPORT_ERRORS, NULL); + stream = php_stream_open_wrapper(ZSTR_VAL(opt), "a", REPORT_ERRORS, NULL); if (!stream) { return FAILURE; } - nbytes = php_stream_write(stream, message, message_len); + nbytes = php_stream_write(stream, ZSTR_VAL(message), ZSTR_LEN(message)); php_stream_close(stream); - if (nbytes != message_len) { + if (nbytes != ZSTR_LEN(message)) { return FAILURE; } break; case 4: /* send to SAPI */ if (sapi_module.log_message) { - sapi_module.log_message(message, -1); + sapi_module.log_message(ZSTR_VAL(message), -1); } else { return FAILURE; } break; default: - php_log_err_with_severity(message, LOG_NOTICE); + php_log_err_with_severity(ZSTR_VAL(message), LOG_NOTICE); break; } return SUCCESS; diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h index 8c8c6ba58dfd6..e5b85fbc2d53c 100644 --- a/ext/standard/basic_functions.h +++ b/ext/standard/basic_functions.h @@ -46,9 +46,7 @@ PHP_MINIT_FUNCTION(user_filters); PHP_RSHUTDOWN_FUNCTION(user_filters); PHP_RSHUTDOWN_FUNCTION(browscap); -/* Left for BC (not binary safe!) */ -PHPAPI int _php_error_log(int opt_err, const char *message, const char *opt, const char *headers); -PHPAPI int _php_error_log_ex(int opt_err, const char *message, size_t message_len, const char *opt, const char *headers); +PHPAPI zend_result _php_error_log(int opt_err, const zend_string *message, const zend_string *opt, const zend_string *headers); typedef struct _php_basic_globals { HashTable *user_shutdown_function_names; From 87278167340dec0d65696118ac402b8783e0875c Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 16:53:09 +0100 Subject: [PATCH 241/252] ext/standard: use RETURN_BOOL() when possible --- ext/standard/basic_functions.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index b87eacfdfda32..c108900103cc1 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1341,11 +1341,7 @@ PHP_FUNCTION(error_log) Z_PARAM_STR_OR_NULL(headers) ZEND_PARSE_PARAMETERS_END(); - if (_php_error_log((int) erropt, message, opt, headers) == FAILURE) { - RETURN_FALSE; - } - - RETURN_TRUE; + RETURN_BOOL(_php_error_log((int) erropt, message, opt, headers) == SUCCESS); } /* }}} */ @@ -2314,11 +2310,7 @@ PHP_FUNCTION(is_uploaded_file) RETURN_FALSE; } - if (zend_hash_exists(SG(rfc1867_uploaded_files), path)) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(zend_hash_exists(SG(rfc1867_uploaded_files), path)); } /* }}} */ From e101706ecfba86fcf19d015d6950aefb51a3a393 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 17:08:16 +0100 Subject: [PATCH 242/252] ext/standard/mail.c: add const qualifiers --- ext/standard/mail.c | 16 ++++++++-------- ext/standard/php_mail.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 3ffb7d05bb4b0..d2abd13643ba0 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -113,7 +113,7 @@ static php_mail_header_value_error_type php_mail_build_headers_check_field_value } -static bool php_mail_build_headers_check_field_name(zend_string *key) +static bool php_mail_build_headers_check_field_name(const zend_string *key) { size_t len = 0; @@ -128,9 +128,9 @@ static bool php_mail_build_headers_check_field_name(zend_string *key) } -static void php_mail_build_headers_elems(smart_str *s, zend_string *key, zval *val); +static void php_mail_build_headers_elems(smart_str *s, const zend_string *key, zval *val); -static void php_mail_build_headers_elem(smart_str *s, zend_string *key, zval *val) +static void php_mail_build_headers_elem(smart_str *s, const zend_string *key, zval *val) { switch(Z_TYPE_P(val)) { case IS_STRING: @@ -174,7 +174,7 @@ static void php_mail_build_headers_elem(smart_str *s, zend_string *key, zval *va } -static void php_mail_build_headers_elems(smart_str *s, zend_string *key, zval *val) +static void php_mail_build_headers_elems(smart_str *s, const zend_string *key, zval *val) { zend_string *tmp_key; zval *tmp_val; @@ -208,7 +208,7 @@ do { \ } \ } while(0) -PHPAPI zend_string *php_mail_build_headers(HashTable *headers) +PHPAPI zend_string *php_mail_build_headers(const HashTable *headers) { zend_ulong idx; zend_string *key; @@ -392,7 +392,7 @@ static void php_mail_log_to_syslog(char *message) { } -static void php_mail_log_to_file(char *filename, char *message, size_t message_size) { +static void php_mail_log_to_file(const char *filename, const char *message, size_t message_size) { /* Write 'message' to the given file. */ uint32_t flags = REPORT_ERRORS | STREAM_DISABLE_OPEN_BASEDIR; php_stream *stream = php_stream_open_wrapper(filename, "a", flags, NULL); @@ -446,7 +446,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c FILE *sendmail; char *sendmail_path = INI_STR("sendmail_path"); char *sendmail_cmd = NULL; - char *mail_log = INI_STR("mail.log"); + const char *mail_log = INI_STR("mail.log"); const char *hdr = headers; char *ahdr = NULL; #if PHP_SIGCHILD @@ -495,7 +495,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c MAIL_RET(false); } - char *line_sep; + const char *line_sep; zend_string *cr_lf_mode = PG(mail_cr_lf_mode); if (cr_lf_mode && !zend_string_equals_literal(cr_lf_mode, "crlf")) { diff --git a/ext/standard/php_mail.h b/ext/standard/php_mail.h index c2e22240c48e9..731b70c0e105f 100644 --- a/ext/standard/php_mail.h +++ b/ext/standard/php_mail.h @@ -19,7 +19,7 @@ PHP_MINFO_FUNCTION(mail); -PHPAPI zend_string *php_mail_build_headers(HashTable *headers); +PHPAPI zend_string *php_mail_build_headers(const HashTable *headers); PHPAPI extern bool php_mail(const char *to, const char *subject, const char *message, const char *headers, const char *extra_cmd); #endif /* PHP_MAIL_H */ From d3a372c70d176966f87bd50ed2a8c00be2cbe665 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 17:14:52 +0100 Subject: [PATCH 243/252] ext/standard/mail.c: use zend_string for mail_log --- ext/standard/mail.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/standard/mail.c b/ext/standard/mail.c index d2abd13643ba0..b3f40847d568e 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -392,10 +392,10 @@ static void php_mail_log_to_syslog(char *message) { } -static void php_mail_log_to_file(const char *filename, const char *message, size_t message_size) { +static void php_mail_log_to_file(const zend_string *filename, const char *message, size_t message_size) { /* Write 'message' to the given file. */ uint32_t flags = REPORT_ERRORS | STREAM_DISABLE_OPEN_BASEDIR; - php_stream *stream = php_stream_open_wrapper(filename, "a", flags, NULL); + php_stream *stream = php_stream_open_wrapper(ZSTR_VAL(filename), "a", flags, NULL); if (stream) { php_stream_write(stream, message, message_size); php_stream_close(stream); @@ -446,7 +446,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c FILE *sendmail; char *sendmail_path = INI_STR("sendmail_path"); char *sendmail_cmd = NULL; - const char *mail_log = INI_STR("mail.log"); + const zend_string *mail_log = zend_ini_str(ZEND_STRL("mail.log"), false); const char *hdr = headers; char *ahdr = NULL; #if PHP_SIGCHILD @@ -459,7 +459,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c } \ return val; \ - if (mail_log && *mail_log) { + if (mail_log && ZSTR_LEN(mail_log)) { char *logline; spprintf(&logline, 0, "mail() on [%s:%d]: To: %s -- Headers: %s -- Subject: %s", zend_get_executed_filename(), zend_get_executed_lineno(), to, hdr ? hdr : "", subject); @@ -468,7 +468,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c php_mail_log_crlf_to_spaces(logline); } - if (!strcmp(mail_log, "syslog")) { + if (zend_string_equals_literal(mail_log, "syslog")) { php_mail_log_to_syslog(logline); } else { /* Add date when logging to file */ From 10425538075238165f3d8d6fb94b57aeec7c5cb3 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 17:23:06 +0100 Subject: [PATCH 244/252] ext/standard/mail.c: use smart_str_append when we have zend_string* --- ext/standard/mail.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/mail.c b/ext/standard/mail.c index b3f40847d568e..b8381be0626b4 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -162,7 +162,7 @@ static void php_mail_build_headers_elem(smart_str *s, const zend_string *key, zv } smart_str_append(s, key); smart_str_appendl(s, ": ", 2); - smart_str_appends(s, Z_STRVAL_P(val)); + smart_str_append(s, Z_STR_P(val)); smart_str_appendl(s, "\r\n", 2); break; case IS_ARRAY: From 67a89f6bf0034f0bc70e0ab08fcb77c4f0443c22 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 17:28:27 +0100 Subject: [PATCH 245/252] ext/standard/mail.c: refactor php_mail_build_headers_check_field_name() Fix incorrect return type Use zend_string macros --- ext/standard/mail.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ext/standard/mail.c b/ext/standard/mail.c index b8381be0626b4..0d1b34d71662c 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -112,14 +112,13 @@ static php_mail_header_value_error_type php_mail_build_headers_check_field_value return NO_HEADER_ERROR; } - -static bool php_mail_build_headers_check_field_name(const zend_string *key) +static zend_result php_mail_build_headers_check_field_name(const zend_string *key) { size_t len = 0; /* https://tools.ietf.org/html/rfc2822#section-2.2 */ - while (len < key->len) { - if (*(key->val+len) < 33 || *(key->val+len) > 126 || *(key->val+len) == ':') { + while (len < ZSTR_LEN(key)) { + if (*(ZSTR_VAL(key)+len) < 33 || *(ZSTR_VAL(key)+len) > 126 || *(ZSTR_VAL(key)+len) == ':') { return FAILURE; } len++; From 402f08437377660ec7cb15aa4b207594dddd7231 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 17:37:43 +0100 Subject: [PATCH 246/252] ext/standard/mail.c: use php_mail_build_headers_elem() directly The branching logic is already defined in the function --- ext/standard/mail.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 0d1b34d71662c..0788d54ebf012 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -245,13 +245,7 @@ PHPAPI zend_string *php_mail_build_headers(const HashTable *headers) } else if (zend_string_equals_literal_ci(key, "subject")) { zend_value_error("The additional headers cannot contain the \"Subject\" header"); } else { - if (Z_TYPE_P(val) == IS_STRING) { - php_mail_build_headers_elem(&s, key, val); - } else if (Z_TYPE_P(val) == IS_ARRAY) { - php_mail_build_headers_elems(&s, key, val); - } else { - zend_type_error("Header \"%s\" must be of type array|string, %s given", ZSTR_VAL(key), zend_zval_value_name(val)); - } + php_mail_build_headers_elem(&s, key, val); } if (EG(exception)) { From 5f8c7dc87c50a6a4077248c69bad1bc443972f98 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 17:55:18 +0100 Subject: [PATCH 247/252] ext/standard/mail.c: refactor php_mail_build_headers_check_field_value() Change paremeter type to be zend_string* rather than assuming the zval IS_STRING Use zend_string macros Add const qualifier --- ext/standard/mail.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 0788d54ebf012..01e1148af0829 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -68,21 +68,20 @@ typedef enum { CONTAINS_NULL } php_mail_header_value_error_type; -static php_mail_header_value_error_type php_mail_build_headers_check_field_value(zval *val) +static php_mail_header_value_error_type php_mail_build_headers_check_field_value(const zend_string *value) { size_t len = 0; - zend_string *value = Z_STR_P(val); /* https://tools.ietf.org/html/rfc2822#section-2.2.1 */ /* https://tools.ietf.org/html/rfc2822#section-2.2.3 */ - while (len < value->len) { - if (*(value->val+len) == '\r') { - if (*(value->val+len+1) != '\n') { + while (len < ZSTR_LEN(value)) { + if (*(ZSTR_VAL(value)+len) == '\r') { + if (*(ZSTR_VAL(value)+len+1) != '\n') { return CONTAINS_CR_ONLY; } - if (value->len - len >= 3 - && (*(value->val+len+2) == ' ' || *(value->val+len+2) == '\t')) { + if (ZSTR_LEN(value) - len >= 3 + && (*(ZSTR_VAL(value)+len+2) == ' ' || *(ZSTR_VAL(value)+len+2) == '\t')) { len += 3; continue; } @@ -96,15 +95,15 @@ static php_mail_header_value_error_type php_mail_build_headers_check_field_value * Therefore, considering such an environment, folding with LF alone * is allowed. */ - if (*(value->val+len) == '\n') { - if (value->len - len >= 2 - && (*(value->val+len+1) == ' ' || *(value->val+len+1) == '\t')) { + if (*(ZSTR_VAL(value)+len) == '\n') { + if (ZSTR_LEN(value) - len >= 2 + && (*(ZSTR_VAL(value)+len+1) == ' ' || *(ZSTR_VAL(value)+len+1) == '\t')) { len += 2; continue; } return CONTAINS_LF_ONLY; } - if (*(value->val+len) == '\0') { + if (*(ZSTR_VAL(value)+len) == '\0') { return CONTAINS_NULL; } len++; @@ -138,7 +137,8 @@ static void php_mail_build_headers_elem(smart_str *s, const zend_string *key, zv return; } - php_mail_header_value_error_type error_type = php_mail_build_headers_check_field_value(val); + zend_string *str_value = Z_STR_P(val); + php_mail_header_value_error_type error_type = php_mail_build_headers_check_field_value(str_value); switch (error_type) { case NO_HEADER_ERROR: break; @@ -161,7 +161,7 @@ static void php_mail_build_headers_elem(smart_str *s, const zend_string *key, zv } smart_str_append(s, key); smart_str_appendl(s, ": ", 2); - smart_str_append(s, Z_STR_P(val)); + smart_str_append(s, str_value); smart_str_appendl(s, "\r\n", 2); break; case IS_ARRAY: From c727f4d6c5f258a69db04fc6f7d21e034afdc412 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 24 Dec 2025 18:08:32 +0100 Subject: [PATCH 248/252] ext/standard/mail: use zend_string* for extra_cmd param of php_mail() --- UPGRADING.INTERNALS | 1 + ext/mbstring/mbstring.c | 2 +- ext/standard/mail.c | 6 +++--- ext/standard/php_mail.h | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 9b30458392a87..22b9e6f660cb1 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -74,6 +74,7 @@ PHP 8.6 INTERNALS UPGRADE NOTES . _php_error_log() now has a formal return type of zend_result. . _php_error_log() now accepts zend_string* values instead of char*. . _php_error_log_ex() has been removed. + . php_mail()'s extra_cmd parameter is now a zend_string*. ======================== 4. OpCode changes diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 12c366c33d5e2..c5331ef017af6 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -4703,7 +4703,7 @@ PHP_FUNCTION(mb_send_mail) extra_cmd = php_escape_shell_cmd(extra_cmd); } - RETVAL_BOOL(php_mail(to_r, ZSTR_VAL(subject), message, ZSTR_VAL(str_headers), extra_cmd ? ZSTR_VAL(extra_cmd) : NULL)); + RETVAL_BOOL(php_mail(to_r, ZSTR_VAL(subject), message, ZSTR_VAL(str_headers), extra_cmd)); if (extra_cmd) { zend_string_release_ex(extra_cmd, 0); diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 01e1148af0829..395c7bb81d4f9 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -343,7 +343,7 @@ PHP_FUNCTION(mail) extra_cmd = php_escape_shell_cmd(extra_cmd); } - if (php_mail(to_r, subject_r, message, headers_str && ZSTR_LEN(headers_str) ? ZSTR_VAL(headers_str) : NULL, extra_cmd ? ZSTR_VAL(extra_cmd) : NULL)) { + if (php_mail(to_r, subject_r, message, headers_str && ZSTR_LEN(headers_str) ? ZSTR_VAL(headers_str) : NULL, extra_cmd)) { RETVAL_TRUE; } else { RETVAL_FALSE; @@ -434,7 +434,7 @@ static int php_mail_detect_multiple_crlf(const char *hdr) { /* {{{ php_mail */ -PHPAPI bool php_mail(const char *to, const char *subject, const char *message, const char *headers, const char *extra_cmd) +PHPAPI bool php_mail(const char *to, const char *subject, const char *message, const char *headers, const zend_string *extra_cmd) { FILE *sendmail; char *sendmail_path = INI_STR("sendmail_path"); @@ -551,7 +551,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c #endif } if (extra_cmd != NULL) { - spprintf(&sendmail_cmd, 0, "%s %s", sendmail_path, extra_cmd); + spprintf(&sendmail_cmd, 0, "%s %s", sendmail_path, ZSTR_VAL(extra_cmd)); } else { sendmail_cmd = sendmail_path; } diff --git a/ext/standard/php_mail.h b/ext/standard/php_mail.h index 731b70c0e105f..bebb526699d0b 100644 --- a/ext/standard/php_mail.h +++ b/ext/standard/php_mail.h @@ -20,6 +20,6 @@ PHP_MINFO_FUNCTION(mail); PHPAPI zend_string *php_mail_build_headers(const HashTable *headers); -PHPAPI extern bool php_mail(const char *to, const char *subject, const char *message, const char *headers, const char *extra_cmd); +PHPAPI extern bool php_mail(const char *to, const char *subject, const char *message, const char *headers, const zend_string *extra_cmd); #endif /* PHP_MAIL_H */ From 6e733a2bd0842f7919d7ad7665b7c8a6729bb717 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 28 Dec 2025 01:32:21 +0100 Subject: [PATCH 249/252] Fix zlib test for 8.5+ --- ext/zlib/tests/gzseek_seek_oob.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/zlib/tests/gzseek_seek_oob.phpt b/ext/zlib/tests/gzseek_seek_oob.phpt index 021f8cb174dab..e05ea4778b94b 100644 --- a/ext/zlib/tests/gzseek_seek_oob.phpt +++ b/ext/zlib/tests/gzseek_seek_oob.phpt @@ -4,7 +4,7 @@ Test function gzseek() by seeking out of bounds zlib --FILE-- Date: Sun, 28 Dec 2025 04:04:16 -0800 Subject: [PATCH 250/252] Fix GH-19961: Static analysis arrayIndexThenCheck warning in firebird (#20790) Static analysis reports that the bounds check comes after reading the byte from the buffer. In practice, this is tagged data that loops until the end tag is found and therefore there isn't really a bug. The extra length check is only there for extra hardening. So we simply silence the static analysers and improve the hardening. See also https://docwiki.embarcadero.com/InterBase/15/en/Isc_dsql_sql_info() --- ext/pdo_firebird/firebird_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 504e739a974d9..ef94d6fcb00cc 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -632,7 +632,7 @@ static zend_long firebird_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /* ret = -1; goto free_statement; } - while (result[i] != isc_info_end && i < result_size) { + while (i < result_size && result[i] != isc_info_end) { short len = (short)isc_vax_integer(&result[i+1],2); /* bail out on bad len */ if (len != 1 && len != 2 && len != 4) { From bae78c614a4437065f7f9e007a580f1e1f2ad07b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 28 Dec 2025 13:08:40 +0100 Subject: [PATCH 251/252] Fix GH-19962: arrayIndexThenCheck static analysis warning in firebird Same as ce534c612b4cd6bd8445c70d8d8ee794d1076b5f. --- ext/pdo_firebird/firebird_statement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/pdo_firebird/firebird_statement.c b/ext/pdo_firebird/firebird_statement.c index 7f4e7aeed87c0..cddfcbf25f39a 100644 --- a/ext/pdo_firebird/firebird_statement.c +++ b/ext/pdo_firebird/firebird_statement.c @@ -172,7 +172,7 @@ static int firebird_stmt_execute(pdo_stmt_t *stmt) /* {{{ */ if (result_size > sizeof(result)) { goto error; } - while (result[i] != isc_info_end && i < result_size) { + while (i < result_size && result[i] != isc_info_end) { short len = (short) isc_vax_integer(&result[i + 1], 2); if (len != 1 && len != 2 && len != 4) { goto error; From 7effcab2cf016aea08de5239b07bef9132974416 Mon Sep 17 00:00:00 2001 From: Sharad Chandran R Date: Mon, 29 Dec 2025 19:19:21 +0530 Subject: [PATCH 252/252] Fix variable assignment for PHP argument escaping in `run-tests.php` (#20799) --- run-tests.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run-tests.php b/run-tests.php index a06f0958d1b1e..266d352260200 100755 --- a/run-tests.php +++ b/run-tests.php @@ -1861,8 +1861,8 @@ function run_test(string $php, $file, array $env): string $skipCache = new SkipCache($enableSkipCache, $cfg['keep']['skip']); } - $orig_php = $php; $php = escapeshellarg($php); + $orig_php = $php; $retried = false; retry: