Skip to content

Commit f0895a5

Browse files
committed
Backport security fixes
Interim backports carried until the upstream branch has its own fix: - backport-convert-filters.patch: GHSA-88hq-2827-7pg6: the convert.base64-encode, convert.quoted-printable-encode and convert.quoted-printable-decode stream filters copy their line-break-chars option with its length (pestrndup()), so that a value with a NUL byte no longer leads to a heap buffer over-read. Origin: php-src commit 8d933a6 of the PHP 7.4 security branch of Remi Collet (remicollet/php-src-security PHP-7.4-security-backports) (07.02.2026, geeknik, cherry-pick of b4e3397), applies to 5.6 as it is - backport-date-period.patch: DatePeriod::__construct() called again on the same object frees the start, current, end and interval of the first call instead of leaking them (phpGH-22643), and refuses a DateInterval, or a start or end date, that was not initialized by its constructor (a subclass that overrides it without calling the parent) instead of reading through a NULL pointer; unlike the 7.4 version it restores the error handling mode on that path, so that later warnings do not turn into exceptions. Origin: php-src commit bbb0272 of the PHP 7.4 security branch of Remi Collet (remicollet/php-src-security PHP-7.4-security-backports) (07.07.2026, Ilija Tovilo), adapted to the PHP 5.6 object layout; the check of the start and end dates is added here (5.6 copies the start time with memcpy()) - backport-filter-encoded.patch: GHSA-ch8v-r6jh-4vvr: FILTER_SANITIZE_ENCODED percent-encodes 0xFF (its table of characters to keep was one byte short and left 0xFF as it is). Origin: php-src commit f517508 of the PHP 7.4 security branch of Remi Collet (remicollet/php-src-security PHP-7.4-security-backports) (29.07.2026, Ilia Alshanetsky), applies to 5.6 as it is - backport-fpm-allowed-clients.patch: GHSA-62xp-839h-2637: listen.allowed_clients of php-fpm compared only the first 96 bits of an IPv6 client address, so that an allowed ::dead:beef let ::1 in; all 128 bits are compared now. Origin: php-src commit 7fb8053 of the PHP 7.4 security branch of Remi Collet (remicollet/php-src-security PHP-7.4-security-backports) (25.08.2026, Alexandre Daubois), in sapi/fpm/fpm/fastcgi.c of 5.6 (main/fastcgi.c of 7.x); the test is rewritten for the FPM test helpers of 5.6, the IPv4-mapped one is left out (php-fpm 5.6 does not clear IPV6_V6ONLY, which FreeBSD sets by default) - backport-gd-gif-lzw.patch: CVE-2026-9672: the GIF LZW decoder of the bundled libgd (imagecreatefromgif()) clears every entry of its code table on a clear code (it cleared table[1][0] over and over and left the other entries stale), stops at the end code instead of decoding on with a stale code, and starts from a zeroed state, so that a crafted GIF cannot make it read uninitialized memory. Origin: php-src commit e9413a5 of the PHP 7.4 security branch of Remi Collet (remicollet/php-src-security PHP-7.4-security-backports) (28.07.2026, libgd fix fcd691b by Pierre Joye), applies to 5.6 as it is - backport-http-redirects.patch: GHSA-fpwc-w8rq-cr92: on a redirect to another origin (scheme, host or port) the http:// stream wrapper drops Authorization, Cookie and Proxy-Authorization from the header option of the context, repeated headers, folded values and whitespace before the colon included; with it phpGH-11274: 307 and 308 redirects keep the method (POST, PATCH) and the body, as RFC 7231 and RFC 7538 require. The fixes of 7.4 for an empty and a 1-character Location (phpGH-23467, phpGH-23521) do not apply: 5.6 does not follow an empty Location and reads it from a fixed-size buffer. Origin: php-src commits c812ca1 (phpGH-11274, Niels Dossche) and dc1f36a (14.09.2026, Alexandre Daubois, Jakub Zelenka) of the PHP 7.4 security branch of Remi Collet (remicollet/php-src-security PHP-7.4-security-backports) - backport-mysqlnd-overread.patch: GHSA-h35g-vwh6-m678 (CVE-2024-8929, 2024) and the 2026 partial fix of GHSA-r6x9-5r99-36j7 for PHP 5.6: a hostile or man-in-the-middle MySQL server could make the mysqlnd client read past the end of a received packet and leak adjacent heap bytes into PHP-visible values, and in some paths free memory wrongly. 5.6 shipped neither fix (both are absent from Remi and Sury for 5.6), so both are carried over from PHP 7.4. php_mysqlnd_net_field_length() now takes the bytes left in the packet and returns MYSQLND_INVALID_NET_FIELD_LENGTH when a length-encoded integer would read past them; a new php_mysqlnd_net_field_length_ll() returns an optional value the callers must check. Every packet reader (greeting, auth response, OK, EOF, result-set header, column definition, prepare response, change-user, sha256 public key, and the text and binary row decoders) is bounded with BAIL_IF_NOT_ENOUGH_DATA_EX / explicit remaining-size checks, NUL-terminated strings are found with memchr instead of being assumed terminated, and the prepared-statement value decoders (ps_fetch_*) reject a declared length that runs past the row or is too short for the fixed type. A decode that fails frees or NULLs the already-decoded row zvals of 5.6 so the row can be freed safely, the BIT text value is bounded by the space reserved from the column length, read_result_metadata frees a failed result through free_result (not a bare efree that would leak the sub-objects), and the prepared-statement EOF reset releases the connection reference and command buffer before the memset. Tests use a new fake MySQL server (ext/mysqli/tests/fake_server.inc), adapted to PHP 5.6 syntax; the client handshake bytes that differ in 5.6 are matched with %s. Origin: azsupport, port of php-src commits 2f5aa9f (GHSA-h35g-vwh6-m678 / CVE-2024-8929, Jakub Zelenka, 08.10.2024, PHP-8.1) and 2af14de (Partial Fix GHSA-r6x9-5r99-36j7, Jakub Zelenka and Nora Dossche, 12.07.2026) of the PHP 7.4 security branch of Remi Collet (remicollet/php-src-security PHP-7.4-security-backports) - backport-openssl-peer-name.patch: GHSA-vvx9-73fr-5jjx: the peer name check of TLS streams no longer falls back to the CN when the certificate presents a DNS-ID, URI-ID or SRV-ID (RFC 6125 6.4.4); GHSA-xr7j-rvgx-xq5p: out-of-bounds read in the wildcard match when the wildcard prefix and suffix overlap in the name (CN "a*aaaa" against "aaaa"). The tests come with CertificateGenerator.inc of 7.4. Origin: php-src commits da1db02 and dbc0299 of the PHP 7.4 security branch of Remi Collet (remicollet/php-src-security PHP-7.4-security-backports) (10.08.2026, Jakub Zelenka) - backport-pgsql-quotes.patch: GHSA-7qpv-r5mr-78m4 (CVE-2026-17543): SQL injection through pg_convert(), pg_insert(), pg_update(), pg_delete() and pg_select(): values escaped with PQescapeStringConn() were quoted as E'...', where a backslash escapes the quote, while PQescapeStringConn() doubles backslashes only with standard_conforming_strings off; they are quoted as '...' now. Origin: php-src commit 6a80d9d of the PHP 7.4 security branch of Remi Collet (remicollet/php-src-security PHP-7.4-security-backports) (27.07.2026, Ilija Tovilo, cherry-pick of ab048bd), with the expected results of the pgsql tests - backport-phar-tar.patch: GHSA-vc5h-9ppw-p5f3 (CVE-2026-7260): a tar archive with a loop of symbolic links no longer exhausts the stack (phar_get_link_source() follows the chain without recursion, with Floyd's cycle detection; a loop resolves to nothing). GHSA-j3wh-g957-2m85: tar entry injection: an entry size that is not plain octal (overflow, GNU base-256, other characters) is refused instead of being read as some other size, entries that carry no data (links, devices, directories, FIFOs) no longer skip data, GNU long link records are refused and a long name longer than the archive is rejected. The test of GHSA-vc5h writes its archives itself, as patch(1) cannot add the binary ones of upstream. Origin: php-src commits 6982130 (03.05.2026) and f3432d2 (13.09.2026, both Jakub Zelenka) of the PHP 7.4 security branch of Remi Collet (remicollet/php-src-security PHP-7.4-security-backports), ported to the PHP 5 API - backport-php2shell-1.patch: CVE-2026-109618 and CVE-2026-109619 for PHP 5.6: the official EOL fix "Avoid state corruption via bailout" (PHP 7.4) carried over to the PHP 5 engine, as proposed for remicollet/php-src-security in pull request php#32: fatal errors of the allocator (memory limit, integer overflow in allocation sizes, string size overflow in concatenation, Balloc) print and _exit() the worker instead of a zend_error() longjmp() out of a half-initialized request; state that an error before the next request startup can reach is reset (user error and exception handler stacks, error handling mode, exception, the autoload guard table, freed $_GET/$_POST/... pointers, SAPI request strings, modules_activated, zend_llist head and tail); multipart parsing keeps only Content-Disposition and Content-Type headers, without duplicates; session upload progress stays off whatever session.upload_progress.enabled says. The code of that pull request as it is, so that sync skips this patch once the branch has it; what the official fix and the other 5.6 ports have on top is in backport-php2shell-2-extra.patch. Origin: remicollet/php-src-security#32 (RalphJS, 22.10.2026), commit 477c04a64, port of php-src 802c210 (PHP 7.4, Arnaud Le Blanc) to PHP-5.6-security-backports-openssl11 @ 5c2cc46; the same code as our own port of 22.10.2026 (131fde8), which it replaces. Skipped by sync once the branch has it - backport-php2shell-2-extra.patch: CVE-2026-109618 and CVE-2026-109619 for PHP 5.6, on top of backport-php2shell-1.patch: what the official fix and the other 5.6 ports have and that patch has not. $_GET, $_POST, $_COOKIE and their sub-arrays start as the smallest array (2 buckets: nTableMask 0 marks an uninitialized table in PHP 5), as the singleton arrays of the official fix; EG(error_handling) and EG(exception) are reset right after the object store is freed, where the official fix does it, so that an error in the rest of the shutdown cannot reach the freed exception; EG(exception_class) is reset with the handlers; SG(callback_func), the header callback of the request, is reset after it; "Out of memory" of the persistent allocator wrappers ends with _exit() instead of exit(), so that no atexit handler runs in that state. Origin: azsupport, from our port of 22.10.2026 (131fde8): the singleton arrays and the early reset from php-src 802c210 (PHP 7.4, Arnaud Le Blanc), SG(callback_func) from the 5.6 port of Ondrej Sury (php5.6 5.6.40-104, debian/patches/0261-Avoid-state-corruption-via-bailout.patch) - backport-soap.patch: GHSA-rgrp-mwpx-f6rm: unbounded recursion in ext/soap: a deeply nested WSDL or SOAP message, or a chain or loop of href references, no longer overflows the stack (iterative cleanup and search, at most 2048 levels of XML with libxml2 older than 2.13, at most 4096 levels of decoding: "Encoding: Nesting level too deep"); GHSA-cj93-vc83-wgqv: the HTTP reader of SoapClient keeps the chunk sizes and the body length within bounds (unsigned sizes, explicit limits instead of a signed overflow check). Origin: php-src commits 0a51c0d (17.09.2026, Alexandre Daubois) and 9b8abad (13.09.2026, Nora Dossche, Jakub Zelenka) of the PHP 7.4 security branch of Remi Collet (remicollet/php-src-security PHP-7.4-security-backports), with the test of 0c5cff7; the limits follow the int body length of 5.6
1 parent 2b2017e commit f0895a5

