From 2f2fb1fbf1117f642cd1efdf33e431d42ee910ee Mon Sep 17 00:00:00 2001 From: Shivam Mathur Date: Mon, 13 Oct 2025 20:48:24 +0000 Subject: [PATCH 1/7] Skip lc_ctype_inheritance.phpt on macos 15+ --- Zend/tests/lc_ctype_inheritance.phpt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Zend/tests/lc_ctype_inheritance.phpt b/Zend/tests/lc_ctype_inheritance.phpt index 8c968f0615e6..69de59f69ac8 100644 --- a/Zend/tests/lc_ctype_inheritance.phpt +++ b/Zend/tests/lc_ctype_inheritance.phpt @@ -2,6 +2,9 @@ Do not inherit LC_CTYPE from environment --SKIPIF-- =')) { + die('skip macOS 15 inherits LC_CTYPE into the thread locale'); +} if (!setlocale(LC_CTYPE, "de_DE", "de-DE")) die("skip requires de_DE locale"); ?> --ENV-- From 9a2113a10443c214ae714b94fdeaa2e5c26056aa Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 9 Jan 2024 20:05:51 +0300 Subject: [PATCH 2/7] Disable inlining and inter-procedure-analyses for zend_string_equal_val() function that may be overriden for valgrind (#13099) This is a more safely way to fix GH-9068 --- Zend/zend_string.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/Zend/zend_string.c b/Zend/zend_string.c index c21b4f5d4610..afc7325713c9 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -366,6 +366,7 @@ ZEND_API void zend_interned_strings_switch_storage(bool request) } } +#if defined(__GNUC__) && (defined(__i386__) || (defined(__x86_64__) && !defined(__ILP32__))) /* Even if we don't build with valgrind support, include the symbol so that valgrind available * only at runtime will not result in false positives. */ #ifndef I_REPLACE_SONAME_FNNAME_ZU @@ -373,29 +374,20 @@ ZEND_API void zend_interned_strings_switch_storage(bool request) #endif /* See GH-9068 */ -#if defined(__GNUC__) && (__GNUC__ >= 11 || defined(__clang__)) && __has_attribute(no_caller_saved_registers) -# define NO_CALLER_SAVED_REGISTERS __attribute__((no_caller_saved_registers)) -# ifndef __clang__ -# pragma GCC push_options -# pragma GCC target ("general-regs-only") -# define POP_OPTIONS -# endif +#if __has_attribute(noipa) +# define NOIPA __attribute__((noipa)) #else -# define NO_CALLER_SAVED_REGISTERS +# define NOIPA #endif -ZEND_API bool ZEND_FASTCALL NO_CALLER_SAVED_REGISTERS I_REPLACE_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2) +ZEND_API bool ZEND_FASTCALL I_REPLACE_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2) { return !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)); } - -#ifdef POP_OPTIONS -# pragma GCC pop_options -# undef POP_OPTIONS #endif #if defined(__GNUC__) && defined(__i386__) -ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2) +ZEND_API zend_never_inline NOIPA bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2) { const char *ptr = ZSTR_VAL(s1); uintptr_t delta = (uintptr_t) s2 - (uintptr_t) s1; @@ -433,7 +425,7 @@ 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) +ZEND_API zend_never_inline NOIPA bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2) { const char *ptr = ZSTR_VAL(s1); uintptr_t delta = (uintptr_t) s2 - (uintptr_t) s1; From e3e37c801fd6aea0f393a1d97c532ad91fac8bdf Mon Sep 17 00:00:00 2001 From: Shivam Mathur Date: Wed, 15 Oct 2025 23:19:02 +0530 Subject: [PATCH 3/7] Upgrade nightly macOS version to macos 15 (#20159) --- .github/workflows/nightly.yml | 15 +++++++++------ .github/workflows/root.yml | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index c310565f3b57..ded06c46b2ec 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -14,6 +14,9 @@ on: libmysqlclient_with_mysqli: required: true type: boolean + macos_arm64_version: + required: true + type: string run_alpine: required: true type: boolean @@ -366,11 +369,11 @@ jobs: matrix: debug: [true, false] zts: [true, false] - os: ['13', '14'] + arch: ['X64', 'ARM64'] exclude: - - os: ${{ !inputs.run_macos_arm64 && '14' || '*never*' }} - name: "MACOS_${{ matrix.os == '13' && 'X64' || 'ARM64' }}_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}" - runs-on: macos-${{ matrix.os }} + - arch: ${{ !inputs.run_macos_arm64 && 'ARM64' || '*never*' }} + name: "MACOS_${{ matrix.arch }}_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}" + runs-on: macos-${{ matrix.arch == 'X64' && '15-intel' || inputs.macos_arm64_version }} steps: - name: git checkout uses: actions/checkout@v5 @@ -393,7 +396,7 @@ jobs: - name: Test uses: ./.github/actions/test-macos - name: Test Tracing JIT - if: matrix.os != '14' || !matrix.zts + if: matrix.arch == 'X64' || !matrix.zts uses: ./.github/actions/test-macos with: jitType: tracing @@ -405,7 +408,7 @@ jobs: runTestsParameters: >- -d opcache.enable_cli=1 - name: Test Function JIT - if: matrix.os != '14' || !matrix.zts + if: matrix.arch == 'X64' || !matrix.zts uses: ./.github/actions/test-macos with: jitType: function diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index b70b026eced0..2983a8e3c908 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -52,6 +52,7 @@ jobs: branch: ${{ matrix.branch.ref }} community_verify_type_inference: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9 }} libmysqlclient_with_mysqli: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1) }} + macos_arm64_version: ${{ ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 5) || matrix.branch.version[0] >= 9) && '15' || '14' }} 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 }} From 56af25cc1c7cde3fd7424eceda38d13ced47f99c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 14 Oct 2025 22:23:09 +0200 Subject: [PATCH 4/7] exif: Fix possible memory leak when tag is empty When `!value_ptr` is handled, memory is allocated at line 3314. At later exit paths, `outside` (pointing to `value_ptr`) is freed, but not when exiting via the `REQUIRE_NON_EMPTY` macro. Closes GH-20169. --- NEWS | 3 +++ ext/exif/exif.c | 1 + 2 files changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 9c0be9d18a3a..de913357bae0 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,9 @@ PHP NEWS . Partially fixed bug GH-16317 (DOM classes do not allow __debugInfo() overrides to work). (nielsdos) +- Exif: + . Fix possible memory leak when tag is empty. (nielsdos) + - FPM: . Fixed bug GH-19974 (fpm_status_export_to_zval segfault for parallel execution). (Jakub Zelenka, txuna) diff --git a/ext/exif/exif.c b/ext/exif/exif.c index edae9dc0f73e..e6be4a1338dc 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -3253,6 +3253,7 @@ static bool exif_process_IFD_in_MAKERNOTE(image_info_type *ImageInfo, char * val #define REQUIRE_NON_EMPTY() do { \ if (byte_count == 0) { \ + EFREE_IF(outside); \ exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Process tag(x%04X=%s): Cannot be empty", tag, exif_get_tagname_debug(tag, tag_table)); \ return false; \ } \ From 472f2fe0a3aa5f2b646452bb8c5dd6157cedca6e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 30 Sep 2025 21:17:34 +0200 Subject: [PATCH 5/7] Fix GH-8978: MySQLi: SSL certificate verification fails (port doubled) If there are 2 ports, only the first is used. However, then the certificate checking fails. So we drop the second port if there is one. Closes GH-20021. --- NEWS | 4 ++++ ext/mysqlnd/mysqlnd_connection.c | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index de913357bae0..fde1c257dc09 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,10 @@ PHP NEWS - LibXML: . Fix not thread safe schema/relaxng calls. (SpencerMalone, nielsdos) +- MySQLnd: + . Fixed bug GH-8978 (SSL certificate verification fails (port doubled)). + (nielsdos) + - Opcache: . Fixed bug GH-20081 (access to uninitialized vars in preload_load()). (Arnaud) diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index a97f2820a319..d8e7304e9665 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -557,7 +557,14 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_ if (hostname.s[0] != '[' && mysqlnd_fast_is_ipv6_address(hostname.s)) { transport.l = mnd_sprintf(&transport.s, 0, "tcp://[%s]:%u", hostname.s, port); } else { - transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port); + /* Not ipv6, but could already contain a port number, in which case we should not add an extra port. + * See GH-8978. In a port doubling scenario, the first port would be used so we do the same to keep BC. */ + if (strchr(hostname.s, ':')) { + /* TODO: Ideally we should be able to get rid of this workaround in the future. */ + transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s", hostname.s); + } else { + transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port); + } } } DBG_INF_FMT("transport=%s", transport.s? transport.s:"OOM"); From bbe2191002bffd25c7fbcbc3eba8c3200ebdb45d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 15 Oct 2025 21:58:23 +0200 Subject: [PATCH 6/7] ROR the callable_convert_cache key for better hash distribution (#20052) --- Zend/zend_vm_def.h | 6 +++++- Zend/zend_vm_execute.h | 12 ++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index d71c06c0a135..3c3065e15687 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -9720,7 +9720,11 @@ ZEND_VM_HANDLER(202, ZEND_CALLABLE_CONVERT, UNUSED, UNUSED, NUM|CACHE_SLOT) if (closure) { ZVAL_OBJ_COPY(EX_VAR(opline->result.var), closure); } else { - zval *closure_zv = zend_hash_index_lookup(&EG(callable_convert_cache), (zend_ulong)(uintptr_t)call->func); + /* Rotate the key for better hash distribution. */ + const int shift = sizeof(size_t) == 4 ? 6 : 7; + zend_ulong key = (zend_ulong)(uintptr_t)call->func; + key = (key >> shift) | (key << ((sizeof(key) * 8) - shift)); + zval *closure_zv = zend_hash_index_lookup(&EG(callable_convert_cache), key); if (Z_TYPE_P(closure_zv) == IS_NULL) { zend_closure_from_frame(closure_zv, call); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 50b65c864ed2..50870ce463de 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -39093,7 +39093,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONV if (closure) { ZVAL_OBJ_COPY(EX_VAR(opline->result.var), closure); } else { - zval *closure_zv = zend_hash_index_lookup(&EG(callable_convert_cache), (zend_ulong)(uintptr_t)call->func); + /* Rotate the key for better hash distribution. */ + const int shift = sizeof(size_t) == 4 ? 6 : 7; + zend_ulong key = (zend_ulong)(uintptr_t)call->func; + key = (key >> shift) | (key << ((sizeof(key) * 8) - shift)); + zval *closure_zv = zend_hash_index_lookup(&EG(callable_convert_cache), key); if (Z_TYPE_P(closure_zv) == IS_NULL) { zend_closure_from_frame(closure_zv, call); } @@ -94331,7 +94335,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_S if (closure) { ZVAL_OBJ_COPY(EX_VAR(opline->result.var), closure); } else { - zval *closure_zv = zend_hash_index_lookup(&EG(callable_convert_cache), (zend_ulong)(uintptr_t)call->func); + /* Rotate the key for better hash distribution. */ + const int shift = sizeof(size_t) == 4 ? 6 : 7; + zend_ulong key = (zend_ulong)(uintptr_t)call->func; + key = (key >> shift) | (key << ((sizeof(key) * 8) - shift)); + zval *closure_zv = zend_hash_index_lookup(&EG(callable_convert_cache), key); if (Z_TYPE_P(closure_zv) == IS_NULL) { zend_closure_from_frame(closure_zv, call); } From 94625a0e4ce5e3d845753e061cd2aa89f41d505e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 5 Sep 2025 23:20:45 +0100 Subject: [PATCH 7/7] Fix GH-19722: Windows: _get_osfhandle asserts in debug mode when given a socket Closes GH-19725. --- NEWS | 4 ++++ win32/select.c | 13 +++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index fde1c257dc09..265cbcea1140 100644 --- a/NEWS +++ b/NEWS @@ -61,6 +61,10 @@ PHP NEWS . Fix arginfo/zpp violations when LIBXML_SCHEMAS_ENABLED is not available. (nielsdos) +- Windows: + . Fix GH-19722 (_get_osfhandle asserts in debug mode when given a socket). + (dktapps) + 23 Oct 2025, PHP 8.3.27 - Core: diff --git a/win32/select.c b/win32/select.c index f443325cf045..507ba91de3e0 100644 --- a/win32/select.c +++ b/win32/select.c @@ -62,8 +62,10 @@ PHPAPI int php_select(php_socket_t max_fd, fd_set *rfds, fd_set *wfds, fd_set *e /* build an array of handles for non-sockets */ for (i = 0; (uint32_t)i < max_fd; i++) { if (SAFE_FD_ISSET(i, rfds) || SAFE_FD_ISSET(i, wfds) || SAFE_FD_ISSET(i, efds)) { - handles[n_handles] = (HANDLE)(uintptr_t)_get_osfhandle(i); - if (handles[n_handles] == INVALID_HANDLE_VALUE) { + int _type; + int _len = sizeof(_type); + + if (getsockopt((SOCKET)i, SOL_SOCKET, SO_TYPE, (char*)&_type, &_len) == 0 || WSAGetLastError() != WSAENOTSOCK) { /* socket */ if (SAFE_FD_ISSET(i, rfds)) { FD_SET((uint32_t)i, &sock_read); @@ -78,8 +80,11 @@ PHPAPI int php_select(php_socket_t max_fd, fd_set *rfds, fd_set *wfds, fd_set *e sock_max_fd = i; } } else { - handle_slot_to_fd[n_handles] = i; - n_handles++; + handles[n_handles] = (HANDLE)(uintptr_t)_get_osfhandle(i); + if (handles[n_handles] != INVALID_HANDLE_VALUE) { + handle_slot_to_fd[n_handles] = i; + n_handles++; + } } } }