From 9ae23bae5f7d20412bac0369c2e72ecc48a9103d Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Thu, 25 Jun 2026 17:07:23 +0800 Subject: [PATCH 1/5] Zend: Use `zend_string_equals*` APIs for string comparisons (#21707) Use the zend_string APIs zend_string_equals* which are clearer, and can also do some pointer comparisons if the strings are interned for any reason. --- ext/openssl/openssl_pwhash.c | 4 ++-- ext/soap/php_encoding.c | 3 ++- ext/soap/php_http.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ext/openssl/openssl_pwhash.c b/ext/openssl/openssl_pwhash.c index 0b439a731d1c..01f7f0124135 100644 --- a/ext/openssl/openssl_pwhash.c +++ b/ext/openssl/openssl_pwhash.c @@ -329,7 +329,7 @@ PHP_FUNCTION(openssl_password_hash) Z_PARAM_ARRAY_HT(options) ZEND_PARSE_PARAMETERS_END(); - if (strcmp(ZSTR_VAL(algo), "argon2i") && strcmp(ZSTR_VAL(algo), "argon2id")) { + if (!zend_string_equals_literal(algo, "argon2i") && !zend_string_equals_literal(algo, "argon2id")) { zend_argument_value_error(1, "must be a valid password openssl hashing algorithm"); RETURN_THROWS(); } @@ -355,7 +355,7 @@ PHP_FUNCTION(openssl_password_verify) Z_PARAM_STR(digest) ZEND_PARSE_PARAMETERS_END(); - if (strcmp(ZSTR_VAL(algo), "argon2i") && strcmp(ZSTR_VAL(algo), "argon2id")) { + if (!zend_string_equals_literal(algo, "argon2i") && !zend_string_equals_literal(algo, "argon2id")) { zend_argument_value_error(1, "must be a valid password openssl hashing algorithm"); RETURN_THROWS(); } diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 151fa811b0fa..f02a16638025 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -253,11 +253,12 @@ static encodePtr find_encoder_by_type_name(sdlPtr sdl, const char *type) { if (sdl && sdl->encoders) { encodePtr enc; + size_t type_len = strlen(type); ZEND_HASH_FOREACH_PTR(sdl->encoders, enc) { if (type[0] == '{') { if (enc->details.clark_notation - && strcmp(ZSTR_VAL(enc->details.clark_notation), type) == 0) { + && zend_string_equals_cstr(enc->details.clark_notation, type, type_len)) { return enc; } } else { diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index 1f0aa6723c76..2594be75fe15 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -325,7 +325,7 @@ static bool in_domain(const zend_string *host, const zend_string *domain) { if (ZSTR_VAL(domain)[0] == '.') { if (ZSTR_LEN(host) > ZSTR_LEN(domain)) { - return strcmp(ZSTR_VAL(host)+ZSTR_LEN(host)-ZSTR_LEN(domain), ZSTR_VAL(domain)) == 0; + return zend_string_equals_cstr(domain, ZSTR_VAL(host) + ZSTR_LEN(host) - ZSTR_LEN(domain), ZSTR_LEN(domain)); } else { return false; } From 21d4da28ac711a5cb6e957417e8b913e48653642 Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 11 Jun 2026 01:36:04 +0000 Subject: [PATCH 2/5] TSRM: use local-exec TLS in PIE executables Closes GH-22278 --- NEWS | 1 + TSRM/TSRM.h | 2 +- UPGRADING | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 71f67c39665f..ce6e46a4c36b 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,7 @@ PHP NEWS surrounding property access). (timwolla) . Fixed GH-22422 (zend_arena layout mismatch leaked memory in separately built extensions under AddressSanitizer). (iliaal) + . TSRM: use local-exec TLS in PIE executables. (henderkes) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h index ea13552c8374..639b1134ddde 100644 --- a/TSRM/TSRM.h +++ b/TSRM/TSRM.h @@ -155,7 +155,7 @@ TSRM_API bool tsrm_is_managed_thread(void); #if !__has_attribute(tls_model) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MUSL__) || defined(__HAIKU__) # define TSRM_TLS_MODEL_ATTR # define TSRM_TLS_MODEL_DEFAULT -#elif __PIC__ +#elif defined(__PIC__) && !defined(__PIE__) # define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("initial-exec"))) # define TSRM_TLS_MODEL_INITIAL_EXEC #else diff --git a/UPGRADING b/UPGRADING index 1ae5bbe1f40a..ae0e3afc97ad 100644 --- a/UPGRADING +++ b/UPGRADING @@ -481,6 +481,7 @@ PHP 8.6 UPGRADE NOTES . The performance of the TAILCALL VM has been improved. . The TAILCALL VM is now enabled on Windows when compiling with Clang >= 19 x86_64. + . The performance of ZTS+PIE builds has been improved. - DOM: . Made splitText() faster and consume less memory. From 98b86328b6e94af3bd204e1bdc708888296416b9 Mon Sep 17 00:00:00 2001 From: Jorg Sowa Date: Fri, 19 Jun 2026 00:12:04 +0200 Subject: [PATCH 3/5] ci: add 180-minute timeout to test-suite jobs The GitHub-hosted test-suite jobs had no timeout-minutes and fell back to GitHub's 6-hour default, so a hung step (e.g. apt) wasted ~6h of runner time. Cap them at 180 min, which clears the slowest legitimate nightly runs (LINUX_X64_ASAN ~158 min) with margin. The self-hosted jobs keep their existing 50-minute caps. Closes GH-22362 --- .github/workflows/test-suite.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 7b1850aa1075..de1fbee018a4 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -16,6 +16,7 @@ jobs: name: LINUX_PPC64_ASAN_DEBUG_ZTS # This runs on a self-hosted runner; see https://wiki.php.net/systems/ci runs-on: [self-hosted, gentoo, ppc64] + timeout-minutes: 180 steps: - name: git checkout uses: actions/checkout@v6 @@ -55,6 +56,7 @@ jobs: if: ${{ fromJson(inputs.branch).jobs.ALPINE }} name: ALPINE_X64_ASAN_DEBUG_ZTS runs-on: ubuntu-24.04 + timeout-minutes: 180 container: image: 'alpine:3.22' steps: @@ -130,6 +132,7 @@ jobs: matrix: ${{ fromJson(inputs.branch).jobs.LINUX_X64.matrix }} name: "LINUX_X64${{ matrix.name }}_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}" runs-on: ubuntu-${{ fromJson(inputs.branch).config.ubuntu_version }} + timeout-minutes: 180 steps: - name: git checkout uses: actions/checkout@v6 @@ -228,6 +231,7 @@ jobs: matrix: ${{ fromJson(inputs.branch).jobs.LINUX_X32.matrix }} name: "LINUX_X32_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}" runs-on: ubuntu-latest + timeout-minutes: 180 container: image: ubuntu:${{ fromJson(inputs.branch).config.ubuntu_version }} env: @@ -309,6 +313,7 @@ jobs: matrix: ${{ fromJson(inputs.branch).jobs.MACOS.matrix }} name: "MACOS_${{ matrix.arch }}_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}" runs-on: macos-${{ matrix.arch == 'X64' && '15-intel' || fromJson(inputs.branch).jobs.MACOS.config.arm64_version }} + timeout-minutes: 180 steps: - name: git checkout uses: actions/checkout@v6 @@ -386,6 +391,7 @@ jobs: FIREBIRD_USER: test FIREBIRD_PASSWORD: test runs-on: ubuntu-24.04 + timeout-minutes: 180 steps: - name: git checkout uses: actions/checkout@v6 @@ -642,6 +648,7 @@ jobs: FIREBIRD_PASSWORD: test name: OPCACHE_VARIATION runs-on: ubuntu-${{ fromJson(inputs.branch).config.ubuntu_version }} + timeout-minutes: 180 steps: - name: git checkout uses: actions/checkout@v6 @@ -705,6 +712,7 @@ jobs: if: ${{ fromJson(inputs.branch).jobs.MSAN }} name: MSAN runs-on: ubuntu-${{ fromJson(inputs.branch).config.ubuntu_version }} + timeout-minutes: 180 steps: - name: git checkout uses: actions/checkout@v6 @@ -796,6 +804,7 @@ jobs: if: ${{ fromJson(inputs.branch).jobs.LIBMYSQLCLIENT }} name: LIBMYSQLCLIENT runs-on: ubuntu-${{ fromJson(inputs.branch).config.ubuntu_version }} + timeout-minutes: 180 steps: - name: git checkout uses: actions/checkout@v6 @@ -834,6 +843,7 @@ jobs: matrix: ${{ fromJson(inputs.branch).jobs.WINDOWS.matrix }} name: "WINDOWS_${{ matrix.x64 && 'X64' || 'X86' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}${{ matrix.asan && '_ASAN' || ''}}${{ matrix.clang && '_CLANG' || ''}}" runs-on: ${{ fromJson(inputs.branch).jobs.WINDOWS.config.runs_on }} + timeout-minutes: 180 env: PHP_BUILD_CACHE_BASE_DIR: C:\build-cache PHP_BUILD_OBJ_DIR: C:\obj From 15d58caa79753525f70e100ce25a468a7c7b8fca Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Thu, 25 Jun 2026 09:18:44 -0400 Subject: [PATCH 4/5] Fix unsigned wrap in bzdecompress() output realloc at source_len UINT_MAX (#22432) The input guard rejects only source_len > UINT_MAX, so source_len == UINT_MAX is permitted and assigned to bzs.avail_out (unsigned int). The per-iteration realloc then computed bzs.avail_out+1 in unsigned int arithmetic, which wraps to 0 at UINT_MAX, allocating no headroom while bz2 still believes avail_out bytes are available at next_out: the next decompress round writes past the buffer. Compute the term as (size_t)bzs.avail_out + 1 so the increment is done in size_t and cannot wrap, matching the (size_t) casts already used on the same call. --- ext/bz2/bz2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/bz2/bz2.c b/ext/bz2/bz2.c index 512632fe8a22..6af8286c5335 100644 --- a/ext/bz2/bz2.c +++ b/ext/bz2/bz2.c @@ -544,7 +544,7 @@ PHP_FUNCTION(bzdecompress) /* no reason to continue if we're going to drop it anyway */ break; } - dest = zend_string_safe_realloc(dest, 1, bzs.avail_out+1, (size_t) size, 0); + dest = zend_string_safe_realloc(dest, 1, (size_t) bzs.avail_out + 1, (size_t) size, 0); bzs.next_out = ZSTR_VAL(dest) + size; } From 1182b14b7f1be71b4c62f1d47410db5c142785ff Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 25 Jun 2026 12:54:31 +0200 Subject: [PATCH 5/5] [skip ci] Fix timeout in Symfony community build FileInputHelperTest::testReadWithPasteDetectionAbortsBeyondMaxBytes() causes massive amounts of system calls with USE_ZEND_ALLOC=0, leading to timeouts. Skip this test in our build. Closes GH-22450 --- .github/workflows/test-suite.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index de1fbee018a4..4c28ccca3561 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -573,6 +573,10 @@ jobs: php -r '$c = file_get_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php"); $c = str_replace("public function testSanitizeDeepNestedString()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n public function testSanitizeDeepNestedString()", $c); file_put_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php", $c);' # Buggy FFI test in Symfony, see https://github.com/symfony/symfony/issues/47668 php -r '$c = file_get_contents("src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php"); $c = str_replace("public function testCastNonTrailingCharPointer()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n public function testCastNonTrailingCharPointer()", $c); file_put_contents("src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php", $c);' + # Causes massive amounts of system calls with USE_ZEND_ALLOC=0, exceeding the timeout + if [ -e 'src/Symfony/Component/Console/Tests/Helper/FileInputHelperTest.php' ]; then + php -r '$c = file_get_contents("src/Symfony/Component/Console/Tests/Helper/FileInputHelperTest.php"); $c = str_replace("public function testReadWithPasteDetectionAbortsBeyondMaxBytes()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n public function testReadWithPasteDetectionAbortsBeyondMaxBytes()", $c); file_put_contents("src/Symfony/Component/Console/Tests/Helper/FileInputHelperTest.php", $c);' + fi export SYMFONY_DEPRECATIONS_HELPER=max[total]=999 X=0 for component in $(find src/Symfony -mindepth 2 -type f -name phpunit.xml.dist -printf '%h\n'); do