43 files changed

Lines changed: 1026 additions & 336 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎Zend/zend_API.c‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,18 @@ ZEND_API int _array_init(zval *arg, uint size ZEND_FILE_LINE_DC) /* {{{ */
10161016
}
10171017
/* }}} */
10181018

1019+
/* The smallest array: $_GET, $_POST, $_COOKIE and their sub-arrays start with
1020+
* it, so that request variables cost as little memory as possible (the
1021+
* "singleton" arrays of the official fix). nTableMask == 0 marks a table that is
1022+
* not initialized yet in PHP 5, so 2 is the smallest size that works. */
1023+
ZEND_API int _array_init_singleton(zval *arg ZEND_FILE_LINE_DC) /* {{{ */
1024+
{
1025+
_array_init(arg, 0 ZEND_FILE_LINE_RELAY_CC);
1026+
Z_ARRVAL_P(arg)->nTableSize = 2;
1027+
return SUCCESS;
1028+
}
1029+
/* }}} */
1030+
10191031
static int zend_merge_property(zval **value TSRMLS_DC, int num_args, va_list args, const zend_hash_key *hash_key) /* {{{ */
10201032
{
10211033
/* which name should a numeric property have ? */

‎Zend/zend_API.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,10 +363,12 @@ ZEND_API char *zend_get_type_by_const(int type);
363363

364364
#define array_init(arg) _array_init((arg), 0 ZEND_FILE_LINE_CC)
365365
#define array_init_size(arg, size) _array_init((arg), (size) ZEND_FILE_LINE_CC)
366+
#define array_init_singleton(arg) _array_init_singleton((arg) ZEND_FILE_LINE_CC)
366367
#define object_init(arg) _object_init((arg) ZEND_FILE_LINE_CC TSRMLS_CC)
367368
#define object_init_ex(arg, ce) _object_init_ex((arg), (ce) ZEND_FILE_LINE_CC TSRMLS_CC)
368369
#define object_and_properties_init(arg, ce, properties) _object_and_properties_init((arg), (ce), (properties) ZEND_FILE_LINE_CC TSRMLS_CC)
369370
ZEND_API int _array_init(zval *arg, uint size ZEND_FILE_LINE_DC);
371+
ZEND_API int _array_init_singleton(zval *arg ZEND_FILE_LINE_DC);
370372
ZEND_API int _object_init(zval *arg ZEND_FILE_LINE_DC TSRMLS_DC);
371373
ZEND_API int _object_init_ex(zval *arg, zend_class_entry *ce ZEND_FILE_LINE_DC TSRMLS_DC);
372374
ZEND_API int _object_and_properties_init(zval *arg, zend_class_entry *ce, HashTable *properties ZEND_FILE_LINE_DC TSRMLS_DC);

‎Zend/zend_alloc.c‎

Lines changed: 44 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,37 @@ static void zend_mm_panic(const char *message)
9191
#if ZEND_DEBUG && defined(HAVE_KILL) && defined(HAVE_GETPID)
9292
kill(getpid(), SIGSEGV);
9393
#endif
94-
exit(1);
94+
_exit(1);
95+
}
96+
97+
/* CVE-2026-109618: fatal allocation error without longjmp(), terminate the process */
98+
ZEND_API void zend_mm_safe_error_ex(const char *format, ...)
99+
{
100+
const char *error_filename = NULL;
101+
uint error_lineno = 0;
102+
char buf[1024];
103+
va_list args;
104+
TSRMLS_FETCH();
105+
106+
if (zend_is_compiling(TSRMLS_C)) {
107+
error_filename = zend_get_compiled_filename(TSRMLS_C);
108+
error_lineno = zend_get_compiled_lineno(TSRMLS_C);
109+
} else if (EG(in_execution)) {
110+
error_filename = EG(active_op_array)?EG(active_op_array)->filename:NULL;
111+
error_lineno = EG(opline_ptr)?(*EG(opline_ptr))->lineno:0;
112+
}
113+
114+
va_start(args, format);
115+
vsnprintf(buf, sizeof(buf), format, args);
116+
va_end(args);
117+
118+
fprintf(stderr, "\nFatal error: %s in %s on line %d\n", buf,
119+
error_filename ? error_filename : "Unknown", error_lineno);
120+
/* See http://support.microsoft.com/kb/190351 */
121+
#ifdef PHP_WIN32
122+
fflush(stderr);
123+
#endif
124+
_exit(1);
95125
}
96126

