@@ -127,15 +127,22 @@ static zend_always_inline zend_string *ZSTR_KNOWN(size_t idx) {
127127
128128#define _ZSTR_STRUCT_SIZE (len ) (_ZSTR_HEADER_SIZE + len + 1)
129129
130- /* ALIGN(n * m + header + l + 1). The "+ ALIGNMENT - 1" of the rounding is
131- * done inside the overflow check, so the final "& MASK" can only shrink
132- * the value and can not wrap. */
133- #define _ZSTR_SAFE_STRUCT_SIZE (n , m , l ) \
134- (zend_safe_address_guarded(n, m, _ZSTR_STRUCT_SIZE(l) + ZEND_MM_ALIGNMENT - 1) & ZEND_MM_ALIGNMENT_MASK)
135-
136130#define ZSTR_MAX_OVERHEAD (ZEND_MM_ALIGNED_SIZE(_ZSTR_HEADER_SIZE + 1))
137131#define ZSTR_MAX_LEN (SIZE_MAX - ZSTR_MAX_OVERHEAD)
138132
133+ /* Returns n * m + l. Errors if that overflows or is above ZSTR_MAX_LEN, so
134+ * the aligned allocation size of the string can not wrap. */
135+ static zend_always_inline size_t zend_string_safe_len (size_t n , size_t m , size_t l )
136+ {
137+ bool overflow ;
138+ size_t len = zend_safe_address (n , m , l , & overflow );
139+
140+ if (UNEXPECTED (overflow || len > ZSTR_MAX_LEN )) {
141+ zend_error_noreturn (E_ERROR , "Possible integer overflow in memory allocation (%zu * %zu + %zu)" , n , m , l );
142+ }
143+ return len ;
144+ }
145+
139146#define ZSTR_ALLOCA_ALLOC (str , _len , use_heap ) do { \
140147 (str) = (zend_string *)do_alloca(ZEND_MM_ALIGNED_SIZE_EX(_ZSTR_STRUCT_SIZE(_len), 8), (use_heap)); \
141148 GC_SET_REFCOUNT(str, 1); \
@@ -204,13 +211,7 @@ static zend_always_inline zend_string *zend_string_alloc(size_t len, bool persis
204211
205212static zend_always_inline zend_string * zend_string_safe_alloc (size_t n , size_t m , size_t l , bool persistent )
206213{
207- zend_string * ret = (zend_string * )pemalloc (_ZSTR_SAFE_STRUCT_SIZE (n , m , l ), persistent );
208-
209- GC_SET_REFCOUNT (ret , 1 );
210- GC_TYPE_INFO (ret ) = GC_STRING | ((persistent ? IS_STR_PERSISTENT : 0 ) << GC_FLAGS_SHIFT );
211- ZSTR_H (ret ) = 0 ;
212- ZSTR_LEN (ret ) = (n * m ) + l ;
213- return ret ;
214+ return zend_string_alloc (zend_string_safe_len (n , m , l ), persistent );
214215}
215216
216217static zend_always_inline zend_string * zend_string_init (const char * str , size_t len , bool persistent )
@@ -327,22 +328,7 @@ static zend_always_inline zend_string *zend_string_truncate(zend_string *s, size
327328
328329static zend_always_inline zend_string * zend_string_safe_realloc (zend_string * s , size_t n , size_t m , size_t l , bool persistent )
329330{
330- zend_string * ret ;
331-
332- if (!ZSTR_IS_INTERNED (s )) {
333- if (GC_REFCOUNT (s ) == 1 ) {
334- ret = (zend_string * )perealloc (s , _ZSTR_SAFE_STRUCT_SIZE (n , m , l ), persistent );
335- ZSTR_LEN (ret ) = (n * m ) + l ;
336- zend_string_forget_hash_val (ret );
337- return ret ;
338- }
339- }
340- ret = zend_string_safe_alloc (n , m , l , persistent );
341- memcpy (ZSTR_VAL (ret ), ZSTR_VAL (s ), MIN ((n * m ) + l , ZSTR_LEN (s )) + 1 );
342- if (!ZSTR_IS_INTERNED (s )) {
343- GC_DELREF (s );
344- }
345- return ret ;
331+ return zend_string_realloc (s , zend_string_safe_len (n , m , l ), persistent );
346332}
347333
348334static zend_always_inline char * zend_cstr_append_char (const char * str , size_t len , char c ) {
0 commit comments