diff --git a/Zend/zend_string.h b/Zend/zend_string.h index d5674f3ff1c4..573677e77fec 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -131,6 +131,20 @@ static zend_always_inline zend_string *ZSTR_KNOWN(size_t idx) { #define ZSTR_MAX_OVERHEAD (ZEND_MM_ALIGNED_SIZE(_ZSTR_HEADER_SIZE + 1)) #define ZSTR_MAX_LEN (SIZE_MAX - ZSTR_MAX_OVERHEAD) +/* Returns n * m + l, or SIZE_MAX on fallible overflow. Rejects lengths above + * ZSTR_MAX_LEN so the aligned allocation size of the string can not wrap. */ +static zend_always_inline size_t zend_string_safe_len(size_t n, size_t m, size_t l, bool fallible) +{ + bool overflow; + size_t len = zend_safe_address(n, m, l, &overflow); + + if (UNEXPECTED(overflow || len > ZSTR_MAX_LEN)) { + zend_mm_delayed_error_or_panic(fallible, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", n, m, l); + return SIZE_MAX; + } + return len; +} + #define ZSTR_ALLOCA_ALLOC(str, _len, use_heap) do { \ (str) = (zend_string *)do_alloca(ZEND_MM_ALIGNED_SIZE_EX(_ZSTR_STRUCT_SIZE(_len), 8), (use_heap)); \ GC_SET_REFCOUNT(str, 1); \ @@ -209,16 +223,11 @@ static zend_always_inline zend_string *zend_string_alloc(size_t len, bool persis /* Fallible variant of zend_string_safe_alloc */ static zend_always_inline zend_string *zend_string_safe_falloc(size_t n, size_t m, size_t l, bool persistent, bool fallible) { - zend_string *ret = (zend_string *)safe_fpemalloc(n, m, ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(l)), persistent, fallible); - if (ZEND_CONST_COND(fallible, true) && UNEXPECTED(ret == NULL)) { - return ret; + size_t len = zend_string_safe_len(n, m, l, fallible && !persistent); + if (UNEXPECTED(len == SIZE_MAX)) { + return NULL; } - - GC_SET_REFCOUNT(ret, 1); - GC_TYPE_INFO(ret) = GC_STRING | ((persistent ? IS_STR_PERSISTENT : 0) << GC_FLAGS_SHIFT); - ZSTR_H(ret) = 0; - ZSTR_LEN(ret) = (n * m) + l; - return ret; + return zend_string_falloc(len, persistent, fallible); } static zend_always_inline zend_string *zend_string_safe_alloc(size_t n, size_t m, size_t l, bool persistent) @@ -391,23 +400,27 @@ static zend_always_inline zend_string *zend_string_truncate(zend_string *s, size static zend_always_inline zend_string *zend_string_safe_frealloc(zend_string *s, size_t n, size_t m, size_t l, bool persistent, bool fallible) { zend_string *ret; + size_t len = zend_string_safe_len(n, m, l, fallible && !persistent); + if (UNEXPECTED(len == SIZE_MAX)) { + return NULL; + } if (!ZSTR_IS_INTERNED(s)) { if (GC_REFCOUNT(s) == 1) { - ret = (zend_string *)safe_fperealloc(s, n, m, ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(l)), persistent, fallible); + ret = (zend_string *)fperealloc(s, ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)), persistent, fallible); if (ZEND_CONST_COND(fallible, true) && !ret) { return NULL; } - ZSTR_LEN(ret) = (n * m) + l; + ZSTR_LEN(ret) = len; zend_string_forget_hash_val(ret); return ret; } } - ret = zend_string_safe_falloc(n, m, l, persistent, fallible); + ret = zend_string_falloc(len, persistent, fallible); if (ZEND_CONST_COND(fallible, true) && !ret) { return NULL; } - memcpy(ZSTR_VAL(ret), ZSTR_VAL(s), MIN((n * m) + l, ZSTR_LEN(s)) + 1); + memcpy(ZSTR_VAL(ret), ZSTR_VAL(s), MIN(len, ZSTR_LEN(s)) + 1); if (!ZSTR_IS_INTERNED(s)) { GC_DELREF(s); } diff --git a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h index 6abc042ff09b..ac21fbbe491a 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h +++ b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h @@ -155,7 +155,7 @@ typedef zend_string* (*mb_cut_fn)(unsigned char *str, size_t from, size_t len, u static inline void mb_convert_buf_init(mb_convert_buf *buf, size_t initsize, uint32_t repl_char, unsigned int err_mode) { buf->state = buf->errors = 0; - buf->str = emalloc(_ZSTR_STRUCT_SIZE(initsize)); + buf->str = emalloc(ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(initsize))); buf->out = (unsigned char*)ZSTR_VAL(buf->str); buf->limit = buf->out + initsize; buf->replacement_char = repl_char; @@ -167,8 +167,11 @@ static inline zend_result mb_convert_buf_ensure(mb_convert_buf *buf, unsigned ch ZEND_ASSERT(*out <= *limit); if ((size_t)(*limit - *out) < needed) { size_t oldsize = *limit - (unsigned char*)ZSTR_VAL((buf)->str); - size_t newsize = oldsize + MAX(oldsize >> 1, needed); - zend_string *newstr = safe_ferealloc((buf)->str, 1, newsize, _ZSTR_STRUCT_SIZE(0), /* fallible */ true); + size_t newsize = zend_string_safe_len(1, oldsize, MAX(oldsize >> 1, needed), /* fallible */ true); + if (UNEXPECTED(newsize == SIZE_MAX)) { + return FAILURE; + } + zend_string *newstr = ferealloc((buf)->str, ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(newsize)), /* fallible */ true); if (!newstr) { return FAILURE; } diff --git a/ext/standard/tests/strings/chunk_split_variation2_32bit.phpt b/ext/standard/tests/strings/chunk_split_variation2_32bit.phpt index c83a37f00edb..3c227c9273da 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 + 65537) in %s on line %d