97127
/*******************/
@@ -1749,59 +1779,14 @@ static void zend_mm_safe_error(zend_mm_heap *heap,
17491779
#endif
17501780
size_t size)
17511781
{
1752-
if (heap->reserve) {
1753-
_zend_mm_free_int(heap, heap->reserve ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC);
1754-
heap->reserve = NULL;
1755-
}
1756-
if (heap->overflow == 0) {
1757-
const char *error_filename;
1758-
uint error_lineno;
1759-
TSRMLS_FETCH();
1760-
if (zend_is_compiling(TSRMLS_C)) {
1761-
error_filename = zend_get_compiled_filename(TSRMLS_C);
1762-
error_lineno = zend_get_compiled_lineno(TSRMLS_C);
1763-
} else if (EG(in_execution)) {
1764-
error_filename = EG(active_op_array)?EG(active_op_array)->filename:NULL;
1765-
error_lineno = EG(opline_ptr)?(*EG(opline_ptr))->lineno:0;
1766-
} else {
1767-
error_filename = NULL;
1768-
error_lineno = 0;
1769-
}
1770-
if (!error_filename) {
1771-
error_filename = "Unknown";
1772-
}
1773-
heap->overflow = 1;
1774-
zend_try {
1775-
zend_error_noreturn(E_ERROR,
1776-
format,
1777-
limit,
1778-
#if ZEND_DEBUG
1779-
filename,
1780-
lineno,
1781-
#endif
1782-
size);
1783-
} zend_catch {
1784-
if (heap->overflow == 2) {
1785-
fprintf(stderr, "\nFatal error: ");
1786-
fprintf(stderr,
1787-
format,
1788-
limit,
1782+
(void) heap;
1783+
zend_mm_safe_error_ex(format,
1784+
limit,
17891785
#if ZEND_DEBUG
1790-
filename,
1791-
lineno,
1786+
filename,
1787+
lineno,
17921788
#endif
1793-
size);
1794-
fprintf(stderr, " in %s on line %d\n", error_filename, error_lineno);
1795-
}
1796-
/* See http://support.microsoft.com/kb/190351 */
1797-
#ifdef PHP_WIN32
1798-
fflush(stderr);
1799-
#endif
1800-
} zend_end_try();
1801-
} else {
1802-
heap->overflow = 2;
1803-
}
1804-
zend_bailout();
1789+
size);
18051790
}
18061791

