From b1e3dcf88a2251fbc3665905560e27dd9539b53f Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 3 Dec 2024 18:45:43 +0100 Subject: [PATCH 01/65] PHP-8.3 is now for PHP 8.3.16-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 b31724b1b0338..e52764d78db64 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.15 +?? ??? ????, PHP 8.3.16 + + +19 Dec 2024, PHP 8.3.15 - Calendar: . Fixed jdtogregorian overflow. (David Carlier) diff --git a/Zend/zend.h b/Zend/zend.h index daf064f8c4e02..3fe973e663bea 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.15-dev" +#define ZEND_VERSION "4.3.16-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index d15c390e09355..b6ec8b2be15fd 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.15-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.16-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 0182b4081dba7..3521615c68fc9 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 15 +#define PHP_RELEASE_VERSION 16 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.15-dev" -#define PHP_VERSION_ID 80315 +#define PHP_VERSION "8.3.16-dev" +#define PHP_VERSION_ID 80316 From 7685fb0e1c241f6a28f0f0999ba54d8d115de044 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 25 Nov 2024 18:34:33 +0100 Subject: [PATCH 02/65] Enable GHSA-9pqp-7h25-4f32.phpt on Windows Closes GH-16933. --- tests/basic/GHSA-9pqp-7h25-4f32.phpt | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt index 29bcb6557d5a2..af81916370500 100644 --- a/tests/basic/GHSA-9pqp-7h25-4f32.phpt +++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt @@ -5,9 +5,6 @@ GHSA-9pqp-7h25-4f32 if (!getenv('TEST_PHP_CGI_EXECUTABLE')) { die("skip php-cgi not available"); } -if (substr(PHP_OS, 0, 3) == 'WIN') { - die("skip not for Windows in CI - probably resource issue"); -} ?> --FILE-- Date: Tue, 3 Dec 2024 22:47:53 +0100 Subject: [PATCH 03/65] Fix GH-17037: UAF in user filter when adding existing filter name due to incorrect error handling There are two functions that can each fail in their own way. If the last function fails we have to remove the filter entry from the hash table, otherwise we risk a UAF. Note also that removing the entry from the table on failure will also free its memory. Closes GH-17038. --- NEWS | 3 +++ ext/standard/tests/filters/gh17037.phpt | 8 ++++++++ ext/standard/user_filters.c | 12 ++++++++---- 3 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 ext/standard/tests/filters/gh17037.phpt diff --git a/NEWS b/NEWS index e52764d78db64..a139c14513a57 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.16 +- Streams: + . Fixed bug GH-17037 (UAF in user filter when adding existing filter name due + to incorrect error handling). (nielsdos) 19 Dec 2024, PHP 8.3.15 diff --git a/ext/standard/tests/filters/gh17037.phpt b/ext/standard/tests/filters/gh17037.phpt new file mode 100644 index 0000000000000..21319ba26bf99 --- /dev/null +++ b/ext/standard/tests/filters/gh17037.phpt @@ -0,0 +1,8 @@ +--TEST-- +GH-17037 (UAF in user filter when adding existing filter name due to incorrect error handling) +--FILE-- + +--EXPECT-- +bool(false) diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index 063895e2f4049..737237f6630cd 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -516,13 +516,17 @@ PHP_FUNCTION(stream_filter_register) fdat = ecalloc(1, sizeof(struct php_user_filter_data)); fdat->classname = zend_string_copy(classname); - if (zend_hash_add_ptr(BG(user_filter_map), filtername, fdat) != NULL && - php_stream_filter_register_factory_volatile(filtername, &user_filter_factory) == SUCCESS) { - RETVAL_TRUE; + if (zend_hash_add_ptr(BG(user_filter_map), filtername, fdat) != NULL) { + if (php_stream_filter_register_factory_volatile(filtername, &user_filter_factory) == SUCCESS) { + RETURN_TRUE; + } + + zend_hash_del(BG(user_filter_map), filtername); } else { zend_string_release_ex(classname, 0); efree(fdat); - RETVAL_FALSE; } + + RETURN_FALSE; } /* }}} */ From 2285d7083ed48f8717974697eacb97be6243947d Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 4 Dec 2024 20:43:49 +0100 Subject: [PATCH 04/65] Revert "Enable GHSA-9pqp-7h25-4f32.phpt on Windows" This reverts commit 7685fb0e1c241f6a28f0f0999ba54d8d115de044. The test fails at least for PHP-8.2+ on CI. Needs closer investigation. --- tests/basic/GHSA-9pqp-7h25-4f32.phpt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt index af81916370500..29bcb6557d5a2 100644 --- a/tests/basic/GHSA-9pqp-7h25-4f32.phpt +++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt @@ -5,6 +5,9 @@ GHSA-9pqp-7h25-4f32 if (!getenv('TEST_PHP_CGI_EXECUTABLE')) { die("skip php-cgi not available"); } +if (substr(PHP_OS, 0, 3) == 'WIN') { + die("skip not for Windows in CI - probably resource issue"); +} ?> --FILE-- Date: Thu, 5 Dec 2024 18:32:02 +0300 Subject: [PATCH 05/65] Backport fix for GH-9011 (#17052) * Backport fix for GH-9011 * Fix build --- ext/opcache/jit/zend_jit_x86.dasc | 64 ++++++++++++++++++++++++------- ext/opcache/tests/jit/gh9011.phpt | 27 +++++++++++++ 2 files changed, 77 insertions(+), 14 deletions(-) create mode 100644 ext/opcache/tests/jit/gh9011.phpt diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 3f17817992a2b..aff19300cd749 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -9240,19 +9240,11 @@ static int zend_jit_init_fcall_guard(dasm_State **Dst, uint32_t level, const zen int32_t exit_point; const void *exit_addr; - if (func->type == ZEND_INTERNAL_FUNCTION) { -#ifdef ZEND_WIN32 - // TODO: ASLR may cause different addresses in different workers ??? - return 0; -#endif - } else if (func->type == ZEND_USER_FUNCTION) { + if (func->type == ZEND_USER_FUNCTION) { if (!zend_accel_in_shm(func->op_array.opcodes)) { /* op_array and op_array->opcodes are not persistent. We can't link. */ return 0; } - } else { - ZEND_UNREACHABLE(); - return 0; } exit_point = zend_jit_trace_get_exit_point(to_opline, ZEND_JIT_EXIT_POLYMORPHISM); @@ -9286,6 +9278,22 @@ static int zend_jit_init_fcall_guard(dasm_State **Dst, uint32_t level, const zen | cmp aword [r1 + offsetof(zend_op_array, opcodes)], opcodes | .endif | jne &exit_addr +#ifdef _WIN32 + } else if (func->type == ZEND_INTERNAL_FUNCTION) { + const zif_handler handler = func->internal_function.handler; + + | .if X64 + || if (!IS_SIGNED_32BIT(handler)) { + | mov64 r2, ((ptrdiff_t)handler) + | cmp aword [r1 + offsetof(zend_internal_function, handler)], r2 + || } else { + | cmp aword [r1 + offsetof(zend_internal_function, handler)], handler + || } + | .else + | cmp aword [r1 + offsetof(zend_internal_function, handler)], handler + | .endif + | jne &exit_addr +#endif } else { | .if X64 || if (!IS_SIGNED_32BIT(func)) { @@ -9432,6 +9440,22 @@ static int zend_jit_init_fcall(dasm_State **Dst, const zend_op *opline, uint32_t | cmp aword [r0 + offsetof(zend_op_array, opcodes)], opcodes | .endif | jz >3 +#ifdef _WIN32 + } else if (func->type == ZEND_INTERNAL_FUNCTION) { + const zif_handler handler = func->internal_function.handler; + + | .if X64 + || if (!IS_SIGNED_32BIT(handler)) { + | mov64 r1, ((ptrdiff_t)handler) + | cmp aword [r0 + offsetof(zend_internal_function, handler)], r1 + || } else { + | cmp aword [r0 + offsetof(zend_internal_function, handler)], handler + || } + | .else + | cmp aword [r0 + offsetof(zend_internal_function, handler)], handler + | .endif + | jz >3 +#endif } else { | .if X64 || if (!IS_SIGNED_32BIT(func)) { @@ -9618,11 +9642,7 @@ static int zend_jit_init_method_call(dasm_State **Dst, if ((!func || zend_jit_may_be_modified(func, op_array)) && trace && trace->op == ZEND_JIT_TRACE_INIT_CALL - && trace->func -#ifdef _WIN32 - && trace->func->type != ZEND_INTERNAL_FUNCTION -#endif - ) { + && trace->func) { int32_t exit_point; const void *exit_addr; @@ -9651,6 +9671,22 @@ static int zend_jit_init_method_call(dasm_State **Dst, | cmp aword [r0 + offsetof(zend_op_array, opcodes)], opcodes | .endif | jne &exit_addr +#ifdef _WIN32 + } else if (func->type == ZEND_INTERNAL_FUNCTION) { + const zif_handler handler = func->internal_function.handler; + + | .if X64 + || if (!IS_SIGNED_32BIT(handler)) { + | mov64 r1, ((ptrdiff_t)handler) + | cmp aword [r0 + offsetof(zend_internal_function, handler)], r1 + || } else { + | cmp aword [r0 + offsetof(zend_internal_function, handler)], handler + || } + | .else + | cmp aword [r0 + offsetof(zend_internal_function, handler)], handler + | .endif + | jne &exit_addr +#endif } else { | .if X64 || if (!IS_SIGNED_32BIT(func)) { diff --git a/ext/opcache/tests/jit/gh9011.phpt b/ext/opcache/tests/jit/gh9011.phpt new file mode 100644 index 0000000000000..3ffefe2ce29be --- /dev/null +++ b/ext/opcache/tests/jit/gh9011.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-9011: Assertion failure with tracing JIT +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +--FILE-- +__toString(); + } +} +?> +DONE +--EXPECT-- +DONE \ No newline at end of file From ddbd396aa284297d2348c4ff1b6a36d7b61a557f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 5 Dec 2024 21:13:55 +0100 Subject: [PATCH 06/65] Fix GH-17047: UAF on iconv filter failure The first while loop sets the bucket variable, and this is freed in out_failure. However, when the second "goto out_failure" is triggered then bucket still refers to the bucket from the first while loop, causing a UAF. Fix this by separating the error paths. Closes GH-17058. --- NEWS | 3 +++ ext/iconv/iconv.c | 11 +++-------- ext/iconv/tests/gh17047.phpt | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 ext/iconv/tests/gh17047.phpt diff --git a/NEWS b/NEWS index a139c14513a57..4a86a80264ce6 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.16 +- Iconv: + . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) + - Streams: . Fixed bug GH-17037 (UAF in user filter when adding existing filter name due to incorrect error handling). (nielsdos) diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c index 75f78dd174238..8ac3bc9b3c480 100644 --- a/ext/iconv/iconv.c +++ b/ext/iconv/iconv.c @@ -2536,7 +2536,8 @@ static php_stream_filter_status_t php_iconv_stream_filter_do_filter( if (php_iconv_stream_filter_append_bucket(self, stream, filter, buckets_out, bucket->buf, bucket->buflen, &consumed, php_stream_is_persistent(stream)) != SUCCESS) { - goto out_failure; + php_stream_bucket_delref(bucket); + return PSFS_ERR_FATAL; } php_stream_bucket_delref(bucket); @@ -2546,7 +2547,7 @@ static php_stream_filter_status_t php_iconv_stream_filter_do_filter( if (php_iconv_stream_filter_append_bucket(self, stream, filter, buckets_out, NULL, 0, &consumed, php_stream_is_persistent(stream)) != SUCCESS) { - goto out_failure; + return PSFS_ERR_FATAL; } } @@ -2555,12 +2556,6 @@ static php_stream_filter_status_t php_iconv_stream_filter_do_filter( } return PSFS_PASS_ON; - -out_failure: - if (bucket != NULL) { - php_stream_bucket_delref(bucket); - } - return PSFS_ERR_FATAL; } /* }}} */ diff --git a/ext/iconv/tests/gh17047.phpt b/ext/iconv/tests/gh17047.phpt new file mode 100644 index 0000000000000..a0307ddbe5553 --- /dev/null +++ b/ext/iconv/tests/gh17047.phpt @@ -0,0 +1,17 @@ +--TEST-- +GH-17047 (UAF on iconv filter failure) +--EXTENSIONS-- +iconv +--FILE-- + +--EXPECTF-- +Warning: stream_get_contents(): iconv stream filter ("UTF-16BE"=>"UTF-16BE"): invalid multibyte sequence in %s on line %d +string(0) "" From def271aaa7b06bc2efda7b61ff454e524144fa61 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 1 Dec 2024 10:36:38 -0500 Subject: [PATCH 07/65] ext/dba/tests/gh16390.phpt: skip if inifile is disabled This test reads an ini "file" from a string, and expects a warning about locking. But if inifile support is disabled, then you'll get Warning: dba_open(): Handler "inifile" is not available in /path/to/ext/dba/tests/gh16390.php on line 3 instead. We skip the test if inifile support is disabled. Closes GH-17011. --- NEWS | 3 +++ ext/dba/tests/gh16390.phpt | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/NEWS b/NEWS index 4a86a80264ce6..f167580c2f4e1 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.16 +- DBA: + . Skip test if inifile is disabled. (orlitzky) + - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) diff --git a/ext/dba/tests/gh16390.phpt b/ext/dba/tests/gh16390.phpt index 0b66e742a40ab..1c9d1f3a52e06 100644 --- a/ext/dba/tests/gh16390.phpt +++ b/ext/dba/tests/gh16390.phpt @@ -2,6 +2,11 @@ GH-16390 (dba_open() can segfault for "pathless" streams) --EXTENSIONS-- dba +--SKIPIF-- + --FILE-- Date: Wed, 4 Dec 2024 22:51:12 +0100 Subject: [PATCH 08/65] Fix GH-17040: SimpleXML's unset can break DOM objects Don't free the underlying nodes if we still have objects pointing to them, otherwise the objects are left with a NULL node pointer. Closes GH-17046. --- NEWS | 3 +++ ext/simplexml/simplexml.c | 25 +++++++++++++++---------- ext/simplexml/tests/gh17040.phpt | 24 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 ext/simplexml/tests/gh17040.phpt diff --git a/NEWS b/NEWS index f167580c2f4e1..547e362ca28b2 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,9 @@ PHP NEWS - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) +- SimpleXML: + . Fixed bug GH-17040 (SimpleXML's unset can break DOM objects). (nielsdos) + - Streams: . Fixed bug GH-17037 (UAF in user filter when adding existing filter name due to incorrect error handling). (nielsdos) diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 6a5a514f9a3d3..6f4a0d6316ec8 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -54,6 +54,16 @@ static void php_sxe_iterator_move_forward(zend_object_iterator *iter); static void php_sxe_iterator_rewind(zend_object_iterator *iter); static zend_result sxe_object_cast_ex(zend_object *readobj, zval *writeobj, int type); +static void sxe_unlink_node(xmlNodePtr node) +{ + xmlUnlinkNode(node); + /* Only destroy the nodes if we have no objects using them anymore. + * Don't assume simplexml owns these. */ + if (!node->_private) { + php_libxml_node_free_resource(node); + } +} + /* {{{ _node_as_zval() */ static void _node_as_zval(php_sxe_object *sxe, xmlNodePtr node, zval *value, SXE_ITER itertype, char *name, const xmlChar *nsprefix, int isprefix) { @@ -554,8 +564,7 @@ static zval *sxe_prop_dim_write(zend_object *object, zval *member, zval *value, } if (value_str) { while ((tempnode = (xmlNodePtr) newnode->children)) { - xmlUnlinkNode(tempnode); - php_libxml_node_free_resource((xmlNodePtr) tempnode); + sxe_unlink_node(tempnode); } change_node_zval(newnode, value_str); } @@ -829,8 +838,7 @@ static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements while (attr && nodendx <= Z_LVAL_P(member)) { if ((!test || xmlStrEqual(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) { if (nodendx == Z_LVAL_P(member)) { - xmlUnlinkNode((xmlNodePtr) attr); - php_libxml_node_free_resource((xmlNodePtr) attr); + sxe_unlink_node((xmlNodePtr) attr); break; } nodendx++; @@ -841,8 +849,7 @@ static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements while (attr) { anext = attr->next; if ((!test || xmlStrEqual(attr->name, sxe->iter.name)) && xmlStrEqual(attr->name, (xmlChar *)Z_STRVAL_P(member)) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) { - xmlUnlinkNode((xmlNodePtr) attr); - php_libxml_node_free_resource((xmlNodePtr) attr); + sxe_unlink_node((xmlNodePtr) attr); break; } attr = anext; @@ -857,8 +864,7 @@ static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements } node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, NULL); if (node) { - xmlUnlinkNode(node); - php_libxml_node_free_resource(node); + sxe_unlink_node(node); } } else { node = node->children; @@ -868,8 +874,7 @@ static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements SKIP_TEXT(node); if (xmlStrEqual(node->name, (xmlChar *)Z_STRVAL_P(member)) && match_ns(sxe, node, sxe->iter.nsprefix, sxe->iter.isprefix)) { - xmlUnlinkNode(node); - php_libxml_node_free_resource(node); + sxe_unlink_node(node); } next_iter: diff --git a/ext/simplexml/tests/gh17040.phpt b/ext/simplexml/tests/gh17040.phpt new file mode 100644 index 0000000000000..20dc9062d559b --- /dev/null +++ b/ext/simplexml/tests/gh17040.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-17040 (SimpleXML's unset can break DOM objects) +--EXTENSIONS-- +dom +simplexml +--FILE-- +appendChild($dom->createElement("style")); +$html = simplexml_import_dom($tag); +unset($html[0]); +$tag->append("foo"); +echo $dom->saveXML(), "\n"; +echo $dom->saveXML($tag), "\n"; +var_dump($html); +?> +--EXPECT-- + + + +object(SimpleXMLElement)#3 (1) { + [0]=> + string(3) "foo" +} From b3b38e2d5c383d091079a0dd6fcfb77dce33a7c5 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Fri, 29 Nov 2024 12:38:19 +0100 Subject: [PATCH 09/65] Skip parse_ini_file_variation6.phpt on Windows While the test obviously succeeds on Windows, it may occasionally conflict with parse_ini_file_variation6-win32.phpt[1], so we skip it like we do for many other of these tests which have win32 pendants. [1] Closes GH-16989. --- ext/standard/tests/file/parse_ini_file_variation6.phpt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/standard/tests/file/parse_ini_file_variation6.phpt b/ext/standard/tests/file/parse_ini_file_variation6.phpt index 70e1a73f988e7..4c84672821d8f 100644 --- a/ext/standard/tests/file/parse_ini_file_variation6.phpt +++ b/ext/standard/tests/file/parse_ini_file_variation6.phpt @@ -2,6 +2,10 @@ Test parse_ini_file() function : variation - various absolute and relative paths --CREDITS-- Dave Kelsey +--SKIPIF-- + --FILE-- Date: Sun, 1 Dec 2024 15:58:32 +0100 Subject: [PATCH 10/65] Properly check for required icu4c libraries Besides that just checking for icuuc.lib does not necessarily imply that the other libraries are available, doing it this way will not copy the PDBs to the build folder, so these are not available in the debug packages. Furthermore, `CHECK_LIB` already adds the library to the flags, so there is no need to do this manually. Closes GH-17010. --- ext/intl/config.w32 | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/ext/intl/config.w32 b/ext/intl/config.w32 index ab7dc42b66246..567d29dc5a786 100644 --- a/ext/intl/config.w32 +++ b/ext/intl/config.w32 @@ -3,7 +3,10 @@ ARG_ENABLE("intl", "Enable internationalization support", "no"); if (PHP_INTL != "no") { - if (CHECK_LIB("icuuc.lib", "intl", PHP_INTL) && + if (CHECK_LIB("icudt.lib", "intl", PHP_INTL) && + CHECK_LIB("icuin.lib", "intl", PHP_INTL) && + CHECK_LIB("icuio.lib", "intl", PHP_INTL) && + CHECK_LIB("icuuc.lib", "intl", PHP_INTL) && CHECK_HEADER_ADD_INCLUDE("unicode/utf.h", "CFLAGS_INTL")) { // always build as shared - zend_strtod.c/ICU type conflict EXTENSION("intl", "php_intl.c intl_convert.c intl_convertcpp.cpp intl_error.c ", true, @@ -115,16 +118,9 @@ if (PHP_INTL != "no") { codepointiterator_methods.cpp ", "intl"); - ADD_FLAG("LIBS_INTL", "icudt.lib icuin.lib icuio.lib"); - /* Compat for ICU before 58.1.*/ - if (CHECK_LIB("icule.lib", "intl", PHP_INTL)) { - ADD_FLAG("LIBS_INTL", "icule.lib"); - } - if (CHECK_LIB("iculx.lib", "intl", PHP_INTL)) { - ADD_FLAG("LIBS_INTL", "iculx.lib"); - } - + CHECK_LIB("icule.lib", "intl", PHP_INTL); + CHECK_LIB("iculx.lib", "intl", PHP_INTL); ADD_FLAG("CFLAGS_INTL", "/EHsc /DUNISTR_FROM_CHAR_EXPLICIT=explicit /DUNISTR_FROM_STRING_EXPLICIT=explicit /DU_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 /DU_HIDE_OBSOLETE_UTF_OLD_H=1"); AC_DEFINE("HAVE_INTL", 1, "Internationalization support enabled"); } else { From 5cbdd5f6dea8ae27c6907f16aff7929c5946c7de Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 4 Dec 2024 15:04:48 +0100 Subject: [PATCH 11/65] Harden proc_open() against cmd.exe hijacking As is, whenever `proc_open()` needs to invoke the shell, cmd.exe is looked up in the usual executable search path. That implies that any cmd.exe which is placed in the current working directory (which is not necessarily what is reported by `getcwd()` for ZTS builds), will be used. This is a known attack vector, and Microsoft recommends to always use the fully qualified path to cmd.exe. To prevent any cmd.exe in the current working directory to be used, but to still allow users to use a drop in replacement for cmd.exe, we search only the `PATH` for cmd.exe (and pass the fully qualified path to `CreateProcessW`), instead of relying on automatic executable search by passing the base name only. To be able to easily test this, we provide a minimalist C file which will be build as test_helper, and used by the new test case. [1] Closes GH-17043. --- NEWS | 3 + ext/standard/Makefile.frag.w32 | 4 ++ ext/standard/proc_open.c | 59 ++++++++++++++++++- .../general_functions/proc_open_cmd.phpt | 28 +++++++++ ext/standard/tests/helpers/bad_cmd.c | 7 +++ win32/build/Makefile | 3 +- 6 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 ext/standard/tests/general_functions/proc_open_cmd.phpt create mode 100644 ext/standard/tests/helpers/bad_cmd.c diff --git a/NEWS b/NEWS index 547e362ca28b2..9d72a87d0a941 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,9 @@ PHP NEWS . Fixed bug GH-17037 (UAF in user filter when adding existing filter name due to incorrect error handling). (nielsdos) +- Windows: + . Hardened proc_open() against cmd.exe hijacking. (cmb) + 19 Dec 2024, PHP 8.3.15 - Calendar: diff --git a/ext/standard/Makefile.frag.w32 b/ext/standard/Makefile.frag.w32 index 7815c7c97d035..f4655cfa8bb27 100644 --- a/ext/standard/Makefile.frag.w32 +++ b/ext/standard/Makefile.frag.w32 @@ -7,3 +7,7 @@ ext\standard\url_scanner_ex.c: ext\standard\url_scanner_ex.re $(RE2C) $(RE2C_FLAGS) --no-generation-date -b -o ext/standard/url_scanner_ex.c ext/standard/url_scanner_ex.re $(BUILD_DIR)\ext\standard\basic_functions.obj: $(PHP_SRC_DIR)\Zend\zend_language_parser.h + +$(PHP_SRC_DIR)\ext\standard\tests\helpers\bad_cmd.exe: $(PHP_SRC_DIR)\ext\standard\tests\helpers\bad_cmd.c + cd $(PHP_SRC_DIR)\ext\standard\tests\helpers + $(PHP_CL) /nologo bad_cmd.c diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index 2d4cb42b7a661..ce77109827740 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -701,22 +701,77 @@ static void init_process_info(PROCESS_INFORMATION *pi) memset(&pi, 0, sizeof(pi)); } +/* on success, returns length of *comspec, which then needs to be efree'd by caller */ +static size_t find_comspec_nt(wchar_t **comspec) +{ + zend_string *path = NULL; + wchar_t *pathw = NULL; + wchar_t *bufp = NULL; + DWORD buflen = MAX_PATH, len = 0; + + path = php_getenv("PATH", 4); + if (path == NULL) { + goto out; + } + pathw = php_win32_cp_any_to_w(ZSTR_VAL(path)); + if (pathw == NULL) { + goto out; + } + bufp = emalloc(buflen * sizeof(wchar_t)); + do { + /* the first call to SearchPathW() fails if the buffer is too small, + * what is unlikely but possible; to avoid an explicit second call to + * SeachPathW() and the error handling, we're looping */ + len = SearchPathW(pathw, L"cmd.exe", NULL, buflen, bufp, NULL); + if (len == 0) { + goto out; + } + if (len < buflen) { + break; + } + buflen = len; + bufp = erealloc(bufp, buflen * sizeof(wchar_t)); + } while (1); + *comspec = bufp; + +out: + if (path != NULL) { + zend_string_release(path); + } + if (pathw != NULL) { + free(pathw); + } + if (bufp != NULL && bufp != *comspec) { + efree(bufp); + } + return len; +} + static zend_result convert_command_to_use_shell(wchar_t **cmdw, size_t cmdw_len) { - size_t len = sizeof(COMSPEC_NT) + sizeof(" /s /c ") + cmdw_len + 3; + wchar_t *comspec; + size_t len = find_comspec_nt(&comspec); + if (len == 0) { + php_error_docref(NULL, E_WARNING, "Command conversion failed"); + return FAILURE; + } + len += sizeof(" /s /c ") + cmdw_len + 3; wchar_t *cmdw_shell = (wchar_t *)malloc(len * sizeof(wchar_t)); if (cmdw_shell == NULL) { + efree(comspec); php_error_docref(NULL, E_WARNING, "Command conversion failed"); return FAILURE; } - if (_snwprintf(cmdw_shell, len, L"%hs /s /c \"%s\"", COMSPEC_NT, *cmdw) == -1) { + if (_snwprintf(cmdw_shell, len, L"%s /s /c \"%s\"", comspec, *cmdw) == -1) { + efree(comspec); free(cmdw_shell); php_error_docref(NULL, E_WARNING, "Command conversion failed"); return FAILURE; } + efree(comspec); free(*cmdw); *cmdw = cmdw_shell; diff --git a/ext/standard/tests/general_functions/proc_open_cmd.phpt b/ext/standard/tests/general_functions/proc_open_cmd.phpt new file mode 100644 index 0000000000000..6c80871d47df8 --- /dev/null +++ b/ext/standard/tests/general_functions/proc_open_cmd.phpt @@ -0,0 +1,28 @@ +--TEST-- +Harden against cmd.exe hijacking +--SKIPIF-- + +--FILE-- + 0) { + foreach ($read as $stream) { + fpassthru($stream); + } +} +?> +--EXPECTF-- +resource(%d) of type (process) +hello +--CLEAN-- + diff --git a/ext/standard/tests/helpers/bad_cmd.c b/ext/standard/tests/helpers/bad_cmd.c new file mode 100644 index 0000000000000..05de19f5686cf --- /dev/null +++ b/ext/standard/tests/helpers/bad_cmd.c @@ -0,0 +1,7 @@ +#include + +int main() +{ + printf("pwnd!\n"); + return 0; +} diff --git a/win32/build/Makefile b/win32/build/Makefile index 9d4c1c0800dc7..1b200502119a7 100644 --- a/win32/build/Makefile +++ b/win32/build/Makefile @@ -54,7 +54,7 @@ DEBUGGER_CMD= DEBUGGER_ARGS= !endif -all: generated_files $(EXT_TARGETS) $(PECL_TARGETS) $(SAPI_TARGETS) +all: generated_files $(EXT_TARGETS) $(PECL_TARGETS) $(SAPI_TARGETS) test_helpers build_dirs: $(BUILD_DIR) $(BUILD_DIRS_SUB) $(BUILD_DIR_DEV) @@ -185,6 +185,7 @@ clean-pgo: clean-all -del /f /q $(BUILD_DIR)\$(DIST_ZIP_PECL) -del /f /q $(BUILD_DIR)\$(DIST_ZIP_TEST_PACK) +test_helpers: $(PHP_SRC_DIR)\ext\standard\tests\helpers\bad_cmd.exe !if $(PHP_TEST_INI_PATH) == "" test: set-tmp-env From 301b8e24c1d82f22d317c73b7ec0dfee6852e511 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 15 Nov 2024 06:13:24 +0000 Subject: [PATCH 12/65] Fix GH-16809: fopen HTTP wrapper timeout stream context option overflow. close GH-16810 --- NEWS | 2 ++ ext/standard/http_fopen_wrapper.c | 12 ++++++++++++ ext/standard/tests/http/gh16810.phpt | 26 ++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 ext/standard/tests/http/gh16810.phpt diff --git a/NEWS b/NEWS index 9d72a87d0a941..32d837a47b675 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,8 @@ PHP NEWS - Streams: . Fixed bug GH-17037 (UAF in user filter when adding existing filter name due to incorrect error handling). (nielsdos) + . Fixed bug GH-16810 (overflow on fopen HTTP wrapper timeout value). + (David Carlier) - Windows: . Hardened proc_open() against cmd.exe hijacking. (cmb) diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index bbc8acc73b9a6..fb987c82e5cc2 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -216,6 +216,18 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (context && (tmpzval = php_stream_context_get_option(context, wrapper->wops->label, "timeout")) != NULL) { double d = zval_get_double(tmpzval); +#ifndef PHP_WIN32 + const double timeoutmax = (double) PHP_TIMEOUT_ULL_MAX / 1000000.0; +#else + const double timeoutmax = (double) LONG_MAX / 1000000.0; +#endif + + if (d > timeoutmax) { + php_stream_wrapper_log_error(wrapper, options, "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax); + zend_string_release(transport_string); + php_url_free(resource); + return NULL; + } #ifndef PHP_WIN32 timeout.tv_sec = (time_t) d; timeout.tv_usec = (size_t) ((d - timeout.tv_sec) * 1000000); diff --git a/ext/standard/tests/http/gh16810.phpt b/ext/standard/tests/http/gh16810.phpt new file mode 100644 index 0000000000000..4aa563b57b270 --- /dev/null +++ b/ext/standard/tests/http/gh16810.phpt @@ -0,0 +1,26 @@ +--TEST-- +Bug #79265 variation: "host:" not at start of header +--INI-- +allow_url_fopen=1 +--SKIPIF-- + +--FILE-- + [ +'timeout' => PHP_INT_MIN, +], +]; +$ctx = stream_context_create($config); +var_dump(fopen($uri, "r", false, $ctx)); + +$config['http']['timeout'] = PHP_INT_MAX; +$ctx = stream_context_create($config); +var_dump(fopen($uri, "r", false, $ctx)); +?> +--EXPECTF-- +resource(%d) of type (stream) + +Warning: fopen(http://www.example.com): Failed to open stream: timeout must be lower than %d in %s on line %d +bool(false) From 3702f9783b6367b7c94e2c11ede0896352da5cfe Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sat, 7 Dec 2024 12:39:07 +0100 Subject: [PATCH 13/65] opcache_get_configuration() properly reports jit_prof_threshold The `jit_prof_threshold` is a float, supposed to be in range [0, 1], and usually very small (the default is 0.005). Reporting it as int is meaningless. Closes GH-17077. --- NEWS | 3 +++ .../tests/opcache_jit_prof_threshold.phpt | 19 +++++++++++++++++++ ext/opcache/zend_accelerator_module.c | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 ext/opcache/tests/opcache_jit_prof_threshold.phpt diff --git a/NEWS b/NEWS index 32d837a47b675..09abb4f1ab1d5 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,9 @@ PHP NEWS - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) +- Opcache: + . opcache_get_configuration() properly reports jit_prof_threshold. (cmb) + - SimpleXML: . Fixed bug GH-17040 (SimpleXML's unset can break DOM objects). (nielsdos) diff --git a/ext/opcache/tests/opcache_jit_prof_threshold.phpt b/ext/opcache/tests/opcache_jit_prof_threshold.phpt new file mode 100644 index 0000000000000..f98c12b99ba91 --- /dev/null +++ b/ext/opcache/tests/opcache_jit_prof_threshold.phpt @@ -0,0 +1,19 @@ +--TEST-- +opcache_get_configuration() properly reports jit_prof_threshold +--EXTENSIONS-- +opcache +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +float(0.0078125) +bool(true) diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 5c69c9f7883a2..90936929f8447 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -849,7 +849,7 @@ ZEND_FUNCTION(opcache_get_configuration) add_assoc_long(&directives, "opcache.jit_max_recursive_returns", JIT_G(max_recursive_returns)); add_assoc_long(&directives, "opcache.jit_max_root_traces", JIT_G(max_root_traces)); add_assoc_long(&directives, "opcache.jit_max_side_traces", JIT_G(max_side_traces)); - add_assoc_long(&directives, "opcache.jit_prof_threshold", JIT_G(prof_threshold)); + add_assoc_double(&directives, "opcache.jit_prof_threshold", JIT_G(prof_threshold)); add_assoc_long(&directives, "opcache.jit_max_trace_length", JIT_G(max_trace_length)); #endif From 3bea6a2ddbe02cd9da10f66091f9996ae43de64e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 28 Nov 2024 13:00:42 +0000 Subject: [PATCH 14/65] ext/sockets: socket_strerror follow-up on GH-16267 fix. boundaries should be INT_MIN <= val < INT_MAX in fact. close GH-16891 --- NEWS | 4 ++++ ext/sockets/sockets.c | 6 +++++- ext/sockets/tests/gh16267.phpt | 14 +++++--------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 09abb4f1ab1d5..2dee36b440be4 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,10 @@ PHP NEWS - SimpleXML: . Fixed bug GH-17040 (SimpleXML's unset can break DOM objects). (nielsdos) +- Sockets: + . Fixed bug GH-16276 (socket_strerror overflow handling with INT_MIN). + (David Carlier / cmb) + - Streams: . Fixed bug GH-17037 (UAF in user filter when adding existing filter name due to incorrect error handling). (nielsdos) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 2f731b3b05acc..e1b350b4045a4 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -354,7 +354,11 @@ char *sockets_strerror(int error) /* {{{ */ #ifndef PHP_WIN32 if (error < -10000) { - error = -error - 10000; + if (error == INT_MIN) { + error = 2147473648; + } else { + error = -error - 10000; + } #ifdef HAVE_HSTRERROR buf = hstrerror(error); diff --git a/ext/sockets/tests/gh16267.phpt b/ext/sockets/tests/gh16267.phpt index d2462b3164530..de3e1b657fbc5 100644 --- a/ext/sockets/tests/gh16267.phpt +++ b/ext/sockets/tests/gh16267.phpt @@ -3,20 +3,16 @@ GH-16267 - overflow on socket_strerror argument --EXTENSIONS-- sockets --SKIPIF-- - + --FILE-- getMessage() . PHP_EOL; -} -try { - socket_strerror(PHP_INT_MAX); + socket_strerror(2147483648); } catch (\ValueError $e) { echo $e->getMessage() . PHP_EOL; } ?> --EXPECTF-- -socket_strerror(): Argument #1 ($error_code) must be between %s and %s -socket_strerror(): Argument #1 ($error_code) must be between %s and %s +string(%d) "%S" +socket_strerror(): Argument #1 ($error_code) must be between %i and %d From e675c1a467e05c44682106dcfd8f58bd7a373aee Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 9 Dec 2024 12:33:44 +0100 Subject: [PATCH 15/65] Drop intl on macOS + PHP 8.1 build Based on the discussion in GH-16286, drop the intl build from macOS + PHP 8.1, since we cannot build with supported intl versions without too many changes. Closes GH-17092 See GH-16286 --- .github/actions/brew/action.yml | 3 +-- .github/actions/configure-macos/action.yml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/actions/brew/action.yml b/.github/actions/brew/action.yml index 3b36ec446ddca..5cd8d33cfa6b7 100644 --- a/.github/actions/brew/action.yml +++ b/.github/actions/brew/action.yml @@ -32,5 +32,4 @@ runs: libjpeg \ libxslt \ postgresql - brew reinstall icu4c@74 - brew link icu4c gettext --force + brew link gettext --force diff --git a/.github/actions/configure-macos/action.yml b/.github/actions/configure-macos/action.yml index b21f6466c444c..87627d4f84848 100644 --- a/.github/actions/configure-macos/action.yml +++ b/.github/actions/configure-macos/action.yml @@ -66,7 +66,6 @@ runs: --with-ffi \ --enable-zend-test \ --enable-dl-test=shared \ - --enable-intl \ --with-mhash \ --with-sodium \ --enable-dba \ From b0b39cdc3e5cf791ccd7f358b32ff1340a652d78 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 19 Nov 2024 18:03:54 +0300 Subject: [PATCH 16/65] Backport JIT fix: set valid EX(opline) before calling gc_possible_root() (#16858) This will finally make the COMMUNTIY build of the PHP 8.1 build green. See https://github.com/php/php-src/pull/16858#issuecomment-2509010556 Closes GH-17091 --- ext/opcache/jit/zend_jit_arm64.dasc | 12 ++++++++++++ ext/opcache/jit/zend_jit_x86.dasc | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc index 1583706855d41..b9a4fbc82e124 100644 --- a/ext/opcache/jit/zend_jit_arm64.dasc +++ b/ext/opcache/jit/zend_jit_arm64.dasc @@ -1533,6 +1533,9 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size) || } | IF_GC_MAY_NOT_LEAK FCARG1x, >4, Rw(tmp_reg1), Rw(tmp_reg2) | // gc_possible_root(Z_COUNTED_P(z)) +|| if (opline) { +| SET_EX_OPLINE opline, TMP1 +|| } | EXT_CALL gc_possible_root, Rx(tmp_reg1) || } || if (cold && ((op_info) & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_INDIRECT|MAY_BE_GUARD)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) != 0) { @@ -5972,6 +5975,9 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) { |4: | IF_GC_MAY_NOT_LEAK FCARG1x, >8, TMP1w, TMP2w + if (opline) { + | SET_EX_OPLINE opline, REG0 + } | EXT_CALL gc_possible_root, REG0 if (in_cold) { | b >8 @@ -5999,6 +6005,9 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, | GET_ZVAL_PTR FCARG1x, var_use_addr, TMP1 | GC_DELREF FCARG1x, TMP1w | IF_GC_MAY_NOT_LEAK FCARG1x, >5, TMP1w, TMP2w + if (opline) { + | SET_EX_OPLINE opline, TMP1 + } | EXT_CALL gc_possible_root, TMP1 if (Z_REG(var_use_addr) != ZREG_FP) { | ldr Rx(Z_REG(var_use_addr)), T1 // restore @@ -11980,6 +11989,9 @@ static int zend_jit_bind_global(dasm_State **Dst, const zend_op *opline, uint32_ |3: | // GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr) | IF_GC_MAY_NOT_LEAK FCARG1x, >5, TMP1w, TMP2w + if (opline) { + | SET_EX_OPLINE opline, REG0 + } | EXT_CALL gc_possible_root, REG0 | b >5 } diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index b3989a4ae254d..a1d0a8e098a2a 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -1443,6 +1443,9 @@ static size_t tsrm_tls_offset; |1: || } | IF_GC_MAY_NOT_LEAK FCARG1a, >4 +|| if (opline) { +| SET_EX_OPLINE opline, r0 +|| } | // gc_possible_root(Z_COUNTED_P(z)) | EXT_CALL gc_possible_root, r0 || } @@ -6526,6 +6529,9 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) { |4: | IF_GC_MAY_NOT_LEAK FCARG1a, >8 + if (opline) { + | SET_EX_OPLINE opline, r0 + } | EXT_CALL gc_possible_root, r0 if (in_cold) { | jmp >8 @@ -6553,6 +6559,9 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, | GET_ZVAL_PTR FCARG1a, var_use_addr | GC_DELREF FCARG1a | IF_GC_MAY_NOT_LEAK FCARG1a, >5 + if (opline) { + | SET_EX_OPLINE opline, r0 + } | EXT_CALL gc_possible_root, r0 if (Z_REG(var_use_addr) != ZREG_FP) { | mov Ra(Z_REG(var_use_addr)), T1 // restore @@ -12742,6 +12751,9 @@ static int zend_jit_bind_global(dasm_State **Dst, const zend_op *opline, uint32_ |3: | // GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr) | IF_GC_MAY_NOT_LEAK FCARG1a, >5 + if (opline) { + | SET_EX_OPLINE opline, r0 + } | EXT_CALL gc_possible_root, r1 | jmp >5 } From 806d2e073c1fe67dfe3c5791f4483f44dd991b28 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 10 Oct 2024 22:39:19 +0200 Subject: [PATCH 17/65] Backport GH-16348 Agreed by RM: https://github.com/php/php-src/issues/16168#issuecomment-2525433557 The inline assembly uses labels with the prefix `.L`. On Linux systems this is the local label prefix. It appears that macOS uses `L` as a local prefix, which means that the prefix used in the inline assembly is not local for macOS systems [1]. When combined with inlining, this causes the compiler to get confused and merge a part of the inline assembly between different functions, causing control flow to jump from one function to another function. This is avoided on PHP 8.2 and up by the fact that it uses `zend_never_inline NOIPA`, but nothing guarantees that compiler changes won't affect this as well. To solve this issue, we instead use local labels. These will make the compiler pick the correct prefix, preventing the issue. Additionally, while here, we also change the computation of `delta`. It is undefined behaviour to compute the pointer difference between two different objects. To circumvent this, we cast first to `uintptr_t`. This change is cleanly backportable to 8.1 for vendors to pick up. [1] https://github.com/php/php-src/issues/16168#issuecomment-2404792553 With the help of investigation and testing of @ryandesign. Closes GH-16348. --- Zend/zend_string.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Zend/zend_string.c b/Zend/zend_string.c index 1da3ce5248522..c21b4f5d4610d 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -397,33 +397,33 @@ ZEND_API bool ZEND_FASTCALL NO_CALLER_SAVED_REGISTERS I_REPLACE_SONAME_FNNAME_ZU #if defined(__GNUC__) && defined(__i386__) ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2) { - char *ptr = ZSTR_VAL(s1); - size_t delta = (char*)s2 - (char*)s1; + const char *ptr = ZSTR_VAL(s1); + uintptr_t delta = (uintptr_t) s2 - (uintptr_t) s1; size_t len = ZSTR_LEN(s1); zend_ulong ret; __asm__ ( - ".LL0%=:\n\t" + "0:\n\t" "movl (%2,%3), %0\n\t" "xorl (%2), %0\n\t" - "jne .LL1%=\n\t" + "jne 1f\n\t" "addl $0x4, %2\n\t" "subl $0x4, %1\n\t" - "ja .LL0%=\n\t" + "ja 0b\n\t" "movl $0x1, %0\n\t" - "jmp .LL3%=\n\t" - ".LL1%=:\n\t" + "jmp 3f\n\t" + "1:\n\t" "cmpl $0x4,%1\n\t" - "jb .LL2%=\n\t" + "jb 2f\n\t" "xorl %0, %0\n\t" - "jmp .LL3%=\n\t" - ".LL2%=:\n\t" + "jmp 3f\n\t" + "2:\n\t" "negl %1\n\t" "lea 0x20(,%1,8), %1\n\t" "shll %b1, %0\n\t" "sete %b0\n\t" "movzbl %b0, %0\n\t" - ".LL3%=:\n" + "3:\n" : "=&a"(ret), "+c"(len), "+r"(ptr) @@ -435,33 +435,33 @@ ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string * #elif defined(__GNUC__) && defined(__x86_64__) && !defined(__ILP32__) ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2) { - char *ptr = ZSTR_VAL(s1); - size_t delta = (char*)s2 - (char*)s1; + const char *ptr = ZSTR_VAL(s1); + uintptr_t delta = (uintptr_t) s2 - (uintptr_t) s1; size_t len = ZSTR_LEN(s1); zend_ulong ret; __asm__ ( - ".LL0%=:\n\t" + "0:\n\t" "movq (%2,%3), %0\n\t" "xorq (%2), %0\n\t" - "jne .LL1%=\n\t" + "jne 1f\n\t" "addq $0x8, %2\n\t" "subq $0x8, %1\n\t" - "ja .LL0%=\n\t" + "ja 0b\n\t" "movq $0x1, %0\n\t" - "jmp .LL3%=\n\t" - ".LL1%=:\n\t" + "jmp 3f\n\t" + "1:\n\t" "cmpq $0x8,%1\n\t" - "jb .LL2%=\n\t" + "jb 2f\n\t" "xorq %0, %0\n\t" - "jmp .LL3%=\n\t" - ".LL2%=:\n\t" + "jmp 3f\n\t" + "2:\n\t" "negq %1\n\t" "lea 0x40(,%1,8), %1\n\t" "shlq %b1, %0\n\t" "sete %b0\n\t" "movzbq %b0, %0\n\t" - ".LL3%=:\n" + "3:\n" : "=&a"(ret), "+c"(len), "+r"(ptr) From 612a34cbec79e7549c725b682fe467fbad3b729a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 7 Dec 2024 23:32:49 +0100 Subject: [PATCH 18/65] Fix bug #79075: FFI header parser chokes on comments The directives for FFI should be first in the file, which is fine, however sometimes there can be comments or whitespace before or between these defines. One practical example is for license information or when a user adds newlines "by accident". In these cases, it's quite confusing that the directives do not work properly. To solve this, make the zend_ffi_parse_directives() aware of comments. Closes GH-17082. --- NEWS | 3 + ext/ffi/ffi.c | 127 +++++++++++++++++------------------- ext/ffi/tests/bug79075.h | 12 ++++ ext/ffi/tests/bug79075.inc | 3 + ext/ffi/tests/bug79075.phpt | 25 +++++++ 5 files changed, 104 insertions(+), 66 deletions(-) create mode 100644 ext/ffi/tests/bug79075.h create mode 100644 ext/ffi/tests/bug79075.inc create mode 100644 ext/ffi/tests/bug79075.phpt diff --git a/NEWS b/NEWS index 2dee36b440be4..209c5dfc1eff5 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,9 @@ PHP NEWS - DBA: . Skip test if inifile is disabled. (orlitzky) +- FFI: + . Fixed bug #79075 (FFI header parser chokes on comments). (nielsdos) + - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 06a79d250a633..2000e15c29ea1 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -4958,85 +4958,85 @@ ZEND_METHOD(FFI_CType, getFuncParameterType) /* {{{ */ } /* }}} */ +static char *zend_ffi_skip_ws_and_comments(char *p, bool allow_standalone_newline) +{ + while (true) { + if (*p == ' ' || *p == '\t') { + p++; + } else if (allow_standalone_newline && (*p == '\r' || *p == '\n' || *p == '\f' || *p == '\v')) { + p++; + } else if (allow_standalone_newline && *p == '/' && p[1] == '/') { + p += 2; + while (*p && *p != '\r' && *p != '\n') { + p++; + } + } else if (*p == '/' && p[1] == '*') { + p += 2; + while (*p && (*p != '*' || p[1] != '/')) { + p++; + } + if (*p == '*') { + p++; + if (*p == '/') { + p++; + } + } + } else { + break; + } + } + + return p; +} + static char *zend_ffi_parse_directives(const char *filename, char *code_pos, char **scope_name, char **lib, bool preload) /* {{{ */ { char *p; + code_pos = zend_ffi_skip_ws_and_comments(code_pos, true); + *scope_name = NULL; *lib = NULL; while (*code_pos == '#') { - if (strncmp(code_pos, "#define FFI_SCOPE", sizeof("#define FFI_SCOPE") - 1) == 0 - && (code_pos[sizeof("#define FFI_SCOPE") - 1] == ' ' - || code_pos[sizeof("#define FFI_SCOPE") - 1] == '\t')) { - p = code_pos + sizeof("#define FFI_SCOPE"); - while (*p == ' ' || *p == '\t') { - p++; - } - if (*p != '"') { - if (preload) { - zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename); - } else { - zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename); - } - return NULL; - } - p++; - if (*scope_name) { - if (preload) { - zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_SCOPE defined twice", filename); - } else { - zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_SCOPE defined twice", filename); - } - return NULL; - } - *scope_name = p; - while (1) { - if (*p == '\"') { - *p = 0; + if (strncmp(code_pos, ZEND_STRL("#define")) == 0) { + p = zend_ffi_skip_ws_and_comments(code_pos + sizeof("#define") - 1, false); + + char **target = NULL; + const char *target_name = NULL; + if (strncmp(p, ZEND_STRL("FFI_SCOPE")) == 0) { + p = zend_ffi_skip_ws_and_comments(p + sizeof("FFI_SCOPE") - 1, false); + target = scope_name; + target_name = "FFI_SCOPE"; + } else if (strncmp(p, ZEND_STRL("FFI_LIB")) == 0) { + p = zend_ffi_skip_ws_and_comments(p + sizeof("FFI_LIB") - 1, false); + target = lib; + target_name = "FFI_LIB"; + } else { + while (*p && *p != '\n' && *p != '\r') { p++; - break; - } else if (*p <= ' ') { - if (preload) { - zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename); - } else { - zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename); - } - return NULL; } - p++; - } - while (*p == ' ' || *p == '\t') { - p++; - } - while (*p == '\r' || *p == '\n') { - p++; - } - code_pos = p; - } else if (strncmp(code_pos, "#define FFI_LIB", sizeof("#define FFI_LIB") - 1) == 0 - && (code_pos[sizeof("#define FFI_LIB") - 1] == ' ' - || code_pos[sizeof("#define FFI_LIB") - 1] == '\t')) { - p = code_pos + sizeof("#define FFI_LIB"); - while (*p == ' ' || *p == '\t') { - p++; + code_pos = zend_ffi_skip_ws_and_comments(p, true); + continue; } + if (*p != '"') { if (preload) { - zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_LIB define", filename); + zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad %s define", filename, target_name); } else { - zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_LIB define", filename); + zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad %s define", filename, target_name); } return NULL; } p++; - if (*lib) { + if (*target) { if (preload) { - zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_LIB defined twice", filename); + zend_error(E_WARNING, "FFI: failed pre-loading '%s', %s defined twice", filename, target_name); } else { - zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_LIB defined twice", filename); + zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', %s defined twice", filename, target_name); } return NULL; } - *lib = p; + *target = p; while (1) { if (*p == '\"') { *p = 0; @@ -5044,21 +5044,16 @@ static char *zend_ffi_parse_directives(const char *filename, char *code_pos, cha break; } else if (*p <= ' ') { if (preload) { - zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_LIB define", filename); + zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad %s define", filename, target_name); } else { - zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_LIB define", filename); + zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad %s define", filename, target_name); } return NULL; } p++; } - while (*p == ' ' || *p == '\t') { - p++; - } - while (*p == '\r' || *p == '\n') { - p++; - } - code_pos = p; + + code_pos = zend_ffi_skip_ws_and_comments(p, true); } else { break; } diff --git a/ext/ffi/tests/bug79075.h b/ext/ffi/tests/bug79075.h new file mode 100644 index 0000000000000..22fa6067a347a --- /dev/null +++ b/ext/ffi/tests/bug79075.h @@ -0,0 +1,12 @@ +/* + * Multiline comment + */ + // whitespace line + +#define ignore_this_line 1 + // +#define/* inline */FFI_SCOPE /* multi- +line */ "bug79075" /* end +*/ + +int printf(const char *format, ...); diff --git a/ext/ffi/tests/bug79075.inc b/ext/ffi/tests/bug79075.inc new file mode 100644 index 0000000000000..ab3daa93d4de3 --- /dev/null +++ b/ext/ffi/tests/bug79075.inc @@ -0,0 +1,3 @@ + +--INI-- +ffi.enable=1 +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/bug79075.inc +opcache.file_cache_only=0 +--FILE-- +printf("Hello World from %s!\n", "PHP"); +?> +--EXPECT-- +Hello World from PHP! From 39c292b1eb9543b26be7484c6e94bdb9afc333d5 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 9 Dec 2024 20:33:05 +0100 Subject: [PATCH 19/65] Use empheral port for mysqli fake server tests --- ext/mysqli/tests/fake_server.inc | 34 ++++++++++++++----- .../ghsa-h35g-vwh6-m678-auth-message.phpt | 9 +++-- ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt | 8 ++--- .../tests/ghsa-h35g-vwh6-m678-filename.phpt | 7 ++-- ...hsa-h35g-vwh6-m678-query-len-overflow.phpt | 7 ++-- .../ghsa-h35g-vwh6-m678-stmt-row-bit.phpt | 7 ++-- .../ghsa-h35g-vwh6-m678-stmt-row-date.phpt | 7 ++-- ...ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt | 7 ++-- .../ghsa-h35g-vwh6-m678-stmt-row-double.phpt | 7 ++-- .../ghsa-h35g-vwh6-m678-stmt-row-float.phpt | 7 ++-- .../ghsa-h35g-vwh6-m678-stmt-row-int.phpt | 7 ++-- ...ghsa-h35g-vwh6-m678-stmt-row-no-space.phpt | 7 ++-- .../ghsa-h35g-vwh6-m678-stmt-row-string.phpt | 7 ++-- .../ghsa-h35g-vwh6-m678-stmt-row-time.phpt | 7 ++-- .../tests/protocol_query_row_fetch_data.phpt | 9 +++-- .../tests/protocol_stmt_row_fetch_data.phpt | 7 ++-- 16 files changed, 72 insertions(+), 72 deletions(-) diff --git a/ext/mysqli/tests/fake_server.inc b/ext/mysqli/tests/fake_server.inc index 1127f6c00e3f9..d6afbb0497734 100644 --- a/ext/mysqli/tests/fake_server.inc +++ b/ext/mysqli/tests/fake_server.inc @@ -615,9 +615,11 @@ class my_mysqli_fake_server_conn class my_mysqli_fake_server_process { + private int $port; + public function __construct(private $process, private array $pipes) {} - public function terminate(bool $wait = false) + public function terminate(bool $wait = false): void { if ($wait) { $this->wait(); @@ -625,9 +627,18 @@ class my_mysqli_fake_server_process proc_terminate($this->process); } - public function wait() + public function wait(): void { - echo fgets($this->pipes[1]); + $line = fgets($this->pipes[1]); + if (preg_match('/\[\*\] Server started on \d+\.\d+\.\d+\.\d+:(\d+)/', $line, $matches)) { + $this->port = (int)$matches[1]; + } + echo $line; + } + + public function getPort(): int + { + return $this->port ?? throw new RuntimeException("Port not set"); } } @@ -807,15 +818,20 @@ function my_mysqli_test_query_response_row_read_two_fields(my_mysqli_fake_server } } -function run_fake_server(string $test_function, $port = 33305): void +function run_fake_server(string $test_function, int|string $port = 0): int { - $address = '127.0.0.1'; + $host = '127.0.0.1'; - $socket = @stream_socket_server("tcp://$address:$port", $errno, $errstr); + $socket = @stream_socket_server("tcp://$host:$port", $errno, $errstr); if (!$socket) { die("Failed to create socket: $errstr ($errno)\n"); } - echo "[*] Server started\n"; + if (intval($port) === 0) { + $address = stream_socket_get_name($socket, false); + list($host, $port) = explode(":", $address); + } + + echo "[*] Server started on $host:$port\n"; try { $conn = new my_mysqli_fake_server_conn($socket); @@ -832,7 +848,7 @@ function run_fake_server(string $test_function, $port = 33305): void } -function run_fake_server_in_background($test_function, $port = 33305): my_mysqli_fake_server_process +function run_fake_server_in_background($test_function, $port = 0): my_mysqli_fake_server_process { $command = [PHP_BINARY, '-n', __FILE__, 'mysqli_fake_server', $test_function, $port]; @@ -852,5 +868,5 @@ function run_fake_server_in_background($test_function, $port = 33305): my_mysqli } if (isset($argv) && $argc > 2 && $argv[1] == 'mysqli_fake_server') { - run_fake_server($argv[2], $argv[3] ?? '33305'); + run_fake_server($argv[2], $argv[3] ?? 0); } diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt index 279aec6a2cba1..666f47f4199fb 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt @@ -6,16 +6,15 @@ mysqli wait(); try { - $conn = new mysqli( $servername, $username, $password, "", $port ); + $conn = new mysqli( $servername, $username, $password, "", $process->getPort()); $info = mysqli_info($conn); var_dump($info); } catch (Exception $e) { @@ -27,12 +26,12 @@ $process->terminate(); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 [*] Sending - Malicious OK Auth Response [Extract heap through buffer over-read]: 0900000200000002000000fcff Warning: mysqli::__construct(): OK packet message length is past the packet size in %s on line %d -Unknown error while trying to connect via tcp://127.0.0.1:33305 +Unknown error while trying to connect via tcp://127.0.0.1:%d done! diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt index 77f2232eca687..0883962321d20 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt @@ -6,16 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Running query on the fake server...\n"; @@ -34,7 +32,7 @@ $process->terminate(); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-filename.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-filename.phpt index 0b4db8ccece95..c397399a278a9 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-filename.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-filename.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Running query on the fake server...\n"; $result = $conn->query("SELECT * from users"); @@ -27,7 +26,7 @@ $process->terminate(); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-query-len-overflow.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-query-len-overflow.phpt index f141a79bdaa85..797acbedff9ad 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-query-len-overflow.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-query-len-overflow.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Query the fake server...\n"; $sql = "SELECT strval, strval FROM data"; @@ -33,7 +32,7 @@ $process->terminate(true); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-bit.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-bit.phpt index e43518217eb63..bf64bb62d1e66 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-bit.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-bit.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Preparing statement on the fake server...\n"; $stmt = $conn->prepare("SELECT bitval, timval FROM data"); @@ -36,7 +35,7 @@ $process->terminate(true); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-date.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-date.phpt index 76158e940d09d..99a7381994ae8 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-date.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-date.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Preparing statement on the fake server...\n"; $stmt = $conn->prepare("SELECT strval, datval FROM data"); @@ -36,7 +35,7 @@ $process->terminate(true); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt index f53d5b83bd432..c6b9bd79fa981 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Preparing statement on the fake server...\n"; $stmt = $conn->prepare("SELECT strval, dtival FROM data"); @@ -36,7 +35,7 @@ $process->terminate(true); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-double.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-double.phpt index 03c9b045d7375..460321d2ee5d5 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-double.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-double.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Preparing statement on the fake server...\n"; $stmt = $conn->prepare("SELECT strval, dblval FROM data"); @@ -36,7 +35,7 @@ $process->terminate(true); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-float.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-float.phpt index b1ec9aa51eca1..a1ea19bb4f6b7 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-float.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-float.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Preparing statement on the fake server...\n"; $stmt = $conn->prepare("SELECT strval, fltval FROM data"); @@ -36,7 +35,7 @@ $process->terminate(true); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-int.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-int.phpt index 426d9ea7b3f9b..839fadb21f0e2 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-int.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-int.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Preparing statement on the fake server...\n"; $stmt = $conn->prepare("SELECT strval, intval FROM data"); @@ -36,7 +35,7 @@ $process->terminate(true); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-no-space.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-no-space.phpt index 6db6952d42a15..ce028483dec21 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-no-space.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-no-space.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Preparing statement on the fake server...\n"; $stmt = $conn->prepare("SELECT strval, strval FROM data"); @@ -36,7 +35,7 @@ $process->terminate(true); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-string.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-string.phpt index 55bad4cc544aa..e40ed1d58c7ff 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-string.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-string.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Preparing statement on the fake server...\n"; $stmt = $conn->prepare("SELECT item FROM items"); @@ -36,7 +35,7 @@ $process->terminate(true); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-time.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-time.phpt index 06918c375f31a..6aa58898a8d55 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-time.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-time.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); echo "[*] Preparing statement on the fake server...\n"; $stmt = $conn->prepare("SELECT strval, timval FROM data"); @@ -36,7 +35,7 @@ $process->terminate(true); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/protocol_query_row_fetch_data.phpt b/ext/mysqli/tests/protocol_query_row_fetch_data.phpt index 524fe5e587c63..22f9712ef7fb0 100644 --- a/ext/mysqli/tests/protocol_query_row_fetch_data.phpt +++ b/ext/mysqli/tests/protocol_query_row_fetch_data.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); function my_query($conn, $field) { @@ -39,8 +38,8 @@ $process->terminate(true); print "done!"; ?> ---EXPECT-- -[*] Server started +--EXPECTF-- +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 diff --git a/ext/mysqli/tests/protocol_stmt_row_fetch_data.phpt b/ext/mysqli/tests/protocol_stmt_row_fetch_data.phpt index af16a9eb2d05f..6e689f9d2be5f 100644 --- a/ext/mysqli/tests/protocol_stmt_row_fetch_data.phpt +++ b/ext/mysqli/tests/protocol_stmt_row_fetch_data.phpt @@ -6,15 +6,14 @@ mysqli wait(); -$conn = new mysqli($servername, $username, $password, "", $port); +$conn = new mysqli($servername, $username, $password, "", $process->getPort()); function my_query($conn, $field) { @@ -41,7 +40,7 @@ $process->terminate(true); print "done!"; ?> --EXPECTF-- -[*] Server started +[*] Server started on 127.0.0.1:%d [*] Connection established [*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 [*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 From 8a9d45b86f89bbaffa6e3f30eb8b75b39c509a1d Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 13 Sep 2024 15:29:21 +0200 Subject: [PATCH 20/65] Backport flaky flag for phar tests 4e12189604d76396f8c3b9d5dda6e8757f1d8618 b6ca871396328befca3f4e65a49a81b47cea1ced 062837aa13a72febf0fb14fd2b8afda338ee3f0a --- ext/phar/tests/033a.phpt | 6 ++++++ ext/phar/tests/phar_oo_002.phpt | 6 ++++++ ext/phar/tests/phar_oo_iswriteable.phpt | 6 ++++++ ext/phar/tests/stat.phpt | 6 ++++++ ext/phar/tests/tar/033a.phpt | 6 ++++++ ext/phar/tests/zip/033a.phpt | 6 ++++++ ext/posix/tests/posix_getgrnam_basic.phpt | 5 ++++- ext/posix/tests/posix_getgroups_basic.phpt | 6 ++++++ run-tests.php | 3 +++ 9 files changed, 49 insertions(+), 1 deletion(-) diff --git a/ext/phar/tests/033a.phpt b/ext/phar/tests/033a.phpt index c36c5c6853985..355617b29f93f 100644 --- a/ext/phar/tests/033a.phpt +++ b/ext/phar/tests/033a.phpt @@ -5,6 +5,12 @@ phar --INI-- phar.readonly=1 phar.require_hash=0 +--SKIPIF-- + --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- setSection('XFAIL', ltrim(substr($output, 5))); + } elseif (!strncasecmp('flaky', $output, 5)) { + // Pretend we have a FLAKY section + $test->setSection('FLAKY', ltrim(substr($output, 5))); } elseif ($output !== '') { show_result("BORK", $output, $tested_file, 'reason: invalid output from SKIPIF', $temp_filenames); $PHP_FAILED_TESTS['BORKED'][] = [ From 754aa7706b4128880a71aaafc42b3331ec0acb37 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:32:35 +0100 Subject: [PATCH 21/65] Resolve GH-17112 for lower branches See https://github.com/php/php-src/pull/17114#issuecomment-2533050450 --- ext/mbstring/libmbfl/mbfl/mbfl_encoding.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h index b25ec71eef9de..2072b848bd5bd 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h +++ b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h @@ -31,9 +31,9 @@ #ifndef MBFL_ENCODING_H #define MBFL_ENCODING_H +#include "zend.h" #include "mbfl_defs.h" #include "mbfl_consts.h" -#include "zend.h" enum mbfl_no_encoding { mbfl_no_encoding_invalid = -1, From cdfd9601504b620610b2b8c5c6cc090706fb33dd Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 10 Dec 2024 13:43:29 +0100 Subject: [PATCH 22/65] Fix ZEND_MATCH_ERROR misoptimization op1 of ZEND_MATCH_ERROR, which refers to the match expression, is not freed by MATCH_ERROR itself. Instead, it is freed by ZEND_HANDLE_EXCEPTION. For normal control flow, a FREE is placed at the end of the match expression. Since FREE may appear after MATCH_ERROR in the opcode sequence, we need to correctly handle op1 of MATCH_ERROR as alive. Fixes GH-17106 Closes GH-17108 --- NEWS | 3 +++ Zend/Optimizer/zend_optimizer.c | 2 ++ Zend/zend_opcode.c | 1 + ext/opcache/tests/gh17106.phpt | 21 +++++++++++++++++++++ 4 files changed, 27 insertions(+) create mode 100644 ext/opcache/tests/gh17106.phpt diff --git a/NEWS b/NEWS index 209c5dfc1eff5..caad15a445eac 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.16 +- Core: + . Fixed bug GH-17106 (ZEND_MATCH_ERROR misoptimization). (ilutov) + - DBA: . Skip test if inifile is disabled. (orlitzky) diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index d8ff9e5f0b8f3..857968e621b38 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -640,6 +640,7 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array, case ZEND_SWITCH_LONG: case ZEND_SWITCH_STRING: case ZEND_MATCH: + case ZEND_MATCH_ERROR: case ZEND_JMP_NULL: { zend_op *end = op_array->opcodes + op_array->last; while (opline < end) { @@ -652,6 +653,7 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array, && opline->opcode != ZEND_SWITCH_LONG && opline->opcode != ZEND_SWITCH_STRING && opline->opcode != ZEND_MATCH + && opline->opcode != ZEND_MATCH_ERROR && opline->opcode != ZEND_JMP_NULL && (opline->opcode != ZEND_FREE || opline->extended_value != ZEND_FREE_ON_RETURN); diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 93195a1be4c54..55bf81376d99d 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -877,6 +877,7 @@ static bool keeps_op1_alive(zend_op *opline) { || opline->opcode == ZEND_SWITCH_LONG || opline->opcode == ZEND_SWITCH_STRING || opline->opcode == ZEND_MATCH + || opline->opcode == ZEND_MATCH_ERROR || opline->opcode == ZEND_FETCH_LIST_R || opline->opcode == ZEND_FETCH_LIST_W || opline->opcode == ZEND_COPY_TMP) { diff --git a/ext/opcache/tests/gh17106.phpt b/ext/opcache/tests/gh17106.phpt new file mode 100644 index 0000000000000..5b56131b5a29f --- /dev/null +++ b/ext/opcache/tests/gh17106.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-17106: ZEND_MATCH_ERROR misoptimization +--EXTENSIONS-- +opcache +--INI-- +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- + 2 }); +var_dump(match (X) { 2 => 2 }); + +?> +--EXPECT-- +int(7) +int(2) +int(2) From e7af08d625f36dc629336f3b362271482b1713c3 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 10 Dec 2024 14:24:27 +0100 Subject: [PATCH 23/65] Hide xfail/xleak test summary We don't show succeeding tests in the summary, and for all intents and purposes, these tests have succeeded, in that they behave as expected. I've seen the output confuse people on multiple occasions, for example GH-17105. Closes GH-17109 --- run-tests.php | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/run-tests.php b/run-tests.php index 2716f2a520a74..d51ab99a205a3 100755 --- a/run-tests.php +++ b/run-tests.php @@ -3144,18 +3144,6 @@ function get_summary(bool $show_ext_summary): string $failed_test_summary .= "=====================================================================\n"; } - if (count($PHP_FAILED_TESTS['XFAILED'])) { - $failed_test_summary .= ' -===================================================================== -EXPECTED FAILED TEST SUMMARY ---------------------------------------------------------------------- -'; - foreach ($PHP_FAILED_TESTS['XFAILED'] as $failed_test_data) { - $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; - } - $failed_test_summary .= "=====================================================================\n"; - } - if (count($PHP_FAILED_TESTS['BORKED'])) { $failed_test_summary .= ' ===================================================================== @@ -3206,19 +3194,6 @@ function get_summary(bool $show_ext_summary): string $failed_test_summary .= "=====================================================================\n"; } - if (count($PHP_FAILED_TESTS['XLEAKED'])) { - $failed_test_summary .= ' -===================================================================== -EXPECTED LEAK TEST SUMMARY ---------------------------------------------------------------------- -'; - foreach ($PHP_FAILED_TESTS['XLEAKED'] as $failed_test_data) { - $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; - } - - $failed_test_summary .= "=====================================================================\n"; - } - if ($failed_test_summary && !getenv('NO_PHPTEST_SUMMARY')) { $summary .= $failed_test_summary; } From 3490ac0cb31f88a9d1b1cb1fba7de31aa99cb980 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sat, 7 Sep 2024 23:11:16 +0100 Subject: [PATCH 24/65] Fix GH-13437: FPM: ERROR: scoreboard: failed to lock (already locked) This changes locking for scoreboard to reduce contention between readers and adds retries for acquiring scoreboard for read. Closes GH-15805 --- NEWS | 4 ++ sapi/fpm/fpm/fpm_atomic.h | 19 ++++++++ sapi/fpm/fpm/fpm_scoreboard.c | 82 +++++++++++++++++++++++++++++++---- sapi/fpm/fpm/fpm_scoreboard.h | 4 ++ 4 files changed, 101 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index caad15a445eac..757cab920c22d 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,10 @@ PHP NEWS - FFI: . Fixed bug #79075 (FFI header parser chokes on comments). (nielsdos) +- FPM: + . Fixed bug GH-13437 (FPM: ERROR: scoreboard: failed to lock (already + locked)). (Jakub Zelenka) + - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) diff --git a/sapi/fpm/fpm/fpm_atomic.h b/sapi/fpm/fpm/fpm_atomic.h index e3926e708c542..02009f6af9118 100644 --- a/sapi/fpm/fpm/fpm_atomic.h +++ b/sapi/fpm/fpm/fpm_atomic.h @@ -156,6 +156,25 @@ static inline int fpm_spinlock(atomic_t *lock, int try_once) /* {{{ */ } /* }}} */ +static inline int fpm_spinlock_with_max_retries(atomic_t *lock, unsigned int max_retries) +{ + unsigned int retries = 0; + + for (;;) { + if (atomic_cmp_set(lock, 0, 1)) { + return 1; + } + + sched_yield(); + + if (++retries > max_retries) { + return 0; + } + } + + return 1; +} + #define fpm_unlock(lock) lock = 0 #endif diff --git a/sapi/fpm/fpm/fpm_scoreboard.c b/sapi/fpm/fpm/fpm_scoreboard.c index 52d10a0416832..a4611d5edb620 100644 --- a/sapi/fpm/fpm/fpm_scoreboard.c +++ b/sapi/fpm/fpm/fpm_scoreboard.c @@ -73,6 +73,22 @@ int fpm_scoreboard_init_main(void) return 0; } +static inline void fpm_scoreboard_readers_decrement(struct fpm_scoreboard_s *scoreboard) +{ + fpm_spinlock(&scoreboard->lock, 1); + if (scoreboard->reader_count > 0) { + scoreboard->reader_count -= 1; + } +#ifdef PHP_FPM_ZLOG_TRACE + unsigned int current_reader_count = scoreboard->reader_count; +#endif + fpm_unlock(scoreboard->lock); +#ifdef PHP_FPM_ZLOG_TRACE + /* this is useful for debugging but currently needs to be hidden as external logger would always log it */ + zlog(ZLOG_DEBUG, "scoreboard: for proc %d reader decremented to value %u", getpid(), current_reader_count); +#endif +} + static struct fpm_scoreboard_s *fpm_scoreboard_get_for_update(struct fpm_scoreboard_s *scoreboard) /* {{{ */ { if (!scoreboard) { @@ -93,7 +109,33 @@ void fpm_scoreboard_update_begin(struct fpm_scoreboard_s *scoreboard) /* {{{ */ return; } - fpm_spinlock(&scoreboard->lock, 0); + int retries = 0; + while (1) { + fpm_spinlock(&scoreboard->lock, 1); + if (scoreboard->reader_count == 0) { + if (!fpm_spinlock_with_max_retries(&scoreboard->writer_active, FPM_SCOREBOARD_SPINLOCK_MAX_RETRIES)) { + /* in this case the writer might have crashed so just warn and continue as the lock was acquired */ + zlog(ZLOG_WARNING, "scoreboard: writer %d waited too long for another writer to release lock.", getpid()); + } +#ifdef PHP_FPM_ZLOG_TRACE + else { + zlog(ZLOG_DEBUG, "scoreboard: writer lock acquired by writer %d", getpid()); + } +#endif + fpm_unlock(scoreboard->lock); + break; + } + fpm_unlock(scoreboard->lock); + + if (++retries > FPM_SCOREBOARD_SPINLOCK_MAX_RETRIES) { + /* decrement reader count by 1 (assuming a killed or crashed reader) */ + fpm_scoreboard_readers_decrement(scoreboard); + zlog(ZLOG_WARNING, "scoreboard: writer detected a potential crashed reader, decrementing reader count."); + retries = 0; + } + + sched_yield(); + } } /* }}} */ @@ -170,7 +212,10 @@ void fpm_scoreboard_update_commit( scoreboard->active_max = scoreboard->active; } - fpm_unlock(scoreboard->lock); + fpm_unlock(scoreboard->writer_active); +#ifdef PHP_FPM_ZLOG_TRACE + zlog(ZLOG_DEBUG, "scoreboard: writer lock released by writer %d", getpid()); +#endif } /* }}} */ @@ -234,16 +279,37 @@ struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get_from_child(struct fpm_chil struct fpm_scoreboard_s *fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang) /* {{{ */ { - struct fpm_scoreboard_s *s; - - s = scoreboard ? scoreboard : fpm_scoreboard; + struct fpm_scoreboard_s *s = scoreboard ? scoreboard : fpm_scoreboard; if (!s) { return NULL; } - if (!fpm_spinlock(&s->lock, nohang)) { - return NULL; + int retries = 0; + while (1) { + /* increment reader if no writer active */ + fpm_spinlock(&s->lock, 1); + if (!s->writer_active) { + s->reader_count += 1; +#ifdef PHP_FPM_ZLOG_TRACE + unsigned int current_reader_count = s->reader_count; +#endif + fpm_unlock(s->lock); +#ifdef PHP_FPM_ZLOG_TRACE + zlog(ZLOG_DEBUG, "scoreboard: for proc %d reader incremented to value %u", getpid(), current_reader_count); +#endif + break; + } + fpm_unlock(s->lock); + + sched_yield(); + + if (++retries > FPM_SCOREBOARD_SPINLOCK_MAX_RETRIES) { + zlog(ZLOG_WARNING, "scoreboard: reader waited too long for writer to release lock."); + fpm_scoreboard_readers_decrement(s); + return NULL; + } } + return s; } /* }}} */ @@ -253,7 +319,7 @@ void fpm_scoreboard_release(struct fpm_scoreboard_s *scoreboard) { return; } - scoreboard->lock = 0; + fpm_scoreboard_readers_decrement(scoreboard); } struct fpm_scoreboard_s *fpm_scoreboard_copy(struct fpm_scoreboard_s *scoreboard, int copy_procs) diff --git a/sapi/fpm/fpm/fpm_scoreboard.h b/sapi/fpm/fpm/fpm_scoreboard.h index c488c64bfefc4..31824940b3f7b 100644 --- a/sapi/fpm/fpm/fpm_scoreboard.h +++ b/sapi/fpm/fpm/fpm_scoreboard.h @@ -18,6 +18,8 @@ #define FPM_SCOREBOARD_LOCK_HANG 0 #define FPM_SCOREBOARD_LOCK_NOHANG 1 +#define FPM_SCOREBOARD_SPINLOCK_MAX_RETRIES 50000 + struct fpm_scoreboard_proc_s { union { atomic_t lock; @@ -52,6 +54,8 @@ struct fpm_scoreboard_s { atomic_t lock; char dummy[16]; }; + atomic_t writer_active; + unsigned int reader_count; char pool[32]; int pm; time_t start_epoch; From 388f63c310f45064fc1faaa20efe2217e37c8c96 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 14 Dec 2024 15:52:39 +0100 Subject: [PATCH 25/65] Fix GH-17158: pg_fetch_result Shows Incorrect ArgumentCountError Message when Called With 1 Argument Closes GH-17161. --- NEWS | 4 ++++ ext/pgsql/pgsql.c | 5 ++++- ext/pgsql/tests/gh17158.phpt | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 ext/pgsql/tests/gh17158.phpt diff --git a/NEWS b/NEWS index 757cab920c22d..ee7e9614b42ca 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,10 @@ PHP NEWS - Opcache: . opcache_get_configuration() properly reports jit_prof_threshold. (cmb) +- PgSql: + . Fixed bug GH-17158 (pg_fetch_result Shows Incorrect ArgumentCountError + Message when Called With 1 Argument). (nielsdos) + - SimpleXML: . Fixed bug GH-17040 (SimpleXML's unset can break DOM objects). (nielsdos) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 4087e61670230..1db33e5b18225 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -1710,7 +1710,7 @@ PHP_FUNCTION(pg_fetch_result) Z_PARAM_OBJECT_OF_CLASS(result, pgsql_result_ce) Z_PARAM_STR_OR_LONG(field_name, field_offset) ZEND_PARSE_PARAMETERS_END(); - } else { + } else if (ZEND_NUM_ARGS() == 3) { ZEND_PARSE_PARAMETERS_START(3, 3) Z_PARAM_OBJECT_OF_CLASS(result, pgsql_result_ce) if (zend_string_equals_literal(EG(current_execute_data)->func->common.function_name, "pg_result")) { @@ -1720,6 +1720,9 @@ PHP_FUNCTION(pg_fetch_result) } Z_PARAM_STR_OR_LONG(field_name, field_offset) ZEND_PARSE_PARAMETERS_END(); + } else { + zend_wrong_parameters_count_error(2, 3); + RETURN_THROWS(); } pg_result = Z_PGSQL_RESULT_P(result); diff --git a/ext/pgsql/tests/gh17158.phpt b/ext/pgsql/tests/gh17158.phpt new file mode 100644 index 0000000000000..ade47ededed12 --- /dev/null +++ b/ext/pgsql/tests/gh17158.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-17158 (pg_fetch_result Shows Incorrect ArgumentCountError Message when Called With 1 Argument) +--EXTENSIONS-- +pgsql +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +pg_fetch_result() expects at least 2 arguments, 1 given From a57a434f95452e7fdbb52cb8f32e41691cc450ef Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 14 Dec 2024 12:10:20 +0100 Subject: [PATCH 26/65] Fix GH-17153: SimpleXML crash when using autovivification on document In the case of a member string, `mynode` may also be a document, which doesn't have a namespace. Closes GH-17156. --- NEWS | 2 ++ ext/simplexml/simplexml.c | 5 +++- ext/simplexml/tests/gh17153.phpt | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 ext/simplexml/tests/gh17153.phpt diff --git a/NEWS b/NEWS index ee7e9614b42ca..63ac5db6b62fc 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,8 @@ PHP NEWS - SimpleXML: . Fixed bug GH-17040 (SimpleXML's unset can break DOM objects). (nielsdos) + . Fixed bug GH-17153 (SimpleXML crash when using autovivification on + document). (nielsdos) - Sockets: . Fixed bug GH-16276 (socket_strerror overflow handling with INT_MIN). diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 6f4a0d6316ec8..00551267a45e9 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -576,7 +576,10 @@ static zval *sxe_prop_dim_write(zend_object *object, zval *member, zval *value, if (!member || Z_TYPE_P(member) == IS_LONG) { newnode = xmlNewTextChild(mynode->parent, mynode->ns, mynode->name, value_str ? (xmlChar *)ZSTR_VAL(value_str) : NULL); } else { - newnode = xmlNewTextChild(mynode, mynode->ns, (xmlChar *)Z_STRVAL_P(member), value_str ? (xmlChar *)ZSTR_VAL(value_str) : NULL); + /* Note: we cannot set the namespace here unconditionally because the parent may be a document. + * Passing NULL will let libxml decide to either inherit the namespace or not set one at all, + * depending on whether the parent is an element. */ + newnode = xmlNewTextChild(mynode, NULL, (xmlChar *)Z_STRVAL_P(member), value_str ? (xmlChar *)ZSTR_VAL(value_str) : NULL); } } else if (!member || Z_TYPE_P(member) == IS_LONG) { if (member && cnt < Z_LVAL_P(member)) { diff --git a/ext/simplexml/tests/gh17153.phpt b/ext/simplexml/tests/gh17153.phpt new file mode 100644 index 0000000000000..0958f613bb74c --- /dev/null +++ b/ext/simplexml/tests/gh17153.phpt @@ -0,0 +1,45 @@ +--TEST-- +GH-17153 (SimpleXML crash when using autovivification on document) +--EXTENSIONS-- +dom +simplexml +xsl +--FILE-- +load(__DIR__ . '/../../xsl/tests/53965/collection.xsl'); +$processor->importStylesheet($dom); +$result = $processor->transformToDoc($sxe, SimpleXMLElement::class); +$result->h = "x"; +var_dump($result); +?> +--EXPECT-- +object(SimpleXMLElement)#4 (4) { + ["h1"]=> + array(2) { + [0]=> + string(19) "Fight for your mind" + [1]=> + string(17) "Electric Ladyland" + } + ["h2"]=> + array(2) { + [0]=> + string(20) "by Ben Harper - 1995" + [1]=> + string(22) "by Jimi Hendrix - 1997" + } + ["hr"]=> + array(2) { + [0]=> + object(SimpleXMLElement)#5 (0) { + } + [1]=> + object(SimpleXMLElement)#6 (0) { + } + } + ["h"]=> + string(1) "x" +} From aafa6ea386fc7334e73a4e9ce60e940683116896 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 9 Dec 2024 12:13:35 +0100 Subject: [PATCH 27/65] Don't run proc_open_cmd.phpt in parallel with other tests This test puts a fake cmd.exe in the CWD and removes it only after the test has finished. We need to avoid that other tests are running while that fake cmd.exe is there, because they may use it instead of the proper cmd.exe. We also unlink the fake cmd.exe as soon as possible, regardless of the test result. Fixes GH-17098. Closes GH-17090. --- ext/standard/tests/general_functions/proc_open_cmd.phpt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/standard/tests/general_functions/proc_open_cmd.phpt b/ext/standard/tests/general_functions/proc_open_cmd.phpt index 6c80871d47df8..2dcc207bfe6ec 100644 --- a/ext/standard/tests/general_functions/proc_open_cmd.phpt +++ b/ext/standard/tests/general_functions/proc_open_cmd.phpt @@ -1,5 +1,7 @@ --TEST-- Harden against cmd.exe hijacking +--CONFLICTS-- +all --SKIPIF-- --EXPECTF-- resource(%d) of type (process) From 142f85e2e18a9574eb1ce941953beb16e679082c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:19:57 +0100 Subject: [PATCH 28/65] Fix GH-17137: Segmentation fault ext/phar/phar.c Commit edae2431 attempted to fix a leak and double free, but didn't properly understand what was going on, causing a reference count mistake and subsequent segfault in this case. The first mistake of that commit is that the reference count should've been increased because we're reusing a phar object. The error handling path should've gotten changed instead to undo this refcount increase instead of not refcounting at all (root cause of this bug). The second mistake is that the alias isn't supposed to be transferred or whatever, that just doesn't make sense. The reason the test bug69958.phpt originally leaked is because in the non-reuse case we borrowed the alias and otherwise we own the alias. If we own the alias the alias information shouldn't get deleted anyway as that would desync the alias map. Fixing these will reveal a third issue in which the alias memory is not always properly in sync with the persistence-ness of the phar, fix this as well. Closes GH-17150. --- NEWS | 3 +++ ext/phar/phar.c | 1 + ext/phar/phar_object.c | 54 ++++++++++++++++++++++--------------- ext/phar/tests/gh17137.phpt | 31 +++++++++++++++++++++ 4 files changed, 67 insertions(+), 22 deletions(-) create mode 100644 ext/phar/tests/gh17137.phpt diff --git a/NEWS b/NEWS index 63ac5db6b62fc..15453be636dcf 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,9 @@ PHP NEWS . Fixed bug GH-17158 (pg_fetch_result Shows Incorrect ArgumentCountError Message when Called With 1 Argument). (nielsdos) +- Phar: + . Fixed bug GH-17137 (Segmentation fault ext/phar/phar.c). (nielsdos) + - SimpleXML: . Fixed bug GH-17040 (SimpleXML's unset can break DOM objects). (nielsdos) . Fixed bug GH-17153 (SimpleXML crash when using autovivification on diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 6f52fc714d6b7..dfdbbb8fb8390 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1504,6 +1504,7 @@ int phar_create_or_parse_filename(char *fname, size_t fname_len, char *alias, si } } + ZEND_ASSERT(!mydata->is_persistent); mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len); mydata->alias_len = alias ? alias_len : fname_len; } diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 470e1e8a993be..98efcf701c6c4 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -2112,9 +2112,8 @@ static zend_object *phar_rename_archive(phar_archive_data **sphar, char *ext) /* efree(newname); if (PHAR_G(manifest_cached) && NULL != (pphar = zend_hash_str_find_ptr(&cached_phars, newpath, phar->fname_len))) { - efree(oldpath); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to add newly converted phar \"%s\" to the list of phars, new phar name is in phar.cache_list", phar->fname); - return NULL; + goto err_oldpath; } if (NULL != (pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), newpath, phar->fname_len))) { @@ -2126,41 +2125,42 @@ static zend_object *phar_rename_archive(phar_archive_data **sphar, char *ext) /* pphar->flags = phar->flags; pphar->fp = phar->fp; phar->fp = NULL; - /* FIX: GH-10755 Double-free issue caught by ASAN check */ - pphar->alias = phar->alias; /* Transfer alias to pphar to */ - phar->alias = NULL; /* avoid being free'd twice */ + /* The alias is not owned by the phar, so set it to NULL to avoid freeing it. */ + phar->alias = NULL; phar_destroy_phar_data(phar); *sphar = NULL; phar = pphar; + /* NOTE: this phar is now reused, so the refcount must be increased. */ + phar->refcount++; newpath = oldpath; goto its_ok; } } - efree(oldpath); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to add newly converted phar \"%s\" to the list of phars, a phar with that name already exists", phar->fname); - return NULL; + goto err_oldpath; } its_ok: if (SUCCESS == php_stream_stat_path(newpath, &ssb)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "phar \"%s\" exists and must be unlinked prior to conversion", newpath); - efree(oldpath); - return NULL; + goto err_reused_oldpath; } if (!phar->is_data) { if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &ext_len, 1, 1, 1)) { - efree(oldpath); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "phar \"%s\" has invalid extension %s", phar->fname, ext); - return NULL; + goto err_reused_oldpath; } phar->ext_len = ext_len; - if (phar->alias) { + /* If we are reusing a phar, then the aliases should be already set up correctly, + * and so we should not clear out the alias information. + * This would also leak memory because, unlike the non-reuse path, we actually own the alias memory. */ + if (phar->alias && phar != pphar) { if (phar->is_temporary_alias) { phar->alias = NULL; phar->alias_len = 0; } else { - phar->alias = estrndup(newpath, strlen(newpath)); + phar->alias = pestrndup(newpath, strlen(newpath), phar->is_persistent); phar->alias_len = strlen(newpath); phar->is_temporary_alias = 1; zend_hash_str_update_ptr(&(PHAR_G(phar_alias_map)), newpath, phar->fname_len, phar); @@ -2170,20 +2170,21 @@ static zend_object *phar_rename_archive(phar_archive_data **sphar, char *ext) /* } else { if (SUCCESS != phar_detect_phar_fname_ext(newpath, phar->fname_len, (const char **) &(phar->ext), &ext_len, 0, 1, 1)) { - efree(oldpath); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "data phar \"%s\" has invalid extension %s", phar->fname, ext); - return NULL; + goto err_reused_oldpath; } phar->ext_len = ext_len; - phar->alias = NULL; - phar->alias_len = 0; + /* See comment in other branch. */ + if (phar != pphar) { + phar->alias = NULL; + phar->alias_len = 0; + } } if ((!pphar || phar == pphar) && NULL == zend_hash_str_update_ptr(&(PHAR_G(phar_fname_map)), newpath, phar->fname_len, phar)) { - efree(oldpath); zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to add newly converted phar \"%s\" to the list of phars", phar->fname); - return NULL; + goto err_oldpath; } phar_flush(phar, 0, 0, 1, &error); @@ -2193,8 +2194,7 @@ static zend_object *phar_rename_archive(phar_archive_data **sphar, char *ext) /* *sphar = NULL; zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s", error); efree(error); - efree(oldpath); - return NULL; + goto err_oldpath; } efree(oldpath); @@ -2217,6 +2217,16 @@ static zend_object *phar_rename_archive(phar_archive_data **sphar, char *ext) /* zend_call_known_instance_method_with_1_params(ce->constructor, Z_OBJ(ret), NULL, &arg1); zval_ptr_dtor(&arg1); return Z_OBJ(ret); + +err_reused_oldpath: + if (pphar == phar) { + /* NOTE: we know it's not the last reference because the phar is reused. */ + phar->refcount--; + } + /* fallthrough */ +err_oldpath: + efree(oldpath); + return NULL; } /* }}} */ @@ -2747,7 +2757,7 @@ PHP_METHOD(Phar, setAlias) old_temp = phar_obj->archive->is_temporary_alias; if (alias_len) { - phar_obj->archive->alias = estrndup(alias, alias_len); + phar_obj->archive->alias = pestrndup(alias, alias_len, phar_obj->archive->is_persistent); } else { phar_obj->archive->alias = NULL; } diff --git a/ext/phar/tests/gh17137.phpt b/ext/phar/tests/gh17137.phpt new file mode 100644 index 0000000000000..b96036420e931 --- /dev/null +++ b/ext/phar/tests/gh17137.phpt @@ -0,0 +1,31 @@ +--TEST-- +GH-17137 (Segmentation fault ext/phar/phar.c) +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +--FILE-- +decompress()); +echo "OK\n"; +?> +--EXPECTF-- +object(Phar)#%d (3) { + ["pathName":"SplFileInfo":private]=> + string(0) "" + ["glob":"DirectoryIterator":private]=> + bool(false) + ["subPathName":"RecursiveDirectoryIterator":private]=> + string(0) "" +} +object(Phar)#%d (3) { + ["pathName":"SplFileInfo":private]=> + string(0) "" + ["glob":"DirectoryIterator":private]=> + bool(false) + ["subPathName":"RecursiveDirectoryIterator":private]=> + string(0) "" +} +OK From 0a3442fbe6401e3c53edf91b84c8eaebbd2554d5 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 15 Dec 2024 10:50:51 +0000 Subject: [PATCH 29/65] ext/pgsql fixing further calls with flexible arguments number. continuation of GH-17161 close GH-17165 --- NEWS | 2 ++ ext/pgsql/pgsql.c | 40 +++++++++++++++++----- ext/pgsql/tests/gh17165.phpt | 65 ++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 ext/pgsql/tests/gh17165.phpt diff --git a/NEWS b/NEWS index 15453be636dcf..29a3290378de8 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,8 @@ PHP NEWS - PgSql: . Fixed bug GH-17158 (pg_fetch_result Shows Incorrect ArgumentCountError Message when Called With 1 Argument). (nielsdos) + . Fixed further ArgumentCountError for calls with flexible + number of arguments. (David Carlier) - Phar: . Fixed bug GH-17137 (Segmentation fault ext/phar/phar.c). (nielsdos) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 1db33e5b18225..e955eed0928c8 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -984,12 +984,15 @@ PHP_FUNCTION(pg_query) } link = FETCH_DEFAULT_LINK(); CHECK_DEFAULT_LINK(link); - } else { + } else if (ZEND_NUM_ARGS() == 2) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pgsql_link, pgsql_link_ce, &query, &query_len) == FAILURE) { RETURN_THROWS(); } link = Z_PGSQL_LINK_P(pgsql_link); CHECK_PGSQL_LINK(link); + } else { + zend_wrong_parameters_count_error(1, 2); + RETURN_THROWS(); } pgsql = link->conn; @@ -1078,12 +1081,15 @@ PHP_FUNCTION(pg_query_params) } link = FETCH_DEFAULT_LINK(); CHECK_DEFAULT_LINK(link); - } else { + } else if (ZEND_NUM_ARGS() == 3) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Osa", &pgsql_link, pgsql_link_ce, &query, &query_len, &pv_param_arr) == FAILURE) { RETURN_THROWS(); } link = Z_PGSQL_LINK_P(pgsql_link); CHECK_PGSQL_LINK(link); + } else { + zend_wrong_parameters_count_error(2, 3); + RETURN_THROWS(); } pgsql = link->conn; @@ -1183,12 +1189,15 @@ PHP_FUNCTION(pg_prepare) } link = FETCH_DEFAULT_LINK(); CHECK_DEFAULT_LINK(link); - } else { + } else if (ZEND_NUM_ARGS() == 3) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oss", &pgsql_link, pgsql_link_ce, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) { RETURN_THROWS(); } link = Z_PGSQL_LINK_P(pgsql_link); CHECK_PGSQL_LINK(link); + } else { + zend_wrong_parameters_count_error(2, 3); + RETURN_THROWS(); } pgsql = link->conn; @@ -1264,12 +1273,15 @@ PHP_FUNCTION(pg_execute) } link = FETCH_DEFAULT_LINK(); CHECK_DEFAULT_LINK(link); - } else { + } else if (ZEND_NUM_ARGS() == 3) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Osa", &pgsql_link, pgsql_link_ce, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) { RETURN_THROWS(); } link = Z_PGSQL_LINK_P(pgsql_link); CHECK_PGSQL_LINK(link); + } else { + zend_wrong_parameters_count_error(2, 3); + RETURN_THROWS(); } pgsql = link->conn; @@ -2024,7 +2036,7 @@ static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type, bo Z_PARAM_OBJECT_OF_CLASS(result, pgsql_result_ce) Z_PARAM_STR_OR_LONG(field_name, field_offset) ZEND_PARSE_PARAMETERS_END(); - } else { + } else if (ZEND_NUM_ARGS() == 3) { ZEND_PARSE_PARAMETERS_START(3, 3) Z_PARAM_OBJECT_OF_CLASS(result, pgsql_result_ce) if (nullable_row) { @@ -2034,6 +2046,9 @@ static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type, bo } Z_PARAM_STR_OR_LONG(field_name, field_offset) ZEND_PARSE_PARAMETERS_END(); + } else { + zend_wrong_parameters_count_error(2, 3); + RETURN_THROWS(); } pg_result = Z_PGSQL_RESULT_P(result); @@ -2849,12 +2864,15 @@ PHP_FUNCTION(pg_set_error_verbosity) } link = FETCH_DEFAULT_LINK(); CHECK_DEFAULT_LINK(link); - } else { + } else if (ZEND_NUM_ARGS() == 2) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pgsql_link, pgsql_link_ce, &verbosity) == FAILURE) { RETURN_THROWS(); } link = Z_PGSQL_LINK_P(pgsql_link); CHECK_PGSQL_LINK(link); + } else { + zend_wrong_parameters_count_error(1, 2); + RETURN_THROWS(); } pgsql = link->conn; @@ -2907,12 +2925,15 @@ PHP_FUNCTION(pg_set_client_encoding) } link = FETCH_DEFAULT_LINK(); CHECK_DEFAULT_LINK(link); - } else { + } else if (ZEND_NUM_ARGS() == 2) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pgsql_link, pgsql_link_ce, &encoding, &encoding_len) == FAILURE) { RETURN_THROWS(); } link = Z_PGSQL_LINK_P(pgsql_link); CHECK_PGSQL_LINK(link); + } else { + zend_wrong_parameters_count_error(1, 2); + RETURN_THROWS(); } pgsql = link->conn; @@ -2996,12 +3017,15 @@ PHP_FUNCTION(pg_put_line) } link = FETCH_DEFAULT_LINK(); CHECK_DEFAULT_LINK(link); - } else { + } else if (ZEND_NUM_ARGS() == 2) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pgsql_link, pgsql_link_ce, &query, &query_len) == FAILURE) { RETURN_THROWS(); } link = Z_PGSQL_LINK_P(pgsql_link); CHECK_PGSQL_LINK(link); + } else { + zend_wrong_parameters_count_error(1, 2); + RETURN_THROWS(); } pgsql = link->conn; diff --git a/ext/pgsql/tests/gh17165.phpt b/ext/pgsql/tests/gh17165.phpt new file mode 100644 index 0000000000000..2fe3f2ad18add --- /dev/null +++ b/ext/pgsql/tests/gh17165.phpt @@ -0,0 +1,65 @@ +--TEST-- +Fix pg_query()/pg_query_params()/pg_prepare()/pg_execute()/pg_set_error_verbosity()/pg_set_client_encoding()/pg_put_line() pg field infos calls ArgumentCountError message. +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +try { + pg_query_params($db, "b", array(), "d"); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + pg_prepare($db, "a", "b", "c"); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + pg_set_error_verbosity($db, 0, PHP_INT_MAX); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + pg_set_client_encoding($db, "foo", "bar"); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + pg_put_line($db, "my", "data"); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + pg_field_is_null($db, false, "myfield", new stdClass()); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +pg_query() expects at most 2 arguments, 3 given +pg_query_params() expects at most 3 arguments, 4 given +pg_prepare() expects at most 3 arguments, 4 given +pg_set_error_verbosity() expects at most 2 arguments, 3 given +pg_set_client_encoding() expects at most 2 arguments, 3 given +pg_put_line() expects at most 2 arguments, 3 given +pg_field_is_null() expects at most 3 arguments, 4 given From ee0daa59dbae069307a8121ff55bb3a9da7a0bf5 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 15 Dec 2024 16:18:43 +0100 Subject: [PATCH 30/65] Fix GH-17162: zend_array_try_init() with dtor can cause engine UAF Closes GH-17167. --- NEWS | 2 ++ Zend/tests/gh17162.phpt | 21 +++++++++++++++++++++ Zend/zend_API.h | 5 ++++- 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/gh17162.phpt diff --git a/NEWS b/NEWS index 29a3290378de8..8caf8cc4573f9 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ PHP NEWS - Core: . Fixed bug GH-17106 (ZEND_MATCH_ERROR misoptimization). (ilutov) + . Fixed bug GH-17162 (zend_array_try_init() with dtor can cause engine UAF). + (nielsdos) - DBA: . Skip test if inifile is disabled. (orlitzky) diff --git a/Zend/tests/gh17162.phpt b/Zend/tests/gh17162.phpt new file mode 100644 index 0000000000000..bdf6ddbb36ba7 --- /dev/null +++ b/Zend/tests/gh17162.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-17162 (zend_array_try_init() with dtor can cause engine UAF) +--FILE-- +value = null; + } +} +$box = [new Test]; +// Using getimagesize() for the test because it's always available, +// but any function that uses zend_try_array_init() would work. +try { + getimagesize("dummy", $box); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Attempt to assign property "value" on null diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 1114eeeec14ef..59d489f7aeea2 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1478,7 +1478,10 @@ static zend_always_inline zval *zend_try_array_init_size(zval *zv, uint32_t size } zv = &ref->val; } - zval_ptr_dtor(zv); + zval garbage; + ZVAL_COPY_VALUE(&garbage, zv); + ZVAL_NULL(zv); + zval_ptr_dtor(&garbage); ZVAL_ARR(zv, arr); return zv; } From 2df9f32732e932b077603262d5c77416f1da5814 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sun, 15 Dec 2024 22:11:37 +0000 Subject: [PATCH 31/65] ext/pcntl: Fix memory leak in cleanup code of pcntl_exec() --- NEWS | 4 +++ ext/pcntl/pcntl.c | 9 ++++--- .../tests/pcntl_exec_invalid_strings.phpt | 25 +++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 ext/pcntl/tests/pcntl_exec_invalid_strings.phpt diff --git a/NEWS b/NEWS index 8caf8cc4573f9..437c704c25eb5 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,10 @@ PHP NEWS - Opcache: . opcache_get_configuration() properly reports jit_prof_threshold. (cmb) +- PCNTL: + . Fix memory leak in cleanup code of pcntl_exec() when a non stringable + value is encountered past the first entry. (Girgias) + - PgSql: . Fixed bug GH-17158 (pg_fetch_result Shows Incorrect ArgumentCountError Message when Called With 1 Argument). (nielsdos) diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 19460ad8b53fd..adb94af2fb463 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -540,7 +540,9 @@ PHP_FUNCTION(pcntl_exec) envs_hash = Z_ARRVAL_P(envs); envc = zend_hash_num_elements(envs_hash); - pair = envp = safe_emalloc((envc + 1), sizeof(char *), 0); + size_t envp_len = (envc + 1); + pair = envp = safe_emalloc(envp_len, sizeof(char *), 0); + memset(envp, 0, sizeof(char *) * envp_len); ZEND_HASH_FOREACH_KEY_VAL(envs_hash, key_num, key, element) { if (envi >= envc) break; if (!key) { @@ -551,9 +553,7 @@ PHP_FUNCTION(pcntl_exec) if (!try_convert_to_string(element)) { zend_string_release(key); - efree(argv); - efree(envp); - RETURN_THROWS(); + goto cleanup_env_vars; } /* Length of element + equal sign + length of key + null */ @@ -576,6 +576,7 @@ PHP_FUNCTION(pcntl_exec) php_error_docref(NULL, E_WARNING, "Error has occurred: (errno %d) %s", errno, strerror(errno)); } +cleanup_env_vars: /* Cleanup */ for (pair = envp; *pair != NULL; pair++) efree(*pair); efree(envp); diff --git a/ext/pcntl/tests/pcntl_exec_invalid_strings.phpt b/ext/pcntl/tests/pcntl_exec_invalid_strings.phpt new file mode 100644 index 0000000000000..7b2b5e51b8faa --- /dev/null +++ b/ext/pcntl/tests/pcntl_exec_invalid_strings.phpt @@ -0,0 +1,25 @@ +--TEST-- +pcntl_exec(): Test cleanup after non-stringable array value has been encountered for $args and $env_vars. +--EXTENSIONS-- +pcntl +--FILE-- +getMessage(), "\n"; +} + +try { + pcntl_exec( + 'cmd', + ['-n'], + ['var1' => 'value1', 'var2' => new stdClass()], + ); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Error: Object of class stdClass could not be converted to string +Error: Object of class stdClass could not be converted to string From 6c198e380e09ff399cb798558af1567627fbcd37 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 15 Dec 2024 20:09:06 +0100 Subject: [PATCH 32/65] Fix GH-16255: Unexpected nan value in ext/gd/libgd/gd_filter.c Closes GH-17169. --- NEWS | 4 ++++ ext/gd/gd.c | 19 ++++++++++++++++++- ext/gd/tests/gh16255.phpt | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 ext/gd/tests/gh16255.phpt diff --git a/NEWS b/NEWS index 437c704c25eb5..43505d08cee90 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,10 @@ PHP NEWS . Fixed bug GH-13437 (FPM: ERROR: scoreboard: failed to lock (already locked)). (Jakub Zelenka) +- GD: + . Fixed bug GH-16255 (Unexpected nan value in ext/gd/libgd/gd_filter.c). + (nielsdos, cmb) + - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 13da099b33506..847e0835bad4f 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -3433,7 +3433,24 @@ PHP_FUNCTION(imageconvolution) } } } - res = gdImageConvolution(im_src, matrix, (float)div, (float)offset); + + if (UNEXPECTED(!zend_finite(div))) { + zend_argument_value_error(3, "must be finite"); + RETURN_THROWS(); + } + + float div_float = (float) div; + if (UNEXPECTED(div_float == 0.0f)) { + zend_argument_value_error(3, "must not be 0"); + RETURN_THROWS(); + } + + if (UNEXPECTED(!zend_finite(offset))) { + zend_argument_value_error(4, "must be finite"); + RETURN_THROWS(); + } + + res = gdImageConvolution(im_src, matrix, div_float, (float) offset); if (res) { RETURN_TRUE; diff --git a/ext/gd/tests/gh16255.phpt b/ext/gd/tests/gh16255.phpt new file mode 100644 index 0000000000000..147dc5adf377b --- /dev/null +++ b/ext/gd/tests/gh16255.phpt @@ -0,0 +1,34 @@ +--TEST-- +GH-16255 (Unexpected nan value in ext/gd/libgd/gd_filter.c) +--EXTENSIONS-- +gd +--CREDITS-- +cmb69 +--FILE-- +getMessage(), "\n"; +} + +try { + imageconvolution($im, $matrix, 2.225E-307, 1.0); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +try { + imageconvolution($im, $matrix, 1, NAN); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +imageconvolution(): Argument #3 ($divisor) must be finite +imageconvolution(): Argument #3 ($divisor) must not be 0 +imageconvolution(): Argument #4 ($offset) must be finite From 8a649a8343678b1a581dee3926bcdf43b3d7283f Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 12 Dec 2024 22:11:22 +0000 Subject: [PATCH 33/65] ext/sockets: socket_set_option switch from convert_to_long to zval_get_long. to be explicit when the expected type is not met. Check SO_LINGER values for possible overflow. close GH-17135 --- NEWS | 3 + ext/sockets/sockets.c | 64 +++++++++++++----- ext/sockets/tests/socket_reuseport_cbpf.phpt | 9 ++- .../tests/socket_set_option_timeo_error.phpt | 66 +++++++++++++++++++ 4 files changed, 123 insertions(+), 19 deletions(-) create mode 100644 ext/sockets/tests/socket_set_option_timeo_error.phpt diff --git a/NEWS b/NEWS index 43505d08cee90..6ac803f463980 100644 --- a/NEWS +++ b/NEWS @@ -48,6 +48,9 @@ PHP NEWS - Sockets: . Fixed bug GH-16276 (socket_strerror overflow handling with INT_MIN). (David Carlier / cmb) + . Fixed overflow on SO_LINGER values setting, strengthening values check + on SO_SNDTIMEO/SO_RCVTIMEO for socket_set_option(). + (David Carlier) - Streams: . Fixed bug GH-17037 (UAF in user filter when adding existing filter name due diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index e1b350b4045a4..e6e231e2e5e7e 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1871,8 +1871,16 @@ PHP_FUNCTION(socket_set_option) const char l_onoff_key[] = "l_onoff"; const char l_linger_key[] = "l_linger"; - convert_to_array(arg4); - opt_ht = Z_ARRVAL_P(arg4); + if (Z_TYPE_P(arg4) != IS_ARRAY) { + if (UNEXPECTED(Z_TYPE_P(arg4) != IS_OBJECT)) { + zend_argument_type_error(4, "must be of type array when argument #3 ($option) is SO_LINGER, %s given", zend_zval_value_name(arg4)); + RETURN_THROWS(); + } else { + opt_ht = Z_OBJPROP_P(arg4); + } + } else { + opt_ht = Z_ARRVAL_P(arg4); + } if ((l_onoff = zend_hash_str_find(opt_ht, l_onoff_key, sizeof(l_onoff_key) - 1)) == NULL) { zend_argument_value_error(4, "must have key \"%s\"", l_onoff_key); @@ -1883,11 +1891,21 @@ PHP_FUNCTION(socket_set_option) RETURN_THROWS(); } - convert_to_long(l_onoff); - convert_to_long(l_linger); + zend_long val_lonoff = zval_get_long(l_onoff); + zend_long val_linger = zval_get_long(l_linger); + + if (val_lonoff < 0 || val_lonoff > USHRT_MAX) { + zend_argument_value_error(4, "\"%s\" must be between 0 and %u", l_onoff_key, USHRT_MAX); + RETURN_THROWS(); + } + + if (val_linger < 0 || val_linger > USHRT_MAX) { + zend_argument_value_error(4, "\"%s\" must be between 0 and %d", l_linger, USHRT_MAX); + RETURN_THROWS(); + } - lv.l_onoff = (unsigned short)Z_LVAL_P(l_onoff); - lv.l_linger = (unsigned short)Z_LVAL_P(l_linger); + lv.l_onoff = (unsigned short)val_lonoff; + lv.l_linger = (unsigned short)val_linger; optlen = sizeof(lv); opt_ptr = &lv; @@ -1899,8 +1917,18 @@ PHP_FUNCTION(socket_set_option) const char sec_key[] = "sec"; const char usec_key[] = "usec"; - convert_to_array(arg4); - opt_ht = Z_ARRVAL_P(arg4); + if (Z_TYPE_P(arg4) != IS_ARRAY) { + if (UNEXPECTED(Z_TYPE_P(arg4) != IS_OBJECT)) { + zend_argument_type_error(4, "must be of type array when argument #3 ($option) is %s, %s given", + optname == SO_RCVTIMEO ? "SO_RCVTIMEO" : "SO_SNDTIMEO", + zend_zval_value_name(arg4)); + RETURN_THROWS(); + } else { + opt_ht = Z_OBJPROP_P(arg4); + } + } else { + opt_ht = Z_ARRVAL_P(arg4); + } if ((sec = zend_hash_str_find(opt_ht, sec_key, sizeof(sec_key) - 1)) == NULL) { zend_argument_value_error(4, "must have key \"%s\"", sec_key); @@ -1911,15 +1939,16 @@ PHP_FUNCTION(socket_set_option) RETURN_THROWS(); } - convert_to_long(sec); - convert_to_long(usec); + zend_long valsec = zval_get_long(sec); + zend_long valusec = zval_get_long(usec); #ifndef PHP_WIN32 - tv.tv_sec = Z_LVAL_P(sec); - tv.tv_usec = Z_LVAL_P(usec); + tv.tv_sec = valsec; + tv.tv_usec = valusec; optlen = sizeof(tv); opt_ptr = &tv; #else - timeout = Z_LVAL_P(sec) * 1000 + Z_LVAL_P(usec) / 1000; + timeout = valsec * 1000 + valusec / 1000; + optlen = sizeof(int); opt_ptr = &timeout; #endif @@ -1971,15 +2000,15 @@ PHP_FUNCTION(socket_set_option) #ifdef SO_ATTACH_REUSEPORT_CBPF case SO_ATTACH_REUSEPORT_CBPF: { - convert_to_long(arg4); + zend_long cbpf_val = zval_get_long(arg4); - if (!Z_LVAL_P(arg4)) { + if (!cbpf_val) { ov = 1; optlen = sizeof(ov); opt_ptr = &ov; optname = SO_DETACH_BPF; } else { - uint32_t k = (uint32_t)Z_LVAL_P(arg4); + uint32_t k = (uint32_t)cbpf_val; static struct sock_filter cbpf[8] = {0}; static struct sock_fprog bpfprog; @@ -2006,8 +2035,7 @@ PHP_FUNCTION(socket_set_option) default: default_case: - convert_to_long(arg4); - ov = Z_LVAL_P(arg4); + ov = zval_get_long(arg4); optlen = sizeof(ov); opt_ptr = &ov; diff --git a/ext/sockets/tests/socket_reuseport_cbpf.phpt b/ext/sockets/tests/socket_reuseport_cbpf.phpt index 1d4824dca1c81..2210c4438f000 100644 --- a/ext/sockets/tests/socket_reuseport_cbpf.phpt +++ b/ext/sockets/tests/socket_reuseport_cbpf.phpt @@ -18,13 +18,20 @@ if (!$socket) { } var_dump(socket_set_option( $socket, SOL_SOCKET, SO_REUSEADDR, true)); var_dump(socket_set_option( $socket, SOL_SOCKET, SO_REUSEPORT, true)); +try { + socket_set_option( $socket, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, array()); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} var_dump(socket_set_option( $socket, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, SKF_AD_CPU)); var_dump(socket_bind($socket, '0.0.0.0')); socket_listen($socket); socket_close($socket); ?> ---EXPECT-- +--EXPECTF-- bool(true) bool(true) + +Warning: socket_set_option(): Unable to set socket option [2]: No such file or directory in %s on line %d bool(true) bool(true) diff --git a/ext/sockets/tests/socket_set_option_timeo_error.phpt b/ext/sockets/tests/socket_set_option_timeo_error.phpt new file mode 100644 index 0000000000000..1db5e01c36228 --- /dev/null +++ b/ext/sockets/tests/socket_set_option_timeo_error.phpt @@ -0,0 +1,66 @@ +--TEST-- +socket_set_option() with SO_RCVTIMEO/SO_SNDTIMEO/SO_LINGER +--EXTENSIONS-- +sockets +--FILE-- + 1, "usec" => "aaaaa"); +$options_2 = array("sec" => new stdClass(), "usec" => "1"); +$options_3 = array("l_onoff" => "aaaa", "l_linger" => "1"); +$options_4 = array("l_onoff" => "1", "l_linger" => []); +$options_5 = array("l_onoff" => PHP_INT_MAX, "l_linger" => "1"); + +try { + socket_set_option( $socket, SOL_SOCKET, SO_RCVTIMEO, new stdClass); +} catch (\ValueError $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + socket_set_option( $socket, SOL_SOCKET, SO_RCVTIMEO, $options_1); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, $options_2); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + socket_set_option( $socket, SOL_SOCKET, SO_RCVTIMEO, "not good"); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + socket_set_option( $socket, SOL_SOCKET, SO_LINGER, "not good neither"); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + socket_set_option( $socket, SOL_SOCKET, SO_LINGER, $options_3); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + socket_set_option( $socket, SOL_SOCKET, SO_LINGER, $options_4); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + socket_set_option( $socket, SOL_SOCKET, SO_LINGER, $options_5); +} catch (\ValueError $e) { + echo $e->getMessage() . PHP_EOL; +} +?> +--EXPECTF-- +socket_set_option(): Argument #4 ($value) must have key "sec" + +Warning: Object of class stdClass could not be converted to int in %s on line %d +socket_set_option(): Argument #4 ($value) must be of type array when argument #3 ($option) is SO_RCVTIMEO, string given +socket_set_option(): Argument #4 ($value) must be of type array when argument #3 ($option) is SO_LINGER, string given +socket_set_option(): Argument #4 ($value) "l_onoff" must be between 0 and %d From 160a4a65ad70bc67e7a0ebc04d375ebab3452bb2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:29:29 +0100 Subject: [PATCH 34/65] Export visibility for promoted property (8.3) --- NEWS | 2 ++ Zend/zend_ast.c | 1 + 2 files changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 6ac803f463980..856e8845f759b 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ PHP NEWS . Fixed bug GH-17106 (ZEND_MATCH_ERROR misoptimization). (ilutov) . Fixed bug GH-17162 (zend_array_try_init() with dtor can cause engine UAF). (nielsdos) + . Fixed bug GH-17101 (AST->string does not reproduce constructor property + promotion correctly). (nielsdos) - DBA: . Skip test if inifile is disabled. (orlitzky) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 525d9dfe9a742..bf602449e5e4e 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2443,6 +2443,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio if (ast->child[3]) { zend_ast_export_attributes(str, ast->child[3], indent, 0); } + zend_ast_export_visibility(str, ast->attr); if (ast->child[0]) { zend_ast_export_type(str, ast->child[0], indent); smart_str_appendc(str, ' '); From f2fdcfc8c37b64696d69206013462eb5c1146304 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Fri, 6 Dec 2024 16:50:57 +0000 Subject: [PATCH 35/65] These were 6bone experimental network allocations, which have been returned to IANA (RFC 3701) --- ext/filter/logical_filters.c | 5 ++--- ext/filter/tests/bug47435.phpt | 8 -------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 0ce2498f3a3a2..f52958769a401 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -954,10 +954,9 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ if (flags & FILTER_FLAG_NO_RES_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) { if ( (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && (ip[7] == 0 || ip[7] == 1)) || - (ip[0] == 0x5f) || (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) || - (ip[0] == 0x2001 && (ip[1] == 0x0db8 || (ip[1] >= 0x0010 && ip[1] <= 0x001f))) || - (ip[0] == 0x3ff3) + (ip[0] == 0x2001 && (ip[1] == 0x0db8 || (ip[1] >= 0x0010 && ip[1] <= 0x001f))) + ) { RETURN_VALIDATION_FAILED } diff --git a/ext/filter/tests/bug47435.phpt b/ext/filter/tests/bug47435.phpt index b035bc0a5f69f..3bf79e1d58d62 100644 --- a/ext/filter/tests/bug47435.phpt +++ b/ext/filter/tests/bug47435.phpt @@ -18,10 +18,6 @@ var_dump(filter_var("2001:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); var_dump(filter_var("2001:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); var_dump(filter_var("240b:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); var_dump(filter_var("240b:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); -var_dump(filter_var("5f::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); -var_dump(filter_var("5f::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); -var_dump(filter_var("3ff3::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); -var_dump(filter_var("3ff3::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); ?> --EXPECT-- string(7) "FC00::1" @@ -38,7 +34,3 @@ string(12) "2001:0010::1" bool(false) string(12) "240b:0010::1" string(12) "240b:0010::1" -string(5) "5f::1" -bool(false) -string(7) "3ff3::1" -bool(false) From d25aac29ceda8e52fc443d7caae583d191c70cc1 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Tue, 10 Dec 2024 14:45:36 +0000 Subject: [PATCH 36/65] Fixed GH-16944: Refactor IP ranges by using the tables from RFC 6890 --- NEWS | 4 + ext/filter/logical_filters.c | 212 +++++++++++++++++++++------------- ext/filter/tests/gh16944.phpt | 10 ++ 3 files changed, 145 insertions(+), 81 deletions(-) create mode 100644 ext/filter/tests/gh16944.phpt diff --git a/NEWS b/NEWS index 856e8845f759b..540a3a1b3bf80 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,10 @@ PHP NEWS - FFI: . Fixed bug #79075 (FFI header parser chokes on comments). (nielsdos) +- Filter: + . Fixed bug GH-16944 (Fix filtering special IPv4 and IPv6 ranges, by using + information from RFC 6890). (Derick) + - FPM: . Fixed bug GH-13437 (FPM: ERROR: scoreboard: failed to lock (already locked)). (Jakub Zelenka) diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index f52958769a401..85d52934768f8 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -869,15 +869,115 @@ static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]) } /* }}} */ -void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ +/* From the tables in RFC 6890 - Special-Purpose IP Address Registries */ +static bool ipv4_get_status_flags(const int ip[8], bool *global, bool *reserved, bool *private) +{ + *global = false; + *reserved = false; + *private = false; + + if (ip[0] == 0) { + /* RFC 1122 - This host on this network */ + *reserved = true; + } else if (ip[0] == 10) { + /* RFC 1918 - Private Use */ + *private = true; + } else if (ip[0] == 100 && ip[1] >= 64 && ip[1] <= 127) { + /* RFC 6598 - Shared Address Space */ + } else if (ip[0] == 127) { + /* RFC 1122 - Loopback */ + *reserved = true; + } else if (ip[0] == 169 && ip[1] == 254) { + /* RFC 3927 - Link Local */ + *reserved = true; + } else if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) { + /* RFC 1918 - Private Use */ + *private = true; + } else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 0) { + /* RFC 6890 - IETF Protocol Assignments */ + } else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 0 && ip[3] >= 0 && ip[3] <= 7) { + /* RFC 6333 - DS-Lite */ + } else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 2) { + /* RFC 5737 - Documentation */ + } else if (ip[0] == 192 && ip[1] == 88 && ip[2] == 99) { + /* RFC 3068 - 6to4 Relay Anycast */ + *global = true; + } else if (ip[0] == 192 && ip[1] == 168) { + /* RFC 1918 - Private Use */ + *private = true; + } else if (ip[0] == 198 && ip[1] >= 18 && ip[1] <= 19) { + /* RFC 2544 - Benchmarking */ + } else if (ip[0] == 198 && ip[1] == 51 && ip[2] == 100) { + /* RFC 5737 - Documentation */ + } else if (ip[0] == 203 && ip[1] == 0 && ip[2] == 113) { + /* RFC 5737 - Documentation */ + } else if (ip[0] >= 240 && ip[1] <= 255) { + /* RFC 1122 - Reserved */ + *reserved = true; + } else if (ip[0] == 255 && ip[1] == 255 && ip[2] == 255 && ip[3] == 255) { + /* RFC 0919 - Limited Broadcast */ + } else { + return false; + } + + return true; +} + +/* From the tables in RFC 6890 - Special-Purpose IP Address Registries */ +static bool ipv6_get_status_flags(const int ip[8], bool *global, bool *reserved, bool *private) { - /* validates an ipv4 or ipv6 IP, based on the flag (4, 6, or both) add a - * flag to throw out reserved ranges; multicast ranges... etc. If both - * allow_ipv4 and allow_ipv6 flags flag are used, then the first dot or - * colon determine the format */ + *global = false; + *reserved = false; + *private = false; + + if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 0) { + /* RFC 4291 - Unspecified Address */ + *reserved = true; + } else if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 1) { + /* RFC 4291 - Loopback Address */ + *reserved = true; + } else if (ip[0] == 0x0064 && ip[1] == 0xff9b) { + /* RFC 6052 - IPv4-IPv6 Translation */ + *global = true; + } else if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0xffff) { + /* RFC 4291 - IPv4-mapped Address */ + *reserved = true; + } else if (ip[0] == 0x0100 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) { + /* RFC 6666 - Discard-Only Address Block */ + } else if (ip[0] == 0x2001 && ip[1] == 0x0000) { + /* RFC 4380 - TEREDO */ + } else if (ip[0] == 0x2001 && ip[1] <= 0x01ff) { + /* RFC 2928 - IETF Protocol Assignments */ + } else if (ip[0] == 0x2001 && ip[1] == 0x0002 && ip[2] == 0) { + /* RFC 5180 - Benchmarking */ + } else if (ip[0] == 0x2001 && ip[1] == 0x0db8) { + /* RFC 3849 - Documentation */ + } else if (ip[0] == 0x2001 && ip[1] >= 0x0010 && ip[1] <= 0x001f) { + /* RFC 4843 - ORCHID */ + } else if (ip[0] == 0x2002) { + /* RFC 3056 - 6to4 */ + } else if (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) { + /* RFC 4193 - Unique-Local */ + *private = true; + } else if (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) { + /* RFC 4291 - Linked-Scoped Unicast */ + *reserved = true; + } else { + return false; + } - int ip[8]; - int mode; + return true; +} + +/* Validates an ipv4 or ipv6 IP, based on the flag (4, 6, or both) add a flag + * to throw out reserved ranges; multicast ranges... etc. If both allow_ipv4 + * and allow_ipv6 flags flag are used, then the first dot or colon determine + * the format */ +void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ +{ + int ip[8]; + int mode; + bool flag_global, flag_reserved, flag_private; /* flags for ranges as determined by RFC 6890 */ if (memchr(Z_STRVAL_P(value), ':', Z_STRLEN_P(value))) { mode = FORMAT_IPV6; @@ -895,85 +995,35 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ RETURN_VALIDATION_FAILED } - switch (mode) { - case FORMAT_IPV4: - if (!_php_filter_validate_ipv4(Z_STRVAL_P(value), Z_STRLEN_P(value), ip)) { - RETURN_VALIDATION_FAILED - } + if (mode == FORMAT_IPV4) { + if (!_php_filter_validate_ipv4(Z_STRVAL_P(value), Z_STRLEN_P(value), ip)) { + RETURN_VALIDATION_FAILED + } - /* Check flags */ - if (flags & FILTER_FLAG_NO_PRIV_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) { - if ( - (ip[0] == 10) || - (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) || - (ip[0] == 192 && ip[1] == 168) - ) { - RETURN_VALIDATION_FAILED - } - } + if (!ipv4_get_status_flags(ip, &flag_global, &flag_reserved, &flag_private)) { + return; /* no special block */ + } + } + else if (mode == FORMAT_IPV6) { + if (_php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value), ip) < 1) { + RETURN_VALIDATION_FAILED + } - if (flags & FILTER_FLAG_NO_RES_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) { - if ( - (ip[0] == 0) || - (ip[0] >= 240) || - (ip[0] == 127) || - (ip[0] == 169 && ip[1] == 254) - ) { - RETURN_VALIDATION_FAILED - } - } + if (!ipv6_get_status_flags(ip, &flag_global, &flag_reserved, &flag_private)) { + return; /* no special block */ + } + } - if (flags & FILTER_FLAG_GLOBAL_RANGE) { - if ( - (ip[0] == 100 && ip[1] >= 64 && ip[1] <= 127 ) || - (ip[0] == 192 && ip[1] == 0 && ip[2] == 0 ) || - (ip[0] == 192 && ip[1] == 0 && ip[2] == 2 ) || - (ip[0] == 198 && ip[1] >= 18 && ip[1] <= 19 ) || - (ip[0] == 198 && ip[1] == 51 && ip[2] == 100 ) || - (ip[0] == 203 && ip[1] == 0 && ip[2] == 113 ) - ) { - RETURN_VALIDATION_FAILED - } - } + if ((flags & FILTER_FLAG_GLOBAL_RANGE) && flag_global != true) { + RETURN_VALIDATION_FAILED + } - break; + if ((flags & FILTER_FLAG_NO_PRIV_RANGE) && flag_private == true) { + RETURN_VALIDATION_FAILED + } - case FORMAT_IPV6: - { - int res = 0; - res = _php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value), ip); - if (res < 1) { - RETURN_VALIDATION_FAILED - } - /* Check flags */ - if (flags & FILTER_FLAG_NO_PRIV_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) { - if (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) { - RETURN_VALIDATION_FAILED - } - } - if (flags & FILTER_FLAG_NO_RES_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) { - if ( - (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && (ip[7] == 0 || ip[7] == 1)) || - (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) || - (ip[0] == 0x2001 && (ip[1] == 0x0db8 || (ip[1] >= 0x0010 && ip[1] <= 0x001f))) - - ) { - RETURN_VALIDATION_FAILED - } - } - if (flags & FILTER_FLAG_GLOBAL_RANGE) { - if ( - (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0xffff) || - (ip[0] == 0x0100 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) || - (ip[0] == 0x2001 && ip[1] <= 0x01ff) || - (ip[0] == 0x2001 && ip[1] == 0x0002 && ip[2] == 0) || - (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) - ) { - RETURN_VALIDATION_FAILED - } - } - } - break; + if ((flags & FILTER_FLAG_NO_RES_RANGE) && flag_reserved == true) { + RETURN_VALIDATION_FAILED } } /* }}} */ diff --git a/ext/filter/tests/gh16944.phpt b/ext/filter/tests/gh16944.phpt new file mode 100644 index 0000000000000..f6c492fed1abb --- /dev/null +++ b/ext/filter/tests/gh16944.phpt @@ -0,0 +1,10 @@ +--TEST-- +Bug GH-16944 (Invalid filtering of IPv6 with FILTER_FLAG_NO_RES_RANGE) +--EXTENSIONS-- +filter +--FILE-- + +--EXPECT-- +bool(false) From 9d1deb97fffb94f2a0d8681447df328933da0e4e Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Fri, 13 Dec 2024 17:09:06 +0000 Subject: [PATCH 37/65] Include changes from RFC 6890 errata --- ext/filter/logical_filters.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 85d52934768f8..1bd9bad5afbe1 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -869,7 +869,8 @@ static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]) } /* }}} */ -/* From the tables in RFC 6890 - Special-Purpose IP Address Registries */ +/* From the tables in RFC 6890 - Special-Purpose IP Address Registriesi + * Including errata: https://www.rfc-editor.org/errata_search.php?rfc=6890&rec_status=1 */ static bool ipv4_get_status_flags(const int ip[8], bool *global, bool *reserved, bool *private) { *global = false; @@ -877,6 +878,9 @@ static bool ipv4_get_status_flags(const int ip[8], bool *global, bool *reserved, *private = false; if (ip[0] == 0) { + /* RFC 0791 - This network */ + *reserved = true; + } else if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) { /* RFC 1122 - This host on this network */ *reserved = true; } else if (ip[0] == 10) { @@ -915,7 +919,8 @@ static bool ipv4_get_status_flags(const int ip[8], bool *global, bool *reserved, /* RFC 1122 - Reserved */ *reserved = true; } else if (ip[0] == 255 && ip[1] == 255 && ip[2] == 255 && ip[3] == 255) { - /* RFC 0919 - Limited Broadcast */ + /* RFC 0919 - Limited Broadcast, Updated by RFC 8190, 2.2. */ + *reserved = true; } else { return false; } From e54c9e6cd340d50f3b33cb1b2288194958efde83 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Tue, 10 Dec 2024 14:45:03 +0000 Subject: [PATCH 38/65] Per RFC 6890, these are explicitly not reserved ranges --- ext/filter/tests/bug47435.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/filter/tests/bug47435.phpt b/ext/filter/tests/bug47435.phpt index 3bf79e1d58d62..64ee6f7fe66f7 100644 --- a/ext/filter/tests/bug47435.phpt +++ b/ext/filter/tests/bug47435.phpt @@ -29,8 +29,8 @@ bool(false) string(11) "fe80:5:6::1" bool(false) string(12) "2001:0db8::1" -bool(false) +string(12) "2001:0db8::1" +string(12) "2001:0010::1" string(12) "2001:0010::1" -bool(false) string(12) "240b:0010::1" string(12) "240b:0010::1" From 3b154eb88c6700414ab46ed0ccc2a6d4fee4dde7 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Tue, 10 Dec 2024 15:06:17 +0000 Subject: [PATCH 39/65] Consistent naming for test titles --- ext/filter/tests/{PMOPB45.phpt => CVE-2007-1900.phpt} | 2 +- ext/filter/tests/bug42718-2.phpt | 2 +- ext/filter/tests/bug49274.phpt | 2 +- ext/filter/tests/bug49510.phpt | 2 +- ext/filter/tests/bug50632.phpt | 2 +- ext/filter/tests/bug51192.phpt | 2 +- ext/filter/tests/bug51368.phpt | 2 +- ext/filter/tests/bug64441.phpt | 2 +- ext/filter/tests/bug67167.01.phpt | 2 +- ext/filter/tests/bug67167.02.phpt | 2 +- ext/filter/tests/bug7715.phpt | 2 +- ext/filter/tests/bug7733.phpt | 2 +- ext/filter/tests/bug80584.phpt | 2 +- ext/filter/tests/bug8315.phpt | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) rename ext/filter/tests/{PMOPB45.phpt => CVE-2007-1900.phpt} (72%) diff --git a/ext/filter/tests/PMOPB45.phpt b/ext/filter/tests/CVE-2007-1900.phpt similarity index 72% rename from ext/filter/tests/PMOPB45.phpt rename to ext/filter/tests/CVE-2007-1900.phpt index 9aba547539718..d18fd4e5026f5 100644 --- a/ext/filter/tests/PMOPB45.phpt +++ b/ext/filter/tests/CVE-2007-1900.phpt @@ -1,5 +1,5 @@ --TEST-- -PMOPB-45-2007:PHP ext/filter Email Validation Vulnerability +CVE-2007-1900: PHP ext/filter Email Validation Vulnerability --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug42718-2.phpt b/ext/filter/tests/bug42718-2.phpt index 675ce1a747d89..ad8383d85db2e 100644 --- a/ext/filter/tests/bug42718-2.phpt +++ b/ext/filter/tests/bug42718-2.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #42718 - 2 (unsafe_raw filter not applied when configured as default filter) +Bug #42718 (unsafe_raw filter not applied when configured as default filter) --EXTENSIONS-- filter --INI-- diff --git a/ext/filter/tests/bug49274.phpt b/ext/filter/tests/bug49274.phpt index 12980db404177..3a2b434b0bc7d 100644 --- a/ext/filter/tests/bug49274.phpt +++ b/ext/filter/tests/bug49274.phpt @@ -1,5 +1,5 @@ --TEST-- -#49274, fatal error when an object does not implement toString +Bug #49274 (fatal error when an object does not implement toString) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug49510.phpt b/ext/filter/tests/bug49510.phpt index 2a45915561161..b34afffcc03b9 100644 --- a/ext/filter/tests/bug49510.phpt +++ b/ext/filter/tests/bug49510.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #49510 boolean validation fails with FILTER_NULL_ON_FAILURE +Bug #49510 (boolean validation fails with FILTER_NULL_ON_FAILURE) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug50632.phpt b/ext/filter/tests/bug50632.phpt index 5fa35c4d0bb69..07eae7aa54d35 100644 --- a/ext/filter/tests/bug50632.phpt +++ b/ext/filter/tests/bug50632.phpt @@ -1,5 +1,5 @@ --TEST-- -bug 50632, filter_input() does not return default value if the variable does not exist +Bug #50632 (filter_input() does not return default value if the variable does not exist) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug51192.phpt b/ext/filter/tests/bug51192.phpt index 397f22d1d8817..fd4fd09c4422a 100644 --- a/ext/filter/tests/bug51192.phpt +++ b/ext/filter/tests/bug51192.phpt @@ -1,5 +1,5 @@ --TEST-- -bug 51192, FILTER_VALIDATE_URL will invalidate a hostname that includes '-' +Bug #51192 (FILTER_VALIDATE_URL will invalidate a hostname that includes '-') --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug51368.phpt b/ext/filter/tests/bug51368.phpt index 73c743b5dee4f..a7bc26d478122 100644 --- a/ext/filter/tests/bug51368.phpt +++ b/ext/filter/tests/bug51368.phpt @@ -1,5 +1,5 @@ --TEST-- -FR #51368 (php_filter_float does not allow custom thousand separators) +Bug #51368 (php_filter_float does not allow custom thousand separators) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug64441.phpt b/ext/filter/tests/bug64441.phpt index 189f79d00c4ef..2dcc2a0f4fbcc 100644 --- a/ext/filter/tests/bug64441.phpt +++ b/ext/filter/tests/bug64441.phpt @@ -1,5 +1,5 @@ --TEST-- -bug 64441, FILTER_VALIDATE_URL will invalidate a hostname that ended by dot +Bug #64441 (FILTER_VALIDATE_URL will invalidate a hostname that ended by dot) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug67167.01.phpt b/ext/filter/tests/bug67167.01.phpt index f51ccbe0dfc95..a55fabda304f1 100644 --- a/ext/filter/tests/bug67167.01.phpt +++ b/ext/filter/tests/bug67167.01.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #67167: object with VALIDATE_BOOLEAN and NULL_ON_FAILURE +Bug #67167 (object with VALIDATE_BOOLEAN and NULL_ON_FAILURE) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug67167.02.phpt b/ext/filter/tests/bug67167.02.phpt index 62927309e62a4..acbb7eeca5fe7 100644 --- a/ext/filter/tests/bug67167.02.phpt +++ b/ext/filter/tests/bug67167.02.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #67167: filter_var(null,FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE) returns null +Bug #67167 (filter_var(null,FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE) returns null) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug7715.phpt b/ext/filter/tests/bug7715.phpt index 7c31b30d91c4b..9855a723fe987 100644 --- a/ext/filter/tests/bug7715.phpt +++ b/ext/filter/tests/bug7715.phpt @@ -1,5 +1,5 @@ --TEST-- -bug 7715, floats value with integer or incomplete input +Bug #7715 (floats value with integer or incomplete input) --INI-- precision=14 --EXTENSIONS-- diff --git a/ext/filter/tests/bug7733.phpt b/ext/filter/tests/bug7733.phpt index a276d8a2b4a31..7a24decea2c54 100644 --- a/ext/filter/tests/bug7733.phpt +++ b/ext/filter/tests/bug7733.phpt @@ -1,5 +1,5 @@ --TEST-- -filter_var() Float exponential weird result +Bug #7733 (filter_var() Float exponential weird result) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug80584.phpt b/ext/filter/tests/bug80584.phpt index 49fa71a921f05..9446c649f4bad 100644 --- a/ext/filter/tests/bug80584.phpt +++ b/ext/filter/tests/bug80584.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #80584: "0x" and "0X" are considered valid hex numbers by filter_var() +Bug #80584 ("0x" and "0X" are considered valid hex numbers by filter_var()) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug8315.phpt b/ext/filter/tests/bug8315.phpt index e5334f43fc87f..55e1a745fa041 100644 --- a/ext/filter/tests/bug8315.phpt +++ b/ext/filter/tests/bug8315.phpt @@ -1,5 +1,5 @@ --TEST-- -bug 8315, NULL values halt the validation +Bug #8315 (NULL values halt the validation) --EXTENSIONS-- filter --FILE-- From 53b69ba8cfd6c96f3095a60eac2216d490427c46 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 8 Dec 2024 18:25:41 +0100 Subject: [PATCH 40/65] Fix GH-17067: glob:// wrapper doesn't cater to CWD for ZTS builds `glob(3)` doesn't know the virtual CWD of PHP, so we need to pass an absolute path for ZTS builds. In lack of a reusable routine, we copy the code from `glob()` and adapt as needed. Closes GH-17074. --- NEWS | 2 ++ ext/standard/tests/streams/gh17067.phpt | 22 ++++++++++++++ main/streams/glob_wrapper.c | 39 ++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/streams/gh17067.phpt diff --git a/NEWS b/NEWS index 540a3a1b3bf80..5c6b7bbe4f616 100644 --- a/NEWS +++ b/NEWS @@ -63,6 +63,8 @@ PHP NEWS to incorrect error handling). (nielsdos) . Fixed bug GH-16810 (overflow on fopen HTTP wrapper timeout value). (David Carlier) + . Fixed bug GH-17067 (glob:// wrapper doesn't cater to CWD for ZTS builds). + (cmb) - Windows: . Hardened proc_open() against cmd.exe hijacking. (cmb) diff --git a/ext/standard/tests/streams/gh17067.phpt b/ext/standard/tests/streams/gh17067.phpt new file mode 100644 index 0000000000000..f3ea6a682a5ef --- /dev/null +++ b/ext/standard/tests/streams/gh17067.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-17067 (glob:// wrapper doesn't cater to CWD for ZTS builds) +--FILE-- + +--CLEAN-- + +--EXPECT-- +array(1) { + [0]=> + string(3) "foo" +} diff --git a/main/streams/glob_wrapper.c b/main/streams/glob_wrapper.c index 66f2e8e0dfc70..8772850f784f3 100644 --- a/main/streams/glob_wrapper.c +++ b/main/streams/glob_wrapper.c @@ -225,10 +225,32 @@ static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const cha *opened_path = zend_string_init(path, strlen(path), 0); } } + const char *pattern = path; +#ifdef ZTS + char cwd[MAXPATHLEN]; + char work_pattern[MAXPATHLEN]; + char *result; + size_t cwd_skip = 0; + if (!IS_ABSOLUTE_PATH(path, strlen(path))) { + result = VCWD_GETCWD(cwd, MAXPATHLEN); + if (!result) { + cwd[0] = '\0'; + } +# ifdef PHP_WIN32 + if (IS_SLASH(*path)) { + cwd[2] = '\0'; + } +# endif + cwd_skip = strlen(cwd)+1; + + snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, path); + pattern = work_pattern; + } +#endif pglob = ecalloc(1, sizeof(*pglob)); - if (0 != (ret = glob(path, pglob->flags & GLOB_FLAGMASK, NULL, &pglob->glob))) { + if (0 != (ret = glob(pattern, pglob->flags & GLOB_FLAGMASK, NULL, &pglob->glob))) { #ifdef GLOB_NOMATCH if (GLOB_NOMATCH != ret) #endif @@ -238,6 +260,21 @@ static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const cha } } +#ifdef ZTS + if (cwd_skip > 0) { + /* strip prepended CWD */ + for (i = 0; i < pglob->glob.gl_pathc; i++) { + char *p = pglob->glob.gl_pathv[i]; + char *q = p + cwd_skip; + char *e = p + strlen(pglob->glob.gl_pathv[i]) - 1; + while (q <= e) { + *p++ = *q++; + } + *p = '\0'; + } + } +#endif + /* if open_basedir in use, check and filter restricted paths */ if ((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) { pglob->open_basedir_used = true; From 0221ceeccd467f21a48f18d4e493174eef9a5b03 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 19 Dec 2024 09:11:10 -0500 Subject: [PATCH 41/65] ext/gettext/gettext.c: handle NULLs from bindtextdomain() According to POSIX, bindtextdomain() returns "the implementation- defined default directory pathname used by the gettext family of functions" when its second parameter is NULL (i.e. when you are querying the directory corresponding to some text domain and that directory has not yet been set). Its PHP counterpart is feeding that result direclty to RETURN_STRING, but this can go wrong in two ways: 1. If an error occurs, even POSIX-compliant implementations may return NULL. 2. At least one non-compliant implementation (musl) lacks a default directory and returns NULL whenever the domain has not yet been bound. In either of those cases, PHP segfaults on the NULL string. In this commit we check for the NULL, and RETURN_FALSE when it happens rather than crashing. This partially addresses GH #13696 --- ext/gettext/gettext.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ext/gettext/gettext.c b/ext/gettext/gettext.c index 15af3cb9b57ff..eba2a591e9f1b 100644 --- a/ext/gettext/gettext.c +++ b/ext/gettext/gettext.c @@ -167,7 +167,7 @@ PHP_FUNCTION(bindtextdomain) char *domain; size_t domain_len; zend_string *dir = NULL; - char *retval, dir_name[MAXPATHLEN]; + char *retval, dir_name[MAXPATHLEN], *btd_result; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS!", &domain, &domain_len, &dir) == FAILURE) { RETURN_THROWS(); @@ -181,7 +181,16 @@ PHP_FUNCTION(bindtextdomain) } if (dir == NULL) { - RETURN_STRING(bindtextdomain(domain, NULL)); + btd_result = bindtextdomain(domain, NULL); + if (btd_result == NULL) { + /* POSIX-compliant implementations can return + * NULL if an error occured. On musl you will + * also get NULL if the domain is not yet + * bound, because musl has no default directory + * to return in that case. */ + RETURN_FALSE; + } + RETURN_STRING(btd_result); } if (ZSTR_LEN(dir) != 0 && !zend_string_equals_literal(dir, "0")) { From bfb0e367f2a2823149be67fad00b66511750f2bf Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 19 Dec 2024 09:17:16 -0500 Subject: [PATCH 42/65] ext/gettext/tests: fix libintl return values under musl Musl has two quirks that are leading to failed internationalization tests. First is that the return value of bindtextdomain(..., NULL) will always be false, rather than an "implementation-defined default directory," because musl does not have an implementation-defined default directory. One test needs a special case for this. Second is that the musl implementation of bind_textdomain_codeset() always returns NULL. The POSIX-correctness of this is debatable, but it is roughly equivalent to correct, because musl only support UTF-8, so the NULL value indicating that the codeset is unchanged from the locale's codeset (UTF-8) is accurate. PHP's bind_textdomain_codeset() function however treats NULL as failure, unconditionally: * https://github.com/php/doc-en/issues/4311 * https://github.com/php/php-src/issues/17163 This unfortunately causes false to be returned consistently on musl -- even when nothing unexpected has happened -- and naturally this is affecting several tests. For now we change two tests to accept "false" in addition to "UTF-8" so that they may pass on musl. If PHP's bind_textdomain_codeset() is updated to differentiate between NULL and NULL-with-errno-set, these tests can also be updated once again to reject the NULL-with-errno result. This partially addresses GH #13696 --- ext/gettext/tests/bug53251.phpt | 28 +++++++++++++------ ...ettext_bind_textdomain_codeset-retval.phpt | 12 ++++++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/ext/gettext/tests/bug53251.phpt b/ext/gettext/tests/bug53251.phpt index 6f37642925d37..d568be6bc079a 100644 --- a/ext/gettext/tests/bug53251.phpt +++ b/ext/gettext/tests/bug53251.phpt @@ -8,18 +8,28 @@ if (getenv('SKIP_REPEAT')) die('skip gettext leaks global state across requests' ?> --FILE-- --EXPECT-- bool(true) -bool(true) -bool(false) -string(5) "UTF-8" -string(5) "UTF-8" diff --git a/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt b/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt index 47c821648fccb..941bab79bffa2 100644 --- a/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt +++ b/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt @@ -5,13 +5,21 @@ gettext --FILE-- --EXPECT-- bool(false) -string(5) "UTF-8" +bool(true) Done --CREDITS-- Florian Holzhauer fh-pt@fholzhauer.de From 471e94ce612ebf9b2f34490804f32c1ad89b6f33 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 19 Dec 2024 09:20:06 -0500 Subject: [PATCH 43/65] ext/gettext/config.m4: symlink en_US.UTF-8 test bits to en_US for musl The gettext() family of functions under musl does not support codeset suffixes like ".UTF-8", because the only codeset it understands is UTF-8. (Yes, it is annoying that it doesn't support the suffix for the codeset that it does understand; no, I am not in charge.) Thanks to this, we have six failing tests on musl, * FAIL Gettext basic test with en_US locale that should be on nearly every system [ext/gettext/tests/gettext_basic-enus.phpt] * FAIL Test if bindtextdomain() returns string id if no directory path is set( if directory path is 'null') [ext/gettext/tests/gettext_bindtextdomain-cwd.phpt] * FAIL Test dcgettext() functionality [ext/gettext/tests/gettext_dcgettext.phpt] * FAIL Test dgettext() functionality [ext/gettext/tests/gettext_dgettext.phpt] * FAIL Test if dngettext() returns the correct translations (optionally plural). [ext/gettext/tests/gettext_dngettext-plural.phpt] * FAIL Test ngettext() functionality [ext/gettext/tests/gettext_ngettext.phpt] These are all fixed by symlinking the en_US.UTF-8 message data to en_US, where musl is able to find it. This does not make the situation any better for developers (who don't know what libc their users will be running), but that problem is inhereted from C and is not the fault of the gettext extension. This partially addresses GH #13696 --- .gitignore | 3 +++ ext/gettext/config.m4 | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index 49acc9f2e1788..449963153f36b 100644 --- a/.gitignore +++ b/.gitignore @@ -177,6 +177,9 @@ php /ext/*/configure.ac /ext/*/run-tests.php +# Generated by ./configure if libc might be musl +/ext/gettext/tests/locale/en_US + # ------------------------------------------------------------------------------ # Generated by Windows build system # ------------------------------------------------------------------------------ diff --git a/ext/gettext/config.m4 b/ext/gettext/config.m4 index 9e304d82b8d29..b3a6c35d6c006 100644 --- a/ext/gettext/config.m4 +++ b/ext/gettext/config.m4 @@ -25,6 +25,17 @@ if test "$PHP_GETTEXT" != "no"; then AC_CHECK_LIB(c, bindtextdomain, [ GETTEXT_LIBS= GETTEXT_CHECK_IN_LIB=c + + dnl If libintl.h is provided by libc, it's possible that libc is musl. + dnl The gettext family of functions under musl ignores the codeset + dnl suffix on directories like "en_US.UTF-8"; instead they look only + dnl in "en_US". To accomodate that, we symlink some test data from one + dnl to the other. + AC_MSG_NOTICE([symlinking en_US.UTF-8 messages to en_US in case you are on musl]) + _linkdest="${srcdir%/}"/ext/gettext/tests/locale/en_US + AS_IF([test ! -e "${_linkdest}"],[ + ln -s en_US.UTF-8 "${_linkdest}" + ]) ],[ AC_MSG_ERROR(Unable to find required gettext library) ]) From a23ecc0a7592621916e974048b0f918c39478c5c Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 19 Dec 2024 18:30:17 +0100 Subject: [PATCH 44/65] NEWS for GH-17168 --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 5c6b7bbe4f616..00332140e87c5 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,10 @@ PHP NEWS . Fixed bug GH-16255 (Unexpected nan value in ext/gd/libgd/gd_filter.c). (nielsdos, cmb) +- Gettext: + . Fixed bug GH-17202 (Segmentation fault ext/gettext/gettext.c + bindtextdomain()). (Michael Orlitzky) + - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) From 16c0e57530005031e7494cd6e86e965da952f268 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 29 Jun 2024 06:46:14 +0100 Subject: [PATCH 45/65] Fix GH-14709 overflow on recurrences for DatePeriod::__construct close GH-14710 --- NEWS | 4 ++++ ext/date/php_date.c | 19 ++++++++++++---- ...eriod_wrong_recurrence_on_constructor.phpt | 6 ++--- .../tests/date_period_bad_iso_format.phpt | 6 ++--- ext/date/tests/gh14709.phpt | 22 +++++++++++++++++++ 5 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 ext/date/tests/gh14709.phpt diff --git a/NEWS b/NEWS index 00332140e87c5..cb27125a7e15f 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,10 @@ PHP NEWS . Fixed bug GH-17101 (AST->string does not reproduce constructor property promotion correctly). (nielsdos) +- Date: + . Fixed bug GH-14709 DatePeriod::__construct() overflow on recurrences. + (David Carlier) + - DBA: . Skip test if inifile is disabled. (orlitzky) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 7cff8e13e23d5..436af32f93536 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -4903,9 +4903,11 @@ static bool date_period_init_iso8601_string(php_period_obj *dpobj, zend_class_en static bool date_period_init_finish(php_period_obj *dpobj, zend_long options, zend_long recurrences) { - if (dpobj->end == NULL && recurrences < 1) { + const zend_long max_recurrences = (INT_MAX - 8); + + if (dpobj->end == NULL && (recurrences < 1 || recurrences > max_recurrences)) { zend_string *func = get_active_function_or_method_name(); - zend_throw_exception_ex(date_ce_date_malformed_period_string_exception, 0, "%s(): Recurrence count must be greater than 0", ZSTR_VAL(func)); + zend_throw_exception_ex(date_ce_date_malformed_period_string_exception, 0, "%s(): Recurrence count must be greater or equal to 1 and lower than " ZEND_LONG_FMT, ZSTR_VAL(func), max_recurrences + 1); zend_string_release(func); return false; } @@ -4914,8 +4916,17 @@ static bool date_period_init_finish(php_period_obj *dpobj, zend_long options, ze dpobj->include_start_date = !(options & PHP_DATE_PERIOD_EXCLUDE_START_DATE); dpobj->include_end_date = options & PHP_DATE_PERIOD_INCLUDE_END_DATE; - /* recurrrences */ - dpobj->recurrences = recurrences + dpobj->include_start_date + dpobj->include_end_date; + /* recurrences */ + recurrences += dpobj->include_start_date + dpobj->include_end_date; + + if (UNEXPECTED(recurrences > max_recurrences)) { + zend_string *func = get_active_function_or_method_name(); + zend_throw_exception_ex(date_ce_date_malformed_string_exception, 0, "%s(): Recurrence count must be greater or equal to 1 and lower than " ZEND_LONG_FMT " (including options)", ZSTR_VAL(func), max_recurrences + 1); + zend_string_release(func); + return false; + } + + dpobj->recurrences = (int)recurrences; dpobj->initialized = 1; diff --git a/ext/date/tests/DatePeriod_wrong_recurrence_on_constructor.phpt b/ext/date/tests/DatePeriod_wrong_recurrence_on_constructor.phpt index f2e121db389a3..31c6868d67fb2 100644 --- a/ext/date/tests/DatePeriod_wrong_recurrence_on_constructor.phpt +++ b/ext/date/tests/DatePeriod_wrong_recurrence_on_constructor.phpt @@ -15,6 +15,6 @@ try { } ?> ---EXPECT-- -DatePeriod::__construct(): Recurrence count must be greater than 0 -DatePeriod::__construct(): Recurrence count must be greater than 0 +--EXPECTF-- +DatePeriod::__construct(): Recurrence count must be greater or equal to 1 and lower than %d +DatePeriod::__construct(): Recurrence count must be greater or equal to 1 and lower than %d diff --git a/ext/date/tests/date_period_bad_iso_format.phpt b/ext/date/tests/date_period_bad_iso_format.phpt index dde177e739871..92b82f9962d19 100644 --- a/ext/date/tests/date_period_bad_iso_format.phpt +++ b/ext/date/tests/date_period_bad_iso_format.phpt @@ -40,10 +40,10 @@ try { } ?> ---EXPECT-- +--EXPECTF-- DateMalformedPeriodStringException: DatePeriod::__construct(): ISO interval must contain a start date, "R4" given DateMalformedPeriodStringException: DatePeriod::createFromISO8601String(): ISO interval must contain a start date, "R4" given DateMalformedPeriodStringException: DatePeriod::__construct(): ISO interval must contain an interval, "R4/2012-07-01T00:00:00Z" given DateMalformedPeriodStringException: DatePeriod::createFromISO8601String(): ISO interval must contain an interval, "R4/2012-07-01T00:00:00Z" given -DateMalformedPeriodStringException: DatePeriod::__construct(): Recurrence count must be greater than 0 -DateMalformedPeriodStringException: DatePeriod::createFromISO8601String(): Recurrence count must be greater than 0 +DateMalformedPeriodStringException: DatePeriod::__construct(): Recurrence count must be greater or equal to 1 and lower than %d +DateMalformedPeriodStringException: DatePeriod::createFromISO8601String(): Recurrence count must be greater or equal to 1 and lower than %d diff --git a/ext/date/tests/gh14709.phpt b/ext/date/tests/gh14709.phpt new file mode 100644 index 0000000000000..4669d74d4f275 --- /dev/null +++ b/ext/date/tests/gh14709.phpt @@ -0,0 +1,22 @@ +--TEST-- +Bug GH-14709 overflow on reccurences parameter +--FILE-- +getMessage(), PHP_EOL; +} + +try { + new DatePeriod($start, $interval, 2147483639, DatePeriod::EXCLUDE_START_DATE | DatePeriod::INCLUDE_END_DATE); +} catch (Exception $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +?> +--EXPECTF-- +DateMalformedPeriodStringException: DatePeriod::__construct(): Recurrence count must be greater or equal to 1 and lower than %d +DateMalformedStringException: DatePeriod::__construct(): Recurrence count must be greater or equal to 1 and lower than %d (including options) From 6f579934f0d4391af0eab961d612929b71320b39 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 19 Dec 2024 19:43:00 +0100 Subject: [PATCH 46/65] Add observer temporary to dl'ed functions When observer is enabled, we normally add an extra temporary to all functions, to store the previously observed frame. However, this is done in zend_observer_post_startup() so it doesn't happen to dl'ed() functions. One possible fix would be to move that from zend_observer_post_startup() to zend_register_functions(), but this would be too early: Observer may not be enabled when zend_register_functions() is called, and may still be enabled later. However, when zend_register_functions() is called at run-time (during dl()), we know definitively whether observer is enabled. Here I update zend_register_functions() to add a temporary to dl'ed() functions when observer is enabled. Fixes: GH-17211 Closes: GH-17220 --- NEWS | 3 ++ Zend/zend_API.c | 9 +++- ext/dl_test/dl_test.c | 18 ++++++++ ext/dl_test/dl_test.stub.php | 4 ++ ext/dl_test/dl_test_arginfo.h | 21 +++++++++- .../tests/general_functions/gh17211.phpt | 42 +++++++++++++++++++ 6 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 ext/standard/tests/general_functions/gh17211.phpt diff --git a/NEWS b/NEWS index f7565624ad7b0..7230fc5bd3b6c 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.2.28 +- Core: + . Fixed bug GH-17211 (observer segfault on function loaded with dl()). + (Arnaud) 19 Dec 2024, PHP 8.2.27 diff --git a/Zend/zend_API.c b/Zend/zend_API.c index eb6e349f7ec21..6aeb2d653faaf 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2718,7 +2718,14 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend } internal_function->type = ZEND_INTERNAL_FUNCTION; internal_function->module = EG(current_module); - internal_function->T = 0; + if (EG(active) && ZEND_OBSERVER_ENABLED) { + /* Add an observer temporary to store previous observed frames. This is + * normally handled by zend_observer_post_startup(), except for + * functions registered at runtime (EG(active)). */ + internal_function->T = 1; + } else { + internal_function->T = 0; + } memset(internal_function->reserved, 0, ZEND_MAX_RESERVED_RESOURCES * sizeof(void*)); while (ptr->fname) { diff --git a/ext/dl_test/dl_test.c b/ext/dl_test/dl_test.c index f4a8ab8198ede..a4f42454c565e 100644 --- a/ext/dl_test/dl_test.c +++ b/ext/dl_test/dl_test.c @@ -76,9 +76,27 @@ PHP_INI_BEGIN() PHP_INI_END() /* }}} */ +PHP_METHOD(DlTest, test) +{ + char *var = "World"; + size_t var_len = sizeof("World") - 1; + zend_string *retval; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(var, var_len) + ZEND_PARSE_PARAMETERS_END(); + + retval = strpprintf(0, "Hello %s", var); + + RETURN_STR(retval); +} + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(dl_test) { + register_class_DlTest(); + /* Test backwards compatibility */ if (getenv("PHP_DL_TEST_USE_OLD_REGISTER_INI_ENTRIES")) { zend_register_ini_entries(ini_entries, module_number); diff --git a/ext/dl_test/dl_test.stub.php b/ext/dl_test/dl_test.stub.php index 524c8206365b8..cd8b3916bae2e 100644 --- a/ext/dl_test/dl_test.stub.php +++ b/ext/dl_test/dl_test.stub.php @@ -8,3 +8,7 @@ function dl_test_test1(): void {} function dl_test_test2(string $str = ""): string {} + +class DlTest { + public function test(string $str = ""): string {} +} diff --git a/ext/dl_test/dl_test_arginfo.h b/ext/dl_test/dl_test_arginfo.h index fdd0e3a89e10e..0618bbdb222ca 100644 --- a/ext/dl_test/dl_test_arginfo.h +++ b/ext/dl_test/dl_test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 547ddbc21e9aa853b491cb17e902bbbb9cc2df00 */ + * Stub hash: 2dbacf5282b0f8e53923ac70495c2da43c7237e3 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_test1, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() @@ -8,9 +8,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_test2, 0, 0, IS_STRING, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, str, IS_STRING, 0, "\"\"") ZEND_END_ARG_INFO() +#define arginfo_class_DlTest_test arginfo_dl_test_test2 + ZEND_FUNCTION(dl_test_test1); ZEND_FUNCTION(dl_test_test2); +ZEND_METHOD(DlTest, test); static const zend_function_entry ext_functions[] = { @@ -18,3 +21,19 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(dl_test_test2, arginfo_dl_test_test2) ZEND_FE_END }; + + +static const zend_function_entry class_DlTest_methods[] = { + ZEND_ME(DlTest, test, arginfo_class_DlTest_test, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static zend_class_entry *register_class_DlTest(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "DlTest", class_DlTest_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + return class_entry; +} diff --git a/ext/standard/tests/general_functions/gh17211.phpt b/ext/standard/tests/general_functions/gh17211.phpt new file mode 100644 index 0000000000000..5df8a6638a677 --- /dev/null +++ b/ext/standard/tests/general_functions/gh17211.phpt @@ -0,0 +1,42 @@ +--TEST-- +dl() / observer segfault +--EXTENSIONS-- +zend_test +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_functions=1 +zend_test.observer.show_output=1 +--FILE-- +test("World!")); +?> +--EXPECTF-- + + +
+
+ + + + + +string(12) "Hello World!" + + + + + +string(12) "Hello World!" + From 2c3b56ded0edc5a8dcb6322de2868dd094d4edf6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 19 Dec 2024 19:57:18 +0100 Subject: [PATCH 47/65] Fix GH-17216: Trampoline crash on error The error handling is incomplete on argument cleanup. 1. The fci is not cleared which means that zend_free_trampoline() is never called. 2. The cleaning for extra named arguments was missing, resulting in memory leak. Closes GH-17219. --- NEWS | 1 + Zend/tests/named_params/gh17216.phpt | 22 ++++++++++++++++++++++ Zend/zend_execute_API.c | 4 ++++ 3 files changed, 27 insertions(+) create mode 100644 Zend/tests/named_params/gh17216.phpt diff --git a/NEWS b/NEWS index c03d6879330d0..af04513c71846 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ PHP NEWS promotion correctly). (nielsdos) . Fixed bug GH-17211 (observer segfault on function loaded with dl()). (Arnaud) + . Fixed bug GH-17216 (Trampoline crash on error). (nielsdos) - Date: . Fixed bug GH-14709 DatePeriod::__construct() overflow on recurrences. diff --git a/Zend/tests/named_params/gh17216.phpt b/Zend/tests/named_params/gh17216.phpt new file mode 100644 index 0000000000000..4cb4df0ec431e --- /dev/null +++ b/Zend/tests/named_params/gh17216.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-17216 (Trampoline crash on error) +--FILE-- + "b", 1]; +try { + forward_static_call_array($callback, $array); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +echo "Done\n"; +?> +--EXPECT-- +Cannot use positional argument after named argument +Done diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index e9b6009af05ae..fe37b024934df 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -842,7 +842,11 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ ZEND_CALL_NUM_ARGS(call) = i; cleanup_args: zend_vm_stack_free_args(call); + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_free_extra_named_params(call->extra_named_params); + } zend_vm_stack_free_call_frame(call); + zend_release_fcall_info_cache(fci_cache); return SUCCESS; } } From 61615d5673d59b46cfd4717d8e79b707253cfbbd Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 21 Dec 2024 11:16:38 +0100 Subject: [PATCH 48/65] Fix GH-17224: UAF in importNode Wrong document pointer is used for the namespace copy. Closes GH-17230. --- NEWS | 3 ++ ext/dom/document.c | 4 +-- ext/dom/tests/gh17224.phpt | 67 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 ext/dom/tests/gh17224.phpt diff --git a/NEWS b/NEWS index af04513c71846..c72dc1d9d065a 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,9 @@ PHP NEWS - DBA: . Skip test if inifile is disabled. (orlitzky) +- DOM: + . Fixed bug GH-17224 (UAF in importNode). (nielsdos) + - FFI: . Fixed bug #79075 (FFI header parser chokes on comments). (nielsdos) diff --git a/ext/dom/document.c b/ext/dom/document.c index ee9c0d4d5ff31..d1437f1f42923 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -809,14 +809,14 @@ PHP_METHOD(DOMDocument, importNode) xmlNsPtr nsptr = NULL; xmlNodePtr root = xmlDocGetRootElement(docp); - nsptr = xmlSearchNsByHref (nodep->doc, root, nodep->ns->href); + nsptr = xmlSearchNsByHref (docp, root, nodep->ns->href); if (nsptr == NULL || nsptr->prefix == NULL) { int errorcode; nsptr = dom_get_ns(root, (char *) nodep->ns->href, &errorcode, (char *) nodep->ns->prefix); /* If there is no root, the namespace cannot be attached to it, so we have to attach it to the old list. */ if (nsptr != NULL && root == NULL) { - php_libxml_set_old_ns(nodep->doc, nsptr); + php_libxml_set_old_ns(docp, nsptr); } } retnodep->ns = nsptr; diff --git a/ext/dom/tests/gh17224.phpt b/ext/dom/tests/gh17224.phpt new file mode 100644 index 0000000000000..9430096a27416 --- /dev/null +++ b/ext/dom/tests/gh17224.phpt @@ -0,0 +1,67 @@ +--TEST-- +GH-17224 (UAF in importNode) +--EXTENSIONS-- +dom +--CREDITS-- +YuanchengJiang +--FILE-- +loadXML(''); +$attr = $fromdom->firstChild->attributes->item(0); +$att = $aDOM->importNode($attr); +$doc = new DOMDocument; +$fromdom->load(__DIR__."/book.xml"); +unset($attr); +var_dump($att); +?> +--EXPECTF-- +object(DOMAttr)#%d (%d) { + ["specified"]=> + bool(true) + ["schemaTypeInfo"]=> + NULL + ["name"]=> + string(4) "attr" + ["value"]=> + string(10) "namespaced" + ["ownerElement"]=> + NULL + ["nodeName"]=> + string(7) "ai:attr" + ["nodeValue"]=> + string(10) "namespaced" + ["nodeType"]=> + int(2) + ["parentNode"]=> + NULL + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + string(22) "(object value omitted)" + ["lastChild"]=> + string(22) "(object value omitted)" + ["previousSibling"]=> + NULL + ["nextSibling"]=> + NULL + ["attributes"]=> + NULL + ["isConnected"]=> + bool(false) + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["namespaceURI"]=> + string(15) "http://test.org/" + ["prefix"]=> + string(2) "ai" + ["localName"]=> + string(4) "attr" + ["baseURI"]=> + NULL + ["textContent"]=> + string(10) "namespaced" +} From 4bfe69bbc4779a9cd301e22c91e162a524f6610a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 21 Dec 2024 12:11:21 +0100 Subject: [PATCH 49/65] Fix GH-17225: NULL deref in spl_directory.c NULL checks for the glob stream are inconsistently applied. To solve this generally, factor it out to a helper function so it's less likely to be forgotten in the future. Closes GH-17231. --- NEWS | 3 +++ ext/spl/spl_directory.c | 16 ++++++++---- ext/spl/tests/gh17225.phpt | 52 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 ext/spl/tests/gh17225.phpt diff --git a/NEWS b/NEWS index c72dc1d9d065a..032bb9f3b06e0 100644 --- a/NEWS +++ b/NEWS @@ -72,6 +72,9 @@ PHP NEWS on SO_SNDTIMEO/SO_RCVTIMEO for socket_set_option(). (David Carlier) +- SPL: + . Fixed bug GH-17225 (NULL deref in spl_directory.c). (nielsdos) + - Streams: . Fixed bug GH-17037 (UAF in user filter when adding existing filter name due to incorrect error handling). (nielsdos) diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 650439dbc8939..e4e79b0edb861 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -212,10 +212,16 @@ static zend_object *spl_filesystem_object_new(zend_class_entry *class_type) } /* }}} */ +static inline bool spl_intern_is_glob(const spl_filesystem_object *intern) +{ + /* NULL check on `dirp` is necessary as destructors may interfere. */ + return intern->u.dir.dirp && php_stream_is(intern->u.dir.dirp, &php_glob_stream_ops); +} + PHPAPI zend_string *spl_filesystem_object_get_path(spl_filesystem_object *intern) /* {{{ */ { #ifdef HAVE_GLOB - if (intern->type == SPL_FS_DIR && php_stream_is(intern->u.dir.dirp, &php_glob_stream_ops)) { + if (intern->type == SPL_FS_DIR && spl_intern_is_glob(intern)) { size_t len = 0; char *tmp = php_glob_stream_get_path(intern->u.dir.dirp, &len); if (len == 0) { @@ -658,7 +664,7 @@ static inline HashTable *spl_filesystem_object_get_debug_info(zend_object *objec if (intern->type == SPL_FS_DIR) { #ifdef HAVE_GLOB pnstr = spl_gen_private_prop_name(spl_ce_DirectoryIterator, "glob", sizeof("glob")-1); - if (intern->u.dir.dirp && php_stream_is(intern->u.dir.dirp ,&php_glob_stream_ops)) { + if (spl_intern_is_glob(intern)) { ZVAL_STR_COPY(&tmp, intern->path); } else { ZVAL_FALSE(&tmp); @@ -1614,11 +1620,11 @@ PHP_METHOD(GlobIterator, count) RETURN_THROWS(); } - if (intern->u.dir.dirp && php_stream_is(intern->u.dir.dirp ,&php_glob_stream_ops)) { + if (spl_intern_is_glob(intern)) { RETURN_LONG(php_glob_stream_get_count(intern->u.dir.dirp, NULL)); } else { - /* should not happen */ - // TODO ZEND_ASSERT ? + /* This can happen by abusing destructors. */ + /* TODO: relax this from E_ERROR to an exception */ php_error_docref(NULL, E_ERROR, "GlobIterator lost glob state"); } } diff --git a/ext/spl/tests/gh17225.phpt b/ext/spl/tests/gh17225.phpt new file mode 100644 index 0000000000000..66a52bce7f4a7 --- /dev/null +++ b/ext/spl/tests/gh17225.phpt @@ -0,0 +1,52 @@ +--TEST-- +GH-17225 (NULL deref in spl_directory.c) +--CREDITS-- +YuanchengJiang +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +--FILE-- +isLink()); +?> +--CLEAN-- + +--EXPECTF-- +bool(false) +object(SplObjectStorage)#%d (1) { + ["storage":"SplObjectStorage":private]=> + array(1) { + [0]=> + array(2) { + ["obj"]=> + object(Phar)#%d (4) { + ["pathName":"SplFileInfo":private]=> + string(0) "" + ["fileName":"SplFileInfo":private]=> + string(0) "" + ["glob":"DirectoryIterator":private]=> + bool(false) + ["subPathName":"RecursiveDirectoryIterator":private]=> + string(0) "" + } + ["inf"]=> + object(HasDestructor)#%d (0) { + } + } + } +} From e0b79cdc5cc4796599814192fce6aa427fdc4dd1 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 22 Dec 2024 14:13:19 +0100 Subject: [PATCH 50/65] Introduce FPM_TEST_DEBUG_FILTER env var and extend multi request tracing --- sapi/fpm/tests/tester.inc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc index e6c971c32e432..c22af549ece76 100644 --- a/sapi/fpm/tests/tester.inc +++ b/sapi/fpm/tests/tester.inc @@ -401,11 +401,15 @@ class Tester bool $debug = null, string $clientTransport = 'stream' ) { - $this->configTemplate = $configTemplate; - $this->code = $code; - $this->options = $options; - $this->fileName = $fileName ?: self::getCallerFileName(); - $this->debug = $debug !== null ? $debug : (bool)getenv('TEST_FPM_DEBUG'); + $this->configTemplate = $configTemplate; + $this->code = $code; + $this->options = $options; + $this->fileName = $fileName ?: self::getCallerFileName(); + if (($debugFilter = getenv('TEST_FPM_DEBUG_FILTER')) !== false) { + $this->debug = str_contains(basename($this->fileName), $debugFilter); + } else { + $this->debug = $debug !== null ? $debug : (bool)getenv('TEST_FPM_DEBUG'); + } $this->logReader = new LogReader($this->debug); $this->logTool = new LogTool($this->logReader, $this->debug); $this->clientTransport = $clientTransport; @@ -930,6 +934,7 @@ class Tester $requestData['headers'] ?? [], $requestData['uri'] ?? null ); + $this->trace('Request params', $params); if (isset($requestData['delay'])) { usleep($requestData['delay']); From fcbfd5a6800cc46158daa4a4ed5ae1c112002421 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Thu, 12 Dec 2024 17:20:00 +0100 Subject: [PATCH 51/65] Include relevant system headers before defining fallbacks Otherwise we may define the fallbacks, and later inclusion of the system headers may attempt to redefine those. Fixes GH-17112. Closes GH-17129. --- NEWS | 4 ++++ sapi/fpm/fpm/fpm_config.h | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/NEWS b/NEWS index 032bb9f3b06e0..376139b086feb 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,7 @@ PHP NEWS - FPM: . Fixed bug GH-13437 (FPM: ERROR: scoreboard: failed to lock (already locked)). (Jakub Zelenka) + . Fixed bug GH-17112 (Macro redefinitions). (cmb, nielsdos) - GD: . Fixed bug GH-16255 (Unexpected nan value in ext/gd/libgd/gd_filter.c). @@ -44,6 +45,9 @@ PHP NEWS - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) +- MBString: + . Fixed bug GH-17112 (Macro redefinitions). (nielsdos, cmb) + - Opcache: . opcache_get_configuration() properly reports jit_prof_threshold. (cmb) diff --git a/sapi/fpm/fpm/fpm_config.h b/sapi/fpm/fpm/fpm_config.h index d34f686a6fbd1..a637326ed767a 100644 --- a/sapi/fpm/fpm/fpm_config.h +++ b/sapi/fpm/fpm/fpm_config.h @@ -2,6 +2,16 @@ #include +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif + /* Solaris does not have it */ #ifndef INADDR_NONE # define INADDR_NONE (-1) From a7f7e169d672a1bb2765d62cd1f0f10306fcff7b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 22 Dec 2024 21:34:32 +0100 Subject: [PATCH 52/65] Fix memory leak on ZEND_FFI_TYPE_CHAR conversion failure The success path frees tmp_str, but the error path does not. Closes GH-17243. --- NEWS | 1 + ext/ffi/ffi.c | 1 + 2 files changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 376139b086feb..f2a0b0246d1fc 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,7 @@ PHP NEWS - FFI: . Fixed bug #79075 (FFI header parser chokes on comments). (nielsdos) + . Fix memory leak on ZEND_FFI_TYPE_CHAR conversion failure. (nielsdos) - Filter: . Fixed bug GH-16944 (Fix filtering special IPv4 and IPv6 ranges, by using diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 2000e15c29ea1..d823f32127cfb 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -807,6 +807,7 @@ static zend_always_inline zend_result zend_ffi_zval_to_cdata(void *ptr, zend_ffi if (ZSTR_LEN(str) == 1) { *(char*)ptr = ZSTR_VAL(str)[0]; } else { + zend_tmp_string_release(tmp_str); zend_ffi_assign_incompatible(value, type); return FAILURE; } From df6db27580025e2089a27e09615389ec03ab8a3d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 23 Dec 2024 18:51:08 +0100 Subject: [PATCH 53/65] Fix GH-17246: GC during SCCP causes segfault This bug happens because of a nested `SHM_UNPROTECT()` sequence. In particular: ``` unprotect memory at ext/opcache/ZendAccelerator.c:2127 protect memory at ext/opcache/ZendAccelerator.c:2160 unprotect memory at ext/opcache/ZendAccelerator.c:2164 unprotect memory at ext/opcache/jit/zend_jit_trace.c:7464 ^^^ Nested protect memory at ext/opcache/jit/zend_jit_trace.c:7591 ^^^ Problem is here: it should not protect again due to the nested unprotect protect memory at ext/opcache/ZendAccelerator.c:2191 ^^^ This one should actually protect, not the previous one ``` The reason this nesting happen is because: 1. We try to include the script, this eventually calls `cache_script_in_shared_memory` 2. `zend_optimize_script` will eventually run SCCP as part of the DFA pass. 3. SCCP will try to replace constants, but can also run destructors when a partial array is destructed here: https://github.com/php/php-src/blob/4e9cde758eadf30cc4d596d6398c2c34c64197b4/Zend/Optimizer/sccp.c#L2387-L2389 In this case, this destruction invokes the GC which invokes the tracing JIT, leading to the nested unprotects. This patch disables the GC to prevent invoking user code, as user code is not supposed to run during the optimizer pipeline. Closes GH-17249. Co-authored-by: Dmitry Stogov --- NEWS | 1 + ext/opcache/ZendAccelerator.c | 3 +++ ext/opcache/tests/jit/gh17246.inc | 8 ++++++ ext/opcache/tests/jit/gh17246.phpt | 39 ++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 ext/opcache/tests/jit/gh17246.inc create mode 100755 ext/opcache/tests/jit/gh17246.phpt diff --git a/NEWS b/NEWS index f2a0b0246d1fc..257623b283397 100644 --- a/NEWS +++ b/NEWS @@ -51,6 +51,7 @@ PHP NEWS - Opcache: . opcache_get_configuration() properly reports jit_prof_threshold. (cmb) + . Fixed bug GH-17246 (GC during SCCP causes segfault). (Dmitry) - PCNTL: . Fix memory leak in cleanup code of pcntl_exec() when a non stringable diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index fb886d39e60ac..66c10021442ad 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2153,7 +2153,10 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) */ from_shared_memory = false; if (persistent_script) { + /* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */ + bool orig_gc_state = gc_enable(false); persistent_script = cache_script_in_shared_memory(persistent_script, key, &from_shared_memory); + gc_enable(orig_gc_state); } /* Caching is disabled, returning op_array; diff --git a/ext/opcache/tests/jit/gh17246.inc b/ext/opcache/tests/jit/gh17246.inc new file mode 100644 index 0000000000000..f9e7d78fc165a --- /dev/null +++ b/ext/opcache/tests/jit/gh17246.inc @@ -0,0 +1,8 @@ +field = function() {}; + } + + public function __destruct() + { + // Necessary because we need to invoke tracing JIT during destruction + } +} + +for ($i = 0; $i < 10000; ++$i) { + $obj = new Test(); +} + +require __DIR__.'/gh17246.inc'; + +?> +--EXPECTF-- +Fatal error: Uncaught Error: Class "NonExistentClass" not found in %s:%d +Stack trace: +#0 %s(%d): require() +#1 {main} + thrown in %s on line %d From a24eada99bd6a92a712d5c0eb9a192066adb3526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 23 Dec 2024 15:01:57 +0100 Subject: [PATCH 54/65] [ci skip] Make build command for program using embed portable Closes GH-17247. --- NEWS | 3 +++ sapi/embed/README.md | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 257623b283397..b89ef952f370d 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,9 @@ PHP NEWS - DOM: . Fixed bug GH-17224 (UAF in importNode). (nielsdos) +- Embed: + . Make build command for program using embed portable. (dunglas) + - FFI: . Fixed bug #79075 (FFI header parser chokes on comments). (nielsdos) . Fix memory leak on ZEND_FFI_TYPE_CHAR conversion failure. (nielsdos) diff --git a/sapi/embed/README.md b/sapi/embed/README.md index c90ff354ab7d9..87b144b4b120d 100644 --- a/sapi/embed/README.md +++ b/sapi/embed/README.md @@ -36,12 +36,12 @@ To compile this, we must point the compiler to the PHP header files. The paths t We must also point the linker and the runtime loader to the `libphp.so` shared lib for linking PHP (`-lphp`) which is located at `$(php-config --prefix)/lib`. So the complete command to compile ends up being: ```bash -$ gcc \ +$ cc \ $(php-config --includes) \ -L$(php-config --prefix)/lib \ embed_sapi_basic_example.c \ -lphp \ - -Wl,-rpath=$(php-config --prefix)/lib + -Wl,-rpath,$(php-config --prefix)/lib ``` > :memo: The embed SAPI is disabled by default. In order for the above example to compile, PHP must be built with the embed SAPI enabled. To see what SAPIs are installed, run `php-config --php-sapis`. If you don't see `embed` in the list, you'll need to rebuild PHP with `./configure --enable-embed`. The PHP shared library `libphp.so` is built when the embed SAPI is enabled. From 643a77dda356664356d44183cd696d6cfce8a683 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 23 Dec 2024 19:55:52 +0100 Subject: [PATCH 55/65] Port fix for libgd bug 276 The issue that BMP RLE occasionally swallowed some pixels[1] had been fixed long ago in libgd, but apparently it has been overlooked to port it to our bundled libgd. We also introduce the test helper `test_image_equals_image()` which compares in-memory images for equality. [1] Closes GH-17250. --- NEWS | 2 ++ ext/gd/libgd/gd_bmp.c | 4 +--- ext/gd/tests/func.inc | 28 +++++++++++++++++++++------- ext/gd/tests/gd276.phpt | 26 ++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 ext/gd/tests/gd276.phpt diff --git a/NEWS b/NEWS index b89ef952f370d..730a38b648b60 100644 --- a/NEWS +++ b/NEWS @@ -41,6 +41,8 @@ PHP NEWS - GD: . Fixed bug GH-16255 (Unexpected nan value in ext/gd/libgd/gd_filter.c). (nielsdos, cmb) + . Ported fix for libgd bug 276 (Sometimes pixels are missing when storing + images as BMPs). (cmb) - Gettext: . Fixed bug GH-17202 (Segmentation fault ext/gettext/gettext.c diff --git a/ext/gd/libgd/gd_bmp.c b/ext/gd/libgd/gd_bmp.c index a80904a62914d..27b5d11ae7683 100644 --- a/ext/gd/libgd/gd_bmp.c +++ b/ext/gd/libgd/gd_bmp.c @@ -350,9 +350,7 @@ static int compress_row(unsigned char *row, int length) } if (compressed_run) { - if (rle_type == BMP_RLE_TYPE_RLE) { - compressed_length += build_rle_packet(row, rle_type, compressed_run, uncompressed_row); - } + compressed_length += build_rle_packet(row, rle_type, compressed_run, uncompressed_row); } gdFree(uncompressed_start); diff --git a/ext/gd/tests/func.inc b/ext/gd/tests/func.inc index 5273f998300b1..0f10aa7d83dee 100644 --- a/ext/gd/tests/func.inc +++ b/ext/gd/tests/func.inc @@ -70,12 +70,8 @@ function get_libxpm_version() * message is printed, and the actual file is stored right beside the temporary * .php test file with the extension .out.png, to be able to manually inspect * the result. - * - * @param string $filename - * @param resource $actual - * @return void */ -function test_image_equals_file($filename, $actual) +function test_image_equals_file(string $filename, GdImage $actual): void { if (!file_exists($filename)) { echo "The expected image does not exist.\n"; @@ -85,13 +81,29 @@ function test_image_equals_file($filename, $actual) $actual = test_to_truecolor($actual); $expected = imagecreatefrompng($filename); $expected = test_to_truecolor($expected); + test_image_equals_image($expected, $actual, true); +} + +/** + * Tests that an in-memory image equals another in-memory image. + * + * It checks for equal image sizes, and whether any pixels are different. + * The textual result is printed, so the EXPECT section should contain the line + * "The images are equal." + * + * If the images are not equal, a diagnostic message is printed. + */ +function test_image_equals_image(GdImage $expected, GdImage $actual, bool $save_actual = false): void +{ $exp_x = imagesx($expected); $exp_y = imagesy($expected); $act_x = imagesx($actual); $act_y = imagesy($actual); if ($exp_x != $act_x || $exp_y != $act_y) { echo "The image size differs: expected {$exp_x}x{$exp_y}, got {$act_x}x{$act_y}.\n"; - save_actual_image($actual); + if ($save_actual) { + save_actual_image($actual); + } return; } $pixels_changed = 0; @@ -108,7 +120,9 @@ function test_image_equals_file($filename, $actual) echo "The images are equal.\n"; } else { echo "The images differ in {$pixels_changed} pixels.\n"; - save_actual_image($actual); + if ($save_actual) { + save_actual_image($actual); + } } } diff --git a/ext/gd/tests/gd276.phpt b/ext/gd/tests/gd276.phpt new file mode 100644 index 0000000000000..a12bce3983049 --- /dev/null +++ b/ext/gd/tests/gd276.phpt @@ -0,0 +1,26 @@ +--TEST-- +libgd bug 276 (Sometimes pixels are missing when storing images as BMPs) +--EXTENSIONS-- +gd +--FILE-- + +--EXPECT-- +bool(true) +The images are equal. +--CLEAN-- + From 99a14b805e4d4d0a39b0b81afc8602495f07f414 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 25 Dec 2024 18:44:51 +0100 Subject: [PATCH 56/65] Fix GH-16013 and bug #80857: Big endian issues The FFI call return values follow widening rules. We must widen to `ffi_arg` in the case we're handling a return value for types shorter than the machine width. From http://www.chiark.greenend.org.uk/doc/libffi-dev/html/The-Closure-API.html: > In most cases, ret points to an object of exactly the size of the type specified when cif was constructed. > However, integral types narrower than the system register size are widened. > In these cases your program may assume that ret points to an ffi_arg object. If we don't do this, we get wrong values when reading the return values. Closes GH-17255. Co-authored-by: Dmitry Stogov --- NEWS | 1 + ext/ffi/ffi.c | 47 ++++++++++++- ext/ffi/tests/gh16013.phpt | 137 +++++++++++++++++++++++++++++++++++++ ext/zend_test/test.c | 35 ++++++++++ 4 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 ext/ffi/tests/gh16013.phpt diff --git a/NEWS b/NEWS index 730a38b648b60..beaee119d3b26 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,7 @@ PHP NEWS - FFI: . Fixed bug #79075 (FFI header parser chokes on comments). (nielsdos) . Fix memory leak on ZEND_FFI_TYPE_CHAR conversion failure. (nielsdos) + . Fixed bug GH-16013 and bug #80857 (Big endian issues). (Dmitry, nielsdos) - Filter: . Fixed bug GH-16944 (Fix filtering special IPv4 and IPv6 ranges, by using diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index d823f32127cfb..f9ad84a192649 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -985,6 +985,27 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v ret_type = ZEND_FFI_TYPE(callback_data->type->func.ret_type); if (ret_type->kind != ZEND_FFI_TYPE_VOID) { zend_ffi_zval_to_cdata(ret, ret_type, &retval); + +#ifdef WORDS_BIGENDIAN + if (ret_type->size < sizeof(ffi_arg) + && ret_type->kind >= ZEND_FFI_TYPE_UINT8 + && ret_type->kind < ZEND_FFI_TYPE_POINTER) { + /* We need to widen the value (zero extend) */ + switch (ret_type->size) { + case 1: + *(ffi_arg*)ret = *(uint8_t*)ret; + break; + case 2: + *(ffi_arg*)ret = *(uint16_t*)ret; + break; + case 4: + *(ffi_arg*)ret = *(uint32_t*)ret; + break; + default: + break; + } + } +#endif } zval_ptr_dtor(&retval); @@ -2855,7 +2876,31 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */ } if (ZEND_FFI_TYPE(type->func.ret_type)->kind != ZEND_FFI_TYPE_VOID) { - zend_ffi_cdata_to_zval(NULL, ret, ZEND_FFI_TYPE(type->func.ret_type), BP_VAR_R, return_value, 0, 1, 0); + zend_ffi_type *func_ret_type = ZEND_FFI_TYPE(type->func.ret_type); + +#ifdef WORDS_BIGENDIAN + if (func_ret_type->size < sizeof(ffi_arg) + && func_ret_type->kind >= ZEND_FFI_TYPE_UINT8 + && func_ret_type->kind < ZEND_FFI_TYPE_POINTER) { + + /* We need to narrow the value (truncate) */ + switch (func_ret_type->size) { + case 1: + *(uint8_t*)ret = *(ffi_arg*)ret; + break; + case 2: + *(uint16_t*)ret = *(ffi_arg*)ret; + break; + case 4: + *(uint32_t*)ret = *(ffi_arg*)ret; + break; + default: + break; + } + } +#endif + + zend_ffi_cdata_to_zval(NULL, ret, func_ret_type, BP_VAR_R, return_value, 0, 1, 0); } else { ZVAL_NULL(return_value); } diff --git a/ext/ffi/tests/gh16013.phpt b/ext/ffi/tests/gh16013.phpt new file mode 100644 index 0000000000000..be57846cc42bc --- /dev/null +++ b/ext/ffi/tests/gh16013.phpt @@ -0,0 +1,137 @@ +--TEST-- +GH-16013 (endianness issue with FFI) +--EXTENSIONS-- +ffi +zend_test +--FILE-- +bug_gh16013_return_char()); +var_dump($ffi->bug_gh16013_return_bool()); +var_dump($ffi->bug_gh16013_return_short()); +var_dump($ffi->bug_gh16013_return_int()); +var_dump($ffi->bug_gh16013_return_enum()); +var_dump($ffi->bug_gh16013_return_struct()); + +echo "--- Callback values ---\n"; +$bug_gh16013_callback_struct = $ffi->new('struct bug_gh16013_callback_struct'); +$bug_gh16013_callback_struct->return_int8 = function($val) use($ffi) { + $cdata = $ffi->new('int8_t'); + $cdata->cdata = $val; + return $cdata; +}; +$bug_gh16013_callback_struct->return_uint8 = function($val) use($ffi) { + $cdata = $ffi->new('uint8_t'); + $cdata->cdata = $val; + return $cdata; +}; +$bug_gh16013_callback_struct->return_int16 = function($val) use($ffi) { + $cdata = $ffi->new('int16_t'); + $cdata->cdata = $val; + return $cdata; +}; +$bug_gh16013_callback_struct->return_uint16 = function($val) use($ffi) { + $cdata = $ffi->new('uint16_t'); + $cdata->cdata = $val; + return $cdata; +}; +$bug_gh16013_callback_struct->return_int32 = function($val) use($ffi) { + $cdata = $ffi->new('int32_t'); + $cdata->cdata = $val; + return $cdata; +}; +$bug_gh16013_callback_struct->return_uint32 = function($val) use($ffi) { + $cdata = $ffi->new('uint32_t'); + $cdata->cdata = $val; + return $cdata; +}; +$bug_gh16013_callback_struct->return_float = function($val) use($ffi) { + $cdata = $ffi->new('float'); + $cdata->cdata = $val; + return $cdata; +}; +$bug_gh16013_callback_struct->return_struct = function($val) use($ffi) { + return $val; +}; +$bug_gh16013_callback_struct->return_enum = function($val) use($ffi) { + $cdata = $ffi->new('enum bug_gh16013_enum'); + $cdata->cdata = $val; + return $cdata; +}; + +var_dump(($bug_gh16013_callback_struct->return_int8)(-4)); +var_dump(($bug_gh16013_callback_struct->return_uint8)(4)); +var_dump(($bug_gh16013_callback_struct->return_int16)(-10000)); +var_dump(($bug_gh16013_callback_struct->return_uint16)(10000)); +var_dump(($bug_gh16013_callback_struct->return_int32)(-100000)); +var_dump(($bug_gh16013_callback_struct->return_uint32)(100000)); +var_dump(($bug_gh16013_callback_struct->return_float)(12.34)); +$struct = $ffi->new('struct bug_gh16013_int_struct'); +$struct->field = 10; +var_dump(($bug_gh16013_callback_struct->return_struct)($struct)); +var_dump(($bug_gh16013_callback_struct->return_enum)($ffi->BUG_GH16013_B)); +?> +--EXPECT-- +--- Return values --- +string(1) "A" +bool(true) +int(12345) +int(123456789) +int(2) +object(FFI\CData:struct bug_gh16013_int_struct)#2 (1) { + ["field"]=> + int(123456789) +} +--- Callback values --- +int(-4) +int(4) +int(-10000) +int(10000) +int(-100000) +int(100000) +float(12.34000015258789) +object(FFI\CData:struct bug_gh16013_int_struct)#13 (1) { + ["field"]=> + int(10) +} +int(2) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 6d349317384e6..b2e2756381fdc 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1449,6 +1449,41 @@ PHP_ZEND_TEST_API void bug_gh9090_void_int_char_var(int i, char *fmt, ...) { PHP_ZEND_TEST_API int gh11934b_ffi_var_test_cdata; +enum bug_gh16013_enum { + BUG_GH16013_A = 1, + BUG_GH16013_B = 2, +}; + +struct bug_gh16013_int_struct { + int field; +}; + +PHP_ZEND_TEST_API char bug_gh16013_return_char(void) { + return 'A'; +} + +PHP_ZEND_TEST_API bool bug_gh16013_return_bool(void) { + return true; +} + +PHP_ZEND_TEST_API short bug_gh16013_return_short(void) { + return 12345; +} + +PHP_ZEND_TEST_API int bug_gh16013_return_int(void) { + return 123456789; +} + +PHP_ZEND_TEST_API enum bug_gh16013_enum bug_gh16013_return_enum(void) { + return BUG_GH16013_B; +} + +PHP_ZEND_TEST_API struct bug_gh16013_int_struct bug_gh16013_return_struct(void) { + struct bug_gh16013_int_struct ret; + ret.field = 123456789; + return ret; +} + #ifdef HAVE_COPY_FILE_RANGE /** * This function allows us to simulate early return of copy_file_range by setting the limit_copy_file_range ini setting. From 7be950f3f6f6caea8d25a1361dacc985fa6918e2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 24 Dec 2024 14:07:53 +0100 Subject: [PATCH 57/65] Fix GH-17223: Memory leak in libxml encoding handling This was a bug in both libxml and PHP. We follow up with the same change as done in GNOME/libxml@b3871dd138. Changing away from `xmlOutputBufferCreateFilenameDefault` is not possible yet because this is a stable branch and would break BC. Closes GH-17254. --- NEWS | 3 +++ ext/dom/tests/gh17223.phpt | 12 ++++++++++++ ext/libxml/libxml.c | 11 ++++++++--- 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 ext/dom/tests/gh17223.phpt diff --git a/NEWS b/NEWS index beaee119d3b26..96376a1818de6 100644 --- a/NEWS +++ b/NEWS @@ -52,6 +52,9 @@ PHP NEWS - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) +- LibXML: + . Fixed bug GH-17223 (Memory leak in libxml encoding handling). (nielsdos) + - MBString: . Fixed bug GH-17112 (Macro redefinitions). (nielsdos, cmb) diff --git a/ext/dom/tests/gh17223.phpt b/ext/dom/tests/gh17223.phpt new file mode 100644 index 0000000000000..6a0f274c2f64d --- /dev/null +++ b/ext/dom/tests/gh17223.phpt @@ -0,0 +1,12 @@ +--TEST-- +GH-17223 (Memory leak in libxml encoding handling) +--EXTENSIONS-- +dom +--FILE-- +save("%00"); +echo "Done\n"; +?> +--EXPECT-- +Done diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 0c252e5e455aa..6590f73f9edd6 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -590,11 +590,11 @@ php_libxml_output_buffer_create_filename(const char *URI, char *unescaped = NULL; if (URI == NULL) - return(NULL); + goto err; if (strstr(URI, "%00")) { php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes"); - return NULL; + goto err; } puri = xmlParseURI(URI); @@ -615,7 +615,7 @@ php_libxml_output_buffer_create_filename(const char *URI, } if (context == NULL) { - return(NULL); + goto err; } /* Allocate the Output buffer front-end. */ @@ -627,6 +627,11 @@ php_libxml_output_buffer_create_filename(const char *URI, } return(ret); + +err: + /* Similarly to __xmlOutputBufferCreateFilename we should also close the encoder on failure. */ + xmlCharEncCloseFunc(encoder); + return NULL; } static void _php_libxml_free_error(void *ptr) From b621b3a00fe04459ec668a291f1badbade41fef8 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 17 Dec 2024 21:14:33 +0100 Subject: [PATCH 58/65] Fix GH-17187: unreachable program point in zend_hash A bunch of different issues: 1) The referenced value is copied without incrementing the refcount. The reason the refcount isn't incremented is because otherwise the array modifications would violate the RC1 constraints. Solve this by copying the reference itself instead and always read the referenced value. 2) No type checks on the array data, so malicious scripts could cause type confusion bugs. 3) Potential overflow when the arrays resize and we access ctag. Closes GH-17205. --- NEWS | 3 + ext/xml/tests/gh17187_1.phpt | 93 ++++++++++++++++++++++++++++ ext/xml/tests/gh17187_2.phpt | 53 ++++++++++++++++ ext/xml/xml.c | 113 ++++++++++++++++++++++++++--------- 4 files changed, 234 insertions(+), 28 deletions(-) create mode 100644 ext/xml/tests/gh17187_1.phpt create mode 100644 ext/xml/tests/gh17187_2.phpt diff --git a/NEWS b/NEWS index 96376a1818de6..5468d134f335e 100644 --- a/NEWS +++ b/NEWS @@ -101,6 +101,9 @@ PHP NEWS - Windows: . Hardened proc_open() against cmd.exe hijacking. (cmb) +- XML: + . Fixed bug GH-1718 (unreachable program point in zend_hash). (nielsdos) + 19 Dec 2024, PHP 8.3.15 - Calendar: diff --git a/ext/xml/tests/gh17187_1.phpt b/ext/xml/tests/gh17187_1.phpt new file mode 100644 index 0000000000000..4de08b33bdb81 --- /dev/null +++ b/ext/xml/tests/gh17187_1.phpt @@ -0,0 +1,93 @@ +--TEST-- +GH-17187 (unreachable program point in zend_hash) +--EXTENSIONS-- +xml +--CREDITS-- +chongwick +--FILE-- +parser = xml_parser_create(); + xml_set_element_handler($this->parser, function ($parser, $name, $attrs) { + echo "open\n"; + var_dump($name, $attrs); + $this->arrayCopy = [$this]; // Create cycle intentionally + $this->immutableData = $this->arrayCopy; + }, function ($parser, $name) { + echo "close\n"; + var_dump($name); + }); + } + + public function parseXml($xml) { + $this->immutableData = array(); + xml_parse_into_struct($this->parser, $xml, $this->immutableData, $this->immutableData); + return $this->immutableData; + } +} +$immutableParser = new ImmutableParser(); +$xml = ""; +$immutableData = $immutableParser->parseXml($xml); +var_dump($immutableData); +?> +--EXPECT-- +open +string(9) "CONTAINER" +array(0) { +} +open +string(5) "CHILD" +array(0) { +} +close +string(5) "CHILD" +close +string(9) "CONTAINER" +array(5) { + [0]=> + object(ImmutableParser)#1 (3) { + ["parser":"ImmutableParser":private]=> + object(XMLParser)#2 (0) { + } + ["immutableData":"ImmutableParser":private]=> + *RECURSION* + ["arrayCopy":"ImmutableParser":private]=> + array(1) { + [0]=> + *RECURSION* + } + } + ["CHILD"]=> + array(1) { + [0]=> + int(1) + } + [1]=> + array(3) { + ["tag"]=> + string(5) "CHILD" + ["type"]=> + string(8) "complete" + ["level"]=> + int(2) + } + ["CONTAINER"]=> + array(1) { + [0]=> + int(2) + } + [2]=> + array(3) { + ["tag"]=> + string(9) "CONTAINER" + ["type"]=> + string(5) "close" + ["level"]=> + int(1) + } +} diff --git a/ext/xml/tests/gh17187_2.phpt b/ext/xml/tests/gh17187_2.phpt new file mode 100644 index 0000000000000..9a43c92cefc03 --- /dev/null +++ b/ext/xml/tests/gh17187_2.phpt @@ -0,0 +1,53 @@ +--TEST-- +GH-17187 (unreachable program point in zend_hash) +--EXTENSIONS-- +xml +--CREDITS-- +chongwick +--FILE-- +parser = xml_parser_create(); + xml_set_element_handler($this->parser, function ($parser, $name, $attrs) { + echo "open\n"; + var_dump($name, $attrs); + $this->immutableData1 = 0xdead; + $this->immutableData2 = 0xbeef; + }, function ($parser, $name) { + echo "close\n"; + var_dump($name); + }); + } + + public function parseXml($xml) { + $this->immutableData1 = array(); + $this->immutableData2 = array(); + xml_parse_into_struct($this->parser, $xml, $this->immutableData1, $this->immutableData2); + } +} +$immutableParser = new ImmutableParser(); +$xml = ""; +$immutableParser->parseXml($xml); +var_dump($immutableParser->immutableData1); +var_dump($immutableParser->immutableData2); +?> +--EXPECT-- +open +string(9) "CONTAINER" +array(0) { +} +open +string(5) "CHILD" +array(0) { +} +close +string(5) "CHILD" +close +string(9) "CONTAINER" +int(57005) +int(48879) diff --git a/ext/xml/xml.c b/ext/xml/xml.c index 73b51b2143df9..62507a5d1307f 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -72,7 +72,7 @@ typedef struct { /* We return a pointer to these zvals in get_gc(), so it's * important that a) they are adjacent b) object is the first * and c) the number of zvals is kept up to date. */ -#define XML_PARSER_NUM_ZVALS 12 +#define XML_PARSER_NUM_ZVALS 14 zval object; zval startElementHandler; zval endElementHandler; @@ -85,6 +85,8 @@ typedef struct { zval unknownEncodingHandler; zval startNamespaceDeclHandler; zval endNamespaceDeclHandler; + zval data; + zval info; zend_function *startElementPtr; zend_function *endElementPtr; @@ -98,12 +100,10 @@ typedef struct { zend_function *startNamespaceDeclPtr; zend_function *endNamespaceDeclPtr; - zval data; - zval info; int level; int toffset; int curtag; - zval *ctag; + zend_long ctag_index; char **ltags; int lastwasopen; int skipwhite; @@ -326,6 +326,8 @@ static void xml_parser_free_obj(zend_object *object) { xml_parser *parser = xml_parser_from_obj(object); + zval_ptr_dtor(&parser->info); + zval_ptr_dtor(&parser->data); if (parser->parser) { XML_ParserFree(parser->parser); } @@ -551,15 +553,18 @@ static void _xml_add_to_info(xml_parser *parser, const char *name) { zval *element; - if (Z_ISUNDEF(parser->info)) { + if (Z_ISUNDEF(parser->info) || UNEXPECTED(Z_TYPE_P(Z_REFVAL(parser->info)) != IS_ARRAY)) { return; } + SEPARATE_ARRAY(Z_REFVAL(parser->info)); + zend_array *arr = Z_ARRVAL_P(Z_REFVAL(parser->info)); + size_t name_len = strlen(name); - if ((element = zend_hash_str_find(Z_ARRVAL(parser->info), name, name_len)) == NULL) { + if ((element = zend_hash_str_find(arr, name, name_len)) == NULL) { zval values; array_init(&values); - element = zend_hash_str_update(Z_ARRVAL(parser->info), name, name_len, &values); + element = zend_hash_str_update(arr, name, name_len, &values); } add_next_index_long(element, parser->curtag); @@ -583,6 +588,28 @@ static zend_string *_xml_decode_tag(xml_parser *parser, const XML_Char *tag) } /* }}} */ +static zval *xml_get_separated_data(xml_parser *parser) +{ + if (EXPECTED(Z_TYPE_P(Z_REFVAL(parser->data)) == IS_ARRAY)) { + SEPARATE_ARRAY(Z_REFVAL(parser->data)); + return Z_REFVAL(parser->data); + } + return NULL; +} + +static zval *xml_get_ctag(xml_parser *parser) +{ + zval *data = xml_get_separated_data(parser); + if (EXPECTED(data)) { + zval *zv = zend_hash_index_find_deref(Z_ARRVAL_P(data), parser->ctag_index); + if (EXPECTED(zv && Z_TYPE_P(zv) == IS_ARRAY)) { + SEPARATE_ARRAY(zv); + return zv; + } + } + return NULL; +} + /* {{{ _xml_startElementHandler() */ void _xml_startElementHandler(void *userData, const XML_Char *name, const XML_Char **attributes) { @@ -662,7 +689,19 @@ void _xml_startElementHandler(void *userData, const XML_Char *name, const XML_Ch zval_ptr_dtor(&atr); } - parser->ctag = zend_hash_next_index_insert(Z_ARRVAL(parser->data), &tag); + zval *data = xml_get_separated_data(parser); + if (EXPECTED(data)) { + /* Note: due to array resizes or user interference, + * we have to store an index instead of a zval into the array's memory. */ + zend_array *arr = Z_ARRVAL_P(data); + if (EXPECTED(zend_hash_next_index_insert(arr, &tag))) { + parser->ctag_index = arr->nNextFreeElement - 1; + } else { + zval_ptr_dtor(&tag); + } + } else { + zval_ptr_dtor(&tag); + } } else if (parser->level == (XML_MAXLEVEL + 1)) { php_error_docref(NULL, E_WARNING, "Maximum depth exceeded - Results truncated"); } @@ -697,17 +736,21 @@ void _xml_endElementHandler(void *userData, const XML_Char *name) zval tag; if (parser->lastwasopen) { - add_assoc_string(parser->ctag, "type", "complete"); + zval *zv = xml_get_ctag(parser); + if (EXPECTED(zv)) { + add_assoc_string(zv, "type", "complete"); + } } else { - array_init(&tag); - _xml_add_to_info(parser, ZSTR_VAL(tag_name) + parser->toffset); - add_assoc_string(&tag, "tag", SKIP_TAGSTART(ZSTR_VAL(tag_name))); /* cast to avoid gcc-warning */ - add_assoc_string(&tag, "type", "close"); - add_assoc_long(&tag, "level", parser->level); - - zend_hash_next_index_insert(Z_ARRVAL(parser->data), &tag); + zval *data = xml_get_separated_data(parser); + if (EXPECTED(data)) { + array_init(&tag); + add_assoc_string(&tag, "tag", SKIP_TAGSTART(ZSTR_VAL(tag_name))); /* cast to avoid gcc-warning */ + add_assoc_string(&tag, "type", "close"); + add_assoc_long(&tag, "level", parser->level); + zend_hash_next_index_insert(Z_ARRVAL_P(data), &tag); + } } parser->lastwasopen = 0; @@ -765,9 +808,15 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len) } } if (parser->lastwasopen) { + zval *ctag = xml_get_ctag(parser); + if (UNEXPECTED(!ctag)) { + zend_string_release_ex(decoded_value, false); + return; + } + zval *myval; /* check if the current tag already has a value - if yes append to that! */ - if ((myval = zend_hash_find(Z_ARRVAL_P(parser->ctag), ZSTR_KNOWN(ZEND_STR_VALUE)))) { + if ((myval = zend_hash_find(Z_ARRVAL_P(ctag), ZSTR_KNOWN(ZEND_STR_VALUE))) && Z_TYPE_P(myval) == IS_STRING) { size_t newlen = Z_STRLEN_P(myval) + ZSTR_LEN(decoded_value); Z_STR_P(myval) = zend_string_extend(Z_STR_P(myval), newlen, 0); strncpy(Z_STRVAL_P(myval) + Z_STRLEN_P(myval) - ZSTR_LEN(decoded_value), @@ -775,7 +824,7 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len) zend_string_release_ex(decoded_value, 0); } else { if (doprint || (! parser->skipwhite)) { - add_assoc_str(parser->ctag, "value", decoded_value); + add_assoc_str(ctag, "value", decoded_value); } else { zend_string_release_ex(decoded_value, 0); } @@ -783,9 +832,17 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len) } else { zval tag; zval *curtag, *mytype, *myval; - ZEND_HASH_REVERSE_FOREACH_VAL(Z_ARRVAL(parser->data), curtag) { - if ((mytype = zend_hash_str_find(Z_ARRVAL_P(curtag),"type", sizeof("type") - 1))) { - if (zend_string_equals_literal(Z_STR_P(mytype), "cdata")) { + + zval *data = xml_get_separated_data(parser); + if (UNEXPECTED(!data)) { + zend_string_release_ex(decoded_value, false); + return; + } + + ZEND_HASH_REVERSE_FOREACH_VAL(Z_ARRVAL_P(data), curtag) { + if (EXPECTED(Z_TYPE_P(curtag) == IS_ARRAY) && (mytype = zend_hash_str_find(Z_ARRVAL_P(curtag),"type", sizeof("type") - 1))) { + if (EXPECTED(Z_TYPE_P(mytype) == IS_STRING) && zend_string_equals_literal(Z_STR_P(mytype), "cdata")) { + SEPARATE_ARRAY(curtag); if ((myval = zend_hash_find(Z_ARRVAL_P(curtag), ZSTR_KNOWN(ZEND_STR_VALUE)))) { size_t newlen = Z_STRLEN_P(myval) + ZSTR_LEN(decoded_value); Z_STR_P(myval) = zend_string_extend(Z_STR_P(myval), newlen, 0); @@ -805,7 +862,7 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len) add_assoc_str(&tag, "value", decoded_value); add_assoc_string(&tag, "type", "cdata"); add_assoc_long(&tag, "level", parser->level); - zend_hash_next_index_insert(Z_ARRVAL(parser->data), &tag); + zend_hash_next_index_insert(Z_ARRVAL_P(data), &tag); } else if (parser->level == (XML_MAXLEVEL + 1)) { php_error_docref(NULL, E_WARNING, "Maximum depth exceeded - Results truncated"); } else { @@ -1266,21 +1323,21 @@ PHP_FUNCTION(xml_parse_into_struct) } if (info) { - info = zend_try_array_init(info); - if (!info) { + if (!zend_try_array_init(info)) { RETURN_THROWS(); } } - xdata = zend_try_array_init(xdata); - if (!xdata) { + if (!zend_try_array_init(xdata)) { RETURN_THROWS(); } - ZVAL_COPY_VALUE(&parser->data, xdata); + zval_ptr_dtor(&parser->data); + ZVAL_COPY(&parser->data, xdata); if (info) { - ZVAL_COPY_VALUE(&parser->info, info); + zval_ptr_dtor(&parser->info); + ZVAL_COPY(&parser->info, info); } parser->level = 0; From 847d1401a052f0db5d1eb9f0a7beef7c9a7589a2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 27 Dec 2024 16:42:21 +0100 Subject: [PATCH 59/65] Fix GH-17208: bug64539-status-json-encoding.phpt fail on 32-bits The reason this breaks is because of a type mismatch. The following line uses fields of the timeval struct which are both 8 bytes on Alpine 32-bit, which results in a computed value of also 8 bytes: https://github.com/php/php-src/blob/b09ed9a0f25cda8c9eea9d140c01587cd50b4aa8/sapi/fpm/fpm/fpm_status.c#L611 However, it is passed to a format string which expects 4 bytes (`unsigned long` and thus the `%lu` format specifier is 4 bytes on Alpine 32-bit), resulting in argument corruption. Since the value is generally small, truncating to 4 bytes is sufficient to fix this. Closes GH-17286. --- NEWS | 2 ++ sapi/fpm/fpm/fpm_status.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 5468d134f335e..72dec4b340d7f 100644 --- a/NEWS +++ b/NEWS @@ -38,6 +38,8 @@ PHP NEWS . Fixed bug GH-13437 (FPM: ERROR: scoreboard: failed to lock (already locked)). (Jakub Zelenka) . Fixed bug GH-17112 (Macro redefinitions). (cmb, nielsdos) + . Fixed bug GH-17208 (bug64539-status-json-encoding.phpt fail on 32-bits). + (nielsdos) - GD: . Fixed bug GH-16255 (Unexpected nan value in ext/gd/libgd/gd_filter.c). diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c index 80be5fb6f476e..f44274e9d9ee4 100644 --- a/sapi/fpm/fpm/fpm_status.c +++ b/sapi/fpm/fpm/fpm_status.c @@ -598,7 +598,7 @@ int fpm_status_handle_request(void) /* {{{ */ time_buffer, (unsigned long) (now_epoch - proc->start_epoch), proc->requests, - duration.tv_sec * 1000000UL + duration.tv_usec, + (unsigned long) (duration.tv_sec * 1000000UL + duration.tv_usec), proc->request_method[0] != '\0' ? proc->request_method : "-", proc->request_uri[0] != '\0' ? proc->request_uri : "-", query_string ? "?" : "", From 26f3bec63ef0e7bceb425a58379574190026134f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 27 Dec 2024 19:36:26 +0100 Subject: [PATCH 60/65] Backport fix GH-17280: ldap_search() fails when $attributes array has holes Backport of GH-17284 to fix GH-17280 on lower branches. Closes GH-17287. --- NEWS | 4 ++ ext/ldap/ldap.c | 95 ++++++++++++++++----------- ext/ldap/tests/gh17280.phpt | 18 +++++ ext/ldap/tests/ldap_add_error.phpt | 4 +- ext/ldap/tests/ldap_search_error.phpt | 4 +- 5 files changed, 82 insertions(+), 43 deletions(-) create mode 100644 ext/ldap/tests/gh17280.phpt diff --git a/NEWS b/NEWS index 72dec4b340d7f..e8397f27c5d69 100644 --- a/NEWS +++ b/NEWS @@ -54,6 +54,10 @@ PHP NEWS - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos) +- LDAP: + . Fixed bug GH-17280 (ldap_search() fails when $attributes array has holes). + (nielsdos) + - LibXML: . Fixed bug GH-17223 (Memory leak in libxml encoding handling). (nielsdos) diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index 0def911c0a3b2..e66ff070577bd 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -235,6 +235,22 @@ static void ldap_result_entry_free_obj(zend_object *obj) } \ } +static bool php_ldap_is_numerically_indexed_array(zend_array *arr) +{ + if (zend_hash_num_elements(arr) == 0 || HT_IS_PACKED(arr)) { + return true; + } + + zend_string *str_key; + ZEND_HASH_MAP_FOREACH_STR_KEY(arr, str_key) { + if (str_key) { + return false; + } + } ZEND_HASH_FOREACH_END(); + + return false; +} + /* {{{ Parse controls from and to arrays */ static void _php_ldap_control_to_array(LDAP *ld, LDAPControl* ctrl, zval* array, int request) { @@ -1471,20 +1487,22 @@ static void php_ldap_do_search(INTERNAL_FUNCTION_PARAMETERS, int scope) num_attribs = zend_hash_num_elements(Z_ARRVAL_P(attrs)); ldap_attrs = safe_emalloc((num_attribs+1), sizeof(char *), 0); - for (i = 0; imod_bvalues[0]->bv_val = Z_STRVAL_P(value); ldap_mods[i]->mod_bvalues[0]->bv_len = Z_STRLEN_P(value); } else { - for (j = 0; j < num_values; j++) { - if ((ivalue = zend_hash_index_find(Z_ARRVAL_P(value), j)) == NULL) { - zend_argument_value_error(3, "must contain arrays with consecutive integer indices starting from 0"); - num_berval[i] = j; - num_attribs = i + 1; - RETVAL_FALSE; - goto cleanup; - } + if (!php_ldap_is_numerically_indexed_array(Z_ARRVAL_P(value))) { + zend_argument_value_error(3, "must be an array with numeric keys"); + RETVAL_FALSE; + num_berval[i] = 0; + num_attribs = i + 1; + goto cleanup; + } + + j = 0; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(value), ivalue) { convert_to_string(ivalue); if (EG(exception)) { num_berval[i] = j; @@ -2275,7 +2295,8 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext) ldap_mods[i]->mod_bvalues[j] = (struct berval *) emalloc (sizeof(struct berval)); ldap_mods[i]->mod_bvalues[j]->bv_val = Z_STRVAL_P(ivalue); ldap_mods[i]->mod_bvalues[j]->bv_len = Z_STRLEN_P(ivalue); - } + j++; + } ZEND_HASH_FOREACH_END(); } ldap_mods[i]->mod_bvalues[num_values] = NULL; zend_hash_move_forward(Z_ARRVAL_P(entry)); @@ -2543,7 +2564,7 @@ PHP_FUNCTION(ldap_modify_batch) zval *fetched; char *dn; size_t dn_len; - int i, j, k; + int i, j; int num_mods, num_modprops, num_modvals; LDAPMod **ldap_mods; LDAPControl **lserverctrls = NULL; @@ -2603,12 +2624,14 @@ PHP_FUNCTION(ldap_modify_batch) num_mods = zend_hash_num_elements(Z_ARRVAL_P(mods)); - for (i = 0; i < num_mods; i++) { - /* is the numbering consecutive? */ - if ((fetched = zend_hash_index_find(Z_ARRVAL_P(mods), i)) == NULL) { - zend_argument_value_error(3, "must have consecutive integer indices starting from 0"); - RETURN_THROWS(); - } + if (!php_ldap_is_numerically_indexed_array(Z_ARRVAL_P(mods))) { + zend_argument_value_error(3, "must be an array with numeric keys"); + RETURN_THROWS(); + } + + i = 0; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(mods), fetched) { + ZVAL_DEREF(fetched); mod = fetched; /* is it an array? */ @@ -2706,19 +2729,10 @@ PHP_FUNCTION(ldap_modify_batch) RETURN_THROWS(); } - /* are its keys integers? */ - if (zend_hash_get_current_key_type(Z_ARRVAL_P(modinfo)) != HASH_KEY_IS_LONG) { - zend_value_error("%s(): Option \"" LDAP_MODIFY_BATCH_VALUES "\" must be integer-indexed", get_active_function_name()); + if (!php_ldap_is_numerically_indexed_array(Z_ARRVAL_P(modinfo))) { + zend_value_error("%s(): Option \"" LDAP_MODIFY_BATCH_VALUES "\" must be an array with numeric keys", get_active_function_name()); RETURN_THROWS(); } - - /* are the keys consecutive? */ - for (k = 0; k < num_modvals; k++) { - if ((fetched = zend_hash_index_find(Z_ARRVAL_P(modinfo), k)) == NULL) { - zend_value_error("%s(): Option \"" LDAP_MODIFY_BATCH_VALUES "\" must have consecutive integer indices starting from 0", get_active_function_name()); - RETURN_THROWS(); - } - } } zend_hash_move_forward(Z_ARRVAL_P(mod)); @@ -2732,7 +2746,9 @@ PHP_FUNCTION(ldap_modify_batch) zend_value_error("%s(): Required option \"" LDAP_MODIFY_BATCH_MODTYPE "\" is missing", get_active_function_name()); RETURN_THROWS(); } - } + + i++; + } ZEND_HASH_FOREACH_END(); } /* validation was successful */ @@ -2786,9 +2802,9 @@ PHP_FUNCTION(ldap_modify_batch) ldap_mods[i]->mod_bvalues = safe_emalloc((num_modvals+1), sizeof(struct berval *), 0); /* for each value */ - for (j = 0; j < num_modvals; j++) { + j = 0; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(vals), fetched) { /* fetch it */ - fetched = zend_hash_index_find(Z_ARRVAL_P(vals), j); modval = zval_get_string(fetched); if (EG(exception)) { RETVAL_FALSE; @@ -2804,7 +2820,8 @@ PHP_FUNCTION(ldap_modify_batch) ldap_mods[i]->mod_bvalues[j]->bv_len = ZSTR_LEN(modval); ldap_mods[i]->mod_bvalues[j]->bv_val = estrndup(ZSTR_VAL(modval), ZSTR_LEN(modval)); zend_string_release(modval); - } + j++; + } ZEND_HASH_FOREACH_END(); /* NULL-terminate values */ ldap_mods[i]->mod_bvalues[num_modvals] = NULL; diff --git a/ext/ldap/tests/gh17280.phpt b/ext/ldap/tests/gh17280.phpt new file mode 100644 index 0000000000000..8c75f1276ab58 --- /dev/null +++ b/ext/ldap/tests/gh17280.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-17280 (ldap_search() fails when $attributes array has holes) +--EXTENSIONS-- +ldap +--FILE-- + +--EXPECTF-- +Warning: ldap_search(): Search: Can't contact LDAP server in %s on line %d +bool(false) diff --git a/ext/ldap/tests/ldap_add_error.phpt b/ext/ldap/tests/ldap_add_error.phpt index d78276eca3e54..5036b3091e835 100644 --- a/ext/ldap/tests/ldap_add_error.phpt +++ b/ext/ldap/tests/ldap_add_error.phpt @@ -43,7 +43,7 @@ try { ldap_add($link, "dc=my-domain2,dc=com", array( "objectClass" => array( 0 => "top", - 2 => "dcObject", + "x" => "dcObject", 5 => "organization"), "dc" => "my-domain", "o" => "my-domain", @@ -104,7 +104,7 @@ Warning: ldap_add(): Add: Already exists in %s on line %d bool(false) string(14) "Already exists" int(68) -ldap_add(): Argument #3 ($entry) must contain arrays with consecutive integer indices starting from 0 +ldap_add(): Argument #3 ($entry) must be an array with numeric keys Warning: ldap_add(): Add: Undefined attribute type in %s on line %d bool(false) diff --git a/ext/ldap/tests/ldap_search_error.phpt b/ext/ldap/tests/ldap_search_error.phpt index 659b8a6c0664b..be07f5409ef7b 100644 --- a/ext/ldap/tests/ldap_search_error.phpt +++ b/ext/ldap/tests/ldap_search_error.phpt @@ -19,7 +19,7 @@ $filter = "(dc=*)"; $result = ldap_search($link, $dn, $filter); var_dump($result); -$result = ldap_search($link, $dn, $filter, array(1 => 'top')); +$result = ldap_search($link, $dn, $filter, array('foo' => 'top')); var_dump($result); try { @@ -57,7 +57,7 @@ try { Warning: ldap_search(): Search: No such object in %s on line %d bool(false) -Warning: ldap_search(): Array initialization wrong in %s on line %d +Warning: ldap_search(): Argument #4 ($attributes) must be an array with numeric keys in %s on line %d bool(false) ldap_search(): Argument #1 ($ldap) cannot be empty ldap_search(): Argument #2 ($base) must have the same number of elements as the links array From 7c9f645393bd8311d8b9fa3f5ca027e62d5cee93 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Mon, 30 Dec 2024 11:40:32 -0500 Subject: [PATCH 61/65] Fix FD getting code on big endian (#17259) * Fix FD getting code on big endian (PHP 8.3) stream casting as FD returns a php_socket_t, which is an int, but zend_long is 64-bit (on those platforms). This works on LE by accidental (unless it forgets to clear the high word), but is fatal on big endian. * change cast to match sig --- ext/posix/posix.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ext/posix/posix.c b/ext/posix/posix.c index ecbb849408723..2c87fbd28d981 100644 --- a/ext/posix/posix.c +++ b/ext/posix/posix.c @@ -23,6 +23,7 @@ #include "ext/standard/info.h" #include "ext/standard/php_string.h" #include "php_posix.h" +#include "main/php_network.h" #ifdef HAVE_POSIX @@ -417,7 +418,7 @@ PHP_FUNCTION(posix_ctermid) /* }}} */ /* Checks if the provides resource is a stream and if it provides a file descriptor */ -static int php_posix_stream_get_fd(zval *zfp, zend_long *fd) /* {{{ */ +static int php_posix_stream_get_fd(zval *zfp, zend_long *ret) /* {{{ */ { php_stream *stream; @@ -427,19 +428,21 @@ static int php_posix_stream_get_fd(zval *zfp, zend_long *fd) /* {{{ */ return 0; } - /* get the fd. + /* get the fd. php_socket_t is used for FDs, and is shorter than zend_long. * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting. * It is only used here so that the buffered data warning is not displayed. */ + php_socket_t fd = -1; if (php_stream_can_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL) == SUCCESS) { - php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)fd, 0); + php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void**)&fd, 0); } else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) { - php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)fd, 0); + php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void**)&fd, 0); } else { php_error_docref(NULL, E_WARNING, "Could not use stream of type '%s'", stream->ops->label); return 0; } + *ret = fd; return 1; } /* }}} */ From b8731767d83acf5ae2be6a26eab7f6721c94eb64 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 16 Dec 2024 20:24:51 +0100 Subject: [PATCH 62/65] Fix GH-16955: Use empheral ports for OpenSSL server client tests And refactor some client server tests. Closes GH-17180 --- ext/openssl/tests/ServerClientTestCase.inc | 65 ++++++++++++++----- ext/openssl/tests/bug46127.phpt | 7 +- ext/openssl/tests/bug48182.phpt | 7 +- ext/openssl/tests/bug54992.phpt | 9 ++- ext/openssl/tests/bug62890.phpt | 8 +-- ext/openssl/tests/bug65538_001.phpt | 7 +- ext/openssl/tests/bug65538_003.phpt | 7 +- ext/openssl/tests/bug65729.phpt | 12 ++-- ext/openssl/tests/bug68265.phpt | 8 +-- ext/openssl/tests/bug68879.phpt | 8 +-- ext/openssl/tests/bug68920.phpt | 8 +-- ext/openssl/tests/bug69215.phpt | 8 +-- ext/openssl/tests/bug72333.phpt | 7 +- ext/openssl/tests/bug73072.phpt | 8 +-- ext/openssl/tests/bug74159.phpt | 8 +-- ext/openssl/tests/bug76705.phpt | 7 +- ext/openssl/tests/bug77390.phpt | 18 ++--- ext/openssl/tests/capture_peer_cert_001.phpt | 7 +- .../tests/openssl_peer_fingerprint_basic.phpt | 10 ++- ext/openssl/tests/peer_verification.phpt | 8 +-- ext/openssl/tests/san_ipv6_peer_matching.phpt | 10 ++- ext/openssl/tests/san_peer_matching.phpt | 10 ++- ext/openssl/tests/session_meta_capture.phpt | 8 +-- .../tests/session_meta_capture_tlsv13.phpt | 13 ++-- ext/openssl/tests/sni_server.phpt | 12 ++-- ext/openssl/tests/sni_server_key_cert.phpt | 12 ++-- .../tests/stream_crypto_flags_001.phpt | 8 +-- .../tests/stream_crypto_flags_002.phpt | 8 +-- .../tests/stream_crypto_flags_003.phpt | 8 +-- .../tests/stream_crypto_flags_004.phpt | 8 +-- ext/openssl/tests/stream_security_level.phpt | 9 ++- .../tests/stream_server_reneg_limit.phpt | 2 +- .../tests/stream_verify_peer_name_001.phpt | 7 +- .../tests/stream_verify_peer_name_002.phpt | 7 +- .../tests/stream_verify_peer_name_003.phpt | 9 ++- ext/openssl/tests/streams_crypto_method.phpt | 7 +- .../tests/tls_min_v1.0_max_v1.1_wrapper.phpt | 18 +++-- ext/openssl/tests/tls_wrapper.phpt | 18 +++-- .../tests/tls_wrapper_with_tls_v1.3.phpt | 20 +++--- ext/openssl/tests/tlsv1.0_wrapper.phpt | 12 ++-- ext/openssl/tests/tlsv1.1_wrapper.phpt | 12 ++-- ext/openssl/tests/tlsv1.2_wrapper.phpt | 12 ++-- ext/openssl/tests/tlsv1.3_wrapper.phpt | 12 ++-- .../stream_context_tcp_nodelay_server.phpt | 40 +++++------- 44 files changed, 236 insertions(+), 273 deletions(-) diff --git a/ext/openssl/tests/ServerClientTestCase.inc b/ext/openssl/tests/ServerClientTestCase.inc index 753366df6f4be..61d45385b6240 100644 --- a/ext/openssl/tests/ServerClientTestCase.inc +++ b/ext/openssl/tests/ServerClientTestCase.inc @@ -4,14 +4,19 @@ const WORKER_ARGV_VALUE = 'RUN_WORKER'; const WORKER_DEFAULT_NAME = 'server'; -function phpt_notify($worker = WORKER_DEFAULT_NAME) +function phpt_notify(string $worker = WORKER_DEFAULT_NAME, string $message = ""): void { - ServerClientTestCase::getInstance()->notify($worker); + ServerClientTestCase::getInstance()->notify($worker, $message); } -function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null) +function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null): ?string { - ServerClientTestCase::getInstance()->wait($worker, $timeout); + return ServerClientTestCase::getInstance()->wait($worker, $timeout); +} + +function phpt_notify_server_start($server): void +{ + ServerClientTestCase::getInstance()->notify_server_start($server); } function phpt_has_sslv3() { @@ -119,43 +124,73 @@ class ServerClientTestCase eval($code); } - public function run($masterCode, $workerCode) + /** + * Run client and all workers + * + * @param string $clientCode The client PHP code + * @param string|array $workerCode + * @param bool $ephemeral Select whether automatic port selection and automatic awaiting is used + * @return void + * @throws Exception + */ + public function run(string $clientCode, string|array $workerCode, bool $ephemeral = true): void { if (!is_array($workerCode)) { $workerCode = [WORKER_DEFAULT_NAME => $workerCode]; } - foreach ($workerCode as $worker => $code) { + reset($workerCode); + $code = current($workerCode); + $worker = key($workerCode); + while ($worker != null) { $this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code)); + $code = next($workerCode); + if ($ephemeral) { + $addr = trim($this->wait($worker)); + if (empty($addr)) { + throw new \Exception("Failed server start"); + } + if ($code === false) { + $clientCode = preg_replace('/{{\s*ADDR\s*}}/', $addr, $clientCode); + } else { + $code = preg_replace('/{{\s*ADDR\s*}}/', $addr, $code); + } + } + $worker = key($workerCode); } - eval($this->stripPhpTagsFromCode($masterCode)); + + eval($this->stripPhpTagsFromCode($clientCode)); foreach ($workerCode as $worker => $code) { $this->cleanupWorkerProcess($worker); } } - public function wait($worker, $timeout = null) + public function wait($worker, $timeout = null): ?string { $handle = $this->isWorker ? STDIN : $this->workerStdOut[$worker]; if ($timeout === null) { - fgets($handle); - return true; + return fgets($handle); } stream_set_blocking($handle, false); $read = [$handle]; $result = stream_select($read, $write, $except, $timeout); if (!$result) { - return false; + return null; } - fgets($handle); + $result = fgets($handle); stream_set_blocking($handle, true); - return true; + return $result; + } + + public function notify(string $worker, string $message = ""): void + { + fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "$message\n"); } - public function notify($worker) + public function notify_server_start($server): void { - fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "\n"); + echo stream_socket_get_name($server, false) . "\n"; } } diff --git a/ext/openssl/tests/bug46127.phpt b/ext/openssl/tests/bug46127.phpt index 72b7bd7488611..7c38192fca95a 100644 --- a/ext/openssl/tests/bug46127.phpt +++ b/ext/openssl/tests/bug46127.phpt @@ -11,14 +11,14 @@ if (!function_exists("proc_open")) die("skip no proc_open"); $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug46127.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', ]]); $sock = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($sock); $link = stream_socket_accept($sock); fwrite($link, "Sending bug 46127\n"); @@ -26,7 +26,7 @@ CODE; $serverCode = sprintf($serverCode, $certFile); $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ @@ -34,7 +34,6 @@ $clientCode = <<<'CODE' 'verify_peer_name' => false ]]); - phpt_wait(); $sock = stream_socket_client($serverUri, $errno, $errstr, 2, $clientFlags, $clientCtx); echo fgets($sock); diff --git a/ext/openssl/tests/bug48182.phpt b/ext/openssl/tests/bug48182.phpt index b5fa518a8f4d2..057b257038f2a 100644 --- a/ext/openssl/tests/bug48182.phpt +++ b/ext/openssl/tests/bug48182.phpt @@ -12,14 +12,14 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug48182.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug48182-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); $client = @stream_socket_accept($server, 1); @@ -30,14 +30,13 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'bug48182'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'cafile' => '%s', 'peer_name' => '%s' ]]); - phpt_wait(); $client = stream_socket_client($serverUri, $errno, $errstr, 10, $clientFlags, $clientCtx); $data = "Sending data over to SSL server in async mode with contents like Hello World\n"; diff --git a/ext/openssl/tests/bug54992.phpt b/ext/openssl/tests/bug54992.phpt index 5cf65ff7a05b8..bc5df171c5f07 100644 --- a/ext/openssl/tests/bug54992.phpt +++ b/ext/openssl/tests/bug54992.phpt @@ -12,14 +12,14 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug54992.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug54992-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); CODE; @@ -28,7 +28,7 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'bug54992_actual_peer_name'; $wrongPeerName = 'bug54992_expected_peer_name'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, @@ -36,7 +36,6 @@ $clientCode = <<<'CODE' 'peer_name' => '%s', ]]); - phpt_wait(); $client = stream_socket_client($serverUri, $errno, $errstr, 2, $clientFlags, $clientCtx); var_dump($client); @@ -61,5 +60,5 @@ Warning: stream_socket_client(): Peer certificate CN=`bug54992_actual_peer_name' Warning: stream_socket_client(): Failed to enable crypto in %s on line %d -Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d +Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:%d (Unknown error) in %s on line %d bool(false) diff --git a/ext/openssl/tests/bug62890.phpt b/ext/openssl/tests/bug62890.phpt index f8b3ee860519d..aa9bec0e69630 100644 --- a/ext/openssl/tests/bug62890.phpt +++ b/ext/openssl/tests/bug62890.phpt @@ -19,8 +19,8 @@ $serverCode = <<<'CODE' 'security_level' => 1, ]]); - $server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); @stream_socket_accept($server, 3); CODE; $serverCode = sprintf($serverCode, $certFile); @@ -33,9 +33,7 @@ $clientCode = <<<'CODE' 'security_level' => 1, ]]); - phpt_wait(); - - $client = stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); CODE; diff --git a/ext/openssl/tests/bug65538_001.phpt b/ext/openssl/tests/bug65538_001.phpt index c6f443627b53e..e04f6cf820e4c 100644 --- a/ext/openssl/tests/bug65538_001.phpt +++ b/ext/openssl/tests/bug65538_001.phpt @@ -12,14 +12,14 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug65538_001.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug65538_001-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); $client = @stream_socket_accept($server); if ($client) { @@ -41,13 +41,12 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'bug65538_001'; $clientCode = <<<'CODE' - $serverUri = "https://127.0.0.1:64321/"; + $serverUri = "https://{{ ADDR }}/"; $clientCtx = stream_context_create(['ssl' => [ 'cafile' => 'file://%s', 'peer_name' => '%s', ]]); - phpt_wait(); $html = file_get_contents($serverUri, false, $clientCtx); var_dump($html); diff --git a/ext/openssl/tests/bug65538_003.phpt b/ext/openssl/tests/bug65538_003.phpt index 3b16b7b030c68..c18a024f0761d 100644 --- a/ext/openssl/tests/bug65538_003.phpt +++ b/ext/openssl/tests/bug65538_003.phpt @@ -17,14 +17,14 @@ $cacertFile = 'bug65538_003-ca.pem'; $cacertPhar = __DIR__ . DIRECTORY_SEPARATOR . 'bug65538_003-ca.phar.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); $client = @stream_socket_accept($server); if ($client) { @@ -46,13 +46,12 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'bug65538_003'; $clientCode = <<<'CODE' - $serverUri = "https://127.0.0.1:64321/"; + $serverUri = "https://{{ ADDR }}/"; $clientCtx = stream_context_create(['ssl' => [ 'cafile' => 'phar://%s/%s', 'peer_name' => '%s', ]]); - phpt_wait(); $html = file_get_contents($serverUri, false, $clientCtx); var_dump($html); diff --git a/ext/openssl/tests/bug65729.phpt b/ext/openssl/tests/bug65729.phpt index 21d2ac7f569cc..6df22fd4a7aac 100644 --- a/ext/openssl/tests/bug65729.phpt +++ b/ext/openssl/tests/bug65729.phpt @@ -12,14 +12,14 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug65729.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug65729-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); $expected_names = ['foo.test.com.sg', 'foo.test.com', 'FOO.TEST.COM', 'foo.bar.test.com']; foreach ($expected_names as $name) { @@ -29,11 +29,9 @@ CODE; $serverCode = sprintf($serverCode, $certFile); $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; - phpt_wait(); - $expected_names = ['foo.test.com.sg', 'foo.test.com', 'FOO.TEST.COM', 'foo.bar.test.com']; foreach ($expected_names as $expected_name) { $clientCtx = stream_context_create(['ssl' => [ @@ -65,7 +63,7 @@ Warning: stream_socket_client(): Peer certificate CN=`*.test.com' did not match Warning: stream_socket_client(): Failed to enable crypto in %s on line %d -Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d +Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:%d (Unknown error) in %s on line %d bool(false) resource(%d) of type (stream) resource(%d) of type (stream) @@ -74,5 +72,5 @@ Warning: stream_socket_client(): Peer certificate CN=`*.test.com' did not match Warning: stream_socket_client(): Failed to enable crypto in %s on line %d -Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d +Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:%d (Unknown error) in %s on line %d bool(false) diff --git a/ext/openssl/tests/bug68265.phpt b/ext/openssl/tests/bug68265.phpt index 9d72d47707edf..ca82543520ca8 100644 --- a/ext/openssl/tests/bug68265.phpt +++ b/ext/openssl/tests/bug68265.phpt @@ -12,21 +12,21 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug68265.pem.tmp'; $san = 'DNS:debs.ak-online.be., DNS:debs.ak-online.net.'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); stream_socket_accept($server, 30); CODE; $serverCode = sprintf($serverCode, $certFile); $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => false, @@ -34,8 +34,6 @@ $clientCode = <<<'CODE' 'peer_name' => 'debs.ak-online.net', ]]); - phpt_wait(); - var_dump(stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx)); CODE; diff --git a/ext/openssl/tests/bug68879.phpt b/ext/openssl/tests/bug68879.phpt index b529b93e7f438..cf87d2b543760 100644 --- a/ext/openssl/tests/bug68879.phpt +++ b/ext/openssl/tests/bug68879.phpt @@ -12,21 +12,21 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug68879.pem.tmp'; $san = 'DNS:test.com, DNS:www.test.com, DNS:subdomain.test.com, IP:0:0:0:0:0:FFFF:A02:1, IP:10.2.0.1'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); stream_socket_accept($server, 30); CODE; $serverCode = sprintf($serverCode, $certFile); $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => false, @@ -34,8 +34,6 @@ $clientCode = <<<'CODE' 'peer_name' => '10.2.0.1', ]]); - phpt_wait(); - var_dump(stream_socket_client($serverUri, $errno, $errstr, 30, $clientFlags, $clientCtx)); CODE; diff --git a/ext/openssl/tests/bug68920.phpt b/ext/openssl/tests/bug68920.phpt index b25dd715dbb5c..4abe12586b0b8 100644 --- a/ext/openssl/tests/bug68920.phpt +++ b/ext/openssl/tests/bug68920.phpt @@ -11,14 +11,14 @@ if (!function_exists("proc_open")) die("skip no proc_open"); $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug68920.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); stream_socket_accept($server, 30); stream_socket_accept($server, 30); @@ -28,11 +28,9 @@ CODE; $serverCode = sprintf($serverCode, $certFile); $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; - phpt_wait(); - $ctx = stream_context_create(['ssl' => ['verify_peer'=> false, 'peer_fingerprint' => true]]); $sock = stream_socket_client($serverUri, $errno, $errstr, 30, $clientFlags, $ctx); var_dump($sock); diff --git a/ext/openssl/tests/bug69215.phpt b/ext/openssl/tests/bug69215.phpt index fcae9c19a9e65..8b5dc5a261f31 100644 --- a/ext/openssl/tests/bug69215.phpt +++ b/ext/openssl/tests/bug69215.phpt @@ -13,7 +13,7 @@ $clientCertFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug69215-client.pem.tmp'; $serverCertFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug69215-server.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', @@ -24,14 +24,14 @@ $serverCode = <<<'CODE' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); stream_socket_accept($server, 30); CODE; $serverCode = sprintf($serverCode, $serverCertFile, $caCertFile); $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', @@ -41,8 +41,6 @@ $clientCode = <<<'CODE' 'peer_name' => 'bug69215-server', ]]); - phpt_wait(); - var_dump(stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx)); CODE; $clientCode = sprintf($clientCode, $clientCertFile, $caCertFile); diff --git a/ext/openssl/tests/bug72333.phpt b/ext/openssl/tests/bug72333.phpt index d484db77f02cf..57faf4d8fc99c 100644 --- a/ext/openssl/tests/bug72333.phpt +++ b/ext/openssl/tests/bug72333.phpt @@ -14,8 +14,8 @@ $serverCode = <<<'CODE' $context = stream_context_create(['ssl' => ['local_cert' => '%s']]); $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN; - $fp = stream_socket_server("ssl://127.0.0.1:10011", $errornum, $errorstr, $flags, $context); - phpt_notify(); + $fp = stream_socket_server("ssl://127.0.0.1:0", $errornum, $errorstr, $flags, $context); + phpt_notify_server_start($fp); $conn = stream_socket_accept($fp); $total = 100000; $result = fread($conn, $total); @@ -40,8 +40,7 @@ $peerName = 'bug72333'; $clientCode = <<<'CODE' $context = stream_context_create(['ssl' => ['verify_peer' => false, 'peer_name' => '%s']]); - phpt_wait(); - $fp = stream_socket_client("ssl://127.0.0.1:10011", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT, $context); + $fp = stream_socket_client("ssl://{{ ADDR }}", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT, $context); stream_set_blocking($fp, false); function blocking_fwrite($fp, $buf) { diff --git a/ext/openssl/tests/bug73072.phpt b/ext/openssl/tests/bug73072.phpt index 953dc74efbeae..881de20937f25 100644 --- a/ext/openssl/tests/bug73072.phpt +++ b/ext/openssl/tests/bug73072.phpt @@ -18,9 +18,9 @@ $serverCode = <<<'CODE' ] ]]); - $server = stream_socket_server('tls://127.0.0.1:64322', $errno, $errstr, $flags, $ctx); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); - phpt_notify(); @stream_socket_accept($server, 3); // if there is a segfault, this won't be called fwrite(STDERR, "done\n"); @@ -33,11 +33,9 @@ $clientCode = <<<'CODE' 'capture_peer_cert' => true ]; - phpt_wait(); - $ctxArr['peer_name'] = 'domain1.com'; $ctx = stream_context_create(['ssl' => $ctxArr]); - @stream_socket_client("tls://127.0.0.1:64322", $errno, $errstr, 1, $flags, $ctx); + @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 1, $flags, $ctx); CODE; include 'ServerClientTestCase.inc'; diff --git a/ext/openssl/tests/bug74159.phpt b/ext/openssl/tests/bug74159.phpt index 92bdba10d27d4..523169e364a80 100644 --- a/ext/openssl/tests/bug74159.phpt +++ b/ext/openssl/tests/bug74159.phpt @@ -15,7 +15,7 @@ $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug74159-ca.pem.tmp'; // not really reliable on more powerful machine but cover different // scenarios which might be useful. More reliable test is bug72333.phpt $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:10012"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', @@ -23,7 +23,7 @@ $serverCode = <<<'CODE' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); $client = stream_socket_accept($server, 1); @@ -73,7 +73,7 @@ $clientCode = <<<'CODE' exit("$errstr\n"); }); - $serverUri = "tcp://127.0.0.1:10012"; + $serverUri = "tcp://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, @@ -81,8 +81,6 @@ $clientCode = <<<'CODE' 'peer_name' => '%s', ]]); - phpt_wait(); - $fp = stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx); stream_set_blocking($fp, false); diff --git a/ext/openssl/tests/bug76705.phpt b/ext/openssl/tests/bug76705.phpt index 0ff9a563932ee..6e898c57b64fa 100644 --- a/ext/openssl/tests/bug76705.phpt +++ b/ext/openssl/tests/bug76705.phpt @@ -9,20 +9,20 @@ if (!function_exists("proc_open")) die("skip no proc_open"); --FILE-- [ 'local_cert' => __DIR__ . '/bug76705.pem' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); CODE; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64323"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, @@ -33,7 +33,6 @@ $clientCode = <<<'CODE' ] ]]); - phpt_wait(); var_dump(stream_socket_client($serverUri, $errno, $errstr, 2, $clientFlags, $clientCtx)); CODE; diff --git a/ext/openssl/tests/bug77390.phpt b/ext/openssl/tests/bug77390.phpt index b45910b766492..a55ee16f4bf02 100644 --- a/ext/openssl/tests/bug77390.phpt +++ b/ext/openssl/tests/bug77390.phpt @@ -15,11 +15,7 @@ $peerName = 'bug77390'; $clientCode = <<<'CODE' $context = stream_context_create(['ssl' => ['verify_peer' => false, 'peer_name' => '%s']]); - phpt_wait('server'); - phpt_notify('proxy'); - - phpt_wait('proxy'); - $fp = stream_socket_client("ssl://127.0.0.1:10012", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT, $context); + $fp = stream_socket_client("ssl://{{ ADDR }}", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT, $context); stream_set_blocking($fp, false); $read = [$fp]; @@ -57,8 +53,8 @@ $serverCode = <<<'CODE' $context = stream_context_create(['ssl' => ['local_cert' => '%s']]); $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN; - $fp = stream_socket_server("ssl://127.0.0.1:10011", $errornum, $errorstr, $flags, $context); - phpt_notify(); + $fp = stream_socket_server("ssl://127.0.0.1:0", $errornum, $errorstr, $flags, $context); + phpt_notify_server_start($fp); $conn = stream_socket_accept($fp); fwrite($conn, 'warmup'); @@ -71,14 +67,12 @@ CODE; $serverCode = sprintf($serverCode, $certFile); $proxyCode = <<<'CODE' - phpt_wait(); - - $upstream = stream_socket_client("tcp://127.0.0.1:10011", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT); + $upstream = stream_socket_client("tcp://{{ ADDR }}", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT); stream_set_blocking($upstream, false); $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN; - $server = stream_socket_server("tcp://127.0.0.1:10012", $errornum, $errorstr, $flags); - phpt_notify(); + $server = stream_socket_server("tcp://127.0.0.1:0", $errornum, $errorstr, $flags); + phpt_notify_server_start($server); $conn = stream_socket_accept($server); stream_set_blocking($conn, false); diff --git a/ext/openssl/tests/capture_peer_cert_001.phpt b/ext/openssl/tests/capture_peer_cert_001.phpt index dc1e2caa6a973..bbf9e6d2c5f5d 100644 --- a/ext/openssl/tests/capture_peer_cert_001.phpt +++ b/ext/openssl/tests/capture_peer_cert_001.phpt @@ -12,14 +12,14 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'capture_peer_cert_001.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'capture_peer_cert_001-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); CODE; @@ -27,14 +27,13 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'capture_peer_cert_001'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'capture_peer_cert' => true, 'cafile' => '%s' ]]); - phpt_wait(); $client = @stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx); $cert = stream_context_get_options($clientCtx)['ssl']['peer_certificate']; var_dump(openssl_x509_parse($cert)['subject']['CN']); diff --git a/ext/openssl/tests/openssl_peer_fingerprint_basic.phpt b/ext/openssl/tests/openssl_peer_fingerprint_basic.phpt index fb2c852160c18..34080446088d2 100644 --- a/ext/openssl/tests/openssl_peer_fingerprint_basic.phpt +++ b/ext/openssl/tests/openssl_peer_fingerprint_basic.phpt @@ -12,14 +12,14 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'openssl_peer_fingerprint_basic.pem. $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'openssl_peer_fingerprint_basic-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); @stream_socket_accept($server, 1); @@ -28,7 +28,7 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'openssl_peer_fingerprint_basic'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, @@ -37,8 +37,6 @@ $clientCode = <<<'CODE' 'peer_name' => '%s', ]]); - phpt_wait(); - stream_context_set_option($clientCtx, 'ssl', 'peer_fingerprint', '%s'); var_dump(stream_socket_client($serverUri, $errno, $errstr, 2, $clientFlags, $clientCtx)); @@ -75,6 +73,6 @@ Warning: stream_socket_client(): peer_fingerprint match failure in %s on line %d Warning: stream_socket_client(): Failed to enable crypto in %s on line %d -Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d +Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:%d (Unknown error) in %s on line %d bool(false) resource(%d) of type (stream) diff --git a/ext/openssl/tests/peer_verification.phpt b/ext/openssl/tests/peer_verification.phpt index 3ca5f8e14136d..bda68c933ca4d 100644 --- a/ext/openssl/tests/peer_verification.phpt +++ b/ext/openssl/tests/peer_verification.phpt @@ -12,14 +12,14 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'peer_verification.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'peer_verification-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); for ($i = 0; $i < 5; $i++) { @stream_socket_accept($server, 1); @@ -29,12 +29,10 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'peer_verification'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $caFile = '%s'; - phpt_wait(); - // Expected to fail -- untrusted server cert and no CA File present var_dump(@stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags)); diff --git a/ext/openssl/tests/san_ipv6_peer_matching.phpt b/ext/openssl/tests/san_ipv6_peer_matching.phpt index 81966025d3969..bc3bd02f269cd 100644 --- a/ext/openssl/tests/san_ipv6_peer_matching.phpt +++ b/ext/openssl/tests/san_ipv6_peer_matching.phpt @@ -17,14 +17,14 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'san_ipv6_peer_matching.pem.tmp'; $san = 'IP:2001:db8:85a3:8d3:1319:8a2e:370:7348'; $serverCode = <<<'CODE' - $serverUri = "ssl://[::1]:64324"; + $serverUri = "ssl://[::1]:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); @stream_socket_accept($server, 1); @@ -32,14 +32,12 @@ CODE; $serverCode = sprintf($serverCode, $certFile); $clientCode = <<<'CODE' - $serverUri = "ssl://[::1]:64324"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => false, ]]); - phpt_wait(); - stream_context_set_option($clientCtx, 'ssl', 'peer_name', '2001:db8:85a3:8d3:1319:8a2e:370:7348'); var_dump(stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx)); @@ -65,5 +63,5 @@ Warning: stream_socket_client(): Unable to locate peer certificate CN in %s on l Warning: stream_socket_client(): Failed to enable crypto in %s on line %d -Warning: stream_socket_client(): Unable to connect to ssl://[::1]:64324 (Unknown error) in %s on line %d +Warning: stream_socket_client(): Unable to connect to ssl://[::1]:%d (Unknown error) in %s on line %d bool(false) diff --git a/ext/openssl/tests/san_peer_matching.phpt b/ext/openssl/tests/san_peer_matching.phpt index cc828fe004bb5..0df18f4a92505 100644 --- a/ext/openssl/tests/san_peer_matching.phpt +++ b/ext/openssl/tests/san_peer_matching.phpt @@ -12,14 +12,14 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'san_peer_matching.pem.tmp'; $san = 'DNS:example.org, DNS:www.example.org, DNS:test.example.org'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); @stream_socket_accept($server, 1); @@ -27,14 +27,12 @@ CODE; $serverCode = sprintf($serverCode, $certFile); $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => false, ]]); - phpt_wait(); - stream_context_set_option($clientCtx, 'ssl', 'peer_name', 'example.org'); var_dump(stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx)); @@ -60,5 +58,5 @@ Warning: stream_socket_client(): Unable to locate peer certificate CN in %s on l Warning: stream_socket_client(): Failed to enable crypto in %s on line %d -Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d +Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:%d (Unknown error) in %s on line %d bool(false) diff --git a/ext/openssl/tests/session_meta_capture.phpt b/ext/openssl/tests/session_meta_capture.phpt index 36e3855f80aac..c642126a12693 100644 --- a/ext/openssl/tests/session_meta_capture.phpt +++ b/ext/openssl/tests/session_meta_capture.phpt @@ -12,7 +12,7 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'session_meta_capture.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'session_meta_capture-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', @@ -20,7 +20,7 @@ $serverCode = <<<'CODE' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); @stream_socket_accept($server, 1); @@ -31,7 +31,7 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'session_meta_capture'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, @@ -40,8 +40,6 @@ $clientCode = <<<'CODE' 'security_level' => 0, ]]); - phpt_wait(); - stream_context_set_option($clientCtx, 'ssl', 'crypto_method', STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT); $stream = stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx); var_dump(stream_get_meta_data($stream)['crypto']['protocol']); diff --git a/ext/openssl/tests/session_meta_capture_tlsv13.phpt b/ext/openssl/tests/session_meta_capture_tlsv13.phpt index e07fb8c24e478..a21813d95031e 100644 --- a/ext/openssl/tests/session_meta_capture_tlsv13.phpt +++ b/ext/openssl/tests/session_meta_capture_tlsv13.phpt @@ -13,7 +13,7 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'session_meta_capture_tlsv13.pem.tmp $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'session_meta_capture_tlsv13-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', @@ -21,7 +21,7 @@ $serverCode = <<<'CODE' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); CODE; @@ -29,7 +29,7 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'session_meta_capture_tlsv13'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, @@ -37,8 +37,6 @@ $clientCode = <<<'CODE' 'peer_name' => '%s' ]]); - phpt_wait(); - stream_context_set_option($clientCtx, 'ssl', 'crypto_method', STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT); $stream = stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx); var_dump(stream_get_meta_data($stream)['crypto']['protocol']); @@ -53,5 +51,10 @@ $certificateGenerator->saveNewCertAsFileWithKey($peerName, $certFile); include 'ServerClientTestCase.inc'; ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> +--CLEAN-- + --EXPECT-- string(7) "TLSv1.3" diff --git a/ext/openssl/tests/sni_server.phpt b/ext/openssl/tests/sni_server.phpt index cb3b3d771684f..36a0621943eb2 100644 --- a/ext/openssl/tests/sni_server.phpt +++ b/ext/openssl/tests/sni_server.phpt @@ -19,8 +19,8 @@ $serverCode = <<<'CODE' ] ]]); - $server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); for ($i=0; $i < 3; $i++) { @stream_socket_accept($server, 3); @@ -34,23 +34,21 @@ $clientCode = <<<'CODE' 'capture_peer_cert' => true ]; - phpt_wait(); - $ctxArr['peer_name'] = 'cs.php.net'; $ctx = stream_context_create(['ssl' => $ctxArr]); - $client = stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx); + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 1, $flags, $ctx); $cert = stream_context_get_options($ctx)['ssl']['peer_certificate']; var_dump(openssl_x509_parse($cert)['subject']['CN']); $ctxArr['peer_name'] = 'uk.php.net'; $ctx = stream_context_create(['ssl' => $ctxArr]); - $client = @stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx); + $client = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 1, $flags, $ctx); $cert = stream_context_get_options($ctx)['ssl']['peer_certificate']; var_dump(openssl_x509_parse($cert)['subject']['CN']); $ctxArr['peer_name'] = 'us.php.net'; $ctx = stream_context_create(['ssl' => $ctxArr]); - $client = @stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx); + $client = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 1, $flags, $ctx); $cert = stream_context_get_options($ctx)['ssl']['peer_certificate']; var_dump(openssl_x509_parse($cert)['subject']['CN']); CODE; diff --git a/ext/openssl/tests/sni_server_key_cert.phpt b/ext/openssl/tests/sni_server_key_cert.phpt index 2d0ef8c194480..b6adf8131de60 100644 --- a/ext/openssl/tests/sni_server_key_cert.phpt +++ b/ext/openssl/tests/sni_server_key_cert.phpt @@ -28,8 +28,8 @@ $serverCode = <<<'CODE' ] ]]); - $server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); for ($i=0; $i < 3; $i++) { @stream_socket_accept($server, 3); @@ -43,23 +43,21 @@ $clientCode = <<<'CODE' 'capture_peer_cert' => true ]; - phpt_wait(); - $ctxArr['peer_name'] = 'cs.php.net'; $ctx = stream_context_create(['ssl' => $ctxArr]); - $client = stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx); + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 1, $flags, $ctx); $cert = stream_context_get_options($ctx)['ssl']['peer_certificate']; var_dump(openssl_x509_parse($cert)['subject']['CN']); $ctxArr['peer_name'] = 'uk.php.net'; $ctx = stream_context_create(['ssl' => $ctxArr]); - $client = @stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx); + $client = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 1, $flags, $ctx); $cert = stream_context_get_options($ctx)['ssl']['peer_certificate']; var_dump(openssl_x509_parse($cert)['subject']['CN']); $ctxArr['peer_name'] = 'us.php.net'; $ctx = stream_context_create(['ssl' => $ctxArr]); - $client = @stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx); + $client = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 1, $flags, $ctx); $cert = stream_context_get_options($ctx)['ssl']['peer_certificate']; var_dump(openssl_x509_parse($cert)['subject']['CN']); CODE; diff --git a/ext/openssl/tests/stream_crypto_flags_001.phpt b/ext/openssl/tests/stream_crypto_flags_001.phpt index 74caacc2ea91e..ed22864a32e5e 100644 --- a/ext/openssl/tests/stream_crypto_flags_001.phpt +++ b/ext/openssl/tests/stream_crypto_flags_001.phpt @@ -12,7 +12,7 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_crypto_flags_001.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_crypto_flags_001-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', @@ -20,7 +20,7 @@ $serverCode = <<<'CODE' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); @stream_socket_accept($server, 1); @@ -30,7 +30,7 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'stream_crypto_flags_001'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, @@ -39,8 +39,6 @@ $clientCode = <<<'CODE' 'security_level' => 0, ]]); - phpt_wait(); - stream_context_set_option($clientCtx, 'ssl', 'crypto_method', STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT); var_dump(stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx)); diff --git a/ext/openssl/tests/stream_crypto_flags_002.phpt b/ext/openssl/tests/stream_crypto_flags_002.phpt index 870fd00e581bf..8b73f883c3fd7 100644 --- a/ext/openssl/tests/stream_crypto_flags_002.phpt +++ b/ext/openssl/tests/stream_crypto_flags_002.phpt @@ -12,7 +12,7 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_crypto_flags_002.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_crypto_flags_002-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', @@ -20,7 +20,7 @@ $serverCode = <<<'CODE' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); @stream_socket_accept($server, 1); @@ -31,7 +31,7 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'stream_crypto_flags_002'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, @@ -40,8 +40,6 @@ $clientCode = <<<'CODE' 'security_level' => 0, ]]); - phpt_wait(); - stream_context_set_option($clientCtx, 'ssl', 'crypto_method', STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT); var_dump(stream_socket_client($serverUri, $errno, $errstr, 2, $clientFlags, $clientCtx)); diff --git a/ext/openssl/tests/stream_crypto_flags_003.phpt b/ext/openssl/tests/stream_crypto_flags_003.phpt index a75dd18cf6a74..a112fcff0b9d2 100644 --- a/ext/openssl/tests/stream_crypto_flags_003.phpt +++ b/ext/openssl/tests/stream_crypto_flags_003.phpt @@ -13,7 +13,7 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_crypto_flags_003.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_crypto_flags_003-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', @@ -24,7 +24,7 @@ $serverCode = <<<'CODE' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); @stream_socket_accept($server, 1); @@ -35,7 +35,7 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'stream_crypto_flags_003'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, @@ -44,8 +44,6 @@ $clientCode = <<<'CODE' 'security_level' => 0, ]]); - phpt_wait(); - stream_context_set_option($clientCtx, 'ssl', 'crypto_method', STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT); var_dump(stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx)); diff --git a/ext/openssl/tests/stream_crypto_flags_004.phpt b/ext/openssl/tests/stream_crypto_flags_004.phpt index 0a2fbc123dfd8..cfd54c9bc1c8b 100644 --- a/ext/openssl/tests/stream_crypto_flags_004.phpt +++ b/ext/openssl/tests/stream_crypto_flags_004.phpt @@ -12,7 +12,7 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_crypto_flags_004.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_crypto_flags_004-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', @@ -21,7 +21,7 @@ $serverCode = <<<'CODE' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); @stream_socket_accept($server, 1); @@ -32,7 +32,7 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'stream_crypto_flags_004'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, @@ -41,8 +41,6 @@ $clientCode = <<<'CODE' 'security_level' => 0, ]]); - phpt_wait(); - // Should succeed because the SSLv23 handshake here is compatible with the // TLSv1 hello method employed in the server var_dump(@stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx)); diff --git a/ext/openssl/tests/stream_security_level.phpt b/ext/openssl/tests/stream_security_level.phpt index 0892857a2df21..d6c967b59d346 100644 --- a/ext/openssl/tests/stream_security_level.phpt +++ b/ext/openssl/tests/stream_security_level.phpt @@ -19,7 +19,7 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_security_level.pem.tmp'; $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_security_level-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64322"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', @@ -29,14 +29,14 @@ $serverCode = <<<'CODE' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); CODE; $serverCode = sprintf($serverCode, $certFile); $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64322"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'security_level' => %d, @@ -45,7 +45,6 @@ $clientCode = <<<'CODE' 'verify_peer_name' => false ]]); - phpt_wait(); $client = stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx); var_dump($client); @@ -71,5 +70,5 @@ error:%s:SSL routines:%S:certificate verify failed in %s : eval()'d code on line Warning: stream_socket_client(): Failed to enable crypto in %s : eval()'d code on line %d -Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:64322 (Unknown error) in %s : eval()'d code on line %d +Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:%d (Unknown error) in %s : eval()'d code on line %d bool(false) diff --git a/ext/openssl/tests/stream_server_reneg_limit.phpt b/ext/openssl/tests/stream_server_reneg_limit.phpt index d661e9dc42331..d84906c81ca75 100644 --- a/ext/openssl/tests/stream_server_reneg_limit.phpt +++ b/ext/openssl/tests/stream_server_reneg_limit.phpt @@ -99,7 +99,7 @@ $certificateGenerator = new CertificateGenerator(); $certificateGenerator->saveNewCertAsFileWithKey('stream_security_level', $certFile); include 'ServerClientTestCase.inc'; -ServerClientTestCase::getInstance()->run($serverCode, $clientCode); +ServerClientTestCase::getInstance()->run($serverCode, $clientCode, false); ?> --CLEAN-- [ 'local_cert' => '%s' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); CODE; @@ -26,14 +26,13 @@ $serverCode = sprintf($serverCode, $certFile); $peerName = 'stream_verify_peer_name_001'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => false, 'peer_name' => '%s' ]]); - phpt_wait(); $client = stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx); var_dump($client); diff --git a/ext/openssl/tests/stream_verify_peer_name_002.phpt b/ext/openssl/tests/stream_verify_peer_name_002.phpt index a18ddd691114e..c6c4045a1a62c 100644 --- a/ext/openssl/tests/stream_verify_peer_name_002.phpt +++ b/ext/openssl/tests/stream_verify_peer_name_002.phpt @@ -12,14 +12,14 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_verify_peer_name_002.pem.tmp $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_verify_peer_name_002-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); CODE; @@ -27,7 +27,7 @@ $serverCode = sprintf($serverCode, $certFile); $actualPeerName = 'stream_verify_peer_name_002'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, @@ -35,7 +35,6 @@ $clientCode = <<<'CODE' 'verify_peer_name' => false ]]); - phpt_wait(); $client = stream_socket_client($serverUri, $errno, $errstr, 2, $clientFlags, $clientCtx); var_dump($client); diff --git a/ext/openssl/tests/stream_verify_peer_name_003.phpt b/ext/openssl/tests/stream_verify_peer_name_003.phpt index 835e55eec0694..1ebd42c2fd455 100644 --- a/ext/openssl/tests/stream_verify_peer_name_003.phpt +++ b/ext/openssl/tests/stream_verify_peer_name_003.phpt @@ -12,14 +12,14 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_verify_peer_name_003.pem.tmp $cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_verify_peer_name_003-ca.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); @stream_socket_accept($server, 1); CODE; @@ -27,14 +27,13 @@ $serverCode = sprintf($serverCode, $certFile); $actualPeerName = 'stream_verify_peer_name_003'; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'verify_peer' => true, 'cafile' => '%s' ]]); - phpt_wait(); $client = stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx); var_dump($client); @@ -59,5 +58,5 @@ Warning: stream_socket_client(): Peer certificate CN=`stream_verify_peer_name_00 Warning: stream_socket_client(): Failed to enable crypto in %s on line %d -Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d +Warning: stream_socket_client(): Unable to connect to ssl://127.0.0.1:%d (Unknown error) in %s on line %d bool(false) diff --git a/ext/openssl/tests/streams_crypto_method.phpt b/ext/openssl/tests/streams_crypto_method.phpt index 92654c877c5e3..39584b50cdbfa 100644 --- a/ext/openssl/tests/streams_crypto_method.phpt +++ b/ext/openssl/tests/streams_crypto_method.phpt @@ -11,14 +11,14 @@ if (!function_exists("proc_open")) die("skip no proc_open"); $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'streams_crypto_method.pem.tmp'; $serverCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($server); $client = @stream_socket_accept($server); if ($client) { @@ -39,7 +39,7 @@ CODE; $serverCode = sprintf($serverCode, $certFile); $clientCode = <<<'CODE' - $serverUri = "https://127.0.0.1:64321/"; + $serverUri = "https://{{ ADDR }}/"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, @@ -47,7 +47,6 @@ $clientCode = <<<'CODE' 'verify_peer_name' => false ]]); - phpt_wait(); echo file_get_contents($serverUri, false, $clientCtx); CODE; diff --git a/ext/openssl/tests/tls_min_v1.0_max_v1.1_wrapper.phpt b/ext/openssl/tests/tls_min_v1.0_max_v1.1_wrapper.phpt index 5d04263cfbc74..5be76547f209d 100644 --- a/ext/openssl/tests/tls_min_v1.0_max_v1.1_wrapper.phpt +++ b/ext/openssl/tests/tls_min_v1.0_max_v1.1_wrapper.phpt @@ -19,8 +19,8 @@ $serverCode = <<<'CODE' 'security_level' => 0, ]]); - $server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); for ($i=0; $i < (phpt_has_sslv3() ? 6 : 5); $i++) { @stream_socket_accept($server, 3); @@ -36,24 +36,22 @@ $clientCode = <<<'CODE' 'security_level' => 0, ]]); - phpt_wait(); - - $client = stream_socket_client("tlsv1.0://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = stream_socket_client("tlsv1.0://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("sslv3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("sslv3://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.1://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.1://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.2://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("ssl://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); CODE; diff --git a/ext/openssl/tests/tls_wrapper.phpt b/ext/openssl/tests/tls_wrapper.phpt index 2220fbc0ac1da..7dcaa704cab89 100644 --- a/ext/openssl/tests/tls_wrapper.phpt +++ b/ext/openssl/tests/tls_wrapper.phpt @@ -18,8 +18,8 @@ $serverCode = <<<'CODE' 'security_level' => 0, ]]); - $server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); for ($i = 0; $i < (phpt_has_sslv3() ? 6 : 5); $i++) { @stream_socket_accept($server, 3); @@ -35,24 +35,22 @@ $clientCode = <<<'CODE' 'security_level' => 0, ]]); - phpt_wait(); - - $client = stream_socket_client("tlsv1.0://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = stream_socket_client("tlsv1.0://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("sslv3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("sslv3://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.1://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.1://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.2://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("ssl://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); CODE; diff --git a/ext/openssl/tests/tls_wrapper_with_tls_v1.3.phpt b/ext/openssl/tests/tls_wrapper_with_tls_v1.3.phpt index 67c30cac87bcb..5d38c5606c4f0 100644 --- a/ext/openssl/tests/tls_wrapper_with_tls_v1.3.phpt +++ b/ext/openssl/tests/tls_wrapper_with_tls_v1.3.phpt @@ -18,8 +18,8 @@ $serverCode = <<<'CODE' 'security_level' => 0, ]]); - $server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); for ($i = 0; $i < (phpt_has_sslv3() ? 7 : 6); $i++) { @stream_socket_accept($server, 3); @@ -35,27 +35,25 @@ $clientCode = <<<'CODE' 'security_level' => 0, ]]); - phpt_wait(); - - $client = stream_socket_client("tlsv1.0://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = stream_socket_client("tlsv1.0://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("sslv3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("sslv3://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.1://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.1://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.2://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.3://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("ssl://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); CODE; diff --git a/ext/openssl/tests/tlsv1.0_wrapper.phpt b/ext/openssl/tests/tlsv1.0_wrapper.phpt index c712217271b1e..38d44847cdb59 100644 --- a/ext/openssl/tests/tlsv1.0_wrapper.phpt +++ b/ext/openssl/tests/tlsv1.0_wrapper.phpt @@ -17,8 +17,8 @@ $serverCode = <<<'CODE' 'security_level' => 0, ]]); - $server = stream_socket_server('tlsv1.0://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tlsv1.0://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); for ($i = 0; $i < (phpt_has_sslv3() ? 3 : 2); $i++) { @stream_socket_accept($server, 3); @@ -34,15 +34,13 @@ $clientCode = <<<'CODE' 'security_level' => 0, ]]); - phpt_wait(); - - $client = stream_socket_client("tlsv1.0://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = stream_socket_client("tlsv1.0://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("sslv3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("sslv3://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.2://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); CODE; diff --git a/ext/openssl/tests/tlsv1.1_wrapper.phpt b/ext/openssl/tests/tlsv1.1_wrapper.phpt index a1525350572bb..6cdcd52fe500f 100644 --- a/ext/openssl/tests/tlsv1.1_wrapper.phpt +++ b/ext/openssl/tests/tlsv1.1_wrapper.phpt @@ -17,8 +17,8 @@ $serverCode = <<<'CODE' 'security_level' => 0, ]]); - $server = stream_socket_server('tlsv1.1://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tlsv1.1://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); for ($i = 0; $i < (phpt_has_sslv3() ? 3 : 2); $i++) { @stream_socket_accept($server, 3); @@ -34,15 +34,13 @@ $clientCode = <<<'CODE' 'security_level' => 0, ]]); - phpt_wait(); - - $client = stream_socket_client("tlsv1.1://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = stream_socket_client("tlsv1.1://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("sslv3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("sslv3://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.2://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); CODE; diff --git a/ext/openssl/tests/tlsv1.2_wrapper.phpt b/ext/openssl/tests/tlsv1.2_wrapper.phpt index b0b2c28c48379..e429687b52c46 100644 --- a/ext/openssl/tests/tlsv1.2_wrapper.phpt +++ b/ext/openssl/tests/tlsv1.2_wrapper.phpt @@ -16,8 +16,8 @@ $serverCode = <<<'CODE' 'local_cert' => '%s', ]]); - $server = stream_socket_server('tlsv1.2://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tlsv1.2://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); for ($i = 0; $i < (phpt_has_sslv3() ? 3 : 2); $i++) { @stream_socket_accept($server, 3); @@ -32,15 +32,13 @@ $clientCode = <<<'CODE' 'verify_peer_name' => false, ]]); - phpt_wait(); - - $client = stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = stream_socket_client("tlsv1.2://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("sslv3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("sslv3://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.1://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.1://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); CODE; diff --git a/ext/openssl/tests/tlsv1.3_wrapper.phpt b/ext/openssl/tests/tlsv1.3_wrapper.phpt index 9b04a2ef002ac..ee22a9e1c4bc8 100644 --- a/ext/openssl/tests/tlsv1.3_wrapper.phpt +++ b/ext/openssl/tests/tlsv1.3_wrapper.phpt @@ -17,8 +17,8 @@ $serverCode = <<<'CODE' 'local_cert' => '%s', ]]); - $server = stream_socket_server('tlsv1.3://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tlsv1.3://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); for ($i=0; $i < 3; $i++) { @stream_socket_accept($server, 3); @@ -33,15 +33,13 @@ $clientCode = <<<'CODE' 'verify_peer_name' => false, ]]); - phpt_wait(); - - $client = stream_socket_client("tlsv1.3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = stream_socket_client("tlsv1.3://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.0://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.0://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); - $client = @stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + $client = @stream_socket_client("tlsv1.2://{{ ADDR }}", $errno, $errstr, 3, $flags, $ctx); var_dump($client); CODE; diff --git a/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt b/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt index c4c3b94d3c17c..6f656ca15f9cc 100644 --- a/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt +++ b/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt @@ -2,6 +2,13 @@ stream context tcp_nodelay server --EXTENSIONS-- sockets +--SKIPIF-- + --FILE-- 0); + $si = socket_get_option(socket_import_stream($server), SOL_TCP, TCP_NODELAY) > 0 ? "nodelay": "delay"; + $ci = socket_get_option(socket_import_stream($conn), SOL_TCP, TCP_NODELAY) > 0 ? "nodelay": "delay"; - var_dump(socket_get_option( - socket_import_stream($client), - SOL_TCP, TCP_NODELAY) > 0); - - fclose($client); - fclose($server); + phpt_notify(message:"server-$si:conn-$ci"); CODE; $clientCode = <<<'CODE' - $test = stream_socket_client( - "tcp://127.0.0.1:9099", $errno, $errstr, 10); - - sleep(1); + $test = stream_socket_client("tcp://{{ ADDR }}", $errno, $errstr, 10); - fclose($test); + echo phpt_wait(); CODE; -include sprintf( - "%s/../../../openssl/tests/ServerClientTestCase.inc", - __DIR__); -ServerClientTestCase::getInstance()->run($serverCode, $clientCode); +include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECT-- -bool(false) -bool(true) +server-delay:conn-nodelay From 37504f123d59c1985944f6b05b7be4bbc14b174e Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 31 Dec 2024 15:02:08 +0100 Subject: [PATCH 63/65] Port OpenSSL gh10495, gh13860 and gh9310 test to use ephemeral ports --- ext/openssl/tests/gh10495.phpt | 18 ++++++------------ ext/openssl/tests/gh13860.phpt | 7 +++---- ext/openssl/tests/gh9310.phpt | 23 ++++++++++------------- 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/ext/openssl/tests/gh10495.phpt b/ext/openssl/tests/gh10495.phpt index 7c743d8de50a5..5ef4894890c1c 100644 --- a/ext/openssl/tests/gh10495.phpt +++ b/ext/openssl/tests/gh10495.phpt @@ -15,11 +15,7 @@ $peerName = 'gh10495'; $clientCode = <<<'CODE' $context = stream_context_create(['ssl' => ['verify_peer' => false, 'peer_name' => '%s']]); - phpt_wait('server'); - phpt_notify('proxy'); - - phpt_wait('proxy'); - $fp = stream_socket_client("tlsv1.2://127.0.0.1:10012", $errornum, $errorstr, 1, STREAM_CLIENT_CONNECT, $context); + $fp = stream_socket_client("tlsv1.2://{{ ADDR }}", $errornum, $errorstr, 1, STREAM_CLIENT_CONNECT, $context); phpt_wait('proxy'); @@ -38,8 +34,8 @@ $serverCode = <<<'CODE' $context = stream_context_create(['ssl' => ['local_cert' => '%s']]); $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN; - $fp = stream_socket_server("tlsv1.2://127.0.0.1:10011", $errornum, $errorstr, $flags, $context); - phpt_notify(); + $fp = stream_socket_server("tlsv1.2://127.0.0.1:0", $errornum, $errorstr, $flags, $context); + phpt_notify_server_start($fp); $conn = stream_socket_accept($fp); fwrite($conn, 'warmup'); @@ -50,14 +46,12 @@ CODE; $serverCode = sprintf($serverCode, $certFile); $proxyCode = <<<'CODE' - phpt_wait(); - - $upstream = stream_socket_client("tcp://127.0.0.1:10011", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT); + $upstream = stream_socket_client("tcp://{{ ADDR }}", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT); stream_set_blocking($upstream, false); $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN; - $server = stream_socket_server("tcp://127.0.0.1:10012", $errornum, $errorstr, $flags); - phpt_notify(); + $server = stream_socket_server("tcp://127.0.0.1:0", $errornum, $errorstr, $flags); + phpt_notify_server_start($server); $conn = stream_socket_accept($server); stream_set_blocking($conn, false); diff --git a/ext/openssl/tests/gh13860.phpt b/ext/openssl/tests/gh13860.phpt index aff0df15cca95..35eb473fe12d8 100644 --- a/ext/openssl/tests/gh13860.phpt +++ b/ext/openssl/tests/gh13860.phpt @@ -9,12 +9,12 @@ if (!function_exists("proc_open")) die("skip no proc_open"); --FILE-- [ 'local_cert' => '%s', @@ -37,13 +37,13 @@ $serverCodeTemplate = <<<'CODE' ]]); $sock = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify_server_start($sock); $link = stream_socket_accept($sock); CODE; $clientCode = <<<'CODE' - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://{{ ADDR }}"; $clientFlags = STREAM_CLIENT_CONNECT; $clientCtx = stream_context_create(['ssl' => [ @@ -51,7 +51,6 @@ $clientCode = <<<'CODE' 'verify_peer_name' => false ]]); - phpt_wait(); @stream_socket_client($serverUri, $errno, $errstr, 2, $clientFlags, $clientCtx); CODE; @@ -65,8 +64,8 @@ $sniServerCodeV1 = <<<'CODE' ] ]]); - $server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); stream_socket_accept($server); CODE; @@ -84,8 +83,8 @@ $sniServerCodeV2 = <<<'CODE' ] ]]); - $server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); stream_socket_accept($server); CODE; @@ -103,8 +102,8 @@ $sniServerCodeV3 = <<<'CODE' ] ]]); - $server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); - phpt_notify(); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); stream_socket_accept($server); CODE; @@ -115,11 +114,9 @@ $sniClientCodeTemplate = <<<'CODE' 'cafile' => __DIR__ . '/sni_server_ca.pem', ]; - phpt_wait(); - $ctxArr['peer_name'] = '%s'; $ctx = stream_context_create(['ssl' => $ctxArr]); - @stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx); + @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 1, $flags, $ctx); CODE; $serverCode = sprintf($serverCodeTemplate, $baseDirCertFile . "\0test", $baseDirPkFile); From 19e2e4d5af952b301a4aa00915d3a4ad100f34af Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 31 Dec 2024 15:18:50 +0100 Subject: [PATCH 64/65] Port stream bug51056 and gh11418 tests to use ephemeral ports --- ext/standard/tests/streams/bug51056.phpt | 8 +++----- ext/standard/tests/streams/gh11418.phpt | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/ext/standard/tests/streams/bug51056.phpt b/ext/standard/tests/streams/bug51056.phpt index e011405298c24..ac5909da7a55c 100644 --- a/ext/standard/tests/streams/bug51056.phpt +++ b/ext/standard/tests/streams/bug51056.phpt @@ -4,8 +4,8 @@ Bug #51056 (fread() on blocking stream will block even if data is available) Date: Tue, 14 Jan 2025 10:25:29 -0800 Subject: [PATCH 65/65] Update versions for PHP 8.3.16 --- NEWS | 2 +- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index e8397f27c5d69..6c8f280205642 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.16 +16 Jan 2025, PHP 8.3.16 - Core: . Fixed bug GH-17106 (ZEND_MATCH_ERROR misoptimization). (ilutov) diff --git a/Zend/zend.h b/Zend/zend.h index 3fe973e663bea..466d8280a013b 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.16-dev" +#define ZEND_VERSION "4.3.16" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index b6ec8b2be15fd..5eba642a5b238 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.16-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.16],[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 3521615c68fc9..4e26b392955ad 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -3,6 +3,6 @@ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 3 #define PHP_RELEASE_VERSION 16 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.16-dev" +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.3.16" #define PHP_VERSION_ID 80316