From 0f7af6078744d57a3581486bc6c4dc5e6ff933e8 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 23 Sep 2026 14:25:42 +0200 Subject: [PATCH 1/2] Fix alignment in `zend_string_safe_alloc()`/`zend_string_safe_realloc()` --- Zend/zend_string.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Zend/zend_string.h b/Zend/zend_string.h index ad66fe168c8e..675a953f2462 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -127,6 +127,12 @@ static zend_always_inline zend_string *ZSTR_KNOWN(size_t idx) { #define _ZSTR_STRUCT_SIZE(len) (_ZSTR_HEADER_SIZE + len + 1) +/* ALIGN(n * m + header + l + 1). The "+ ALIGNMENT - 1" of the rounding is + * done inside the overflow check, so the final "& MASK" can only shrink + * the value and can not wrap. */ +#define _ZSTR_SAFE_STRUCT_SIZE(n, m, l) \ + (zend_safe_address_guarded(n, m, _ZSTR_STRUCT_SIZE(l) + ZEND_MM_ALIGNMENT - 1) & ZEND_MM_ALIGNMENT_MASK) + #define ZSTR_MAX_OVERHEAD (ZEND_MM_ALIGNED_SIZE(_ZSTR_HEADER_SIZE + 1)) #define ZSTR_MAX_LEN (SIZE_MAX - ZSTR_MAX_OVERHEAD) @@ -198,7 +204,7 @@ static zend_always_inline zend_string *zend_string_alloc(size_t len, bool persis static zend_always_inline zend_string *zend_string_safe_alloc(size_t n, size_t m, size_t l, bool persistent) { - zend_string *ret = (zend_string *)safe_pemalloc(n, m, ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(l)), persistent); + zend_string *ret = (zend_string *)pemalloc(_ZSTR_SAFE_STRUCT_SIZE(n, m, l), persistent); GC_SET_REFCOUNT(ret, 1); GC_TYPE_INFO(ret) = GC_STRING | ((persistent ? IS_STR_PERSISTENT : 0) << GC_FLAGS_SHIFT); @@ -325,7 +331,7 @@ static zend_always_inline zend_string *zend_string_safe_realloc(zend_string *s, if (!ZSTR_IS_INTERNED(s)) { if (GC_REFCOUNT(s) == 1) { - ret = (zend_string *)safe_perealloc(s, n, m, ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(l)), persistent); + ret = (zend_string *)perealloc(s, _ZSTR_SAFE_STRUCT_SIZE(n, m, l), persistent); ZSTR_LEN(ret) = (n * m) + l; zend_string_forget_hash_val(ret); return ret; From e48596b41d04102fd7ec090767a6e7fa57a25a09 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Thu, 24 Sep 2026 08:26:59 +0200 Subject: [PATCH 2/2] Update chunk_split 32bit test for new overflow offset --- ext/standard/tests/strings/chunk_split_variation2_32bit.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/tests/strings/chunk_split_variation2_32bit.phpt b/ext/standard/tests/strings/chunk_split_variation2_32bit.phpt index c83a37f00edb..8f56cd96fbc3 100644 --- a/ext/standard/tests/strings/chunk_split_variation2_32bit.phpt +++ b/ext/standard/tests/strings/chunk_split_variation2_32bit.phpt @@ -16,4 +16,4 @@ var_dump(chunk_split($a,$b,$c)); --EXPECTF-- *** Testing chunk_split() : unexpected large 'end' string argument variation 2 *** -Fatal error: Possible integer overflow in memory allocation (65537 * 65537 + %r65556|65560%r) in %s on line %d +Fatal error: Possible integer overflow in memory allocation (65537 * 65537 + %r65557|65561%r) in %s on line %d