18071792
static zend_mm_free_block *zend_mm_search_large_block(zend_mm_heap *heap, size_t true_size)
@@ -2472,7 +2457,7 @@ static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
24722457
"rm"(offset));
24732458

24742459
if (UNEXPECTED(overflow)) {
2475-
zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
2460+
zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
24762461
return 0;
24772462
}
24782463
return res;
@@ -2501,7 +2486,7 @@ static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
25012486

25022487
#undef LP_SUFF
25032488
if (UNEXPECTED(overflow)) {
2504-
zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
2489+
zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
25052490
return 0;
25062491
}
25072492
return res;
@@ -2522,7 +2507,7 @@ static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
25222507
"1"(0));
25232508

25242509
if (UNEXPECTED(overflow)) {
2525-
zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
2510+
zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
25262511
return 0;
25272512
}
25282513
return res;
@@ -2542,7 +2527,7 @@ static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
25422527
"r"(offset));
25432528

25442529
if (UNEXPECTED(overflow)) {
2545-
zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
2530+
zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
25462531
return 0;
25472532
}
25482533
return res;
@@ -2555,7 +2540,7 @@ static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
25552540
zend_ulong64 res = (zend_ulong64)nmemb * (zend_ulong64)size + (zend_ulong64)offset;
25562541

25572542
if (UNEXPECTED(res > (zend_ulong64)0xFFFFFFFFL)) {
2558-
zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
2543+
zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
25592544
return 0;
25602545
}
25612546
return (size_t) res;
@@ -2570,7 +2555,7 @@ static inline size_t safe_address(size_t nmemb, size_t size, size_t offset)
25702555
double _delta = (double)res - _d;
25712556

