From 858c378930eb4172b94528e80c95058207469a04 Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Tue, 11 Mar 2025 16:34:23 -0500 Subject: [PATCH 01/19] PHP-8.1 is now for PHP 8.1.33-dev --- NEWS | 4 ++++ Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 87a7e7d080c44..67f6b8f728be7 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +?? ??? ????, PHP 8.1.33 + + + 13 Mar 2025, PHP 8.1.32 - LibXML: diff --git a/Zend/zend.h b/Zend/zend.h index dcf69979f952c..6562119e8363c 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.1.31-dev" +#define ZEND_VERSION "4.1.33-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index f6902707abb15..0f45db92ba7de 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.1.31-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.1.33-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 bff6722583474..2aee6f19d16a4 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 1 -#define PHP_RELEASE_VERSION 31 +#define PHP_RELEASE_VERSION 33 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.1.31-dev" -#define PHP_VERSION_ID 80131 +#define PHP_VERSION "8.1.33-dev" +#define PHP_VERSION_ID 80133 From 00ebd2d7f25c521a10feae6c8217f6be0825f84d Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 13 Mar 2025 13:01:53 +0100 Subject: [PATCH 02/19] Fix flaky connection count in mysqli test Use connection ID instead of count to check whether we're using a persistent connection. This allows the test to be run in parallel with the other tests, but also protects against the possibility that some other service connects to the mysql server. Closes GH-18040 --- ext/mysqli/tests/bug73462.phpt | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/ext/mysqli/tests/bug73462.phpt b/ext/mysqli/tests/bug73462.phpt index c4d68ca4f5935..318b8dd24b34d 100644 --- a/ext/mysqli/tests/bug73462.phpt +++ b/ext/mysqli/tests/bug73462.phpt @@ -5,13 +5,6 @@ mysqli --SKIPIF-- --FILE-- query("SHOW STATUS LIKE 'Connections'"); + $result = $mysql_1->query("SELECT CONNECTION_ID()"); $c1 = $result->fetch_row(); $result->free(); $mysql_1->close(); @@ -35,7 +28,7 @@ if (gethostname() == "php-ci-ppc64be") { /* Re-use persistent connection */ $mysql_3 = new mysqli('p:'.$host, $user, $passwd, $db, $port); $error = mysqli_connect_errno(); - $result = $mysql_3->query("SHOW STATUS LIKE 'Connections'"); + $result = $mysql_3->query("SELECT CONNECTION_ID()"); $c3 = $result->fetch_row(); $result->free(); $mysql_3->close(); From c62523666c9fef743d34f2adc1d6c7de479e4870 Mon Sep 17 00:00:00 2001 From: Pierrick Charron Date: Thu, 13 Mar 2025 13:45:08 -0400 Subject: [PATCH 03/19] [skip ci] Fix invalid release date of 8.1.1 --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 67f6b8f728be7..f34fc5c90e5de 100644 --- a/NEWS +++ b/NEWS @@ -1590,7 +1590,7 @@ PHP NEWS . Fixed bug GH-7815 (php_uname doesn't recognise latest Windows versions). (David Warner) -02 Dec 2021, PHP 8.1.1 +16 Dec 2021, PHP 8.1.1 - IMAP: . Fixed bug #81649 (imap_(un)delete accept sequences, not single numbers). From 70c2ebb69807d517fa38487e690462454c83f1ec Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 13 Mar 2025 22:24:49 +0100 Subject: [PATCH 04/19] Fix typo in GHSA-hgf5-96fm-v528 NEWS entry --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index f34fc5c90e5de..29400e6ef5b5c 100644 --- a/NEWS +++ b/NEWS @@ -12,7 +12,7 @@ PHP NEWS when requesting a redirected resource). (CVE-2025-1219) (timwolla) - Streams: - . Fixed GHSA-hgf54-96fm-v528 (Stream HTTP wrapper header check might omit + . Fixed GHSA-hgf5-96fm-v528 (Stream HTTP wrapper header check might omit basic auth header). (CVE-2025-1736) (Jakub Zelenka) . Fixed GHSA-52jp-hrpf-2jff (Stream HTTP wrapper truncate redirect location to 1024 bytes). (CVE-2025-1861) (Jakub Zelenka) From 7a3383b482af333c03aca7d52a2012857840e746 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 3 Apr 2025 13:01:59 +0200 Subject: [PATCH 05/19] [skip ci] Restrict on-push freebsd build to main repo The same applies to all other push jobs, it was just forgotten here. --- .github/workflows/push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index ddc90935018ba..214798af62b5c 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -171,6 +171,7 @@ jobs: - name: Test run: .github/scripts/windows/test.bat FREEBSD: + if: github.repository == 'php/php-src' || github.event_name == 'pull_request' name: FREEBSD runs-on: ubuntu-latest steps: From 35936bfa79e8972495190cc3cbf0dc60a53fdcea Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 22 Apr 2025 16:10:34 +0200 Subject: [PATCH 06/19] Drop tidyp from FreeBSD build It looks like it's no longer supported. We don't test tidy on FreeBSD anyway. --- .github/actions/freebsd/action.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/actions/freebsd/action.yml b/.github/actions/freebsd/action.yml index ce9ba24451582..1f7c670f27227 100644 --- a/.github/actions/freebsd/action.yml +++ b/.github/actions/freebsd/action.yml @@ -27,7 +27,6 @@ runs: bzip2 \ t1lib \ gmp \ - tidyp \ libsodium \ libzip \ libxml2 \ From 3fdd3ed9f722d9f0aa3122b2340a611db2dfa110 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 22 Apr 2025 19:58:10 +0100 Subject: [PATCH 07/19] backporting C++17 detection support for recent icu4c releases. --- ext/intl/config.m4 | 11 ++++++++- ext/intl/tests/bug62070_3.phpt | 1 + .../tests/collator_get_sort_key_variant7.phpt | 1 + ext/intl/tests/locale_get_display_name8.phpt | 24 +++++++++---------- .../tests/locale_get_display_variant2.phpt | 18 +++++++------- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/ext/intl/config.m4 b/ext/intl/config.m4 index daadd3e73d8db..27cd8b3015596 100644 --- a/ext/intl/config.m4 +++ b/ext/intl/config.m4 @@ -85,7 +85,16 @@ if test "$PHP_INTL" != "no"; then breakiterator/codepointiterator_methods.cpp" PHP_REQUIRE_CXX() - PHP_CXX_COMPILE_STDCXX(11, mandatory, PHP_INTL_STDCXX) + + AC_MSG_CHECKING([if intl requires -std=gnu++17]) + AS_IF([$PKG_CONFIG icu-uc --atleast-version=74],[ + AC_MSG_RESULT([yes]) + PHP_CXX_COMPILE_STDCXX(17, mandatory, PHP_INTL_STDCXX) + ],[ + AC_MSG_RESULT([no]) + PHP_CXX_COMPILE_STDCXX(11, mandatory, PHP_INTL_STDCXX) + ]) + PHP_INTL_CXX_FLAGS="$INTL_COMMON_FLAGS $PHP_INTL_STDCXX $ICU_CXXFLAGS" case $host_alias in *cygwin*) PHP_INTL_CXX_FLAGS="$PHP_INTL_CXX_FLAGS -D_POSIX_C_SOURCE=200809L" diff --git a/ext/intl/tests/bug62070_3.phpt b/ext/intl/tests/bug62070_3.phpt index 08c1bbf45f8ba..60e0593acfd3d 100644 --- a/ext/intl/tests/bug62070_3.phpt +++ b/ext/intl/tests/bug62070_3.phpt @@ -4,6 +4,7 @@ Bug #62070: Collator::getSortKey() returns garbage intl --SKIPIF-- = 62.1'); ?> += 0) die('skip for ICU < 76.1'); ?> --FILE-- = 62.1 intl --SKIPIF-- = 62.1'); ?> += 0) die('skip for ICU < 76.1'); ?> --FILE-- Date: Thu, 24 Apr 2025 11:26:17 +0200 Subject: [PATCH 08/19] [skip ci] Increase tolerance for cve-2014-3538 tests These regularly fail with "Failed, time=1.5x". --- ext/fileinfo/tests/cve-2014-3538-nojit.phpt | 2 +- ext/fileinfo/tests/cve-2014-3538.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/fileinfo/tests/cve-2014-3538-nojit.phpt b/ext/fileinfo/tests/cve-2014-3538-nojit.phpt index 2010d538da951..3fe21168ab8fc 100644 --- a/ext/fileinfo/tests/cve-2014-3538-nojit.phpt +++ b/ext/fileinfo/tests/cve-2014-3538-nojit.phpt @@ -24,7 +24,7 @@ $t = microtime(true); var_dump(finfo_file($fi, $fd)); $t = microtime(true) - $t; finfo_close($fi); -if ($t < 1.5) { +if ($t < 2) { echo "Ok\n"; } else { printf("Failed, time=%.2f\n", $t); diff --git a/ext/fileinfo/tests/cve-2014-3538.phpt b/ext/fileinfo/tests/cve-2014-3538.phpt index c5dba2b428c5f..2e222a6c49901 100644 --- a/ext/fileinfo/tests/cve-2014-3538.phpt +++ b/ext/fileinfo/tests/cve-2014-3538.phpt @@ -22,7 +22,7 @@ $t = microtime(true); var_dump(finfo_file($fi, $fd)); $t = microtime(true) - $t; finfo_close($fi); -if ($t < 1.5) { +if ($t < 2) { echo "Ok\n"; } else { printf("Failed, time=%.2f\n", $t); From 0a42e6fbc5476638aed2403288c9b84103c95459 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 24 Apr 2025 12:25:06 +0200 Subject: [PATCH 09/19] Use --ignore-platform-req=php+ in community build --ignore-platform-reqs may accidentally install versions of dependencies that no longer support the given PHP version. --ignore-platform-req=php+ will only suppress errors for new PHP version but not change behavior for older versions. Thanks to Tim for the hint. Also skip the Laravel build for PHP 8.1, which is no longer supported on Laravel's default branch. --- .github/workflows/nightly.yml | 21 ++++++++++++--------- .github/workflows/root.yml | 1 + 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index c9e6850604312..1b1532af7f799 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -29,6 +29,9 @@ on: windows_version: required: true type: string + skip_laravel: + required: true + type: boolean skip_symfony: required: true type: boolean @@ -550,7 +553,7 @@ jobs: git clone "https://github.com/amphp/$repository.git" "amphp-$repository" --depth 1 cd "amphp-$repository" git rev-parse HEAD - php /usr/bin/composer install --no-progress --ignore-platform-reqs + php /usr/bin/composer install --no-progress --ignore-platform-req=php+ vendor/bin/phpunit || EXIT_CODE=$? if [ ${EXIT_CODE:-0} -gt 128 ]; then X=1; @@ -559,12 +562,12 @@ jobs: done exit $X - name: Test Laravel - if: ${{ !cancelled() }} + if: ${{ !cancelled() && !inputs.skip_laravel }} run: | git clone https://github.com/laravel/framework.git --depth=1 cd framework git rev-parse HEAD - php /usr/bin/composer install --no-progress --ignore-platform-reqs + php /usr/bin/composer install --no-progress --ignore-platform-req=php+ # Hack to disable a test that hangs php -r '$c = file_get_contents("tests/Filesystem/FilesystemTest.php"); $c = str_replace("public function testSharedGet()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n public function testSharedGet()", $c); file_put_contents("tests/Filesystem/FilesystemTest.php", $c);' php vendor/bin/phpunit --exclude-group skip || EXIT_CODE=$? @@ -581,7 +584,7 @@ jobs: git clone "https://github.com/reactphp/$repository.git" "reactphp-$repository" --depth 1 cd "reactphp-$repository" git rev-parse HEAD - php /usr/bin/composer install --no-progress --ignore-platform-reqs + php /usr/bin/composer install --no-progress --ignore-platform-req=php+ vendor/bin/phpunit || EXIT_CODE=$? if [ $[EXIT_CODE:-0} -gt 128 ]; then X=1; @@ -595,7 +598,7 @@ jobs: git clone https://github.com/revoltphp/event-loop.git --depth=1 cd event-loop git rev-parse HEAD - php /usr/bin/composer install --no-progress --ignore-platform-reqs + php /usr/bin/composer install --no-progress --ignore-platform-req=php+ vendor/bin/phpunit || EXIT_CODE=$? if [ ${EXIT_CODE:-0} -gt 128 ]; then exit 1 @@ -606,7 +609,7 @@ jobs: git clone https://github.com/symfony/symfony.git --depth=1 cd symfony git rev-parse HEAD - php /usr/bin/composer install --no-progress --ignore-platform-reqs + php /usr/bin/composer install --no-progress --ignore-platform-req=php+ php ./phpunit install # Test causes a heap-buffer-overflow but I cannot reproduce it locally... php -r '$c = file_get_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php"); $c = str_replace("public function testSanitizeDeepNestedString()", "/** @group skip */\n public function testSanitizeDeepNestedString()", $c); file_put_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php", $c);' @@ -627,7 +630,7 @@ jobs: git clone https://github.com/sebastianbergmann/phpunit.git --branch=main --depth=1 cd phpunit git rev-parse HEAD - php /usr/bin/composer install --no-progress --ignore-platform-reqs + php /usr/bin/composer install --no-progress --ignore-platform-req=php+ php ./phpunit || EXIT_CODE=$? if [ ${EXIT_CODE:-0} -gt 128 ]; then exit 1 @@ -635,7 +638,7 @@ jobs: - name: 'Symfony Preloading' if: ${{ !cancelled() && !inputs.skip_symfony }} run: | - php /usr/bin/composer create-project symfony/symfony-demo symfony_demo --no-progress --ignore-platform-reqs + php /usr/bin/composer create-project symfony/symfony-demo symfony_demo --no-progress --ignore-platform-req=php+ cd symfony_demo git rev-parse HEAD sed -i 's/PHP_SAPI/"cli-server"/g' var/cache/dev/App_KernelDevDebugContainer.preload.php @@ -646,7 +649,7 @@ jobs: git clone https://github.com/WordPress/wordpress-develop.git wordpress --depth=1 cd wordpress git rev-parse HEAD - php /usr/bin/composer install --no-progress --ignore-platform-reqs + php /usr/bin/composer install --no-progress --ignore-platform-req=php+ cp wp-tests-config-sample.php wp-tests-config.php sed -i 's/youremptytestdbnamehere/test/g' wp-tests-config.php sed -i 's/yourusernamehere/root/g' wp-tests-config.php diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index 2bb895e96b668..a98bb39ba0d92 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -59,6 +59,7 @@ jobs: (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 5) || matrix.branch.version[0] >= 9) && '24.04') || '22.04' }} windows_version: ${{ ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9) && '2022' || '2019' }} + skip_laravel: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_symfony: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_wordpress: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} secrets: inherit From 9cacc57350e1bb2f070b4a7bda6803da1d71e140 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 19 Jun 2025 16:28:45 +0200 Subject: [PATCH 10/19] Track heap->real_size for USE_TRACKED_ALLOC real_size is returned by memory_get_usage(true), which previously returned 0. Discovered in Symfony ConsumeMessagesCommandTest::testRunWithMemoryLimit() through nightly. Closes GH-18880 --- Zend/zend_alloc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index c41f6118607e2..61792b37c9c50 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2256,6 +2256,7 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) heap->custom_heap.std._free = free; } heap->size = 0; + heap->real_size = 0; } if (full) { @@ -2799,6 +2800,7 @@ static void *tracked_malloc(size_t size) void *ptr = __zend_malloc(size); tracked_add(heap, ptr, size); heap->size += size; + heap->real_size = heap->size; return ptr; } @@ -2810,6 +2812,7 @@ static void tracked_free(void *ptr) { zend_mm_heap *heap = AG(mm_heap); zval *size_zv = tracked_get_size_zv(heap, ptr); heap->size -= Z_LVAL_P(size_zv); + heap->real_size = heap->size; zend_hash_del_bucket(heap->tracked_allocs, (Bucket *) size_zv); free(ptr); } @@ -2835,6 +2838,7 @@ static void *tracked_realloc(void *ptr, size_t new_size) { ptr = __zend_realloc(ptr, new_size); tracked_add(heap, ptr, new_size); heap->size += new_size - old_size; + heap->real_size = heap->size; return ptr; } From 391bd2a48fb1e256ec7108166857997eea5da93a Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 20 Jun 2025 22:39:51 +0200 Subject: [PATCH 11/19] Remove bug61371 test These tests attempt to test that no memory is leaked for stream calls. However, it is incorrect to assume the memory will not increase for other reasons, e.g. when growing resource buffers, for the output buffer, etc. This was discovered through 9cacc57350e1bb2f070b4a7bda6803da1d71e140 with USE_TRACKED_ALLOC=1, but this can also fail with USE_ZEND_ALLOC=1 when increasing loop iterations. --- ext/standard/tests/streams/bug61371-unix.phpt | 45 ------------------- ext/standard/tests/streams/bug61371.phpt | 40 ----------------- 2 files changed, 85 deletions(-) delete mode 100644 ext/standard/tests/streams/bug61371-unix.phpt delete mode 100644 ext/standard/tests/streams/bug61371.phpt diff --git a/ext/standard/tests/streams/bug61371-unix.phpt b/ext/standard/tests/streams/bug61371-unix.phpt deleted file mode 100644 index e196c028cc941..0000000000000 --- a/ext/standard/tests/streams/bug61371-unix.phpt +++ /dev/null @@ -1,45 +0,0 @@ ---TEST-- -Bug #61371: stream_context_create() causes memory leaks on use streams_socket_create ---SKIPIF-- - ---EXPECTF-- -memory: %dkb -bool(true) -memory: %dkb -bool(true) -memory: %dkb -memory: %dkb -bool(true) -memory: %dkb -bool(true) -memory: %dkb diff --git a/ext/standard/tests/streams/bug61371.phpt b/ext/standard/tests/streams/bug61371.phpt deleted file mode 100644 index 00e6372e85a4f..0000000000000 --- a/ext/standard/tests/streams/bug61371.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -Bug #61371: stream_context_create() causes memory leaks on use streams_socket_create ---FILE-- - ---EXPECTF-- -memory: %dkb -bool(true) -memory: %dkb -bool(true) -memory: %dkb -memory: %dkb -bool(true) -memory: %dkb -bool(true) -memory: %dkb From cac8f7f1cf4939f55f06b68120040f057682d89c Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 10 Apr 2025 15:15:36 +0200 Subject: [PATCH 12/19] Fix GHSA-3cr5-j632-f35r: Null byte in hostnames This fixes stream_socket_client() and fsockopen(). Specifically it adds a check to parse_ip_address_ex and it also makes sure that the \0 is not ignored in fsockopen() hostname formatting. --- ext/standard/fsock.c | 27 +++++++++++++++++-- .../tests/network/ghsa-3cr5-j632-f35r.phpt | 21 +++++++++++++++ .../tests/streams/ghsa-3cr5-j632-f35r.phpt | 26 ++++++++++++++++++ main/streams/xp_socket.c | 9 ++++--- 4 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt create mode 100644 ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c index 9e1a53c0ec2a0..67c68468f5140 100644 --- a/ext/standard/fsock.c +++ b/ext/standard/fsock.c @@ -23,6 +23,28 @@ #include "php_network.h" #include "file.h" +static size_t php_fsockopen_format_host_port(char **message, const char *prefix, size_t prefix_len, + const char *host, size_t host_len, zend_long port) +{ + char portbuf[32]; + int portlen = snprintf(portbuf, sizeof(portbuf), ":" ZEND_LONG_FMT, port); + size_t total_len = prefix_len + host_len + portlen; + + char *result = emalloc(total_len + 1); + + if (prefix_len > 0) { + memcpy(result, prefix, prefix_len); + } + memcpy(result + prefix_len, host, host_len); + memcpy(result + prefix_len + host_len, portbuf, portlen); + + result[total_len] = '\0'; + + *message = result; + + return total_len; +} + /* {{{ php_fsockopen() */ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) @@ -62,11 +84,12 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) } if (persistent) { - spprintf(&hashkey, 0, "pfsockopen__%s:" ZEND_LONG_FMT, host, port); + php_fsockopen_format_host_port(&hashkey, "pfsockopen__", strlen("pfsockopen__"), host, + host_len, port); } if (port > 0) { - hostname_len = spprintf(&hostname, 0, "%s:" ZEND_LONG_FMT, host, port); + hostname_len = php_fsockopen_format_host_port(&hostname, "", 0, host, host_len, port); } else { hostname_len = host_len; hostname = host; diff --git a/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 0000000000000..7556c3be94ccd --- /dev/null +++ b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,21 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in fsockopen() +--FILE-- + +--EXPECTF-- + +Warning: fsockopen(): Unable to connect to localhost:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 0000000000000..52f9263c99aaa --- /dev/null +++ b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,26 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in stream_socket_client() +--FILE-- + +--EXPECTF-- + +Warning: stream_socket_client(): Unable to connect to tcp://localhost\0.example.com:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index b17eccf2eeb18..38f11d149dea2 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -581,12 +581,15 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po char *colon; char *host = NULL; -#ifdef HAVE_IPV6 - char *p; + if (memchr(str, '\0', str_len)) { + *err = ZSTR_INIT_LITERAL("The hostname must not contain null bytes", 0); + return NULL; + } +#ifdef HAVE_IPV6 if (*(str) == '[' && str_len > 1) { /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ - p = memchr(str + 1, ']', str_len - 2); + char *p = memchr(str + 1, ']', str_len - 2); if (!p || *(p + 1) != ':') { if (get_err) { *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str); From 9376aeef9f8ff81f2705b8016237ec3e30bdee44 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 4 Mar 2025 17:23:01 +0100 Subject: [PATCH 13/19] Fix GHSA-hrwm-9436-5mv3: pgsql escaping no error checks This adds error checks for escape function is pgsql and pdo_pgsql extensions. It prevents possibility of storing not properly escaped data which could potentially lead to some security issues. --- ext/pdo_pgsql/pgsql_driver.c | 10 +- ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 24 ++++ ext/pgsql/pgsql.c | 126 ++++++++++++++++--- ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 64 ++++++++++ 4 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt create mode 100644 ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index f84bfba9f8453..2ea29490856b5 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -354,11 +354,15 @@ static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo zend_string *quoted_str; pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; size_t tmp_len; + int err; switch (paramtype) { case PDO_PARAM_LOB: /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ escaped = PQescapeByteaConn(H->server, (unsigned char *)ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len); + if (escaped == NULL) { + return NULL; + } quotedlen = tmp_len + 1; quoted = emalloc(quotedlen + 1); memcpy(quoted+1, escaped, quotedlen-2); @@ -370,7 +374,11 @@ static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo default: quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); quoted[0] = '\''; - quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL); + quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &err); + if (err) { + efree(quoted); + return NULL; + } quoted[quotedlen + 1] = '\''; quoted[quotedlen + 2] = '\0'; quotedlen += 2; diff --git a/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt new file mode 100644 index 0000000000000..8566a26753b40 --- /dev/null +++ b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt @@ -0,0 +1,24 @@ +--TEST-- +#GHSA-hrwm-9436-5mv3: pdo_pgsql extension does not check for errors during escaping +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$invalid = "ABC\xff\x30';"; +var_dump($db->quote($invalid)); + +?> +--EXPECT-- +bool(false) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 9f04f2c843b9c..01fb9dde3bae6 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3297,8 +3297,14 @@ PHP_FUNCTION(pg_escape_string) to = zend_string_safe_alloc(ZSTR_LEN(from), 2, 0, 0); if (link) { + int err; pgsql = link->conn; - ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL); + ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), &err); + if (err) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escaping string failed"); + zend_string_efree(to); + RETURN_THROWS(); + } } else { ZSTR_LEN(to) = PQescapeString(ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from)); @@ -3341,6 +3347,10 @@ PHP_FUNCTION(pg_escape_bytea) } else { to = (char *)PQescapeBytea((unsigned char *)ZSTR_VAL(from), ZSTR_LEN(from), &to_len); } + if (to == NULL) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escape failure"); + RETURN_THROWS(); + } RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */ PQfreemem(to); @@ -4257,7 +4267,7 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string char *escaped; smart_str querystr = {0}; size_t new_len; - int i, num_rows; + int i, num_rows, err; zval elem; ZEND_ASSERT(ZSTR_LEN(table_name) != 0); @@ -4296,7 +4306,14 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string "WHERE a.attnum > 0 AND c.relname = '"); } escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table name '%s' failed", ZSTR_VAL(table_name)); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -4304,7 +4321,14 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string smart_str_appends(&querystr, "' AND n.nspname = '"); escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table namespace '%s' failed", ZSTR_VAL(table_name)); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -4565,7 +4589,7 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * { zend_string *field = NULL; zval meta, *def, *type, *not_null, *has_default, *is_enum, *val, new_val; - int err = 0, skip_field; + int err = 0, escape_err = 0, skip_field; php_pgsql_data_type data_type; ZEND_ASSERT(pg_link != NULL); @@ -4818,8 +4842,13 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * /* PostgreSQL ignores \0 */ str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0); /* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */ - ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); - ZVAL_STR(&new_val, php_pgsql_add_quotes(str)); + ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), + Z_STRVAL_P(val), Z_STRLEN_P(val), &escape_err); + if (escape_err) { + err = 1; + } else { + ZVAL_STR(&new_val, php_pgsql_add_quotes(str)); + } zend_string_release_ex(str, false); } break; @@ -4842,7 +4871,15 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * } PGSQL_CONV_CHECK_IGNORE(); if (err) { - php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); + if (escape_err) { + php_error_docref(NULL, E_NOTICE, + "String value escaping failed for PostgreSQL '%s' (%s)", + Z_STRVAL_P(type), ZSTR_VAL(field)); + } else { + php_error_docref(NULL, E_NOTICE, + "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", + Z_STRVAL_P(type), ZSTR_VAL(field)); + } } break; @@ -5113,6 +5150,11 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * zend_string *tmp_zstr; tmp = PQescapeByteaConn(pg_link, (unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val), &to_len); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Escaping value failed for %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); + err = 1; + break; + } tmp_zstr = zend_string_init((char *)tmp, to_len - 1, false); /* PQescapeBytea's to_len includes additional '\0' */ PQfreemem(tmp); @@ -5191,6 +5233,12 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * zend_hash_update(Z_ARRVAL_P(result), field, &new_val); } else { char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field)); + if (escaped == NULL) { + /* This cannot fail because of invalid string but only due to failed memory allocation */ + php_error_docref(NULL, E_NOTICE, "Escaping field '%s' failed", ZSTR_VAL(field)); + err = 1; + break; + } add_assoc_zval(result, escaped, &new_val); PQfreemem(escaped); } @@ -5269,7 +5317,7 @@ static bool do_exec(smart_str *querystr, ExecStatusType expect, PGconn *pg_link, } /* }}} */ -static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const zend_string *table) /* {{{ */ +static inline zend_result build_tablename(smart_str *querystr, PGconn *pg_link, const zend_string *table) /* {{{ */ { /* schema.table should be "schema"."table" */ const char *dot = memchr(ZSTR_VAL(table), '.', ZSTR_LEN(table)); @@ -5279,6 +5327,10 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const z smart_str_appendl(querystr, ZSTR_VAL(table), len); } else { char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(table), len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", ZSTR_VAL(table)); + return FAILURE; + } smart_str_appends(querystr, escaped); PQfreemem(escaped); } @@ -5291,11 +5343,17 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const z smart_str_appendl(querystr, after_dot, len); } else { char *escaped = PQescapeIdentifier(pg_link, after_dot, len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", ZSTR_VAL(table)); + return FAILURE; + } smart_str_appendc(querystr, '.'); smart_str_appends(querystr, escaped); PQfreemem(escaped); } } + + return SUCCESS; } /* }}} */ @@ -5316,7 +5374,9 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t ZVAL_UNDEF(&converted); if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) { smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " DEFAULT VALUES"); goto no_values; @@ -5332,7 +5392,9 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " ("); ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) { @@ -5342,6 +5404,10 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t } if (opt & PGSQL_DML_ESCAPE) { tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + goto cleanup; + } smart_str_appends(&querystr, tmp); PQfreemem(tmp); } else { @@ -5353,15 +5419,19 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t smart_str_appends(&querystr, ") VALUES ("); /* make values string */ - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(var_array), fld, val) { /* we can avoid the key_type check here, because we tested it in the other loop */ switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { - size_t new_len; - char *tmp; - tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + int error; + char *tmp = safe_emalloc(Z_STRLEN_P(val), 2, 1); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + goto cleanup; + } smart_str_appendc(&querystr, '\''); smart_str_appendl(&querystr, tmp, new_len); smart_str_appendc(&querystr, '\''); @@ -5517,6 +5587,10 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, } if (opt & PGSQL_DML_ESCAPE) { char *tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + return -1; + } smart_str_appends(querystr, tmp); PQfreemem(tmp); } else { @@ -5532,8 +5606,14 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { + int error; char *tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + return -1; + } smart_str_appendc(querystr, '\''); smart_str_appendl(querystr, tmp, new_len); smart_str_appendc(querystr, '\''); @@ -5601,7 +5681,9 @@ PHP_PGSQL_API zend_result php_pgsql_update(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "UPDATE "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " SET "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt)) @@ -5704,7 +5786,9 @@ PHP_PGSQL_API zend_result php_pgsql_delete(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "DELETE FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " WHERE "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) @@ -5844,7 +5928,9 @@ PHP_PGSQL_API zend_result php_pgsql_select(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "SELECT * FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " WHERE "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) diff --git a/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt new file mode 100644 index 0000000000000..c1c5e05dce623 --- /dev/null +++ b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt @@ -0,0 +1,64 @@ +--TEST-- +#GHSA-hrwm-9436-5mv3: pgsql extension does not check for errors during escaping +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- + 'test'])); // table name str escape in php_pgsql_meta_data +var_dump(pg_insert($db, "$invalid.tbl", ['bar' => 'test'])); // schema name str escape in php_pgsql_meta_data +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid])); // converted value str escape in php_pgsql_convert +var_dump(pg_insert($db, $invalid, [])); // ident escape in build_tablename +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', [$invalid => 'foo'], $flags)); // ident escape for field php_pgsql_insert +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid], $flags)); // str escape for field value in php_pgsql_insert +var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], [$invalid => 'test'], $flags)); // ident escape in build_assignment_string +var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], ['bar' => $invalid], $flags)); // invalid str escape in build_assignment_string +var_dump(pg_escape_literal($db, $invalid)); // pg_escape_literal escape +var_dump(pg_escape_identifier($db, $invalid)); // pg_escape_identifier escape + +?> +--EXPECTF-- + +Warning: pg_insert(): Escaping table name 'ABC%s';' failed in %s on line %d +bool(false) + +Warning: pg_insert(): Escaping table namespace 'ABC%s';.tbl' failed in %s on line %d +bool(false) + +Notice: pg_insert(): String value escaping failed for PostgreSQL 'text' (bar) in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape table name 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape field 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape field 'bar' value in %s on line %d +bool(false) + +Notice: pg_update(): Failed to escape field 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_update(): Failed to escape field 'bar' value in %s on line %d +bool(false) + +Warning: pg_escape_literal(): Failed to escape in %s on line %d +bool(false) + +Warning: pg_escape_identifier(): Failed to escape in %s on line %d +bool(false) From 6233dc6210b159762a97b7759ea0883d027feac1 Mon Sep 17 00:00:00 2001 From: Shivam Mathur Date: Wed, 25 Jun 2025 01:57:07 +0530 Subject: [PATCH 14/19] Switch to windows-2022 in CI (#18927) * Switch to windows-2022 in CI windows-2019 runner will be dropped by GitHub on 2025-06-30. * xfail test cases that fail on windows-2022 --- .github/scripts/windows/build.bat | 4 +- .github/scripts/windows/find-vs-toolset.bat | 49 +++++++++++++++++++++ .github/scripts/windows/test.bat | 3 +- .github/workflows/push.yml | 2 +- .github/workflows/root.yml | 2 +- Zend/tests/bug70258.phpt | 3 ++ Zend/tests/gh11189.phpt | 3 ++ Zend/tests/gh11189_1.phpt | 3 ++ 8 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 .github/scripts/windows/find-vs-toolset.bat diff --git a/.github/scripts/windows/build.bat b/.github/scripts/windows/build.bat index ebe08c86b5ea9..139cce8be816e 100644 --- a/.github/scripts/windows/build.bat +++ b/.github/scripts/windows/build.bat @@ -43,7 +43,9 @@ if not exist "%SDK_RUNNER%" ( exit /b 3 ) -cmd /c %SDK_RUNNER% -t .github\scripts\windows\build_task.bat +for /f "delims=" %%T in ('call .github\scripts\windows\find-vs-toolset.bat %PHP_BUILD_CRT%') do set "VS_TOOLSET=%%T" +echo Got VS Toolset %VS_TOOLSET% +cmd /c %SDK_RUNNER% -s %VS_TOOLSET% -t .github\scripts\windows\build_task.bat if %errorlevel% neq 0 exit /b 3 exit /b 0 diff --git a/.github/scripts/windows/find-vs-toolset.bat b/.github/scripts/windows/find-vs-toolset.bat new file mode 100644 index 0000000000000..2d9e68e730318 --- /dev/null +++ b/.github/scripts/windows/find-vs-toolset.bat @@ -0,0 +1,49 @@ +@echo off + +setlocal enabledelayedexpansion + +if "%~1"=="" ( + echo ERROR: Usage: %~nx0 [vc14^|vc15^|vs16^|vs17] + exit /b 1 +) + +set "toolsets_vc14=14.0" +set "toolsets_vc15=" +set "toolsets_vs16=" +set "toolsets_vs17=" + + +for /f "usebackq tokens=*" %%I in (`vswhere.exe -latest -find "VC\Tools\MSVC"`) do set "MSVCDIR=%%I" + +if not defined MSVCDIR ( + echo ERROR: could not locate VC\Tools\MSVC + exit /b 1 +) + +for /f "delims=" %%D in ('dir /b /ad "%MSVCDIR%"') do ( + for /f "tokens=1,2 delims=." %%A in ("%%D") do ( + set "maj=%%A" & set "min=%%B" + if "!maj!"=="14" ( + if !min! LEQ 9 ( + set "toolsets_vc14=%%D" + ) else if !min! LEQ 19 ( + set "toolsets_vc15=%%D" + ) else if !min! LEQ 29 ( + set "toolsets_vs16=%%D" + ) else ( + set "toolsets_vs17=%%D" + ) + ) + ) +) + +set "KEY=%~1" +set "VAR=toolsets_%KEY%" +call set "RESULT=%%%VAR%%%" +if defined RESULT ( + echo %RESULT% + exit /b 0 +) else ( + echo ERROR: no toolset found for %KEY% + exit /b 1 +) diff --git a/.github/scripts/windows/test.bat b/.github/scripts/windows/test.bat index 510e9bc78f4ed..7ef60534cc780 100644 --- a/.github/scripts/windows/test.bat +++ b/.github/scripts/windows/test.bat @@ -11,7 +11,8 @@ if not exist "%SDK_RUNNER%" ( exit /b 3 ) -cmd /c %SDK_RUNNER% -t .github\scripts\windows\test_task.bat +for /f "delims=" %%T in ('call .github\scripts\windows\find-vs-toolset.bat %PHP_BUILD_CRT%') do set "VS_TOOLSET=%%T" +cmd /c %SDK_RUNNER% -s %VS_TOOLSET% -t .github\scripts\windows\test_task.bat if %errorlevel% neq 0 exit /b 3 exit /b 0 diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 214798af62b5c..5353ef7d0ea44 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -147,7 +147,7 @@ jobs: WINDOWS: if: github.repository == 'php/php-src' || github.event_name == 'pull_request' name: WINDOWS_X64_ZTS - runs-on: windows-2019 + runs-on: windows-2022 env: PHP_BUILD_CACHE_BASE_DIR: C:\build-cache PHP_BUILD_OBJ_DIR: C:\obj diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index a98bb39ba0d92..78e0d47aa1d15 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -58,7 +58,7 @@ jobs: ubuntu_version: ${{ (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 5) || matrix.branch.version[0] >= 9) && '24.04') || '22.04' }} - windows_version: ${{ ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9) && '2022' || '2019' }} + windows_version: '2022' skip_laravel: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_symfony: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_wordpress: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} diff --git a/Zend/tests/bug70258.phpt b/Zend/tests/bug70258.phpt index 40915a286ef9e..d346dbdf3a35b 100644 --- a/Zend/tests/bug70258.phpt +++ b/Zend/tests/bug70258.phpt @@ -4,6 +4,9 @@ Bug #70258 (Segfault if do_resize fails to allocated memory) memory_limit=2M --SKIPIF-- --INI-- diff --git a/Zend/tests/gh11189_1.phpt b/Zend/tests/gh11189_1.phpt index 53727908e5e2a..17b9967bc3182 100644 --- a/Zend/tests/gh11189_1.phpt +++ b/Zend/tests/gh11189_1.phpt @@ -2,6 +2,9 @@ GH-11189: Exceeding memory limit in zend_hash_do_resize leaves the array in an invalid state (not packed array) --SKIPIF-- --INI-- From 9cb3d8d200f0c822b17bda35a2a67a97b039d3e1 Mon Sep 17 00:00:00 2001 From: Ahmed Lekssays Date: Tue, 3 Jun 2025 09:00:55 +0000 Subject: [PATCH 15/19] Fix GHSA-453j-q27h-5p8x Libxml versions prior to 2.13 cannot correctly handle a call to xmlNodeSetName() with a name longer than 2G. It will leave the node object in an invalid state with a NULL name. This later causes a NULL pointer dereference when using the name during message serialization. To solve this, implement a workaround that resets the name to the sentinel name if this situation arises. Versions of libxml of 2.13 and higher are not affected. This can be exploited if a SoapVar is created with a fully qualified name that is longer than 2G. This would be possible if some application code uses a namespace prefix from an untrusted source like from a remote SOAP service. Co-authored-by: Niels Dossche <7771979+nielsdos@users.noreply.github.com> --- ext/soap/soap.c | 6 ++-- ext/soap/tests/soap_qname_crash.phpt | 48 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 ext/soap/tests/soap_qname_crash.phpt diff --git a/ext/soap/soap.c b/ext/soap/soap.c index fbf6546beb824..3bc713ca76bd8 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -4019,8 +4019,10 @@ static xmlNodePtr serialize_zval(zval *val, sdlParamPtr param, char *paramName, } xmlParam = master_to_xml(enc, val, style, parent); zval_ptr_dtor(&defval); - if (!strcmp((char*)xmlParam->name, "BOGUS")) { - xmlNodeSetName(xmlParam, BAD_CAST(paramName)); + if (xmlParam != NULL) { + if (xmlParam->name == NULL || strcmp((char*)xmlParam->name, "BOGUS") == 0) { + xmlNodeSetName(xmlParam, BAD_CAST(paramName)); + } } return xmlParam; } diff --git a/ext/soap/tests/soap_qname_crash.phpt b/ext/soap/tests/soap_qname_crash.phpt new file mode 100644 index 0000000000000..bcf01d574fab4 --- /dev/null +++ b/ext/soap/tests/soap_qname_crash.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test SoapClient with excessively large QName prefix in SoapVar +--EXTENSIONS-- +soap +--SKIPIF-- + +--INI-- +memory_limit=6144M +--FILE-- + 'http://127.0.0.1/', + 'uri' => 'urn:dummy', + 'trace' => 1, + 'exceptions' => true, +]; +$client = new TestSoapClient(null, $options); +$client->__soapCall("DummyFunction", [$var]); +?> +--EXPECT-- +Attempting to create SoapVar with very large QName +Attempting encoding + +value From 7b33b1c916a3da1c1d00c1c65fa0cf12a80d2a34 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 26 Jun 2025 11:24:54 +0200 Subject: [PATCH 16/19] Update NEWS with entries for security fixes --- NEWS | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 29400e6ef5b5c..8c8b28fb98186 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,18 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.1.33 +03 Jul 2025, PHP 8.1.33 +- PGSQL: + . Fixed GHSA-hrwm-9436-5mv3 (pgsql extension does not check for errors during + escaping). (CVE-2025-1735) (Jakub Zelenka) + +- SOAP: + . Fixed GHSA-453j-q27h-5p8x (NULL Pointer Dereference in PHP SOAP Extension + via Large XML Namespace Prefix). (CVE-2025-6491) (Lekssays, nielsdos) +- Standard: + . Fixed GHSA-3cr5-j632-f35r (Null byte termination in hostnames). + (CVE-2025-1220) (Jakub Zelenka) 13 Mar 2025, PHP 8.1.32 From 85522c0d4803688c957c5bcccfc4f38696392bb9 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 27 Jun 2025 09:14:54 +0200 Subject: [PATCH 17/19] Add FreeBSD ZTS nightly build Closes GH-18959 --- .github/actions/freebsd/action.yml | 8 +++++++- .github/workflows/nightly.yml | 14 +++++++++++++- .github/workflows/root.yml | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/actions/freebsd/action.yml b/.github/actions/freebsd/action.yml index 1f7c670f27227..fd37e92bbe5f9 100644 --- a/.github/actions/freebsd/action.yml +++ b/.github/actions/freebsd/action.yml @@ -1,4 +1,8 @@ name: FreeBSD +inputs: + configurationParameters: + default: '' + required: false runs: using: composite steps: @@ -79,7 +83,9 @@ runs: --with-mhash \ --with-sodium \ --with-config-file-path=/etc \ - --with-config-file-scan-dir=/etc/php.d + --with-config-file-scan-dir=/etc/php.d \ + ${{ inputs.configurationParameters }} + gmake -j2 mkdir /etc/php.d gmake install > /dev/null diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1b1532af7f799..1167b9c5d2934 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -23,6 +23,9 @@ on: run_macos_arm64: required: true type: boolean + run_freebsd_zts: + required: true + type: boolean ubuntu_version: required: true type: string @@ -1052,7 +1055,13 @@ jobs: - name: Test run: .github/scripts/windows/test.bat FREEBSD: - name: FREEBSD + strategy: + fail-fast: false + matrix: + zts: [true, false] + exclude: + - zts: ${{ !inputs.run_freebsd_zts && true || '*never*' }} + name: "FREEBSD_${{ matrix.zts && 'ZTS' || 'NTS' }}" runs-on: ubuntu-latest steps: - name: git checkout @@ -1061,3 +1070,6 @@ jobs: ref: ${{ inputs.branch }} - name: FreeBSD uses: ./.github/actions/freebsd + with: + configurationParameters: >- + --${{ matrix.zts && 'enable' || 'disable' }}-zts diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index 78e0d47aa1d15..30abc1da852bb 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -55,6 +55,7 @@ jobs: run_alpine: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9 }} run_linux_ppc64: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9 }} run_macos_arm64: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9 }} + run_freebsd_zts: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 3) || matrix.branch.version[0] >= 9 }} ubuntu_version: ${{ (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 5) || matrix.branch.version[0] >= 9) && '24.04') || '22.04' }} From 8ddc210bf783d6dbe1ec756996ad9a0fb2c77469 Mon Sep 17 00:00:00 2001 From: Shivam Mathur Date: Mon, 30 Jun 2025 20:00:25 +0530 Subject: [PATCH 18/19] Fix PHP_BUILD_CRT input in the nightly workflow (#18982) --- .github/workflows/nightly.yml | 5 ++++- .github/workflows/root.yml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1167b9c5d2934..5817c647a871a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -32,6 +32,9 @@ on: windows_version: required: true type: string + vs_crt_version: + required: true + type: string skip_laravel: required: true type: boolean @@ -1034,7 +1037,7 @@ jobs: PHP_BUILD_OBJ_DIR: C:\obj PHP_BUILD_CACHE_SDK_DIR: C:\build-cache\sdk PHP_BUILD_SDK_BRANCH: php-sdk-2.3.0 - PHP_BUILD_CRT: ${{ inputs.windows_version == '2022' && 'vs17' || 'vs16' }} + PHP_BUILD_CRT: ${{ inputs.vs_crt_version }} PLATFORM: ${{ matrix.x64 && 'x64' || 'x86' }} THREAD_SAFE: "${{ matrix.zts && '1' || '0' }}" INTRINSICS: "${{ matrix.zts && 'AVX2' || '' }}" diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index 30abc1da852bb..96943a8cfb2aa 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -60,6 +60,7 @@ jobs: (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 5) || matrix.branch.version[0] >= 9) && '24.04') || '22.04' }} windows_version: '2022' + vs_crt_version: ${{ ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) && 'vs17') || 'vs16' }} skip_laravel: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_symfony: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_wordpress: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} From 1996831969293a866863f7148f5416e99ea123cb Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Tue, 1 Jul 2025 15:31:30 -0500 Subject: [PATCH 19/19] Update versions for PHP 8.1.33 --- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/zend.h b/Zend/zend.h index 6562119e8363c..c4e7693c29bb5 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.1.33-dev" +#define ZEND_VERSION "4.1.33" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 0f45db92ba7de..a4b2a1e6411b2 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.1.33-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.1.33],[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 2aee6f19d16a4..1f4298c68c741 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -3,6 +3,6 @@ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 1 #define PHP_RELEASE_VERSION 33 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.1.33-dev" +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.1.33" #define PHP_VERSION_ID 80133