From 1d7dec178a094e6e75b229ce22754045f8c6070a Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Sun, 18 Oct 2026 20:12:35 +0200 Subject: [PATCH] Avoid state corruption via bailout Minimal mitigation for EOL version. Fix GHSA-w8hw-w9fx-j79w (CVE-2026-109618) Fix GHSA-6wmf-4rv5-p3rp (CVE-2026-109619) Co-authored-by: Ilija Tovilo --- Zend/zend_API.h | 1 + Zend/zend_alloc.c | 68 +++++++++----- Zend/zend_alloc.h | 2 + Zend/zend_execute_API.c | 15 ++++ Zend/zend_hash.c | 13 ++- Zend/zend_hash.h | 1 + Zend/zend_multiply.h | 4 +- Zend/zend_strtod.c | 4 +- Zend/zend_types.h | 26 +++--- Zend/zend_vm_def.h | 11 ++- Zend/zend_vm_execute.h | 94 ++++++++++++++++++-- ext/opcache/ZendAccelerator.c | 5 ++ ext/opcache/ZendAccelerator.h | 11 ++- ext/opcache/zend_file_cache.c | 8 ++ ext/opcache/zend_persist.c | 67 ++++++++++++-- ext/session/session.c | 2 +- ext/zend_test/tests/observer_bug81430_2.phpt | 2 - ext/zend_test/tests/observer_bug81435.phpt | 1 - ext/zend_test/tests/observer_error_01.phpt | 2 - main/SAPI.c | 5 ++ main/main.c | 6 ++ main/php_variables.c | 8 +- main/rfc1867.c | 28 ++++-- 23 files changed, 311 insertions(+), 73 deletions(-) diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 9ed1a3fbab..a32b83c053 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -490,6 +490,7 @@ ZEND_API const char *zend_get_type_by_const(int type); #define array_init(arg) ZVAL_ARR((arg), zend_new_array(0)) #define array_init_size(arg, size) ZVAL_ARR((arg), zend_new_array(size)) +#define array_init_singleton(arg) ZVAL_ARR((arg), zend_new_array_singleton()) ZEND_API void object_init(zval *arg); ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *ce); ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_entry *ce, HashTable *properties); diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 61792b37c9..7f43a7f6df 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -57,6 +57,7 @@ #include "zend_operators.h" #include "zend_multiply.h" #include "zend_bitset.h" +#include "zend_execute.h" #include #ifdef HAVE_UNISTD_H @@ -364,7 +365,42 @@ static ZEND_COLD ZEND_NORETURN void zend_mm_panic(const char *message) #if ZEND_DEBUG && defined(HAVE_KILL) && defined(HAVE_GETPID) kill(getpid(), SIGSEGV); #endif - exit(1); + _exit(1); +} + +ZEND_API ZEND_COLD ZEND_NORETURN void zend_mm_safe_error_ex(const char *format, ...) +{ + zend_string *script_filename = NULL; + uint32_t script_lineno = 0; + + if (zend_is_executing()) { + script_filename = zend_get_executed_filename_ex(); + script_lineno = zend_get_executed_lineno(); + } else if (zend_is_compiling()) { + script_filename = zend_get_compiled_filename(); + script_lineno = zend_get_compiled_lineno(); + } + + va_list args; + va_start(args, format); + + char buf[1024]; + vsnprintf(buf, sizeof(buf), format, args); + + va_end(args); + + fprintf(stderr, + "\nFatal error: %s in %s on line %d\n", + buf, + script_filename ? ZSTR_VAL(script_filename) : "Unknown", + script_lineno); + + /* See http://support.microsoft.com/kb/190351 */ +# ifdef ZEND_WIN32 + fflush(stderr); +# endif + + _exit(1); } static ZEND_COLD ZEND_NORETURN void zend_mm_safe_error(zend_mm_heap *heap, @@ -376,22 +412,14 @@ static ZEND_COLD ZEND_NORETURN void zend_mm_safe_error(zend_mm_heap *heap, #endif size_t size) { + (void) heap; - heap->overflow = 1; - zend_try { - zend_error_noreturn(E_ERROR, - format, - limit, + zend_mm_safe_error_ex(format, limit, #if ZEND_DEBUG - filename, - lineno, + filename, + lineno, #endif - size); - } zend_catch { - } zend_end_try(); - heap->overflow = 0; - zend_bailout(); - exit(1); + size); } #ifdef _WIN32 @@ -1356,7 +1384,7 @@ static zend_always_inline void *zend_mm_alloc_heap(zend_mm_heap *heap, size_t si size = MAX(size, 1); size = ZEND_MM_ALIGNED_SIZE(size) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)); if (UNEXPECTED(size < real_size)) { - zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu + %zu)", ZEND_MM_ALIGNED_SIZE(real_size), ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info))); + zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu + %zu)", ZEND_MM_ALIGNED_SIZE(real_size), ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info))); return NULL; } #endif @@ -1800,7 +1828,7 @@ static void *zend_mm_alloc_huge(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_D void *ptr; if (UNEXPECTED(new_size < size)) { - zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu + %zu)", size, alignment); + zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu + %zu)", size, alignment); } #if ZEND_MM_LIMIT @@ -2660,7 +2688,7 @@ ZEND_API char* ZEND_FASTCALL _estrdup(const char *s ZEND_FILE_LINE_DC ZEND_FILE_ length = strlen(s); if (UNEXPECTED(length + 1 == 0)) { - zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (1 * %zu + 1)", length); + zend_mm_safe_error_ex("Possible integer overflow in memory allocation (1 * %zu + 1)", length); } p = (char *) _emalloc(length + 1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); memcpy(p, s, length+1); @@ -2672,7 +2700,7 @@ ZEND_API char* ZEND_FASTCALL _estrndup(const char *s, size_t length ZEND_FILE_LI char *p; if (UNEXPECTED(length + 1 == 0)) { - zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (1 * %zu + 1)", length); + zend_mm_safe_error_ex("Possible integer overflow in memory allocation (1 * %zu + 1)", length); } p = (char *) _emalloc(length + 1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); memcpy(p, s, length); @@ -2686,7 +2714,7 @@ ZEND_API char* ZEND_FASTCALL zend_strndup(const char *s, size_t length) char *p; if (UNEXPECTED(length + 1 == 0)) { - zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (1 * %zu + 1)", length); + zend_mm_safe_error_ex("Possible integer overflow in memory allocation (1 * %zu + 1)", length); } p = (char *) malloc(length + 1); if (UNEXPECTED(p == NULL)) { @@ -3084,7 +3112,7 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void static ZEND_COLD ZEND_NORETURN void zend_out_of_memory(void) { fprintf(stderr, "Out of memory\n"); - exit(1); + _exit(1); } ZEND_API void * __zend_malloc(size_t len) diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h index 9603af8a27..72becadc77 100644 --- a/Zend/zend_alloc.h +++ b/Zend/zend_alloc.h @@ -403,6 +403,8 @@ static void apc_init_heap(void) size_t zend_mm_globals_size(void); #endif +ZEND_API ZEND_COLD ZEND_NORETURN void zend_mm_safe_error_ex(const char *format, ...); + END_EXTERN_C() #endif diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 1e31934f15..82cea225a8 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -385,6 +385,12 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) #endif } else { zend_hash_discard(EG(zend_constants), EG(persistent_constants_count)); + /* may be used by zend_error_zstr_at before init_executor */ + ZVAL_UNDEF(&EG(user_error_handler)); + ZVAL_UNDEF(&EG(user_exception_handler)); + memset(&EG(user_error_handlers_error_reporting), 0, sizeof(EG(user_error_handlers_error_reporting))); + memset(&EG(user_error_handlers), 0, sizeof(EG(user_error_handlers))); + memset(&EG(user_exception_handlers), 0, sizeof(EG(user_exception_handlers))); } zend_objects_store_free_object_storage(&EG(objects_store), fast_shutdown); @@ -406,6 +412,13 @@ void shutdown_executor(void) /* {{{ */ zend_shutdown_executor_values(fast_shutdown); + /* may be used by zend_error_zstr_at before init_executor */ + EG(record_errors) = false; + EG(num_errors) = 0; + EG(errors) = NULL; + EG(error_handling) = EH_NORMAL; + EG(exception) = NULL; + zend_weakrefs_shutdown(); zend_max_execution_timer_shutdown(); zend_fiber_shutdown(); @@ -422,6 +435,8 @@ void shutdown_executor(void) /* {{{ */ */ zend_hash_discard(EG(function_table), EG(persistent_functions_count)); zend_hash_discard(EG(class_table), EG(persistent_classes_count)); + EG(objects_store).object_buckets = NULL; + EG(objects_store).top = 1; } else { zend_vm_stack_destroy(); diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index b96a951101..65f31c2b9a 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -119,7 +119,7 @@ static zend_always_inline uint32_t zend_hash_check_size(uint32_t nSize) if (nSize <= HT_MIN_SIZE) { return HT_MIN_SIZE; } else if (UNEXPECTED(nSize >= HT_MAX_SIZE)) { - zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", nSize, sizeof(Bucket), sizeof(Bucket)); + zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%u * %zu + %zu)", nSize, sizeof(Bucket), sizeof(Bucket)); } #ifdef ZEND_WIN32 @@ -276,6 +276,13 @@ ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_0(void) return ht; } +ZEND_API HashTable* ZEND_FASTCALL zend_new_array_singleton(void) +{ + HashTable *ht = _zend_new_array_0(); + ht->nTableSize = 1; + return ht; +} + ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t nSize) { HashTable *ht = emalloc(sizeof(HashTable)); @@ -307,7 +314,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_packed_grow(HashTable *ht) { HT_ASSERT_RC1(ht); if (ht->nTableSize >= HT_MAX_SIZE) { - zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket), sizeof(Bucket)); + zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket), sizeof(Bucket)); } uint32_t newTableSize = ht->nTableSize * 2; HT_SET_DATA_ADDR(ht, perealloc2(HT_GET_DATA_ADDR(ht), HT_SIZE_EX(newTableSize, HT_MIN_MASK), HT_USED_SIZE(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)); @@ -1233,7 +1240,7 @@ static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht) pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); zend_hash_rehash(ht); } else { - zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket) + sizeof(uint32_t), sizeof(Bucket)); + zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket) + sizeof(uint32_t), sizeof(Bucket)); } } diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 092682d677..09a7994d91 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -314,6 +314,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht); #endif ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_0(void); +ZEND_API HashTable* ZEND_FASTCALL zend_new_array_singleton(void); ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t size); ZEND_API HashTable* ZEND_FASTCALL zend_new_pair(zval *val1, zval *val2); ZEND_API uint32_t zend_array_count(HashTable *ht); diff --git a/Zend/zend_multiply.h b/Zend/zend_multiply.h index c6cd472a23..bfe1b8e886 100644 --- a/Zend/zend_multiply.h +++ b/Zend/zend_multiply.h @@ -321,7 +321,7 @@ static zend_always_inline size_t zend_safe_address_guarded(size_t nmemb, size_t size_t ret = zend_safe_address(nmemb, size, offset, &overflow); if (UNEXPECTED(overflow)) { - zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset); + zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset); return 0; } return ret; @@ -334,7 +334,7 @@ static zend_always_inline size_t zend_safe_addmult(size_t nmemb, size_t size, si size_t ret = zend_safe_address(nmemb, size, offset, &overflow); if (UNEXPECTED(overflow)) { - zend_error_noreturn(E_ERROR, "Possible integer overflow in %s (%zu * %zu + %zu)", message, nmemb, size, offset); + zend_mm_safe_error_ex("Possible integer overflow in %s (%zu * %zu + %zu)", message, nmemb, size, offset); return 0; } return ret; diff --git a/Zend/zend_strtod.c b/Zend/zend_strtod.c index 3e7f90378e..7f72c82f61 100644 --- a/Zend/zend_strtod.c +++ b/Zend/zend_strtod.c @@ -593,7 +593,7 @@ Balloc rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); if (!rv) { FREE_DTOA_LOCK(0); - zend_error_noreturn(E_ERROR, "Balloc() failed to allocate memory"); + zend_mm_safe_error_ex("Balloc() failed to allocate memory"); } #else len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) @@ -606,7 +606,7 @@ Balloc rv = (Bigint*)MALLOC(len*sizeof(double)); if (!rv) { FREE_DTOA_LOCK(0); - zend_error_noreturn(E_ERROR, "Balloc() failed to allocate memory"); + zend_mm_safe_error_ex("Balloc() failed to allocate memory"); } #endif rv->k = k; diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 5bc47dd319..ab28b0fa16 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -448,17 +448,21 @@ struct _zend_array { # define HT_HASH_RESET(ht) do { \ char *p = (char*)&HT_HASH(ht, (ht)->nTableMask); \ size_t size = HT_HASH_SIZE((ht)->nTableMask); \ - __m128i xmm0 = _mm_setzero_si128(); \ - xmm0 = _mm_cmpeq_epi8(xmm0, xmm0); \ - ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \ - do { \ - _mm_storeu_si128((__m128i*)p, xmm0); \ - _mm_storeu_si128((__m128i*)(p+16), xmm0); \ - _mm_storeu_si128((__m128i*)(p+32), xmm0); \ - _mm_storeu_si128((__m128i*)(p+48), xmm0); \ - p += 64; \ - size -= 64; \ - } while (size != 0); \ + if (UNEXPECTED(size < 64)) { \ + memset(p, HT_INVALID_IDX, size); \ + } else { \ + __m128i xmm0 = _mm_setzero_si128(); \ + xmm0 = _mm_cmpeq_epi8(xmm0, xmm0); \ + ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \ + do { \ + _mm_storeu_si128((__m128i*)p, xmm0); \ + _mm_storeu_si128((__m128i*)(p+16), xmm0); \ + _mm_storeu_si128((__m128i*)(p+32), xmm0); \ + _mm_storeu_si128((__m128i*)(p+48), xmm0); \ + p += 64; \ + size -= 64; \ + } while (size != 0); \ + } \ } while (0) #else # define HT_HASH_RESET(ht) \ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index a6f86a9eab..ad893ebfb5 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -408,7 +408,7 @@ ZEND_VM_HANDLER(8, ZEND_CONCAT, CONST|TMPVAR|CV, CONST|TMPVAR|CV, SPEC(NO_CONST_ size_t len = ZSTR_LEN(op1_str); if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { - zend_error_noreturn(E_ERROR, "Integer overflow in memory allocation"); + zend_mm_safe_error_ex("Integer overflow in memory allocation"); } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -417,6 +417,9 @@ ZEND_VM_HANDLER(8, ZEND_CONCAT, CONST|TMPVAR|CV, CONST|TMPVAR|CV, SPEC(NO_CONST_ zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -3183,6 +3186,9 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { size_t len = ZSTR_LEN(op1_str); + if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); ZVAL_NEW_STR(EX_VAR(opline->result.var), str); @@ -3190,6 +3196,9 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 5ee1c2c94d..dc78ba4a08 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -6549,6 +6549,9 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_ !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { size_t len = ZSTR_LEN(op1_str); + if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); ZVAL_NEW_STR(EX_VAR(opline->result.var), str); @@ -6556,6 +6559,9 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_ zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -8456,7 +8462,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CONST_TMPVAR_HANDL size_t len = ZSTR_LEN(op1_str); if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { - zend_error_noreturn(E_ERROR, "Integer overflow in memory allocation"); + zend_mm_safe_error_ex("Integer overflow in memory allocation"); } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -8465,6 +8471,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CONST_TMPVAR_HANDL zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -8872,6 +8881,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_TMPVAR_ !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { size_t len = ZSTR_LEN(op1_str); + if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); ZVAL_NEW_STR(EX_VAR(opline->result.var), str); @@ -8879,6 +8891,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_TMPVAR_ zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -10821,7 +10836,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CONST_CV_HANDLER(Z size_t len = ZSTR_LEN(op1_str); if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { - zend_error_noreturn(E_ERROR, "Integer overflow in memory allocation"); + zend_mm_safe_error_ex("Integer overflow in memory allocation"); } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -10830,6 +10845,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CONST_CV_HANDLER(Z zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -11237,6 +11255,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_CV_HAND !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { size_t len = ZSTR_LEN(op1_str); + if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); ZVAL_NEW_STR(EX_VAR(opline->result.var), str); @@ -11244,6 +11265,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_CV_HAND zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -14866,7 +14890,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_CONST_HANDL size_t len = ZSTR_LEN(op1_str); if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { - zend_error_noreturn(E_ERROR, "Integer overflow in memory allocation"); + zend_mm_safe_error_ex("Integer overflow in memory allocation"); } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -14875,6 +14899,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_CONST_HANDL zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -15596,6 +15623,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_CONST_ !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { size_t len = ZSTR_LEN(op1_str); + if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); ZVAL_NEW_STR(EX_VAR(opline->result.var), str); @@ -15603,6 +15633,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_CONST_ zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -16295,7 +16328,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HAND size_t len = ZSTR_LEN(op1_str); if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { - zend_error_noreturn(E_ERROR, "Integer overflow in memory allocation"); + zend_mm_safe_error_ex("Integer overflow in memory allocation"); } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -16304,6 +16337,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HAND zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -17025,6 +17061,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { size_t len = ZSTR_LEN(op1_str); + if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); ZVAL_NEW_STR(EX_VAR(opline->result.var), str); @@ -17032,6 +17071,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -17981,7 +18023,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_CV_HANDLER( size_t len = ZSTR_LEN(op1_str); if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { - zend_error_noreturn(E_ERROR, "Integer overflow in memory allocation"); + zend_mm_safe_error_ex("Integer overflow in memory allocation"); } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -17990,6 +18032,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_CV_HANDLER( zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -18349,6 +18394,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_CV_HAN !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { size_t len = ZSTR_LEN(op1_str); + if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); ZVAL_NEW_STR(EX_VAR(opline->result.var), str); @@ -18356,6 +18404,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_CV_HAN zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -39651,7 +39702,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_CONST_HANDLER(Z size_t len = ZSTR_LEN(op1_str); if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { - zend_error_noreturn(E_ERROR, "Integer overflow in memory allocation"); + zend_mm_safe_error_ex("Integer overflow in memory allocation"); } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -39660,6 +39711,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_CONST_HANDLER(Z zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -42142,6 +42196,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_CONST_HAND !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { size_t len = ZSTR_LEN(op1_str); + if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); ZVAL_NEW_STR(EX_VAR(opline->result.var), str); @@ -42149,6 +42206,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_CONST_HAND zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -43460,7 +43520,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_TMPVAR_HANDLER( size_t len = ZSTR_LEN(op1_str); if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { - zend_error_noreturn(E_ERROR, "Integer overflow in memory allocation"); + zend_mm_safe_error_ex("Integer overflow in memory allocation"); } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -43469,6 +43529,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_TMPVAR_HANDLER( zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -45880,6 +45943,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_TMPVAR_HAN !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { size_t len = ZSTR_LEN(op1_str); + if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); ZVAL_NEW_STR(EX_VAR(opline->result.var), str); @@ -45887,6 +45953,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_TMPVAR_HAN zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -48757,7 +48826,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_CV_HANDLER(ZEND size_t len = ZSTR_LEN(op1_str); if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { - zend_error_noreturn(E_ERROR, "Integer overflow in memory allocation"); + zend_mm_safe_error_ex("Integer overflow in memory allocation"); } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -48766,6 +48835,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_CV_HANDLER(ZEND zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); @@ -51276,6 +51348,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_CV_HANDLER !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { size_t len = ZSTR_LEN(op1_str); + if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); ZVAL_NEW_STR(EX_VAR(opline->result.var), str); @@ -51283,6 +51358,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_CV_HANDLER zend_string_release_ex(op2_str, 0); } } else { + if (UNEXPECTED(ZSTR_LEN(op1_str) > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { + zend_mm_safe_error_ex("Integer overflow in memory allocation"); + } str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0); memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str)); memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 530620f625..e030b44131 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2410,6 +2410,7 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, } zend_map_ptr_extend(ZCSG(map_ptr_last)); + zend_accel_pending_ce_cache_begin(); #if ZEND_MM_ALIGNMENT < 8 /* Align to 8-byte boundary */ @@ -2456,6 +2457,7 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, EG(errors) = NULL; ZCSG(map_ptr_last) = CG(map_ptr_last); + zend_accel_pending_ce_cache_commit(); zend_shared_alloc_destroy_xlat_table(); @@ -2780,6 +2782,9 @@ zend_result accel_post_deactivate(void) ZCG(cwd) = NULL; } + /* A persist window left through a bailout never published its slots */ + zend_accel_pending_ce_cache_discard(); + if (!ZCG(enabled) || !accel_startup_ok) { return SUCCESS; } diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index de5ec45de7..72d3a23934 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -225,6 +225,12 @@ typedef struct _zend_accel_globals { /* preallocated buffer for keys */ zend_string key; char _key[MAXPATHLEN * 8]; + + /* CE cache slots allocated during the current persist window, keyed by the + * address of the shared class name string. They are written into the + * strings by zend_accel_pending_ce_cache_commit() once ZCSG(map_ptr_last) + * covers them. NULL outside a persist window. */ + HashTable *pending_ce_cache; } zend_accel_globals; typedef struct _zend_string_table { @@ -322,7 +328,10 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type); zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str); -uint32_t zend_accel_get_class_name_map_ptr(zend_string *type_name); +void zend_accel_get_class_name_map_ptr(zend_string *type_name); +void zend_accel_pending_ce_cache_begin(void); +void zend_accel_pending_ce_cache_commit(void); +void zend_accel_pending_ce_cache_discard(void); /* memory write protection */ #define SHM_PROTECT() \ diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 7b4b4cbb12..f1b1da2baa 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -1696,6 +1696,7 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl int cache_it = 1; unsigned int actual_checksum; int ok; + size_t orig_map_ptr_last = 0; if (!full_path) { return NULL; @@ -1831,6 +1832,8 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl } memcpy(buf, mem, info.mem_size); zend_map_ptr_extend(ZCSG(map_ptr_last)); + orig_map_ptr_last = CG(map_ptr_last); + zend_accel_pending_ce_cache_begin(); } else { use_process_mem: buf = mem; @@ -1849,6 +1852,10 @@ use_process_mem: } zend_end_try(); if (!ok) { if (cache_it) { + /* The slots allocated by the failed unserialization are referenced + * only by the abandoned buffer, so they can be reclaimed. */ + zend_accel_pending_ce_cache_discard(); + CG(map_ptr_last) = orig_map_ptr_last; zend_shared_alloc_unlock(); goto use_process_mem; } else { @@ -1862,6 +1869,7 @@ use_process_mem: if (cache_it) { ZCSG(map_ptr_last) = CG(map_ptr_last); + zend_accel_pending_ce_cache_commit(); script->dynamic_members.checksum = zend_accel_script_checksum(script); script->dynamic_members.last_used = ZCG(request_time); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 49dfea34e3..4ff93a12b3 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -299,31 +299,78 @@ static HashTable *zend_persist_attributes(HashTable *attributes) return ptr; } -uint32_t zend_accel_get_class_name_map_ptr(zend_string *type_name) +/* We delay the publishing of CE cache slots until after ZCSG(map_ptr_last) is + * published. + * CE cache slots stored in interned strings are immediately visible to other + * processes, so if we fail to update ZCSG(map_ptr_last) after publishing a + * slot (due to a bailout/exit), that slot may be re-allocated for something + * else by other processes. */ +void zend_accel_pending_ce_cache_begin(void) +{ + ZEND_ASSERT(ZCG(locked)); + + /* A persist window left through a bailout caught by the caller (e.g. + * opcache_compile_file()) never published its slots. Drop them. */ + zend_accel_pending_ce_cache_discard(); + + ZCG(pending_ce_cache) = emalloc(sizeof(HashTable)); + zend_hash_init(ZCG(pending_ce_cache), 8, NULL, NULL, 0); +} + +void zend_accel_pending_ce_cache_commit(void) +{ + ZEND_ASSERT(ZCG(pending_ce_cache)); + /* Every pending slot must be covered by ZCSG(map_ptr_last) */ + ZEND_ASSERT(ZCSG(map_ptr_last) == CG(map_ptr_last)); + + ZEND_HASH_FOREACH_NUM_KEY_VAL(ZCG(pending_ce_cache), zend_ulong h, zval *zv) { + zend_string *type_name = (zend_string*)(uintptr_t)h; + + ZEND_ASSERT(!ZSTR_HAS_CE_CACHE(type_name)); + GC_SET_REFCOUNT(type_name, (uint32_t)Z_LVAL_P(zv)); + GC_ADD_FLAGS(type_name, IS_STR_CLASS_NAME_MAP_PTR); + } ZEND_HASH_FOREACH_END(); + + zend_accel_pending_ce_cache_discard(); +} + +void zend_accel_pending_ce_cache_discard(void) +{ + if (ZCG(pending_ce_cache)) { + zend_hash_destroy(ZCG(pending_ce_cache)); + efree(ZCG(pending_ce_cache)); + ZCG(pending_ce_cache) = NULL; + } +} + +void zend_accel_get_class_name_map_ptr(zend_string *type_name) { uint32_t ret; + zval *slot; if (zend_string_equals_literal_ci(type_name, "self") || zend_string_equals_literal_ci(type_name, "parent")) { - return 0; + return; } /* We use type.name.gc.refcount to keep map_ptr of corresponding type */ if (ZSTR_HAS_CE_CACHE(type_name)) { - return GC_REFCOUNT(type_name); + return; } if ((GC_FLAGS(type_name) & GC_IMMUTABLE) && (GC_FLAGS(type_name) & IS_STR_PERMANENT)) { + ZEND_ASSERT(ZCG(pending_ce_cache) && "Must be called inside a persist window"); + slot = zend_hash_index_lookup(ZCG(pending_ce_cache), (zend_ulong)(uintptr_t)type_name); + if (Z_TYPE_P(slot) != IS_NULL) { + /* Already allocated earlier in this window */ + return; + } do { ret = ZEND_MAP_PTR_NEW_OFFSET(); } while (ret <= 2); - GC_SET_REFCOUNT(type_name, ret); - GC_ADD_FLAGS(type_name, IS_STR_CLASS_NAME_MAP_PTR); - return ret; + ZVAL_LONG(slot, ret); } - - return 0; } static HashTable *zend_persist_backed_enum_table(HashTable *backed_enum_table) @@ -1312,6 +1359,9 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script #endif zend_map_ptr_extend(ZCSG(map_ptr_last)); + if (for_shm) { + zend_accel_pending_ce_cache_begin(); + } zend_accel_persist_class_table(&script->script.class_table); zend_hash_persist(&script->script.function_table); @@ -1336,6 +1386,7 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script if (for_shm) { ZCSG(map_ptr_last) = CG(map_ptr_last); + zend_accel_pending_ce_cache_commit(); } #ifdef HAVE_JIT diff --git a/ext/session/session.c b/ext/session/session.c index 114df24b46..d72ae27b44 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -3000,7 +3000,7 @@ static int php_session_rfc1867_callback(unsigned int event, void *event_data, vo if (php_session_rfc1867_orig_callback) { retval = php_session_rfc1867_orig_callback(event, event_data, extra); } - if (!PS(rfc1867_enabled)) { + if (!PS(rfc1867_enabled) || true) { return retval; } diff --git a/ext/zend_test/tests/observer_bug81430_2.phpt b/ext/zend_test/tests/observer_bug81430_2.phpt index 90e0fa5ce0..2cdbca9a70 100644 --- a/ext/zend_test/tests/observer_bug81430_2.phpt +++ b/ext/zend_test/tests/observer_bug81430_2.phpt @@ -30,5 +30,3 @@ call_user_func([$r->getAttributes(A::class)[0], 'newInstance']); Fatal error: Allowed memory size of %d bytes exhausted %s in %s on line %d - - diff --git a/ext/zend_test/tests/observer_bug81435.phpt b/ext/zend_test/tests/observer_bug81435.phpt index 7b927e55fb..e435292da0 100644 --- a/ext/zend_test/tests/observer_bug81435.phpt +++ b/ext/zend_test/tests/observer_bug81435.phpt @@ -45,4 +45,3 @@ a(); Fatal error: Allowed memory size of 20971520 bytes exhausted %s in %s on line %d - diff --git a/ext/zend_test/tests/observer_error_01.phpt b/ext/zend_test/tests/observer_error_01.phpt index 10b1e569b0..70a761016a 100644 --- a/ext/zend_test/tests/observer_error_01.phpt +++ b/ext/zend_test/tests/observer_error_01.phpt @@ -26,5 +26,3 @@ echo 'You should not see this.'; Fatal error: Allowed memory size of 2097152 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d - - diff --git a/main/SAPI.c b/main/SAPI.c index 561cf6d598..656418a08e 100644 --- a/main/SAPI.c +++ b/main/SAPI.c @@ -507,18 +507,23 @@ SAPI_API void sapi_deactivate_module(void) } if (SG(request_info).auth_user) { efree(SG(request_info).auth_user); + SG(request_info).auth_user = NULL; } if (SG(request_info).auth_password) { efree(SG(request_info).auth_password); + SG(request_info).auth_password = NULL; } if (SG(request_info).auth_digest) { efree(SG(request_info).auth_digest); + SG(request_info).auth_digest = NULL; } if (SG(request_info).content_type_dup) { efree(SG(request_info).content_type_dup); + SG(request_info).content_type_dup = NULL; } if (SG(request_info).current_user) { efree(SG(request_info).current_user); + SG(request_info).current_user = NULL; } if (sapi_module.deactivate) { sapi_module.deactivate(); diff --git a/main/main.c b/main/main.c index 9c62aa914e..b9994ccf9d 100644 --- a/main/main.c +++ b/main/main.c @@ -1850,6 +1850,8 @@ void php_request_shutdown(void *dummy) for (i=0; i