25722557
if (UNEXPECTED((_d + _delta ) != _d)) {
2573-
zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
2558+
zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset);
25742559
return 0;
25752560
}
25762561
return res;
@@ -2582,7 +2567,7 @@ ZEND_API void *_safe_emalloc_string(size_t nmemb, size_t size, size_t offset ZEN
25822567
{
25832568
size_t str_size = safe_address(nmemb, size, offset);
25842569
if (UNEXPECTED(str_size > INT_MAX)) {
2585-
zend_error_noreturn(E_ERROR, "String allocation overflow, max size is %d", INT_MAX);
2570+
zend_mm_safe_error_ex("String allocation overflow, max size is %d", INT_MAX);
25862571
}
25872572
return emalloc_rel(str_size);
25882573
}

‎Zend/zend_alloc.h‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626

2727
#include "../TSRM/TSRM.h"
2828
#include "zend.h"
29+
#ifdef HAVE_UNISTD_H
30+
# include <unistd.h>
31+
#endif
2932

3033
#ifndef ZEND_MM_ALIGNMENT
3134
# define ZEND_MM_ALIGNMENT 8
@@ -99,7 +102,7 @@ inline static void * __zend_malloc(size_t len)
99102
return tmp;
100103
}
101104
fprintf(stderr, "Out of memory\n");
102-
exit(1);
105+
_exit(1);
103106
}
104107

105108
inline static void * __zend_calloc(size_t nmemb, size_t len)
@@ -116,7 +119,7 @@ inline static void * __zend_realloc(void *p, size_t len)
116119
return p;
117120
}
118121
fprintf(stderr, "Out of memory\n");
119-
exit(1);
122+
_exit(1);
120123
}
121124

122125

@@ -161,6 +164,8 @@ void zend_debug_alloc_output(char *format, ...);
161164
ZEND_API size_t zend_memory_usage(int real_usage TSRMLS_DC);
162165
ZEND_API size_t zend_memory_peak_usage(int real_usage TSRMLS_DC);
163166

167+
ZEND_API void zend_mm_safe_error_ex(const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 1, 2);
168+
164169
END_EXTERN_C()
165170

166171
/* fast cache for zval's */

‎Zend/zend_execute_API.c‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,12 @@ void shutdown_executor(TSRMLS_D) /* {{{ */
307307
}
308308
} zend_end_try();
309309

310+
/* May be used by an error in the rest of the shutdown or before the next
311+
* init_executor(): the exception object is freed above (as in the
312+
* official fix, right after the object store). */
313+
EG(error_handling) = EH_NORMAL;
314+
EG(exception) = NULL;
315+
310316
zend_try {
311317
clean_non_persistent_constants(TSRMLS_C);
312318
} zend_end_try();
@@ -328,6 +334,17 @@ void shutdown_executor(TSRMLS_D) /* {{{ */
328334
}
329335
} zend_end_try();
330336

337+
/* CVE-2026-109619: may be used by zend_error before init_executor */
338+
EG(user_error_handler) = NULL;
339+
EG(user_exception_handler) = NULL;
340+
memset(&EG(user_error_handlers_error_reporting), 0, sizeof(EG(user_error_handlers_error_reporting)));
341+
memset(&EG(user_error_handlers), 0, sizeof(EG(user_error_handlers)));
342+
memset(&EG(user_exception_handlers), 0, sizeof(EG(user_exception_handlers)));
343+
EG(error_handling) = EH_NORMAL;
344+
EG(exception_class) = NULL;
345+
EG(exception) = NULL;
346+
EG(in_autoload) = NULL;
347+
331348
zend_shutdown_fpu(TSRMLS_C);
332349

333350
EG(active) = 0;

‎Zend/zend_llist.c‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ ZEND_API void zend_llist_destroy(zend_llist *l)
115115
current = next;
116116
}
117117

118+
l->head = NULL;
119+
l->tail = NULL;
118120
l->count = 0;
119121
}
120122

‎Zend/zend_operators.c‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,7 +1400,7 @@ ZEND_API int add_char_to_string(zval *result, const zval *op1, const zval *op2)
14001400
char *buf;
14011401

14021402
if (UNEXPECTED(length < 0)) {
1403-
zend_error(E_ERROR, "String size overflow");
1403+
zend_mm_safe_error_ex("String size overflow");
14041404
}
14051405

14061406
buf = str_erealloc(Z_STRVAL_P(op1), length + 1);
@@ -1419,7 +1419,7 @@ ZEND_API int add_string_to_string(zval *result, const zval *op1, const zval *op2
14191419
char *buf;
14201420

14211421
if (UNEXPECTED(length < 0)) {
1422-
zend_error(E_ERROR, "String size overflow");
1422+
zend_mm_safe_error_ex("String size overflow");
14231423
}
14241424

14251425
buf = str_erealloc(Z_STRVAL_P(op1), length + 1);
@@ -1465,7 +1465,7 @@ ZEND_API int concat_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{
14651465
if (Z_STRLEN_P(result) < 0 || (int) (Z_STRLEN_P(op1) + Z_STRLEN_P(op2)) < 0) {
14661466
efree(Z_STRVAL_P(result));
14671467
ZVAL_EMPTY_STRING(result);
1468-
zend_error(E_ERROR, "String size overflow");
1468+
zend_mm_safe_error_ex("String size overflow");
14691469
}
14701470

14711471
Z_STRVAL_P(result) = safe_erealloc(Z_STRVAL_P(result), res_len, 1, 1);
@@ -1478,7 +1478,7 @@ ZEND_API int concat_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{
14781478
char *buf;
14791479

14801480
if (Z_STRLEN_P(op1) < 0 || Z_STRLEN_P(op2) < 0 || (int) (Z_STRLEN_P(op1) + Z_STRLEN_P(op2)) < 0) {
1481-
zend_error(E_ERROR, "String size overflow");
1481+
zend_mm_safe_error_ex("String size overflow");
14821482
}
14831483
buf = (char *) safe_emalloc(length, 1, 1);
14841484

‎Zend/zend_strtod.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ static Bigint * Balloc(int k)
490490
rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(Long));
491491
if (!rv) {
492492
_THREAD_PRIVATE_MUTEX_UNLOCK(dtoa_mutex);
493-
zend_error(E_ERROR, "Balloc() failed to allocate memory");
493+
zend_mm_safe_error_ex("Balloc() failed to allocate memory");
494494
}
495495
rv->k = k;
496496
rv->maxwds = x;

‎ext/date/php_date.c‎

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4412,6 +4412,30 @@ static int date_period_initialize(timelib_time **st, timelib_time **et, timelib_
44124412
return retval;
44134413
}
44144414

4415+
static void date_period_reset(php_period_obj *period_obj) /* {{{ */
4416+
{
4417+
if (period_obj->start) {
4418+
timelib_time_dtor(period_obj->start);
4419+
}
4420+
if (period_obj->current) {
4421+
timelib_time_dtor(period_obj->current);
4422+
}
4423+
if (period_obj->end) {
4424+
timelib_time_dtor(period_obj->end);
4425+
}
4426+
if (period_obj->interval) {
4427+
timelib_rel_time_dtor(period_obj->interval);
4428+
}
4429+
period_obj->start = NULL;
4430+
period_obj->start_ce = NULL;
4431+
period_obj->current = NULL;
4432+
period_obj->end = NULL;
4433+
period_obj->interval = NULL;
4434+
period_obj->recurrences = 0;
4435+
period_obj->initialized = 0;
4436+
period_obj->include_start_date = 0;
4437+
} /* }}} */
4438+
44154439
/* {{{ proto DatePeriod::__construct(DateTime $start, DateInterval $interval, int recurrences|DateTime $end)
44164440
Creates new DatePeriod object.
44174441
*/
@@ -4439,7 +4463,7 @@ PHP_METHOD(DatePeriod, __construct)
44394463
}
44404464

44414465
dpobj = zend_object_store_get_object(getThis() TSRMLS_CC);
4442-
dpobj->current = NULL;
4466+
date_period_reset(dpobj);
44434467

44444468
if (isostr) {
44454469
date_period_initialize(&(dpobj->start), &(dpobj->end), &(dpobj->interval), &recurrences, isostr, isostr_len TSRMLS_CC);
@@ -4463,9 +4487,19 @@ PHP_METHOD(DatePeriod, __construct)
44634487
} else {
44644488
/* init */
44654489
intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC);
4490+
if (!intobj->initialized) {
4491+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The DateInterval object has not been correctly initialized by its constructor");
4492+
zend_restore_error_handling(&error_handling TSRMLS_CC);
4493+
return;
4494+
}
4495+
dateobj = (php_date_obj *) zend_object_store_get_object(start TSRMLS_CC);
4496+
if (!dateobj->time || (end && !((php_date_obj *) zend_object_store_get_object(end TSRMLS_CC))->time)) {
4497+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The DateTimeInterface object has not been correctly initialized by its constructor");
4498+
zend_restore_error_handling(&error_handling TSRMLS_CC);
4499+
return;
4500+
}
44664501

44674502
/* start date */
4468-
dateobj = (php_date_obj *) zend_object_store_get_object(start TSRMLS_CC);
44694503
clone = timelib_time_ctor();
44704504
memcpy(clone, dateobj->time, sizeof(timelib_time));
44714505
if (dateobj->time->tz_abbr) {

‎ext/filter/sanitizing_filters.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ static void php_filter_encode_url(zval *value, const unsigned char* chars, const
7171
unsigned char *s = (unsigned char *)chars;
7272
unsigned char *e = s + char_len;
7373

74-
memset(tmp, 1, sizeof(tmp)-1);
74+
memset(tmp, 1, sizeof(tmp));
7575

7676
while (s < e) {
7777
tmp[*s++] = 0;

0 commit comments

Comments
 (0)