Commit c1a5f468d09 for php

commit c1a5f468d09fc4afc5d738872d5b1d4e87ff1e39
Merge: 54b4c1a67cf a34a205c298
Author: Ilija Tovilo <ilija.tovilo@me.com>
Date:   Wed Oct 21 16:12:35 2026 +0200

    Merge branch 'PHP-8.2' into PHP-8.3

    * PHP-8.2:
      Add NEWS entry
      Avoid state corruption via bailout

diff --cc NEWS
index 1240399e8c7,ad524d0bb94..91bd9fa4a64
--- a/NEWS
+++ b/NEWS
@@@ -1,9 -1,17 +1,17 @@@
  PHP                                                                        NEWS
  |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
- ?? ??? ????, PHP 8.3.36
 -?? ??? ????, PHP 8.2.36
++?? ??? ????, PHP 8.3.37


+
 -21 Oct 2026, PHP 8.2.35
++21 Oct 2026, PHP 8.3.36
+
+ - Core:
+   . Fixed GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp (Avoid state corruption via
+     bailout). (CVE-2026-109618, CVE-2026-109619) (Arnaud, ilutov, Jakub Zelenka,
+     ndossche, Nicolas Grekas)
+
 -24 Sep 2026, PHP 8.2.34
 +24 Sep 2026, PHP 8.3.35

  - Filter:
    . Fixed GHSA-ch8v-r6jh-4vvr (FILTER_SANITIZE_ENCODED does not encode 0xFF).
diff --cc Zend/tests/debug_backtrace_sensitive_named_variadic.phpt
index 00000000000,00000000000..3599437a68b
new file mode 100644
--- /dev/null
+++ b/Zend/tests/debug_backtrace_sensitive_named_variadic.phpt
@@@ -1,0 -1,0 +1,21 @@@
++--TEST--
++Backtrace args of a #[SensitiveParameter] variadic called with named arguments
++--FILE--
++<?php
++class E extends Exception {}
++
++function outer(#[SensitiveParameter] ...$args) {
++    return new E();
++}
++
++var_dump(outer(a: 1, b: 'secret')->getTrace()[0]['args']);
++?>
++--EXPECT--
++array(2) {
++  ["a"]=>
++  object(SensitiveParameterValue)#2 (0) {
++  }
++  ["b"]=>
++  object(SensitiveParameterValue)#3 (0) {
++  }
++}
diff --cc Zend/tests/oom_array_fill_003.phpt
index 00000000000,cdf90d30477..b232e62a29e
mode 000000,100644..100644
--- a/Zend/tests/oom_array_fill_003.phpt
+++ b/Zend/tests/oom_array_fill_003.phpt
@@@ -1,0 -1,18 +1,18 @@@
+ --TEST--
+ OOM array_fill() packed > HT_MAX_SIZE
+ --INI--
+ memory_limit=100M
+ --SKIPIF--
+ <?php
+ if (PHP_INT_SIZE !== 8) {
+     die('skip 64-bit only');
+ }
+ ?>
+ --FILE--
+ <?php
+
 -var_dump(array_fill(0, 1024*1024*1024, (string)time()));
++var_dump(array_fill(0, 1024*1024*1024+1, (string)time()));
+
+ ?>
+ --EXPECTF--
 -Fatal error: Possible integer overflow in memory allocation (1073741824 * 32 + 32) in %s on line 3
++Fatal error: Possible integer overflow in memory allocation (1073741825 * 32 + 32) in %s on line 3
diff --cc Zend/tests/oom_array_fill_004.phpt
index 00000000000,0bbb100f183..8a70a406547
mode 000000,100644..100644
--- a/Zend/tests/oom_array_fill_004.phpt
+++ b/Zend/tests/oom_array_fill_004.phpt
@@@ -1,0 -1,18 +1,18 @@@
+ --TEST--
+ OOM array_fill() mixed > HT_MAX_SIZE
+ --INI--
+ memory_limit=100M
+ --SKIPIF--
+ <?php
+ if (PHP_INT_SIZE !== 8) {
+     die('skip 64-bit only');
+ }
+ ?>
+ --FILE--
+ <?php
+
 -var_dump(array_fill(-1, 1024*1024*1024, (string)time()));
++var_dump(array_fill(-1, 1024*1024*1024+1, (string)time()));
+
+ ?>
+ --EXPECTF--
 -Fatal error: Possible integer overflow in memory allocation (1073741824 * 32 + 32) in %s on line 3
++Fatal error: Possible integer overflow in memory allocation (1073741825 * 32 + 32) in %s on line 3
diff --cc Zend/zend.c
index e10df10c46f,a57d271d326..b60214ed24d
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@@ -1333,8 -1310,19 +1336,10 @@@ ZEND_API void zend_deactivate(void) /*
  		zend_map_ptr_reset();
  	}

+ 	zend_mm_oom_info_dtor(&EG(delayed_oom));
+
  #if GC_BENCH
 -	fprintf(stderr, "GC Statistics\n");
 -	fprintf(stderr, "-------------\n");
 -	fprintf(stderr, "Runs:               %d\n", GC_G(gc_runs));
 -	fprintf(stderr, "Collected:          %d\n", GC_G(collected));
 -	fprintf(stderr, "Root buffer length: %d\n", GC_G(root_buf_length));
 -	fprintf(stderr, "Root buffer peak:   %d\n\n", GC_G(root_buf_peak));
 -	fprintf(stderr, "      Possible            Remove from  Marked\n");
 -	fprintf(stderr, "        Root    Buffered     buffer     grey\n");
 -	fprintf(stderr, "      --------  --------  -----------  ------\n");
 -	fprintf(stderr, "ZVAL  %8d  %8d  %9d  %8d\n", GC_G(zval_possible_root), GC_G(zval_buffered), GC_G(zval_remove_from_buffer), GC_G(zval_marked_grey));
 +	gc_bench_print();
  #endif
  }
  /* }}} */
diff --cc Zend/zend_alloc.c
index 0d3b3522383,1ec7d7ca423..85eed43812e
--- a/Zend/zend_alloc.c
+++ b/Zend/zend_alloc.c
@@@ -2649,12 -2771,31 +2787,26 @@@ ZEND_API void* ZEND_FASTCALL _erealloc2

  ZEND_API size_t ZEND_FASTCALL _zend_mem_block_size(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
  {
 -#if ZEND_MM_CUSTOM
 -	if (UNEXPECTED(AG(mm_heap)->use_custom_heap)) {
 -		return 0;
 -	}
 -#endif
 -	return zend_mm_size(AG(mm_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
 +	return _zend_mm_block_size(AG(mm_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
  }

+ #define ZEND_MM_SAFE_ADDRESS_GUARDED(dest, nmemb, size, offset) do { \
+ 		size_t __nmemb = (nmemb); \
+ 		size_t __size = (size); \
+ 		size_t __offset = (offset); \
+ 		bool overflow; \
+ 		(dest) = zend_safe_address(__nmemb, __size, __offset, &overflow); \
+ 		if (UNEXPECTED(overflow)) { \
+ 			zend_mm_delayed_error_or_panic(AG(mm_heap)->fallible, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", __nmemb, __size, __offset); \
+ 			return NULL; \
+ 		} \
+ 	} while (0)
+
  ZEND_API void* ZEND_FASTCALL _safe_emalloc(size_t nmemb, size_t size, size_t offset ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
  {
- 	return _emalloc(zend_safe_address_guarded(nmemb, size, offset) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ 	size_t actual_size;
+ 	ZEND_MM_SAFE_ADDRESS_GUARDED(actual_size, nmemb, size, offset);
+ 	return _emalloc(actual_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
  }

  ZEND_API void* ZEND_FASTCALL _safe_malloc(size_t nmemb, size_t size, size_t offset)
diff --cc Zend/zend_attributes.c
index 08dfd6dc8aa,9a764b7b226..8099502caf3
--- a/Zend/zend_attributes.c
+++ b/Zend/zend_attributes.c
@@@ -115,6 -114,12 +115,12 @@@ ZEND_METHOD(SensitiveParameter, __const
  	ZEND_PARSE_PARAMETERS_NONE();
  }

+ void zend_sensitive_parameter_value_init(zend_object *obj, zval *value)
+ {
+ 	ZEND_ASSERT(obj->ce == zend_ce_sensitive_parameter_value);
 -	zend_update_property(zend_ce_sensitive_parameter_value, obj, "value", strlen("value"), value);
++	zend_update_property_ex(zend_ce_sensitive_parameter_value, obj, ZSTR_KNOWN(ZEND_STR_VALUE), value);
+ }
+
  ZEND_METHOD(SensitiveParameterValue, __construct)
  {
  	zval *value;
diff --cc Zend/zend_builtin_functions.c
index 374d4b3734e,d52fd32f06d..3d49b49ab63
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@@ -1699,14 -1687,9 +1715,18 @@@ static bool debug_backtrace_get_args(ze
  		SEPARATE_ARRAY(arg_array);
  		ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(call->extra_named_params, name, arg) {
  			ZVAL_DEREF(arg);
 -			Z_TRY_ADDREF_P(arg);
 -			if (UNEXPECTED(zend_hash_fadd_new(Z_ARRVAL_P(arg_array), name, arg) == HASH_NOMEM)) {
++			zval *add_result;
 +			if (is_sensitive) {
 +				zval redacted_arg;
 +				object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value);
- 				zend_call_method_with_1_params(Z_OBJ_P(&redacted_arg), zend_ce_sensitive_parameter_value, &zend_ce_sensitive_parameter_value->constructor, "__construct", NULL, arg);
- 				zend_hash_add_new(Z_ARRVAL_P(arg_array), name, &redacted_arg);
++				zend_sensitive_parameter_value_init(Z_OBJ(redacted_arg), arg);
++				add_result = zend_hash_fadd_new(Z_ARRVAL_P(arg_array), name, &redacted_arg);
 +			} else {
 +				Z_TRY_ADDREF_P(arg);
- 				zend_hash_add_new(Z_ARRVAL_P(arg_array), name, arg);
++				add_result = zend_hash_fadd_new(Z_ARRVAL_P(arg_array), name, arg);
++			}
++			if (UNEXPECTED(add_result == HASH_NOMEM)) {
+ 				goto oom;
  			}
  		} ZEND_HASH_FOREACH_END();
  	}
@@@ -1949,10 -1919,16 +1980,17 @@@ ZEND_API void zend_fetch_debug_backtrac
  		}

  		ZVAL_ARR(&tmp, stack_frame);
- 		zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
+ 		if (UNEXPECTED(zend_hash_next_index_finsert_new(Z_ARRVAL_P(return_value), &tmp) == HASH_NOMEM)) {
+ 			zend_array_destroy(stack_frame);
+ 			return;
+ 		}
 +		frameno++;

  skip_frame:
+ 		if (ZEND_HAS_OOM()) {
+ 			return;
+ 		}
+
  		if (UNEXPECTED(ZEND_CALL_KIND(call) == ZEND_CALL_TOP_FUNCTION)
  		 && !fake_frame
  		 && prev
diff --cc Zend/zend_hash.c
index fdc05ff8cbf,58e3832fe55..d2d39d494db
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@@ -121,8 -121,9 +124,9 @@@ static zend_always_inline uint32_t zend
  	/* size should be between HT_MIN_SIZE and HT_MAX_SIZE */
  	if (nSize <= HT_MIN_SIZE) {
  		return HT_MIN_SIZE;
 -	} else if (UNEXPECTED(nSize >= HT_MAX_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_delayed_error_or_panic(fallible, "Possible integer overflow in memory allocation (%u * %zu + %zu)", nSize, sizeof(Bucket), sizeof(Bucket));
+ 		return 0;
  	}

  #ifdef ZEND_WIN32
diff --cc Zend/zend_hash.h
index 861848755f8,5042f7a684a..a2d47df24b0
--- a/Zend/zend_hash.h
+++ b/Zend/zend_hash.h
@@@ -214,8 -228,11 +228,11 @@@ static zend_always_inline zval *zend_ha
  ZEND_API zval* ZEND_FASTCALL zend_hash_lookup(HashTable *ht, zend_string *key);
  ZEND_API zval* ZEND_FASTCALL zend_hash_index_lookup(HashTable *ht, zend_ulong h);

+ ZEND_API zval* ZEND_FASTCALL zend_hash_flookup(HashTable *ht, zend_string *key);
+ ZEND_API zval* ZEND_FASTCALL zend_hash_index_flookup(HashTable *ht, zend_ulong h);
+
  #define ZEND_HASH_INDEX_LOOKUP(_ht, _h, _ret) do { \
 -		if (EXPECTED(HT_FLAGS(_ht) & HASH_FLAG_PACKED)) { \
 +		if (EXPECTED(HT_IS_PACKED(_ht))) { \
  			if (EXPECTED((zend_ulong)(_h) < (zend_ulong)(_ht)->nNumUsed)) { \
  				_ret = &_ht->arPacked[_h]; \
  				if (EXPECTED(Z_TYPE_P(_ret) != IS_UNDEF)) { \
diff --cc Zend/zend_objects.c
index 8d9bbcc6aa7,8e35e56bf0d..a900d85042d
--- a/Zend/zend_objects.c
+++ b/Zend/zend_objects.c
@@@ -99,8 -96,12 +99,12 @@@ ZEND_API void zend_objects_destroy_obje
  	zend_function *destructor = object->ce->destructor;

  	if (destructor) {
+ 		if (ZEND_HAS_OOM()) {
+ 			return;
+ 		}
+
  		zend_object *old_exception;
 -		const zend_op *old_opline_before_exception;
 +		const zend_op *old_opline_before_exception = NULL;

  		if (destructor->op_array.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) {
  			if (destructor->op_array.fn_flags & ZEND_ACC_PRIVATE) {
@@@ -197,10 -193,21 +201,22 @@@ ZEND_API zend_object* ZEND_FASTCALL zen
  	return object;
  }

+ ZEND_API zend_object* ZEND_FASTCALL zend_objects_fnew(zend_class_entry *ce)
+ {
+ 	zend_object *object = femalloc(sizeof(zend_object) + zend_object_properties_size(ce), /* fallible */ true);
+ 	if (UNEXPECTED(!object)) {
+ 		return NULL;
+ 	}
+
+ 	_zend_object_std_init(object, ce);
+ 	object->handlers = &std_object_handlers;
+ 	return object;
+ }
+
  ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object)
  {
 +	bool has_clone_method = old_object->ce->clone != NULL;
 +
  	if (old_object->ce->default_properties_count) {
  		zval *src = old_object->properties_table;
  		zval *dst = new_object->properties_table;
diff --cc Zend/zend_smart_str.h
index e271835e41d,ce799bcd780..0626f8d5bb0
--- a/Zend/zend_smart_str.h
+++ b/Zend/zend_smart_str.h
@@@ -32,11 -34,11 +34,11 @@@ ZEND_API void ZEND_FASTCALL smart_str_a
  		smart_str *str, double num, int precision, bool zero_fraction);
  ZEND_API void smart_str_append_printf(smart_str *dest, const char *format, ...)
  	ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
 -ZEND_API void ZEND_FASTCALL smart_str_append_escaped_truncated(smart_str *str, zend_string *value, size_t length);
 -ZEND_API void ZEND_FASTCALL smart_str_append_scalar(smart_str *str, zval *value, size_t truncate);
 +ZEND_API void ZEND_FASTCALL smart_str_append_escaped_truncated(smart_str *str, const zend_string *value, size_t length);
 +ZEND_API void ZEND_FASTCALL smart_str_append_scalar(smart_str *str, const zval *value, size_t truncate);
  END_EXTERN_C()

- static zend_always_inline size_t smart_str_alloc(smart_str *str, size_t len, bool persistent) {
+ static zend_always_inline size_t smart_str_falloc(smart_str *str, size_t len, bool persistent, bool fallible) {
  	if (UNEXPECTED(!str->s)) {
  		goto do_smart_str_realloc;
  	} else {
diff --cc Zend/zend_types.h
index 63f6ddbc972,914a1479904..19e8d19441b
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@@ -483,51 -463,26 +483,63 @@@ struct _zend_array
  	HT_PACKED_SIZE_EX((ht)->nTableSize, (ht)->nTableMask)
  #define HT_PACKED_USED_SIZE(ht) \
  	(HT_HASH_SIZE((ht)->nTableMask) + ((size_t)(ht)->nNumUsed * sizeof(zval)))
 -#ifdef __SSE2__
 +#if defined(__AVX2__)
 +# define HT_HASH_RESET(ht) do { \
 +		char *p = (char*)&HT_HASH(ht, (ht)->nTableMask); \
 +		size_t size = HT_HASH_SIZE((ht)->nTableMask); \
- 		__m256i ymm0 = _mm256_setzero_si256(); \
- 		ymm0 = _mm256_cmpeq_epi64(ymm0, ymm0); \
- 		ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \
- 		do { \
- 			_mm256_storeu_si256((__m256i*)p, ymm0); \
- 			_mm256_storeu_si256((__m256i*)(p+32), ymm0); \
- 			p += 64; \
- 			size -= 64; \
- 		} while (size != 0); \
++		if (UNEXPECTED(size < 64)) { \
++			memset(p, HT_INVALID_IDX, size); \
++		} else { \
++			__m256i ymm0 = _mm256_setzero_si256(); \
++			ymm0 = _mm256_cmpeq_epi64(ymm0, ymm0); \
++			ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \
++			do { \
++				_mm256_storeu_si256((__m256i*)p, ymm0); \
++				_mm256_storeu_si256((__m256i*)(p+32), ymm0); \
++				p += 64; \
++				size -= 64; \
++			} while (size != 0); \
++		} \
 +	} while (0)
 +#elif defined(__SSE2__)
  # 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)
 +#elif defined(__aarch64__) || defined(_M_ARM64)
 +# define HT_HASH_RESET(ht) do { \
 +		char *p = (char*)&HT_HASH(ht, (ht)->nTableMask); \
 +		size_t size = HT_HASH_SIZE((ht)->nTableMask); \
- 		int32x4_t t = vdupq_n_s32(-1); \
- 		ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \
- 		do { \
- 			vst1q_s32((int32_t*)p, t); \
- 			vst1q_s32((int32_t*)(p+16), t); \
- 			vst1q_s32((int32_t*)(p+32), t); \
- 			vst1q_s32((int32_t*)(p+48), t); \
- 			p += 64; \
- 			size -= 64; \
- 		} while (size != 0); \
++		if (UNEXPECTED(size < 64)) { \
++			memset(p, HT_INVALID_IDX, size); \
++		} else { \
++			int32x4_t t = vdupq_n_s32(-1); \
++			ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \
++			do { \
++				vst1q_s32((int32_t*)p, t); \
++				vst1q_s32((int32_t*)(p+16), t); \
++				vst1q_s32((int32_t*)(p+32), t); \
++				vst1q_s32((int32_t*)(p+48), t); \
++				p += 64; \
++				size -= 64; \
++			} while (size != 0); \
++		} \
 +	} while (0)
  #else
  # define HT_HASH_RESET(ht) \
  	memset(&HT_HASH(ht, (ht)->nTableMask), HT_INVALID_IDX, HT_HASH_SIZE((ht)->nTableMask))
diff --cc Zend/zend_vm_def.h
index f1d4f7448ce,5fd5ecf443b..d63afcc71e6
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@@ -3369,7 -3330,10 +3369,8 @@@ ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP
  	zend_string **rope;
  	zval *var, *ret;
  	uint32_t i;
 -	size_t len = 0;
 -	char *target;

+ 	SAVE_OPLINE();
  	rope = (zend_string**)EX_VAR(opline->op1.var);
  	if (OP2_TYPE == IS_CONST) {
  		var = GET_OP2_ZVAL_PTR(BP_VAR_R);
diff --cc Zend/zend_vm_execute.h
index 5f7f4997ce8,0c2938b37d8..3f4ead26d27
Binary files differ
diff --cc ext/bcmath/tests/gh17398.phpt
index 6a0cda09ec7,00000000000..c7f096706d5
mode 100644,000000..100644
--- a/ext/bcmath/tests/gh17398.phpt
+++ b/ext/bcmath/tests/gh17398.phpt
@@@ -1,10 -1,0 +1,12 @@@
 +--TEST--
 +GH-17398 (bcmul memory leak)
 +--EXTENSIONS--
 +bcmath
 +--FILE--
 +<?php
 +bcmul('0', '0', 2147483647);
 +?>
 +--EXPECTF--
- Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d
++Hard memory limit of 268435456 bytes exhausted %S(tried to allocate %d bytes) in %s on line %d
++
++Termsig=9
diff --cc ext/bz2/bz2.c
index 060a2df6400,706725a71ae..972c330e678
--- a/ext/bz2/bz2.c
+++ b/ext/bz2/bz2.c
@@@ -459,18 -459,13 +459,21 @@@ PHP_FUNCTION(bzcompress
  	   + .01 x length of data + 600 which is the largest size the results of the compression
  	   could possibly be, at least that's what the libbz2 docs say (thanks to jeremy@nirvani.net
  	   for pointing this out).  */
 -	dest_len = (unsigned int) (source_len + (0.01 * source_len) + 600);
 +	size_t chunk_len = source_len + source_len / 100 + 600;
 +	const size_t min = MIN(ZSTR_MAX_LEN, UINT_MAX);
 +
 +	if (chunk_len < source_len || chunk_len > min) {
 +		zend_argument_value_error(1, "must have a length less than or equal to %zu", min);
 +		RETURN_THROWS();
 +	}
 +
 +	dest_len = (unsigned int) chunk_len;

  	/* Allocate the destination buffer */
- 	dest = zend_string_alloc(dest_len, 0);
+ 	dest = zend_string_falloc(dest_len, 0, /* fallible */ true);
+ 	if (!dest) {
+ 		RETURN_OOM();
+ 	}

  	/* Handle the optional arguments */
  	if (argc > 1) {
diff --cc ext/date/lib/parse_date.c
index 1e51279f3da,4d83333532e..d8186dd14fa
--- a/ext/date/lib/parse_date.c
+++ b/ext/date/lib/parse_date.c
@@@ -1,4 -1,4 +1,4 @@@
- /* Generated by re2c 1.0.3 on Mon Sep 15 10:38:03 2025 */
 -/* Generated by re2c 1.0.3 on Tue Sep 15 16:40:58 2026 */
++/* Generated by re2c 1.0.3 on Wed Sep 30 15:20:23 2026 */
  #line 1 "ext/date/lib/parse_date.re"
  /*
   * The MIT License (MIT)
@@@ -1021,11 -1035,11 +1038,11 @@@ static int scan(Scanner *s, timelib_tz_
  std:
  	s->tok = cursor;
  	s->len = 0;
- #line 1154 "ext/date/lib/parse_date.re"
 -#line 1168 "ext/date/lib/parse_date.re"
++#line 1171 "ext/date/lib/parse_date.re"



- #line 1029 "ext/date/lib/parse_date.c"
 -#line 1043 "<stdout>"
++#line 1046 "ext/date/lib/parse_date.c"
  {
  	YYCTYPE yych;
  	unsigned int yyaccept = 0;
@@@ -1206,23 -1220,23 +1223,23 @@@ yy2
  	YYDEBUG(2, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(3, *YYCURSOR);
- #line 1987 "ext/date/lib/parse_date.re"
 -#line 2001 "ext/date/lib/parse_date.re"
++#line 2004 "ext/date/lib/parse_date.re"
  	{
  		s->pos = cursor; s->line++;
  		goto std;
  	}
- #line 1215 "ext/date/lib/parse_date.c"
 -#line 1229 "<stdout>"
++#line 1232 "ext/date/lib/parse_date.c"
  yy4:
  	YYDEBUG(4, *YYCURSOR);
  	++YYCURSOR;
  yy5:
  	YYDEBUG(5, *YYCURSOR);
- #line 1993 "ext/date/lib/parse_date.re"
 -#line 2007 "ext/date/lib/parse_date.re"
++#line 2010 "ext/date/lib/parse_date.re"
  	{
  		add_error(s, TIMELIB_ERR_UNEXPECTED_CHARACTER, "Unexpected character");
  		goto std;
  	}
- #line 1226 "ext/date/lib/parse_date.c"
 -#line 1240 "<stdout>"
++#line 1243 "ext/date/lib/parse_date.c"
  yy6:
  	YYDEBUG(6, *YYCURSOR);
  	yyaccept = 0;
@@@ -1237,11 -1251,11 +1254,11 @@@
  	if (yych <= '9') goto yy58;
  yy8:
  	YYDEBUG(8, *YYCURSOR);
- #line 1982 "ext/date/lib/parse_date.re"
 -#line 1996 "ext/date/lib/parse_date.re"
++#line 1999 "ext/date/lib/parse_date.re"
  	{
  		goto std;
  	}
- #line 1245 "ext/date/lib/parse_date.c"
 -#line 1259 "<stdout>"
++#line 1262 "ext/date/lib/parse_date.c"
  yy9:
  	YYDEBUG(9, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -1275,11 -1289,11 +1292,11 @@@ yy11
  	YYDEBUG(11, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(12, *YYCURSOR);
- #line 1977 "ext/date/lib/parse_date.re"
 -#line 1991 "ext/date/lib/parse_date.re"
++#line 1994 "ext/date/lib/parse_date.re"
  	{
  		goto std;
  	}
- #line 1283 "ext/date/lib/parse_date.c"
 -#line 1297 "<stdout>"
++#line 1300 "ext/date/lib/parse_date.c"
  yy13:
  	YYDEBUG(13, *YYCURSOR);
  	yyaccept = 1;
@@@ -1780,7 -1794,7 +1797,7 @@@ yy19
  	}
  yy20:
  	YYDEBUG(20, *YYCURSOR);
- #line 1892 "ext/date/lib/parse_date.re"
 -#line 1906 "ext/date/lib/parse_date.re"
++#line 1909 "ext/date/lib/parse_date.re"
  	{
  		int tz_not_found;
  		DEBUG_OUTPUT("tzcorrection | tz");
@@@ -1794,7 -1808,7 +1811,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_TIMEZONE;
  	}
- #line 1798 "ext/date/lib/parse_date.c"
 -#line 1812 "<stdout>"
++#line 1815 "ext/date/lib/parse_date.c"
  yy21:
  	YYDEBUG(21, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -3599,7 -3613,7 +3616,7 @@@ yy80
  	}
  yy81:
  	YYDEBUG(81, *YYCURSOR);
- #line 1639 "ext/date/lib/parse_date.re"
 -#line 1653 "ext/date/lib/parse_date.re"
++#line 1656 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("datenoyearrev");
  		TIMELIB_INIT;
@@@ -3610,7 -3624,7 +3627,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_DATE_TEXT;
  	}
- #line 3614 "ext/date/lib/parse_date.c"
 -#line 3628 "<stdout>"
++#line 3631 "ext/date/lib/parse_date.c"
  yy82:
  	YYDEBUG(82, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -4125,7 -4139,7 +4142,7 @@@ yy112
  	}
  	if (yych == '.') goto yy289;
  	YYDEBUG(114, *YYCURSOR);
- #line 1214 "ext/date/lib/parse_date.re"
 -#line 1228 "ext/date/lib/parse_date.re"
++#line 1231 "ext/date/lib/parse_date.re"
  	{
  		timelib_ull i;

@@@ -4150,7 -4164,7 +4167,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_RELATIVE;
  	}
- #line 4154 "ext/date/lib/parse_date.c"
 -#line 4168 "<stdout>"
++#line 4171 "ext/date/lib/parse_date.c"
  yy115:
  	YYDEBUG(115, *YYCURSOR);
  	++YYCURSOR;
@@@ -5876,7 -5890,7 +5893,7 @@@ yy176
  	}
  yy177:
  	YYDEBUG(177, *YYCURSOR);
- #line 1380 "ext/date/lib/parse_date.re"
 -#line 1394 "ext/date/lib/parse_date.re"
++#line 1397 "ext/date/lib/parse_date.re"
  	{
  		int tz_not_found;
  		DEBUG_OUTPUT("timetiny24 | timeshort24 | timelong24 | iso8601long");
@@@ -5903,7 -5917,7 +5920,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_TIME24_WITH_ZONE;
  	}
- #line 5907 "ext/date/lib/parse_date.c"
 -#line 5921 "<stdout>"
++#line 5924 "ext/date/lib/parse_date.c"
  yy178:
  	YYDEBUG(178, *YYCURSOR);
  	yyaccept = 4;
@@@ -6932,7 -6946,7 +6949,7 @@@ yy223
  	}
  yy224:
  	YYDEBUG(224, *YYCURSOR);
- #line 1474 "ext/date/lib/parse_date.re"
 -#line 1488 "ext/date/lib/parse_date.re"
++#line 1491 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("americanshort | american");
@@@ -6947,7 -6961,7 +6964,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_AMERICAN;
  	}
- #line 6951 "ext/date/lib/parse_date.c"
 -#line 6965 "<stdout>"
++#line 6968 "ext/date/lib/parse_date.c"
  yy225:
  	YYDEBUG(225, *YYCURSOR);
  	yyaccept = 5;
@@@ -7190,7 -7204,7 +7207,7 @@@ yy250
  	if (yych <= '9') goto yy431;
  yy251:
  	YYDEBUG(251, *YYCURSOR);
- #line 1556 "ext/date/lib/parse_date.re"
 -#line 1570 "ext/date/lib/parse_date.re"
++#line 1573 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("datefull");
@@@ -7204,7 -7218,7 +7221,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_DATE_FULL;
  	}
- #line 7208 "ext/date/lib/parse_date.c"
 -#line 7222 "<stdout>"
++#line 7225 "ext/date/lib/parse_date.c"
  yy252:
  	YYDEBUG(252, *YYCURSOR);
  	yyaccept = 3;
@@@ -7318,7 -7332,7 +7335,7 @@@ yy259
  	if (yych == 'e') goto yy440;
  yy260:
  	YYDEBUG(260, *YYCURSOR);
- #line 1961 "ext/date/lib/parse_date.re"
 -#line 1975 "ext/date/lib/parse_date.re"
++#line 1978 "ext/date/lib/parse_date.re"
  	{
  		timelib_ull i;
  		DEBUG_OUTPUT("relative");
@@@ -7333,7 -7347,7 +7350,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_RELATIVE;
  	}
- #line 7337 "ext/date/lib/parse_date.c"
 -#line 7351 "<stdout>"
++#line 7354 "ext/date/lib/parse_date.c"
  yy261:
  	YYDEBUG(261, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -7779,7 -7793,7 +7796,7 @@@ yy289
  	if (yych <= '9') goto yy471;
  yy290:
  	YYDEBUG(290, *YYCURSOR);
- #line 1240 "ext/date/lib/parse_date.re"
 -#line 1254 "ext/date/lib/parse_date.re"
++#line 1257 "ext/date/lib/parse_date.re"
  	{
  		timelib_sll i;
  		timelib_ull us;
@@@ -7818,7 -7832,7 +7835,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_RELATIVE;
  	}
- #line 7822 "ext/date/lib/parse_date.c"
 -#line 7836 "<stdout>"
++#line 7839 "ext/date/lib/parse_date.c"
  yy291:
  	YYDEBUG(291, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -7843,7 -7857,7 +7860,7 @@@ yy292
  	}
  yy293:
  	YYDEBUG(293, *YYCURSOR);
- #line 1802 "ext/date/lib/parse_date.re"
 -#line 1816 "ext/date/lib/parse_date.re"
++#line 1819 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("ago");
  		TIMELIB_INIT;
@@@ -7863,7 -7877,7 +7880,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_AGO;
  	}
- #line 7867 "ext/date/lib/parse_date.c"
 -#line 7881 "<stdout>"
++#line 7884 "ext/date/lib/parse_date.c"
  yy294:
  	YYDEBUG(294, *YYCURSOR);
  	yyaccept = 7;
@@@ -7902,7 -7916,7 +7919,7 @@@
  	}
  yy295:
  	YYDEBUG(295, *YYCURSOR);
- #line 1882 "ext/date/lib/parse_date.re"
 -#line 1896 "ext/date/lib/parse_date.re"
++#line 1899 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("monthtext");
  		TIMELIB_INIT;
@@@ -7911,7 -7925,7 +7928,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_DATE_TEXT;
  	}
- #line 7915 "ext/date/lib/parse_date.c"
 -#line 7929 "<stdout>"
++#line 7932 "ext/date/lib/parse_date.c"
  yy296:
  	YYDEBUG(296, *YYCURSOR);
  	yyaccept = 7;
@@@ -8486,7 -8500,7 +8503,7 @@@ yy314
  	}
  yy315:
  	YYDEBUG(315, *YYCURSOR);
- #line 1823 "ext/date/lib/parse_date.re"
 -#line 1837 "ext/date/lib/parse_date.re"
++#line 1840 "ext/date/lib/parse_date.re"
  	{
  		const timelib_relunit* relunit;
  		DEBUG_OUTPUT("daytext");
@@@ -8503,7 -8517,7 +8520,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_WEEKDAY;
  	}
- #line 8507 "ext/date/lib/parse_date.c"
 -#line 8521 "<stdout>"
++#line 8524 "ext/date/lib/parse_date.c"
  yy316:
  	YYDEBUG(316, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -8771,7 -8785,7 +8788,7 @@@ yy324
  	}
  yy325:
  	YYDEBUG(325, *YYCURSOR);
- #line 1625 "ext/date/lib/parse_date.re"
 -#line 1639 "ext/date/lib/parse_date.re"
++#line 1642 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("datetextual | datenoyear");
@@@ -8784,7 -8798,7 +8801,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_DATE_TEXT;
  	}
- #line 8788 "ext/date/lib/parse_date.c"
 -#line 8802 "<stdout>"
++#line 8805 "ext/date/lib/parse_date.c"
  yy326:
  	YYDEBUG(326, *YYCURSOR);
  	yyaccept = 10;
@@@ -9478,7 -9492,7 +9495,7 @@@ yy350
  	}
  yy351:
  	YYDEBUG(351, *YYCURSOR);
- #line 1171 "ext/date/lib/parse_date.re"
 -#line 1185 "ext/date/lib/parse_date.re"
++#line 1188 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("now");
  		TIMELIB_INIT;
@@@ -9486,7 -9500,7 +9503,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_RELATIVE;
  	}
- #line 9490 "ext/date/lib/parse_date.c"
 -#line 9504 "<stdout>"
++#line 9507 "ext/date/lib/parse_date.c"
  yy352:
  	YYDEBUG(352, *YYCURSOR);
  	yyaccept = 2;
@@@ -10989,7 -11003,7 +11006,7 @@@ yy419
  	}
  yy420:
  	YYDEBUG(420, *YYCURSOR);
- #line 1408 "ext/date/lib/parse_date.re"
 -#line 1422 "ext/date/lib/parse_date.re"
++#line 1425 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("gnunocolon");
  		TIMELIB_INIT;
@@@ -11011,7 -11025,7 +11028,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_GNU_NOCOLON;
  	}
- #line 11015 "ext/date/lib/parse_date.c"
 -#line 11029 "<stdout>"
++#line 11032 "ext/date/lib/parse_date.c"
  yy421:
  	YYDEBUG(421, *YYCURSOR);
  	yyaccept = 13;
@@@ -11092,7 -11106,7 +11109,7 @@@
  	}
  yy422:
  	YYDEBUG(422, *YYCURSOR);
- #line 1793 "ext/date/lib/parse_date.re"
 -#line 1807 "ext/date/lib/parse_date.re"
++#line 1810 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("year4");
  		TIMELIB_INIT;
@@@ -11100,7 -11114,7 +11117,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_CLF;
  	}
- #line 11104 "ext/date/lib/parse_date.c"
 -#line 11118 "<stdout>"
++#line 11121 "ext/date/lib/parse_date.c"
  yy423:
  	YYDEBUG(423, *YYCURSOR);
  	yyaccept = 3;
@@@ -11707,7 -11721,7 +11724,7 @@@ yy456
  	YYDEBUG(456, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(457, *YYCURSOR);
- #line 1342 "ext/date/lib/parse_date.re"
 -#line 1356 "ext/date/lib/parse_date.re"
++#line 1359 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12");
  		TIMELIB_INIT;
@@@ -11724,7 -11738,7 +11741,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_TIME12;
  	}
- #line 11728 "ext/date/lib/parse_date.c"
 -#line 11742 "<stdout>"
++#line 11745 "ext/date/lib/parse_date.c"
  yy458:
  	YYDEBUG(458, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -13051,7 -13065,7 +13068,7 @@@ yy525
  	}
  yy526:
  	YYDEBUG(526, *YYCURSOR);
- #line 1180 "ext/date/lib/parse_date.re"
 -#line 1194 "ext/date/lib/parse_date.re"
++#line 1197 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("noon");
  		TIMELIB_INIT;
@@@ -13062,7 -13076,7 +13079,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_RELATIVE;
  	}
- #line 13066 "ext/date/lib/parse_date.c"
 -#line 13080 "<stdout>"
++#line 13083 "ext/date/lib/parse_date.c"
  yy527:
  	YYDEBUG(527, *YYCURSOR);
  	yyaccept = 2;
@@@ -14108,7 -14122,7 +14125,7 @@@ yy566
  	}
  yy567:
  	YYDEBUG(567, *YYCURSOR);
- #line 1542 "ext/date/lib/parse_date.re"
 -#line 1556 "ext/date/lib/parse_date.re"
++#line 1559 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("gnudateshort");
@@@ -14121,7 -14135,7 +14138,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_ISO_DATE;
  	}
- #line 14125 "ext/date/lib/parse_date.c"
 -#line 14139 "<stdout>"
++#line 14142 "ext/date/lib/parse_date.c"
  yy568:
  	YYDEBUG(568, *YYCURSOR);
  	yyaccept = 15;
@@@ -14572,7 -14586,7 +14589,7 @@@ yy599
  	}
  yy600:
  	YYDEBUG(600, *YYCURSOR);
- #line 1611 "ext/date/lib/parse_date.re"
 -#line 1625 "ext/date/lib/parse_date.re"
++#line 1628 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("datenodayrev");
@@@ -14585,7 -14599,7 +14602,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_DATE_NO_DAY;
  	}
- #line 14589 "ext/date/lib/parse_date.c"
 -#line 14603 "<stdout>"
++#line 14606 "ext/date/lib/parse_date.c"
  yy601:
  	YYDEBUG(601, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -15960,7 -15974,7 +15977,7 @@@ yy696
  	YYDEBUG(696, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(697, *YYCURSOR);
- #line 1597 "ext/date/lib/parse_date.re"
 -#line 1611 "ext/date/lib/parse_date.re"
++#line 1614 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("datenoday");
@@@ -15973,7 -15987,7 +15990,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_DATE_NO_DAY;
  	}
- #line 15977 "ext/date/lib/parse_date.c"
 -#line 15991 "<stdout>"
++#line 15994 "ext/date/lib/parse_date.c"
  yy698:
  	YYDEBUG(698, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -16534,7 -16548,7 +16551,7 @@@ yy721
  	}
  yy722:
  	YYDEBUG(722, *YYCURSOR);
- #line 1192 "ext/date/lib/parse_date.re"
 -#line 1206 "ext/date/lib/parse_date.re"
++#line 1209 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("midnight | today");
  		TIMELIB_INIT;
@@@ -16543,7 -16557,7 +16560,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_RELATIVE;
  	}
- #line 16547 "ext/date/lib/parse_date.c"
 -#line 16561 "<stdout>"
++#line 16564 "ext/date/lib/parse_date.c"
  yy723:
  	YYDEBUG(723, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -16853,7 -16867,7 +16870,7 @@@ yy738
  	if (yych <= '9') goto yy897;
  yy739:
  	YYDEBUG(739, *YYCURSOR);
- #line 1583 "ext/date/lib/parse_date.re"
 -#line 1597 "ext/date/lib/parse_date.re"
++#line 1600 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("pointed date YY");
@@@ -16866,7 -16880,7 +16883,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_DATE_FULL_POINTED;
  	}
- #line 16870 "ext/date/lib/parse_date.c"
 -#line 16884 "<stdout>"
++#line 16887 "ext/date/lib/parse_date.c"
  yy740:
  	YYDEBUG(740, *YYCURSOR);
  	yyaccept = 15;
@@@ -16978,7 -16992,7 +16995,7 @@@ yy751
  	}
  yy752:
  	YYDEBUG(752, *YYCURSOR);
- #line 1528 "ext/date/lib/parse_date.re"
 -#line 1542 "ext/date/lib/parse_date.re"
++#line 1545 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("gnudateshorter");
@@@ -16991,7 -17005,7 +17008,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_ISO_DATE;
  	}
- #line 16995 "ext/date/lib/parse_date.c"
 -#line 17009 "<stdout>"
++#line 17012 "ext/date/lib/parse_date.c"
  yy753:
  	YYDEBUG(753, *YYCURSOR);
  	yyaccept = 18;
@@@ -17240,7 -17254,7 +17257,7 @@@ yy776
  	}
  yy777:
  	YYDEBUG(777, *YYCURSOR);
- #line 1454 "ext/date/lib/parse_date.re"
 -#line 1468 "ext/date/lib/parse_date.re"
++#line 1471 "ext/date/lib/parse_date.re"
  	{
  		int tz_not_found;
  		DEBUG_OUTPUT("iso8601nocolon");
@@@ -17259,7 -17273,7 +17276,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_ISO_NOCOLON;
  	}
- #line 17263 "ext/date/lib/parse_date.c"
 -#line 17277 "<stdout>"
++#line 17280 "ext/date/lib/parse_date.c"
  yy778:
  	YYDEBUG(778, *YYCURSOR);
  	yyaccept = 19;
@@@ -18487,7 -18501,7 +18504,7 @@@ yy848
  	}
  yy849:
  	YYDEBUG(849, *YYCURSOR);
- #line 1931 "ext/date/lib/parse_date.re"
 -#line 1945 "ext/date/lib/parse_date.re"
++#line 1948 "ext/date/lib/parse_date.re"
  	{
  		int tz_not_found;
  		DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz");
@@@ -18516,7 -18530,7 +18533,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_SHORTDATE_WITH_TIME;
  	}
- #line 18520 "ext/date/lib/parse_date.c"
 -#line 18534 "<stdout>"
++#line 18537 "ext/date/lib/parse_date.c"
  yy850:
  	YYDEBUG(850, *YYCURSOR);
  	yyaccept = 20;
@@@ -19560,7 -19574,7 +19577,7 @@@ yy925
  	}
  yy926:
  	YYDEBUG(926, *YYCURSOR);
- #line 1689 "ext/date/lib/parse_date.re"
 -#line 1703 "ext/date/lib/parse_date.re"
++#line 1706 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("pgydotd");
@@@ -19573,7 -19587,7 +19590,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_PG_YEARDAY;
  	}
- #line 19577 "ext/date/lib/parse_date.c"
 -#line 19591 "<stdout>"
++#line 19594 "ext/date/lib/parse_date.c"
  yy927:
  	YYDEBUG(927, *YYCURSOR);
  	yyaccept = 21;
@@@ -19827,7 -19841,7 +19844,7 @@@ yy941
  	if (yych <= '7') goto yy1059;
  yy942:
  	YYDEBUG(942, *YYCURSOR);
- #line 1722 "ext/date/lib/parse_date.re"
 -#line 1736 "ext/date/lib/parse_date.re"
++#line 1739 "ext/date/lib/parse_date.re"
  	{
  		timelib_sll w, d;
  		DEBUG_OUTPUT("isoweek");
@@@ -19845,7 -19859,7 +19862,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_ISO_WEEK;
  	}
- #line 19849 "ext/date/lib/parse_date.c"
 -#line 19863 "<stdout>"
++#line 19866 "ext/date/lib/parse_date.c"
  yy943:
  	YYDEBUG(943, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -20321,7 -20335,7 +20338,7 @@@ yy981
  	if (yych == 'e') goto yy1094;
  yy982:
  	YYDEBUG(982, *YYCURSOR);
- #line 1865 "ext/date/lib/parse_date.re"
 -#line 1879 "ext/date/lib/parse_date.re"
++#line 1882 "ext/date/lib/parse_date.re"
  	{
  		timelib_sll i;
  		int         behavior = 0;
@@@ -20337,7 -20351,7 +20354,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_RELATIVE;
  	}
- #line 20341 "ext/date/lib/parse_date.c"
 -#line 20355 "<stdout>"
++#line 20358 "ext/date/lib/parse_date.c"
  yy983:
  	YYDEBUG(983, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -20684,7 -20698,7 +20701,7 @@@ yy1020
  	YYDEBUG(1020, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(1021, *YYCURSOR);
- #line 1571 "ext/date/lib/parse_date.re"
 -#line 1585 "ext/date/lib/parse_date.re"
++#line 1588 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("pointed date YYYY");
  		TIMELIB_INIT;
@@@ -20695,7 -20709,7 +20712,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_DATE_FULL_POINTED;
  	}
- #line 20699 "ext/date/lib/parse_date.c"
 -#line 20713 "<stdout>"
++#line 20716 "ext/date/lib/parse_date.c"
  yy1022:
  	YYDEBUG(1022, *YYCURSOR);
  	++YYCURSOR;
@@@ -20724,7 -20738,7 +20741,7 @@@ yy1024
  	}
  yy1025:
  	YYDEBUG(1025, *YYCURSOR);
- #line 1502 "ext/date/lib/parse_date.re"
 -#line 1516 "ext/date/lib/parse_date.re"
++#line 1519 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("iso8601date2");
@@@ -20737,7 -20751,7 +20754,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_ISO_DATE;
  	}
- #line 20741 "ext/date/lib/parse_date.c"
 -#line 20755 "<stdout>"
++#line 20758 "ext/date/lib/parse_date.c"
  yy1026:
  	YYDEBUG(1026, *YYCURSOR);
  	yyaccept = 15;
@@@ -20957,7 -20971,7 +20974,7 @@@ yy1042
  	}
  yy1043:
  	YYDEBUG(1043, *YYCURSOR);
- #line 1490 "ext/date/lib/parse_date.re"
 -#line 1504 "ext/date/lib/parse_date.re"
++#line 1507 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash");
  		TIMELIB_INIT;
@@@ -20968,7 -20982,7 +20985,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_ISO_DATE;
  	}
- #line 20972 "ext/date/lib/parse_date.c"
 -#line 20986 "<stdout>"
++#line 20989 "ext/date/lib/parse_date.c"
  yy1044:
  	YYDEBUG(1044, *YYCURSOR);
  	yyaccept = 26;
@@@ -21083,7 -21097,7 +21100,7 @@@ yy1047
  	}
  yy1048:
  	YYDEBUG(1048, *YYCURSOR);
- #line 1651 "ext/date/lib/parse_date.re"
 -#line 1665 "ext/date/lib/parse_date.re"
++#line 1668 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("datenocolon");
  		TIMELIB_INIT;
@@@ -21094,7 -21108,7 +21111,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_DATE_NOCOLON;
  	}
- #line 21098 "ext/date/lib/parse_date.c"
 -#line 21112 "<stdout>"
++#line 21115 "ext/date/lib/parse_date.c"
  yy1049:
  	YYDEBUG(1049, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -21164,7 -21178,7 +21181,7 @@@ yy1059
  	YYDEBUG(1059, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(1060, *YYCURSOR);
- #line 1703 "ext/date/lib/parse_date.re"
 -#line 1717 "ext/date/lib/parse_date.re"
++#line 1720 "ext/date/lib/parse_date.re"
  	{
  		timelib_sll w, d;
  		DEBUG_OUTPUT("isoweekday");
@@@ -21182,7 -21196,7 +21199,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_ISO_WEEK;
  	}
- #line 21186 "ext/date/lib/parse_date.c"
 -#line 21200 "<stdout>"
++#line 21203 "ext/date/lib/parse_date.c"
  yy1061:
  	YYDEBUG(1061, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -21245,7 -21259,7 +21262,7 @@@ yy1069
  	if (yych <= '9') goto yy1143;
  yy1070:
  	YYDEBUG(1070, *YYCURSOR);
- #line 1741 "ext/date/lib/parse_date.re"
 -#line 1755 "ext/date/lib/parse_date.re"
++#line 1758 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("pgtextshort");
@@@ -21258,7 -21272,7 +21275,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_PG_TEXT;
  	}
- #line 21262 "ext/date/lib/parse_date.c"
 -#line 21276 "<stdout>"
++#line 21279 "ext/date/lib/parse_date.c"
  yy1071:
  	YYDEBUG(1071, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -21731,7 -21745,7 +21748,7 @@@ yy1106
  	++YYCURSOR;
  yy1107:
  	YYDEBUG(1107, *YYCURSOR);
- #line 1202 "ext/date/lib/parse_date.re"
 -#line 1216 "ext/date/lib/parse_date.re"
++#line 1219 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("tomorrow");
  		TIMELIB_INIT;
@@@ -21742,7 -21756,7 +21759,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_RELATIVE;
  	}
- #line 21746 "ext/date/lib/parse_date.c"
 -#line 21760 "<stdout>"
++#line 21763 "ext/date/lib/parse_date.c"
  yy1108:
  	YYDEBUG(1108, *YYCURSOR);
  	yyaccept = 28;
@@@ -22079,7 -22093,7 +22096,7 @@@ yy1140
  	YYDEBUG(1140, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(1141, *YYCURSOR);
- #line 1755 "ext/date/lib/parse_date.re"
 -#line 1769 "ext/date/lib/parse_date.re"
++#line 1772 "ext/date/lib/parse_date.re"
  	{
  		int length = 0;
  		DEBUG_OUTPUT("pgtextreverse");
@@@ -22092,7 -22106,7 +22109,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_PG_TEXT;
  	}
- #line 22096 "ext/date/lib/parse_date.c"
 -#line 22110 "<stdout>"
++#line 22113 "ext/date/lib/parse_date.c"
  yy1142:
  	YYDEBUG(1142, *YYCURSOR);
  	++YYCURSOR;
@@@ -22136,7 -22150,7 +22153,7 @@@ yy1144
  	}
  yy1145:
  	YYDEBUG(1145, *YYCURSOR);
- #line 1297 "ext/date/lib/parse_date.re"
 -#line 1311 "ext/date/lib/parse_date.re"
++#line 1314 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("backof | frontof");
  		TIMELIB_INIT;
@@@ -22158,7 -22172,7 +22175,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_LF_DAY_OF_MONTH;
  	}
- #line 22162 "ext/date/lib/parse_date.c"
 -#line 22176 "<stdout>"
++#line 22179 "ext/date/lib/parse_date.c"
  yy1146:
  	YYDEBUG(1146, *YYCURSOR);
  	yyaccept = 29;
@@@ -22482,7 -22496,7 +22499,7 @@@ yy1171
  	}
  yy1172:
  	YYDEBUG(1172, *YYCURSOR);
- #line 1841 "ext/date/lib/parse_date.re"
 -#line 1855 "ext/date/lib/parse_date.re"
++#line 1858 "ext/date/lib/parse_date.re"
  	{
  		timelib_sll i;
  		int         behavior = 0;
@@@ -22505,7 -22519,7 +22522,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_RELATIVE;
  	}
- #line 22509 "ext/date/lib/parse_date.c"
 -#line 22523 "<stdout>"
++#line 22526 "ext/date/lib/parse_date.c"
  yy1173:
  	YYDEBUG(1173, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -22517,7 -22531,7 +22534,7 @@@ yy1174
  	++YYCURSOR;
  yy1175:
  	YYDEBUG(1175, *YYCURSOR);
- #line 1159 "ext/date/lib/parse_date.re"
 -#line 1173 "ext/date/lib/parse_date.re"
++#line 1176 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("yesterday");
  		TIMELIB_INIT;
@@@ -22528,7 -22542,7 +22545,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_RELATIVE;
  	}
- #line 22532 "ext/date/lib/parse_date.c"
 -#line 22546 "<stdout>"
++#line 22549 "ext/date/lib/parse_date.c"
  yy1176:
  	YYDEBUG(1176, *YYCURSOR);
  	yyaccept = 31;
@@@ -23021,7 -23035,7 +23038,7 @@@ yy1222
  	YYDEBUG(1222, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(1223, *YYCURSOR);
- #line 1907 "ext/date/lib/parse_date.re"
 -#line 1921 "ext/date/lib/parse_date.re"
++#line 1924 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12");
  		TIMELIB_INIT;
@@@ -23044,7 -23058,7 +23061,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_SHORTDATE_WITH_TIME;
  	}
- #line 23048 "ext/date/lib/parse_date.c"
 -#line 23062 "<stdout>"
++#line 23065 "ext/date/lib/parse_date.c"
  yy1224:
  	YYDEBUG(1224, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -23546,7 -23560,7 +23563,7 @@@ yy1268
  	YYDEBUG(1268, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(1269, *YYCURSOR);
- #line 1320 "ext/date/lib/parse_date.re"
 -#line 1334 "ext/date/lib/parse_date.re"
++#line 1337 "ext/date/lib/parse_date.re"
  	{
  		timelib_sll i;
  		int         behavior = 0;
@@@ -23567,7 -23581,7 +23584,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_WEEK_DAY_OF_MONTH;
  	}
- #line 23571 "ext/date/lib/parse_date.c"
 -#line 23585 "<stdout>"
++#line 23588 "ext/date/lib/parse_date.c"
  yy1270:
  	YYDEBUG(1270, *YYCURSOR);
  	yyaccept = 24;
@@@ -23614,7 -23628,7 +23631,7 @@@ yy1273
  	YYDEBUG(1273, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(1274, *YYCURSOR);
- #line 1280 "ext/date/lib/parse_date.re"
 -#line 1294 "ext/date/lib/parse_date.re"
++#line 1297 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("firstdayof | lastdayof");
  		TIMELIB_INIT;
@@@ -23630,12 -23644,12 +23647,12 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_LF_DAY_OF_MONTH;
  	}
- #line 23634 "ext/date/lib/parse_date.c"
 -#line 23648 "<stdout>"
++#line 23651 "ext/date/lib/parse_date.c"
  yy1275:
  	YYDEBUG(1275, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(1276, *YYCURSOR);
- #line 1516 "ext/date/lib/parse_date.re"
 -#line 1530 "ext/date/lib/parse_date.re"
++#line 1533 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("iso8601datex");
  		TIMELIB_INIT;
@@@ -23646,7 -23660,7 +23663,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_ISO_DATE;
  	}
- #line 23650 "ext/date/lib/parse_date.c"
 -#line 23664 "<stdout>"
++#line 23667 "ext/date/lib/parse_date.c"
  yy1277:
  	YYDEBUG(1277, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -23749,7 -23763,7 +23766,7 @@@ yy1290
  	YYDEBUG(1290, *YYCURSOR);
  	++YYCURSOR;
  	YYDEBUG(1291, *YYCURSOR);
- #line 1360 "ext/date/lib/parse_date.re"
 -#line 1374 "ext/date/lib/parse_date.re"
++#line 1377 "ext/date/lib/parse_date.re"
  	{
  		DEBUG_OUTPUT("mssqltime");
  		TIMELIB_INIT;
@@@ -23768,7 -23782,7 +23785,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_TIME24_WITH_ZONE;
  	}
- #line 23772 "ext/date/lib/parse_date.c"
 -#line 23786 "<stdout>"
++#line 23789 "ext/date/lib/parse_date.c"
  yy1292:
  	YYDEBUG(1292, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -24192,7 -24206,7 +24209,7 @@@ yy1328
  	if (yych <= '9') goto yy1331;
  yy1329:
  	YYDEBUG(1329, *YYCURSOR);
- #line 1663 "ext/date/lib/parse_date.re"
 -#line 1677 "ext/date/lib/parse_date.re"
++#line 1680 "ext/date/lib/parse_date.re"
  	{
  		int tz_not_found;
  		DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif");
@@@ -24217,7 -24231,7 +24234,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_XMLRPC_SOAP;
  	}
- #line 24221 "ext/date/lib/parse_date.c"
 -#line 24235 "<stdout>"
++#line 24238 "ext/date/lib/parse_date.c"
  yy1330:
  	YYDEBUG(1330, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -24587,7 -24601,7 +24604,7 @@@ yy1374
  	if (yych <= ':') goto yy1383;
  yy1375:
  	YYDEBUG(1375, *YYCURSOR);
- #line 1769 "ext/date/lib/parse_date.re"
 -#line 1783 "ext/date/lib/parse_date.re"
++#line 1786 "ext/date/lib/parse_date.re"
  	{
  		int tz_not_found;
  		DEBUG_OUTPUT("clf");
@@@ -24610,7 -24624,7 +24627,7 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_CLF;
  	}
- #line 24614 "ext/date/lib/parse_date.c"
 -#line 24628 "<stdout>"
++#line 24631 "ext/date/lib/parse_date.c"
  yy1376:
  	YYDEBUG(1376, *YYCURSOR);
  	yyaccept = 33;
@@@ -24842,7 -24856,7 +24859,7 @@@ yy1406
  	if (yych == ':') goto yy1286;
  	goto yy1329;
  }
- #line 1997 "ext/date/lib/parse_date.re"
 -#line 2011 "ext/date/lib/parse_date.re"
++#line 2014 "ext/date/lib/parse_date.re"

  }

diff --cc ext/date/lib/parse_iso_intervals.c
index bfa38e42aa8,93a5b227afb..bbc9cd2337f
--- a/ext/date/lib/parse_iso_intervals.c
+++ b/ext/date/lib/parse_iso_intervals.c
@@@ -1,4 -1,4 +1,4 @@@
- /* Generated by re2c 1.0.3 on Mon Sep 15 10:38:16 2025 */
 -/* Generated by re2c 1.0.3 on Tue Sep 15 16:40:58 2026 */
++/* Generated by re2c 1.0.3 on Wed Sep 30 15:20:07 2026 */
  #line 1 "ext/date/lib/parse_iso_intervals.re"
  /*
   * The MIT License (MIT)
@@@ -180,7 -185,7 +185,7 @@@ std



- #line 184 "ext/date/lib/parse_iso_intervals.c"
 -#line 189 "<stdout>"
++#line 189 "ext/date/lib/parse_iso_intervals.c"
  {
  	YYCTYPE yych;
  	unsigned int yyaccept = 0;
@@@ -252,7 -257,7 +257,7 @@@ yy2
  		s->pos = cursor; s->line++;
  		goto std;
  	}
- #line 256 "ext/date/lib/parse_iso_intervals.c"
 -#line 261 "<stdout>"
++#line 261 "ext/date/lib/parse_iso_intervals.c"
  yy4:
  	YYDEBUG(4, *YYCURSOR);
  	++YYCURSOR;
@@@ -263,7 -268,7 +268,7 @@@ yy5
  		add_error(s, "Unexpected character");
  		goto std;
  	}
- #line 267 "ext/date/lib/parse_iso_intervals.c"
 -#line 272 "<stdout>"
++#line 272 "ext/date/lib/parse_iso_intervals.c"
  yy6:
  	YYDEBUG(6, *YYCURSOR);
  	++YYCURSOR;
@@@ -272,7 -277,7 +277,7 @@@
  	{
  		goto std;
  	}
- #line 276 "ext/date/lib/parse_iso_intervals.c"
 -#line 281 "<stdout>"
++#line 281 "ext/date/lib/parse_iso_intervals.c"
  yy8:
  	YYDEBUG(8, *YYCURSOR);
  	yyaccept = 0;
@@@ -330,7 -335,7 +335,7 @@@ yy10
  		TIMELIB_DEINIT;
  		return TIMELIB_PERIOD;
  	}
- #line 334 "ext/date/lib/parse_iso_intervals.c"
 -#line 339 "<stdout>"
++#line 339 "ext/date/lib/parse_iso_intervals.c"
  yy11:
  	YYDEBUG(11, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -399,7 -404,7 +404,7 @@@ yy16
  		s->have_recurrences = 1;
  		return TIMELIB_PERIOD;
  	}
- #line 403 "ext/date/lib/parse_iso_intervals.c"
 -#line 408 "<stdout>"
++#line 408 "ext/date/lib/parse_iso_intervals.c"
  yy19:
  	YYDEBUG(19, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -917,7 -922,7 +922,7 @@@ yy89
  		TIMELIB_DEINIT;
  		return TIMELIB_ISO_DATE;
  	}
- #line 921 "ext/date/lib/parse_iso_intervals.c"
 -#line 926 "<stdout>"
++#line 926 "ext/date/lib/parse_iso_intervals.c"
  yy91:
  	YYDEBUG(91, *YYCURSOR);
  	yych = *++YYCURSOR;
@@@ -956,9 -961,9 +961,9 @@@
  		TIMELIB_DEINIT;
  		return TIMELIB_PERIOD;
  	}
- #line 960 "ext/date/lib/parse_iso_intervals.c"
 -#line 965 "<stdout>"
++#line 965 "ext/date/lib/parse_iso_intervals.c"
  }
- #line 321 "ext/date/lib/parse_iso_intervals.re"
+ #line 326 "ext/date/lib/parse_iso_intervals.re"

  }
  #ifdef PHP_WIN32
diff --cc ext/date/tests/oom_dateinterval_construct.phpt
index 00000000000,1ff6b61f8a1..203a275c280
mode 000000,100644..100644
--- a/ext/date/tests/oom_dateinterval_construct.phpt
+++ b/ext/date/tests/oom_dateinterval_construct.phpt
@@@ -1,0 -1,18 +1,18 @@@
+ --TEST--
+ DateInterval::__construct() doesn't reach hard limit
+ --INI--
+ memory_limit=64M
+ --SKIPIF--
+ <?php
+ if (getenv('SKIP_ASAN')) die("skip ASan-instrumented realloc() is slow");
+ ?>
+ --FILE--
+ <?php
+ try {
+     new DateInterval(str_repeat("\x01", 4 * 1024 * 1024));
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), "\n";
+ }
+ ?>
+ --EXPECTF--
 -Exception: Unknown or bad format (%s)
++DateMalformedIntervalStringException: Unknown or bad format (%s)
diff --cc ext/date/tests/oom_dateinterval_createfromdatestring.phpt
index 00000000000,58087cfc30c..cf8785ecb91
mode 000000,100644..100644
--- a/ext/date/tests/oom_dateinterval_createfromdatestring.phpt
+++ b/ext/date/tests/oom_dateinterval_createfromdatestring.phpt
@@@ -1,0 -1,11 +1,14 @@@
+ --TEST--
+ DateInterval::createFromDateString() doesn't reach hard limit
+ --INI--
+ memory_limit=64M
+ --FILE--
+ <?php
 -var_dump(DateInterval::createFromDateString(str_repeat("\x01", 4 * 1024 * 1024)));
++try {
++    var_dump(DateInterval::createFromDateString(str_repeat("\x01", 4 * 1024 * 1024)));
++} catch (\Throwable $e) {
++    echo $e::class, ': ', $e->getMessage(), "\n";
++}
+ ?>
+ --EXPECTF--
 -Warning: DateInterval::createFromDateString(): Unknown or bad format (%s) at position 0 (%s): Unexpected character in %s on line %d
 -bool(false)
++DateMalformedIntervalStringException: Unknown or bad format (%s) at position 0 (%s): Unexpected character
diff --cc ext/date/tests/oom_dateperiod_construct.phpt
index 00000000000,77d6ea8a9b8..e7eda58d756
mode 000000,100644..100644
--- a/ext/date/tests/oom_dateperiod_construct.phpt
+++ b/ext/date/tests/oom_dateperiod_construct.phpt
@@@ -1,0 -1,18 +1,18 @@@
+ --TEST--
+ DatePeriod::__construct() doesn't reach hard limit
+ --INI--
+ memory_limit=64M
+ --SKIPIF--
+ <?php
+ if (getenv('SKIP_ASAN')) die("skip ASan-instrumented realloc() is slow");
+ ?>
+ --FILE--
+ <?php
+ try {
+     new DatePeriod(str_repeat("\x01", 4 * 1024 * 1024));
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), "\n";
+ }
+ ?>
+ --EXPECTF--
 -Exception: Unknown or bad format (%s)
++DateMalformedPeriodStringException: Unknown or bad format (%s)
diff --cc ext/date/tests/oom_datetime_construct.phpt
index 00000000000,2eec0755dc8..917b0f6d325
mode 000000,100644..100644
--- a/ext/date/tests/oom_datetime_construct.phpt
+++ b/ext/date/tests/oom_datetime_construct.phpt
@@@ -1,0 -1,14 +1,14 @@@
+ --TEST--
+ DateTime::__construct() doesn't reach hard limit
+ --INI--
+ memory_limit=64M
+ --FILE--
+ <?php
+ try {
+     new DateTime(str_repeat("\x01", 4 * 1024 * 1024));
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), "\n";
+ }
+ ?>
+ --EXPECTF--
 -Exception: Failed to parse time string (%s) at position 0 (%s): Unexpected character
++DateMalformedStringException: Failed to parse time string (%s) at position 0 (%s): Unexpected character
diff --cc ext/date/tests/oom_datetime_modify.phpt
index 00000000000,4e7d9628850..47cdb269b46
mode 000000,100644..100644
--- a/ext/date/tests/oom_datetime_modify.phpt
+++ b/ext/date/tests/oom_datetime_modify.phpt
@@@ -1,0 -1,11 +1,14 @@@
+ --TEST--
+ DateTime::modify() doesn't reach hard limit
+ --INI--
+ memory_limit=64M
+ --FILE--
+ <?php
 -var_dump((new DateTime())->modify(str_repeat("\x01", 4 * 1024 * 1024)));
++try {
++    var_dump((new DateTime())->modify(str_repeat("\x01", 4 * 1024 * 1024)));
++} catch (\Throwable $e) {
++    echo $e::class, ': ', $e->getMessage(), "\n";
++}
+ ?>
+ --EXPECTF--
 -Warning: DateTime::modify(): Failed to parse time string (%s) at position 0 (%s): Unexpected character in %s on line %d
 -bool(false)
++DateMalformedStringException: DateTime::modify(): Failed to parse time string (%s) at position 0 (%s): Unexpected character
diff --cc ext/date/tests/oom_datetimeimmutable_construct.phpt
index 00000000000,e1d8d7dcddb..1266e53279e
mode 000000,100644..100644
--- a/ext/date/tests/oom_datetimeimmutable_construct.phpt
+++ b/ext/date/tests/oom_datetimeimmutable_construct.phpt
@@@ -1,0 -1,14 +1,14 @@@
+ --TEST--
+ DateTimeImmutable::__construct() doesn't reach hard limit
+ --INI--
+ memory_limit=64M
+ --FILE--
+ <?php
+ try {
+     new DateTimeImmutable(str_repeat("\x01", 4 * 1024 * 1024));
+ } catch (\Throwable $e) {
+     echo $e::class, ': ', $e->getMessage(), "\n";
+ }
+ ?>
+ --EXPECTF--
 -Exception: Failed to parse time string (%s) at position 0 (%s): Unexpected character
++DateMalformedStringException: Failed to parse time string (%s) at position 0 (%s): Unexpected character
diff --cc ext/date/tests/oom_datetimeimmutable_modify.phpt
index 00000000000,932e5ae7582..f846789c23e
mode 000000,100644..100644
--- a/ext/date/tests/oom_datetimeimmutable_modify.phpt
+++ b/ext/date/tests/oom_datetimeimmutable_modify.phpt
@@@ -1,0 -1,11 +1,14 @@@
+ --TEST--
+ DateTimeImmutable::modify() doesn't reach hard limit
+ --INI--
+ memory_limit=64M
+ --FILE--
+ <?php
 -var_dump((new DateTimeImmutable())->modify(str_repeat("\x01", 4 * 1024 * 1024)));
++try {
++    var_dump((new DateTimeImmutable())->modify(str_repeat("\x01", 4 * 1024 * 1024)));
++} catch (\Throwable $e) {
++    echo $e::class, ': ', $e->getMessage(), "\n";
++}
+ ?>
+ --EXPECTF--
 -Warning: DateTimeImmutable::modify(): Failed to parse time string (%s) at position 0 (%s): Unexpected character in %s on line %d
 -bool(false)
++DateMalformedStringException: DateTimeImmutable::modify(): Failed to parse time string (%s) at position 0 (%s): Unexpected character
diff --cc ext/exif/exif.c
index e6be4a1338d,7944988d097..571e3234c84
--- a/ext/exif/exif.c
+++ b/ext/exif/exif.c
@@@ -3789,7 -3850,11 +3834,11 @@@ static bool exif_scan_JPEG_header(image
  	size_t fpos, size, got, itemlen;
  	jpeg_sof_info sof_info;

 -	for(section=0;;section++) {
 +	while (true) {
+ 		/* Every section is retained, so stop once over the limit. */
+ 		if (ZEND_HAS_OOM()) {
+ 			return false;
+ 		}
  #ifdef EXIF_DEBUG
  		fpos = php_stream_tell(ImageInfo->infile);
  		exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Needing section %d @ 0x%08X", ImageInfo->file.count, fpos);
diff --cc ext/fileinfo/libmagic.patch
index 8fb5efbcac0,5920bec5e3b..efed868b885
--- a/ext/fileinfo/libmagic.patch
+++ b/ext/fileinfo/libmagic.patch
@@@ -1,19 -1,68 +1,19 @@@
  diff -u libmagic.orig/apprentice.c libmagic/apprentice.c
 ---- libmagic.orig/apprentice.c	2021-02-23 01:51:11.000000000 +0100
 -+++ libmagic/apprentice.c	2023-12-09 11:51:31.700896278 +0100
 -@@ -29,6 +29,8 @@
 -  * apprentice - make one pass through /etc/magic, learning its secrets.
 -  */
 -
 -+#include "php.h"
 -+
 - #include "file.h"
 -
 - #ifndef	lint
 -@@ -37,19 +39,33 @@
 -
 - #include "magic.h"
 - #include <stdlib.h>
 -+
 -+#if defined(__hpux) && !defined(HAVE_STRTOULL)
 -+#if SIZEOF_LONG == 8
 -+# define strtoull strtoul
 -+#else
 -+# define strtoull __strtoull
 -+#endif
 -+#endif
 -+
 -+#ifdef PHP_WIN32
 -+#include "win32/unistd.h"
 -+#define strtoull _strtoui64
 -+#else
 - #ifdef HAVE_UNISTD_H
 - #include <unistd.h>
 +--- libmagic.orig/apprentice.c	2022-09-13 20:46:07.000000000 +0200
- +++ libmagic/apprentice.c	2023-10-25 17:56:17.066628832 +0200
+++++ libmagic/apprentice.c	2026-09-30 14:44:16.056910654 +0200
 +@@ -48,7 +48,9 @@
 + #ifdef QUICK
 + #include <sys/mman.h>
   #endif
 --#include <stddef.h>
 ++#ifdef HAVE_DIRENT_H
 + #include <dirent.h>
  +#endif
 - #include <string.h>
 - #include <assert.h>
 - #include <ctype.h>
 - #include <fcntl.h>
 --#ifdef QUICK
 --#include <sys/mman.h>
 -+
 -+#ifndef SSIZE_MAX
 -+#define MAXMAGIC_SIZE        ((ssize_t)0x7fffffff)
 -+#else
 -+#define MAXMAGIC_SIZE        SSIZE_MAX
 - #endif
 --#include <dirent.h>
 --#include <limits.h>
 -
 -
 - #define	EATAB {while (isascii(CAST(unsigned char, *l)) && \
 -@@ -66,6 +82,10 @@
 - #endif
 + #include <limits.h>
 + #ifdef HAVE_BYTESWAP_H
 + #include <byteswap.h>
 +@@ -147,10 +149,7 @@
   #endif

 -+#ifndef offsetof
 -+#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD))
 -+#endif
 -+
 - #ifndef MAP_FAILED
 - #define MAP_FAILED (void *) -1
 - #endif
 -@@ -128,10 +148,7 @@
 - private uint32_t swap4(uint32_t);
 - private uint64_t swap8(uint64_t);
   private char *mkdbname(struct magic_set *, const char *, int);
  -private struct magic_map *apprentice_buf(struct magic_set *, struct magic *,
  -    size_t);
@@@ -22,16 -71,47 +22,22 @@@
   private void apprentice_unmap(struct magic_map *);
   private int apprentice_compile(struct magic_set *, struct magic_map *,
       const char *);
- @@ -186,6 +185,8 @@
 -@@ -167,38 +184,7 @@
++@@ -186,6 +185,14 @@
   	{ NULL, 0, NULL }
   };

 --#ifdef COMPILE_ONLY
 --
 --int main(int, char *[]);
 --
 --int
 --main(int argc, char *argv[])
 --{
 --	int ret;
 --	struct magic_set *ms;
 --	char *progname;
 --
 --	if ((progname = strrchr(argv[0], '/')) != NULL)
 --		progname++;
 --	else
 --		progname = argv[0];
 --
 --	if (argc != 2) {
 --		(void)fprintf(stderr, "Usage: %s file\n", progname);
 --		return 1;
 --	}
 --
 --	if ((ms = magic_open(MAGIC_CHECK)) == NULL) {
 --		(void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
 --		return 1;
 --	}
 --	ret = magic_compile(ms, argv[1]) == -1 ? 1 : 0;
 --	if (ret == 1)
 --		(void)fprintf(stderr, "%s: %s\n", progname, magic_error(ms));
 --	magic_close(ms);
 --	return ret;
 --}
 --#endif /* COMPILE_ONLY */
+++#if __has_attribute(nonstring)
+++# define ZEND_NONSTRING __attribute__((nonstring))
+++#else
+++# define ZEND_NONSTRING
+++#endif
+++
  +#include "../data_file.c"
 ++
 + #ifdef COMPILE_ONLY

 - struct type_tbl_s {
 - 	const char name[16];
 -@@ -417,7 +403,7 @@
 + int main(int, char *[]);
- @@ -446,21 +447,13 @@
++@@ -446,21 +453,13 @@
   	struct mlist *ml;

   	mlp->map = NULL;
@@@ -40,22 -120,7 +46,22 @@@
   		return -1;

   	ml->map = idx == 0 ? map : NULL;
 -@@ -502,10 +488,16 @@
 + 	ml->magic = map->magic[idx];
 + 	ml->nmagic = map->nmagic[idx];
 +-	if (ml->nmagic) {
 +-		ml->magic_rxcomp = CAST(file_regex_t **,
 +-		    calloc(ml->nmagic, sizeof(*ml->magic_rxcomp)));
 +-		if (ml->magic_rxcomp == NULL) {
 +-			free(ml);
 +-			return -1;
 +-		}
 +-	} else
 +-		ml->magic_rxcomp = NULL;
 ++
 + 	mlp->prev->next = ml;
 + 	ml->prev = mlp->prev;
 + 	ml->next = mlp;
- @@ -539,13 +532,19 @@
++@@ -539,13 +538,19 @@
   		return;
   	for (i = 0; i < MAGIC_SETS; i++)
   		mlist_free(ms->mlist[i]);
@@@ -79,7 -141,7 +85,7 @@@
   }

   protected struct magic_set *
- @@ -554,7 +553,7 @@
 -@@ -514,7 +506,7 @@
++@@ -554,7 +559,7 @@
   	struct magic_set *ms;
   	size_t i, len;

@@@ -88,7 -150,7 +94,7 @@@
   	    sizeof(struct magic_set)))) == NULL)
   		return NULL;

- @@ -567,7 +566,7 @@
 -@@ -527,7 +519,7 @@
++@@ -567,7 +572,7 @@
   	ms->o.blen = 0;
   	len = (ms->c.len = 10) * sizeof(*ms->c.li);

@@@ -97,7 -159,15 +103,7 @@@
   		goto free;

   	ms->event_flags = 0;
- @@ -597,42 +596,28 @@
 -@@ -546,48 +538,35 @@
 - 	ms->encoding_max = FILE_ENCODING_MAX;
 - 	return ms;
 - free:
 --	free(ms);
 -+	efree(ms);
 - 	return NULL;
 - }
 -
++@@ -597,42 +602,28 @@
   private void
   apprentice_unmap(struct magic_map *map)
   {
@@@ -152,30 -221,16 +158,30 @@@
   		return NULL;
   	}
   	mlist->next = mlist->prev = mlist;
- @@ -653,21 +638,9 @@
 -@@ -610,7 +589,7 @@
++@@ -653,21 +644,9 @@
 + private void
 + mlist_free_one(struct mlist *ml)
   {
 +-	size_t i;
 +-
   	if (ml->map)
   		apprentice_unmap(CAST(struct magic_map *, ml->map));
 +-
 +-	for (i = 0; i < ml->nmagic; ++i) {
 +-		if (ml->magic_rxcomp[i]) {
 +-			file_regfree(ml->magic_rxcomp[i]);
 +-			free(ml->magic_rxcomp[i]);
 +-			ml->magic_rxcomp[i] = NULL;
 +-		}
 +-	}
 +-	free(ml->magic_rxcomp);
 +-	ml->magic_rxcomp = NULL;
  -	free(ml);
  +	efree(ml);
   }

   private void
- @@ -686,51 +659,6 @@
 -@@ -629,51 +608,6 @@
++@@ -686,51 +665,6 @@
   	mlist_free_one(mlist);
   }

@@@ -227,7 -282,7 +233,7 @@@
   /* const char *fn: list of magic files and directories */
   protected int
   file_apprentice(struct magic_set *ms, const char *fn, int action)
- @@ -741,12 +669,28 @@
 -@@ -684,12 +618,28 @@
++@@ -741,12 +675,28 @@

   	(void)file_reset(ms, 0);

@@@ -257,7 -312,7 +263,7 @@@
   		file_oomem(ms, strlen(fn));
   		return -1;
   	}
- @@ -759,7 +703,7 @@
 -@@ -702,7 +652,7 @@
++@@ -759,7 +709,7 @@
   				mlist_free(ms->mlist[j]);
   				ms->mlist[j] = NULL;
   			}
@@@ -266,7 -321,7 +272,7 @@@
   			return -1;
   		}
   	}
- @@ -776,7 +720,7 @@
 -@@ -719,7 +669,7 @@
++@@ -776,7 +726,7 @@
   		fn = p;
   	}

@@@ -275,16 -330,16 +281,16 @@@

   	if (errs == -1) {
   		for (i = 0; i < MAGIC_SETS; i++) {
- @@ -1289,7 +1233,7 @@
 -@@ -1159,7 +1109,7 @@
++@@ -1289,7 +1239,7 @@

 - 		mset[i].max += ALLOC_INCR;
 + 		size_t incr = mset[i].max + ALLOC_INCR;
   		if ((mp = CAST(struct magic_entry *,
 --		    realloc(mset[i].me, sizeof(*mp) * mset[i].max))) ==
 -+		    erealloc(mset[i].me, sizeof(*mp) * mset[i].max))) ==
 +-		    realloc(mset[i].me, sizeof(*mp) * incr))) ==
 ++		    erealloc(mset[i].me, sizeof(*mp) * incr))) ==
   		    NULL) {
 - 			file_oomem(ms, sizeof(*mp) * mset[i].max);
 + 			file_oomem(ms, sizeof(*mp) * incr);
   			return -1;
- @@ -1312,13 +1256,19 @@
 -@@ -1180,13 +1130,19 @@
++@@ -1312,13 +1262,19 @@
   load_1(struct magic_set *ms, int action, const char *fn, int *errs,
      struct magic_entry_set *mset)
   {
@@@ -308,7 -363,7 +314,7 @@@
   		if (errno != ENOENT)
   			file_error(ms, errno, "cannot read magic file `%s'",
   				   fn);
- @@ -1328,8 +1278,7 @@
 -@@ -1196,8 +1152,7 @@
++@@ -1328,8 +1284,7 @@

   	memset(&me, 0, sizeof(me));
   	/* read and parse this file */
@@@ -318,7 -373,16 +324,7 @@@
   		if (len == 0) /* null line, garbage, etc */
   			continue;
   		if (line[len - 1] == '\n') {
- @@ -1388,8 +1337,8 @@
 -@@ -1232,7 +1187,7 @@
 - 					continue;
 - 				}
 - 				if ((*bang[i].fun)(ms, &me,
 --				    line + bang[i].len + 2,
 -+				    line + bang[i].len + 2,
 - 				    len - bang[i].len - 2) != 0) {
 - 					(*errs)++;
 - 					continue;
 -@@ -1256,8 +1211,8 @@
++@@ -1388,8 +1343,8 @@
   	}
   	if (me.mp)
   		(void)addentry(ms, &me, mset);
@@@ -329,8 -393,8 +335,8 @@@
   }

   /*
- @@ -1474,7 +1423,7 @@
 -@@ -1336,7 +1291,7 @@
 - 		mentrycount += me[i].cont_count;
++@@ -1474,7 +1429,7 @@
 + 	}

   	slen = sizeof(**ma) * mentrycount;
  -	if ((*ma = CAST(struct magic *, malloc(slen))) == NULL) {
@@@ -338,7 -402,7 +344,7 @@@
   		file_oomem(ms, slen);
   		return -1;
   	}
- @@ -1496,8 +1445,8 @@
 -@@ -1358,8 +1313,8 @@
++@@ -1496,8 +1451,8 @@
   	if (me == NULL)
   		return;
   	for (i = 0; i < nme; i++)
@@@ -349,7 -413,7 +355,7 @@@
   }

   private struct magic_map *
- @@ -1506,18 +1455,19 @@
 -@@ -1368,18 +1323,19 @@
++@@ -1506,18 +1461,19 @@
   	int errs = 0;
   	uint32_t i, j;
   	size_t files = 0, maxfiles = 0;
@@@ -374,7 -438,7 +380,7 @@@
   	{
   		file_oomem(ms, sizeof(*map));
   		return NULL;
- @@ -1529,52 +1479,50 @@
 -@@ -1391,52 +1347,50 @@
++@@ -1529,52 +1485,50 @@
   		(void)fprintf(stderr, "%s\n", usg_hdr);

   	/* load directory or file */
@@@ -446,7 -510,16 +452,7 @@@
   		}
   	} else
   		load_1(ms, action, fn, &errs, mset);
- @@ -1612,7 +1560,6 @@
 -@@ -1465,7 +1419,7 @@
 - 		/* coalesce per file arrays into a single one, if needed */
 - 		if (mset[j].count == 0)
 - 			continue;
 --
 -+
 - 		if (coalesce_entries(ms, mset[j].me, mset[j].count,
 - 		    &map->magic[j], &map->nmagic[j]) == -1) {
 - 			errs++;
 -@@ -1474,7 +1428,6 @@
++@@ -1612,7 +1566,6 @@
   	}

   out:
@@@ -454,7 -527,7 +460,7 @@@
   	for (j = 0; j < MAGIC_SETS; j++)
   		magic_entry_free(mset[j].me, mset[j].count);

- @@ -2060,7 +2007,7 @@
 -@@ -1910,7 +1863,7 @@
++@@ -2060,7 +2013,7 @@
   		if (me->cont_count == me->max_count) {
   			struct magic *nm;
   			size_t cnt = me->max_count + ALLOC_CHUNK;
@@@ -463,7 -536,7 +469,7 @@@
   			    sizeof(*nm) * cnt))) == NULL) {
   				file_oomem(ms, sizeof(*nm) * cnt);
   				return -1;
- @@ -2075,7 +2022,7 @@
 -@@ -1925,7 +1878,7 @@
++@@ -2075,7 +2028,7 @@
   		static const size_t len = sizeof(*m) * ALLOC_CHUNK;
   		if (me->mp != NULL)
   			return 1;
@@@ -472,7 -545,7 +478,7 @@@
   			file_oomem(ms, len);
   			return -1;
   		}
- @@ -2301,7 +2248,7 @@
 -@@ -2148,7 +2101,7 @@
++@@ -2301,7 +2254,7 @@

   	m->mask_op = 0;
   	if (*l == '~') {
@@@ -481,7 -554,7 +487,7 @@@
   			m->mask_op |= FILE_OPINVERSE;
   		else if (ms->flags & MAGIC_CHECK)
   			file_magwarn(ms, "'~' invalid for string types");
- @@ -2310,7 +2257,7 @@
 -@@ -2157,7 +2110,7 @@
++@@ -2310,7 +2263,7 @@
   	m->str_range = 0;
   	m->str_flags = m->type == FILE_PSTRING ? PSTRING_1_LE : 0;
   	if ((op = get_op(*l)) != -1) {
@@@ -490,7 -563,16 +496,7 @@@
   			int r;

   			if (op != FILE_OPDIVIDE) {
- @@ -2493,8 +2440,7 @@
 -@@ -2277,7 +2230,7 @@
 -  */
 - private int
 - parse_strength(struct magic_set *ms, struct magic_entry *me, const char *line,
 --    size_t len __attribute__((__unused__)))
 -+    size_t len)
 - {
 - 	const char *l = line;
 - 	char *el;
 -@@ -2339,8 +2292,7 @@
++@@ -2493,8 +2446,7 @@

   private int
   parse_extra(struct magic_set *ms, struct magic_entry *me, const char *line,
@@@ -500,7 -582,7 +506,7 @@@
   {
   	size_t i;
   	const char *l = line;
- @@ -2852,13 +2798,19 @@
 -@@ -2705,14 +2657,19 @@
++@@ -2852,13 +2804,19 @@
   			return -1;
   		}
   		if (m->type == FILE_REGEX) {
@@@ -526,7 -609,7 +532,7 @@@
   		}
   		return 0;
   	default:
- @@ -3230,120 +3182,83 @@
 -@@ -3068,120 +3025,83 @@
++@@ -3230,120 +3188,83 @@
   }

   /*
@@@ -578,13 -661,13 +584,13 @@@
   		file_oomem(ms, sizeof(*map));
  -		goto error;
  +		return NULL;
-- 	}
---	map->type = MAP_TYPE_USER;	/* unspecified */
+++	}
  +
  +	if (fn == NULL) {
  +		map->p = (void *)&php_magic_database;
  +		goto internal_loaded;
--+	}
++ 	}
++-	map->type = MAP_TYPE_USER;	/* unspecified */
  +
  +#ifdef PHP_WIN32
  +	/* Don't bother on windows with php_stream_open_wrapper,
@@@ -657,10 -740,10 +663,12 @@@
   		goto error;
   	}
  -#endif
---
++
  -	free(dbname);
  -	return map;
---
+++	php_stream_close(stream);
+++	stream = NULL;
++
  -error:
  -	if (fd != -1)
  -		(void)close(fd);
@@@ -668,7 -751,7 +676,7 @@@
  -	free(dbname);
  -	return rv;
  -}
--
++-
  -private int
  -check_buffer(struct magic_set *ms, struct magic_map *map, const char *dbname)
  -{
@@@ -676,9 -759,9 +684,7 @@@
  -	uint32_t entries, nentries;
  -	uint32_t version;
  -	int i, needsbyteswap;
--+	php_stream_close(stream);
--+	stream = NULL;
--
++-
  -	ptr = CAST(uint32_t *, map->p);
  +internal_loaded:
  +	ptr = (uint32_t *)(void *)map->p;
@@@ -690,7 -773,7 +696,7 @@@
   		}
   		needsbyteswap = 1;
   	} else
- @@ -3353,17 +3268,29 @@
 -@@ -3191,17 +3111,29 @@
++@@ -3353,17 +3274,29 @@
   	else
   		version = ptr[1];
   	if (version != VERSIONNO) {
@@@ -729,7 -812,7 +735,7 @@@
   	}
   	map->magic[0] = CAST(struct magic *, map->p) + 1;
   	nentries = 0;
- @@ -3376,15 +3303,29 @@
 -@@ -3214,15 +3146,29 @@
++@@ -3376,15 +3309,29 @@
   			map->magic[i + 1] = map->magic[i] + map->nmagic[i];
   		nentries += map->nmagic[i];
   	}
@@@ -762,7 -845,7 +768,7 @@@
   }

   /*
- @@ -3395,7 +3336,7 @@
 -@@ -3233,7 +3179,6 @@
++@@ -3395,7 +3342,7 @@
   {
   	static const size_t nm = sizeof(*map->nmagic) * MAGIC_SETS;
   	static const size_t m = sizeof(**map->magic);
@@@ -771,7 -853,14 +777,7 @@@
   	size_t len;
   	char *dbname;
   	int rv = -1;
- @@ -3410,8 +3351,10 @@
 -@@ -3242,14 +3187,17 @@
 - 		struct magic m;
 - 		uint32_t h[2 + MAGIC_SETS];
 - 	} hdr;
 -+	php_stream *stream;
 -
 - 	dbname = mkdbname(ms, fn, 1);
 -
++@@ -3410,8 +3357,10 @@
   	if (dbname == NULL)
   		goto out;

@@@ -784,7 -873,7 +790,7 @@@
   		file_error(ms, errno, "cannot open `%s'", dbname);
   		goto out;
   	}
- @@ -3420,26 +3363,25 @@
 -@@ -3258,26 +3206,25 @@
++@@ -3420,26 +3369,25 @@
   	hdr.h[1] = VERSIONNO;
   	memcpy(hdr.h + 2, map->nmagic, nm);

@@@ -819,7 -908,7 +825,7 @@@
   	return rv;
   }

- @@ -3473,17 +3415,18 @@
 -@@ -3311,17 +3258,18 @@
++@@ -3473,17 +3421,18 @@
   	q++;
   	/* Compatibility with old code that looked in .mime */
   	if (ms->flags & MAGIC_MIME) {
@@@ -845,7 -934,7 +851,7 @@@

   	/* Compatibility with old code that looked in .mime */
   	if (strstr(fn, ".mime") != NULL)
- @@ -3605,7 +3548,7 @@
 -@@ -3411,7 +3359,7 @@
++@@ -3605,7 +3554,7 @@
   	m->offset = swap4(CAST(uint32_t, m->offset));
   	m->in_offset = swap4(CAST(uint32_t, m->in_offset));
   	m->lineno = swap4(CAST(uint32_t, m->lineno));
@@@ -854,17 -943,9 +860,17 @@@
   		m->str_range = swap4(m->str_range);
   		m->str_flags = swap4(m->str_flags);
   	}
- @@ -3702,7 +3645,6 @@
++@@ -3702,7 +3651,6 @@
 + 				continue;
 + 			if (strcmp(ma[i].value.s, name) == 0) {
 + 				v->magic = &ma[i];
 +-				v->magic_rxcomp = &(ml->magic_rxcomp[i]);
 + 				for (j = i + 1; j < ml->nmagic; j++)
 + 				    if (ma[j].cont_level == 0)
 + 					    break;
  diff -u libmagic.orig/ascmagic.c libmagic/ascmagic.c
 ---- libmagic.orig/ascmagic.c	2021-02-23 01:49:06.000000000 +0100
 -+++ libmagic/ascmagic.c	2024-02-11 00:59:23.954358532 +0100
 +--- libmagic.orig/ascmagic.c	2022-09-13 20:40:30.000000000 +0200
 ++++ libmagic/ascmagic.c	2024-02-13 21:14:11.091069093 +0100
  @@ -96,7 +96,7 @@
   		rv = file_ascmagic_with_encoding(ms, &bb,
   		    ubuf, ulen, code, type, text);
@@@ -968,9 -1040,9 +974,9 @@@ diff -u libmagic.orig/buffer.c libmagic
   		goto out;
   	}
  diff -u libmagic.orig/cdf.c libmagic/cdf.c
 ---- libmagic.orig/cdf.c	2021-02-23 01:49:06.000000000 +0100
 -+++ libmagic/cdf.c	2026-10-02 19:29:25.144332062 +0200
 -@@ -43,7 +43,17 @@
 +--- libmagic.orig/cdf.c	2022-09-13 20:34:25.000000000 +0200
- +++ libmagic/cdf.c	2023-10-25 17:56:17.069962152 +0200
+++++ libmagic/cdf.c	2026-10-02 19:34:05.613268663 +0200
 +@@ -43,7 +43,9 @@
   #include <err.h>
   #endif
   #include <stdlib.h>
@@@ -980,55 -1055,62 +986,61 @@@
   #include <string.h>
   #include <time.h>
   #include <ctype.h>
- @@ -91,44 +93,9 @@
-  			    CDF_TOLE8(CAST(uint64_t, x))))
-  #define CDF_GETUINT32(x, y)	cdf_getuint32(x, y)
 -@@ -89,35 +99,39 @@
++@@ -95,39 +97,39 @@
+  #define CDF_REALLOC(p, n) cdf_realloc(__FILE__, __LINE__, (p), (n))
+  #define CDF_CALLOC(n, u) cdf_calloc(__FILE__, __LINE__, (n), (u))

- -#define CDF_MALLOC(n) cdf_malloc(__FILE__, __LINE__, (n))
- -#define CDF_REALLOC(p, n) cdf_realloc(__FILE__, __LINE__, (p), (n))
- -#define CDF_CALLOC(n, u) cdf_calloc(__FILE__, __LINE__, (n), (u))
- -
  -
- -/*ARGSUSED*/
- -static void *
+  /*ARGSUSED*/
+  static void *
  -cdf_malloc(const char *file __attribute__((__unused__)),
  -    size_t line __attribute__((__unused__)), size_t n)
- -{
- -	DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u\n",
- -	    file, line, __func__, n));
+ +cdf_malloc(const char *file,
+ +    size_t line, size_t n)
+  {
+  	DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u\n",
+  	    file, line, __func__, n));
 +-	if (n == 0)
 +-	    n++;
  -	return malloc(n);
- -}
- -
- -/*ARGSUSED*/
- -static void *
+ +	return femalloc(n, /* fallible */ true);
+  }
+
+  /*ARGSUSED*/
+  static void *
  -cdf_realloc(const char *file __attribute__((__unused__)),
  -    size_t line __attribute__((__unused__)), void *p, size_t n)
- -{
- -	DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u\n",
- -	    file, line, __func__, n));
+ +cdf_realloc(const char *file,
+ +    size_t line, void *p, size_t n)
+  {
+  	DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u\n",
+  	    file, line, __func__, n));
  -	return realloc(p, n);
- -}
- -
- -/*ARGSUSED*/
- -static void *
+ +	return safe_ferealloc(p, 1, n, 0, /* fallible */ true);
+  }
+
+  /*ARGSUSED*/
+  static void *
  -cdf_calloc(const char *file __attribute__((__unused__)),
  -    size_t line __attribute__((__unused__)), size_t n, size_t u)
- -{
- -	DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u %"
- -	    SIZE_T_FORMAT "u\n", file, line, __func__, n, u));
+ +cdf_calloc(const char *file,
+ +    size_t line, size_t n, size_t u)
+  {
+  	DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u %"
+  	    SIZE_T_FORMAT "u\n", file, line, __func__, n, u));
 +-	if (n == 0)
 +-	    n++;
  -	return calloc(n, u);
- -}
- +#define CDF_MALLOC(n) emalloc(n)
- +#define CDF_REALLOC(p, n) erealloc(p, n)
- +#define CDF_CALLOC(n, u) ecalloc(n, u)
+ +	void *data = safe_femalloc(n, u, 0, /* fallible */ true);
+ +	if (!data) {
+ +		return NULL;
+ +	}
+ +	memset(data, 0, n * u);
+ +	return data;
+  }

 - /*
 -@@ -314,7 +328,7 @@
 + #if defined(HAVE_BYTESWAP_H)
-  # define _cdf_tole2(x)	bswap_16(x)
- @@ -334,7 +301,7 @@
++@@ -334,7 +336,7 @@
   	scn->sst_len = 0;
   	scn->sst_dirlen = 0;
   	scn->sst_ss = 0;
@@@ -1037,7 -1119,7 +1049,7 @@@
   	scn->sst_tab = NULL;
   	return -1;
   }
- @@ -342,9 +309,11 @@
 -@@ -322,9 +336,11 @@
++@@ -342,9 +344,11 @@
   static size_t
   cdf_check_stream(const cdf_stream_t *sst, const cdf_header_t *h)
   {
@@@ -1049,7 -1131,7 +1061,7 @@@
   	return sst->sst_ss;
   }

- @@ -367,11 +336,11 @@
 -@@ -347,11 +363,11 @@
++@@ -367,11 +371,11 @@
   }

   static ssize_t
@@@ -1063,7 -1145,7 +1075,7 @@@
   		goto out;

   	if (info->i_buf != NULL && info->i_len >= siz) {
- @@ -382,7 +351,10 @@
 -@@ -362,7 +378,10 @@
++@@ -382,7 +386,10 @@
   	if (info->i_fd == -1)
   		goto out;

@@@ -1075,7 -1157,7 +1087,7 @@@
   		return -1;

   	return CAST(ssize_t, len);
- @@ -397,7 +369,7 @@
 -@@ -377,7 +396,7 @@
++@@ -397,7 +404,7 @@
   	char buf[512];

   	(void)memcpy(cdf_bo.s, "\01\02\03\04", 4);
@@@ -1084,7 -1166,7 +1096,7 @@@
   		return -1;
   	cdf_unpack_header(h, buf);
   	cdf_swap_header(h);
- @@ -544,14 +516,14 @@
 -@@ -524,14 +543,14 @@
++@@ -544,14 +551,14 @@
   	}
   out:
   	sat->sat_len = i;
@@@ -1102,7 -1184,7 +1114,7 @@@
   	return -1;
   }

- @@ -719,7 +691,7 @@
 -@@ -699,7 +718,7 @@
++@@ -719,7 +726,7 @@
   		return -1;

   	if ((buf = CAST(char *, CDF_MALLOC(ss))) == NULL) {
@@@ -1111,7 -1193,7 +1123,7 @@@
   		return -1;
   	}

- @@ -742,11 +714,11 @@
 -@@ -722,11 +741,11 @@
++@@ -742,11 +749,11 @@
   	if (NEED_SWAP)
   		for (i = 0; i < dir->dir_len; i++)
   			cdf_swap_dir(&dir->dir_tab[i]);
@@@ -1126,7 -1208,7 +1138,7 @@@
   	errno = EFTYPE;
   	return -1;
   }
- @@ -791,7 +763,7 @@
 -@@ -771,7 +790,7 @@
++@@ -791,7 +798,7 @@
   out:
   	errno = EFTYPE;
   out1:
@@@ -1135,7 -1217,7 +1147,7 @@@
   	return -1;
   }

- @@ -953,7 +925,7 @@
 -@@ -933,7 +952,7 @@
++@@ -953,7 +960,7 @@
   	*maxcount = newcount;
   	return inp;
   out:
@@@ -1144,7 -1226,7 +1156,7 @@@
   	*maxcount = 0;
   	*info = NULL;
   	return NULL;
- @@ -1136,7 +1108,7 @@
 -@@ -1115,7 +1134,7 @@
++@@ -1136,7 +1143,7 @@
   	}
   	return 0;
   out:
@@@ -1153,7 -1235,7 +1165,7 @@@
   	*info = NULL;
   	*count = 0;
   	*maxcount = 0;
- @@ -1428,7 +1400,7 @@
 -@@ -1407,7 +1426,7 @@
++@@ -1428,7 +1435,7 @@
   	cdf_directory_t *d;
   	char name[__arraycount(d->d_name)];
   	cdf_stream_t scn;
@@@ -1162,7 -1244,7 +1174,7 @@@

   	static const char *types[] = { "empty", "user storage",
   	    "user stream", "lockbytes", "property", "root storage" };
- @@ -1470,7 +1442,7 @@
 -@@ -1449,7 +1468,7 @@
++@@ -1470,7 +1477,7 @@
   				break;
   			}
   			cdf_dump_stream(&scn);
@@@ -1171,7 -1253,7 +1183,7 @@@
   			break;
   		default:
   			break;
- @@ -1483,7 +1455,7 @@
 -@@ -1462,7 +1481,7 @@
++@@ -1483,7 +1490,7 @@
   cdf_dump_property_info(const cdf_property_info_t *info, size_t count)
   {
   	cdf_timestamp_t tp;
@@@ -1180,7 -1262,7 +1192,7 @@@
   	char buf[64];
   	size_t i, j;

- @@ -1568,7 +1540,7 @@
 -@@ -1547,7 +1566,7 @@
++@@ -1568,7 +1575,7 @@
   	(void)fprintf(stderr, "Class %s\n", buf);
   	(void)fprintf(stderr, "Count %d\n", ssi.si_count);
   	cdf_dump_property_info(info, count);
@@@ -1189,7 -1271,7 +1201,7 @@@
   }


- @@ -1589,7 +1561,7 @@
 -@@ -1568,7 +1587,7 @@
++@@ -1589,7 +1596,7 @@
   		    cdf_u16tos8(sbuf, ce[i].ce_namlen, ce[i].ce_name),
   		    cdf_ctime(&ts.tv_sec, tbuf));
   	}
diff --cc ext/fileinfo/libmagic/cdf.c
index 71baaad6a8e,cd08c01a398..3187573bfa1
--- a/ext/fileinfo/libmagic/cdf.c
+++ b/ext/fileinfo/libmagic/cdf.c
@@@ -93,19 -95,45 +93,54 @@@ static union
  			    CDF_TOLE8(CAST(uint64_t, x))))
  #define CDF_GETUINT32(x, y)	cdf_getuint32(x, y)

- #define CDF_MALLOC(n) emalloc(n)
- #define CDF_REALLOC(p, n) erealloc(p, n)
- #define CDF_CALLOC(n, u) ecalloc(n, u)
+ #define CDF_MALLOC(n) cdf_malloc(__FILE__, __LINE__, (n))
+ #define CDF_REALLOC(p, n) cdf_realloc(__FILE__, __LINE__, (p), (n))
+ #define CDF_CALLOC(n, u) cdf_calloc(__FILE__, __LINE__, (n), (u))
+
+ /*ARGSUSED*/
+ static void *
+ cdf_malloc(const char *file,
+     size_t line, size_t n)
+ {
+ 	DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u\n",
+ 	    file, line, __func__, n));
+ 	return femalloc(n, /* fallible */ true);
+ }
+
+ /*ARGSUSED*/
+ static void *
+ cdf_realloc(const char *file,
+     size_t line, void *p, size_t n)
+ {
+ 	DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u\n",
+ 	    file, line, __func__, n));
+ 	return safe_ferealloc(p, 1, n, 0, /* fallible */ true);
+ }
+
+ /*ARGSUSED*/
+ static void *
+ cdf_calloc(const char *file,
+     size_t line, size_t n, size_t u)
+ {
+ 	DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u %"
+ 	    SIZE_T_FORMAT "u\n", file, line, __func__, n, u));
+ 	void *data = safe_femalloc(n, u, 0, /* fallible */ true);
+ 	if (!data) {
+ 		return NULL;
+ 	}
+ 	memset(data, 0, n * u);
+ 	return data;
+ }

 +#if defined(HAVE_BYTESWAP_H)
 +# define _cdf_tole2(x)	bswap_16(x)
 +# define _cdf_tole4(x)	bswap_32(x)
 +# define _cdf_tole8(x)	bswap_64(x)
 +#elif defined(HAVE_SYS_BSWAP_H)
 +# define _cdf_tole2(x)	bswap16(x)
 +# define _cdf_tole4(x)	bswap32(x)
 +# define _cdf_tole8(x)	bswap64(x)
 +#else
  /*
   * swap a short
   */
diff --cc ext/gd/libgd/gd.c
index 0bd6e4b587e,1fda19a6b21..4c41ec4fa08
--- a/ext/gd/libgd/gd.c
+++ b/ext/gd/libgd/gd.c
@@@ -3108,11 -3130,7 +3132,11 @@@ int gdImagePaletteToTrueColor(gdImagePt
  		const unsigned int sy = gdImageSY(src);
  		const unsigned int sx = gdImageSX(src);

 -		src->tpixels = (int **) gdFMalloc(sizeof(int *) * sy);
 +		// Note: do not revert back to gdMalloc() below ; reason here,
 +		// due to a bug with a certain memory_limit INI value treshold,
 +		// imagepalettetotruecolor crashes with even unrelated ZendMM allocations.
 +		// See GH-17772 for an use case.
- 		src->tpixels = (int **) gdCalloc(sizeof(int *), sy);
++		src->tpixels = (int **) gdFCalloc(sizeof(int *), sy);
  		if (src->tpixels == NULL) {
  			return 0;
  		}
diff --cc ext/iconv/iconv.c
index ae93e0573e2,5ef5fff8028..cc0bba205b7
--- a/ext/iconv/iconv.c
+++ b/ext/iconv/iconv.c
@@@ -2533,11 -2557,12 +2557,13 @@@ static php_stream_filter_status_t php_i

  		php_stream_bucket_unlink(bucket);

- 		if (php_iconv_stream_filter_append_bucket(self, stream, filter,
- 				buckets_out, bucket->buf, bucket->buflen, &consumed,
- 				php_stream_is_persistent(stream)) != SUCCESS) {
+ 		/* The conversion may grow the input several times over. */
+ 		if (!ZEND_HAS_OOM()
+ 				&& php_iconv_stream_filter_append_bucket(self, stream, filter,
+ 					buckets_out, bucket->buf, bucket->buflen, &consumed,
+ 					php_stream_is_persistent(stream)) != SUCCESS) {
 -			goto out_failure;
 +			php_stream_bucket_delref(bucket);
 +			return PSFS_ERR_FATAL;
  		}

  		php_stream_bucket_delref(bucket);
diff --cc ext/mbstring/libmbfl/mbfl/mbfl_encoding.h
index 2072b848bd5,e7f3129359e..3a4c8009d5b
--- a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h
+++ b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h
@@@ -160,20 -159,34 +160,38 @@@ static inline void mb_convert_buf_init(
  	buf->error_mode = err_mode;
  }

+ static inline zend_result mb_convert_buf_ensure(mb_convert_buf *buf, unsigned char **out, unsigned char **limit, size_t needed)
+ {
+ 	ZEND_ASSERT(*out <= *limit);
+ 	if ((*limit - *out) < needed) {
 -		size_t oldsize = *limit - (unsigned char*)ZSTR_VAL(buf->str);
++		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);
++		zend_string *newstr = safe_ferealloc((buf)->str, 1, newsize, _ZSTR_STRUCT_SIZE(0), /* fallible */ true);
+ 		if (!newstr) {
+ 			/* An earlier growth may have moved the buffer. */
+ 			buf->out = *out;
+ 			buf->limit = *limit;
+ 			return FAILURE;
+ 		}
 -		*out = (unsigned char*)ZSTR_VAL(newstr) + (*out - (unsigned char*)ZSTR_VAL(buf->str));
++		*out = (unsigned char*)ZSTR_VAL(newstr) + (*out - (unsigned char*)ZSTR_VAL((buf)->str));
+ 		*limit = (unsigned char*)ZSTR_VAL(newstr) + newsize;
 -		buf->str = newstr;
++		(buf)->str = newstr;
+ 	}
+ 	return SUCCESS;
++
+ }
+
  #define MB_CONVERT_BUF_ENSURE(buf, out, limit, needed) \
- 	ZEND_ASSERT(out <= limit); \
- 	if ((limit - out) < (needed)) { \
- 		size_t oldsize = limit - (unsigned char*)ZSTR_VAL((buf)->str); \
- 		size_t newsize = oldsize + MAX(oldsize >> 1, needed); \
- 		zend_string *newstr = erealloc((buf)->str, _ZSTR_STRUCT_SIZE(newsize)); \
- 		out = (unsigned char*)ZSTR_VAL(newstr) + (out - (unsigned char*)ZSTR_VAL((buf)->str)); \
- 		limit = (unsigned char*)ZSTR_VAL(newstr) + newsize; \
- 		(buf)->str = newstr; \
 -	if (UNEXPECTED(mb_convert_buf_ensure((buf), &(out), &(limit), (needed)) == FAILURE)) { \
++	if (!MB_CONVERT_BUF_ENSURE_EX(buf, out, limit, needed)) { \
+ 		return; \
  	}

 -#define MB_CONVERT_BUF_STORE(buf, _out, _limit) buf->out = _out; buf->limit = _limit
++#define MB_CONVERT_BUF_ENSURE_EX(buf, out, limit, needed) \
++	EXPECTED(mb_convert_buf_ensure(buf, &(out), &(limit), (needed)) == SUCCESS)
++
 +#define MB_CONVERT_BUF_STORE(buf, _out, _limit) (buf)->out = _out; (buf)->limit = _limit

 -#define MB_CONVERT_BUF_LOAD(buf, _out, _limit) _out = buf->out; _limit = buf->limit
 +#define MB_CONVERT_BUF_LOAD(buf, _out, _limit) _out = (buf)->out; _limit = (buf)->limit

  #define MB_CONVERT_ERROR(buf, out, limit, bad_cp, conv_fn) \
  	MB_CONVERT_BUF_STORE(buf, out, limit); \
diff --cc ext/mbstring/mbstring.c
index 86707f45279,b5c1d482e9a..2ee6306bfb4
--- a/ext/mbstring/mbstring.c
+++ b/ext/mbstring/mbstring.c
@@@ -1639,16 -1706,16 +1639,20 @@@ PHP_FUNCTION(mb_str_split
  		Z_PARAM_STR_OR_NULL(encoding)
  	ZEND_PARSE_PARAMETERS_END();

 -	if (split_length <= 0) {
 +	if (split_len <= 0) {
  		zend_argument_value_error(2, "must be greater than 0");
  		RETURN_THROWS();
 +	} else if (split_len > UINT_MAX / 4) {
 +		zend_argument_value_error(2, "is too large");
 +		RETURN_THROWS();
  	}

 -	/* fill mbfl_string structure */
 -	string.val = (unsigned char *) ZSTR_VAL(str);
 -	string.len = ZSTR_LEN(str);
 -	string.encoding = php_mb_get_encoding(encoding, 3);
 -	if (!string.encoding) {
++	if (split_len > ZSTR_LEN(str)) {
++		split_len = ZSTR_LEN(str);
++	}
++
 +	const mbfl_encoding *enc = php_mb_get_encoding(encoding, 3);
 +	if (!enc) {
  		RETURN_THROWS();
  	}

@@@ -1656,164 -1723,125 +1660,184 @@@
  		RETURN_EMPTY_ARRAY();
  	}

 -	p = ZSTR_VAL(str); /* string cursor pointer */
 -	last = ZSTR_VAL(str) + ZSTR_LEN(str); /* last string char pointer */
 +	unsigned char *p = (unsigned char*)ZSTR_VAL(str), *e = p + ZSTR_LEN(str);

 -	mbfl_encoding = string.encoding;
 -
 -	/* first scenario: 1,2,4-bytes fixed width encodings (head part) */
 -	if (mbfl_encoding->flag & MBFL_ENCTYPE_SBCS) { /* 1 byte */
 -		mb_len = string.len;
 -		chunk_len = (size_t)split_length; /* chunk length in bytes */
 -	} else if (mbfl_encoding->flag & MBFL_ENCTYPE_WCS2) { /* 2 bytes */
 -		mb_len = string.len / 2;
 -		chunk_len = split_length * 2;
 -	} else if (mbfl_encoding->flag & MBFL_ENCTYPE_WCS4) { /* 4 bytes */
 -		mb_len = string.len / 4;
 -		chunk_len = split_length * 4;
 -	} else if (mbfl_encoding->mblen_table != NULL) {
 -		/* second scenario: variable width encodings with length table */
 -		char unsigned const *mbtab = mbfl_encoding->mblen_table;
 +	unsigned int char_len = enc->flag & (MBFL_ENCTYPE_SBCS | MBFL_ENCTYPE_WCS2 | MBFL_ENCTYPE_WCS4);
 +	if (char_len) {
 +		unsigned int chunk_len = char_len * split_len;
 +		unsigned int chunks = ((ZSTR_LEN(str) / chunk_len) + split_len - 1) / split_len; /* round up */
- 		array_init_size(return_value, chunks);
++		if (UNEXPECTED(!array_finit_size_packed(return_value, chunks))) {
++			RETURN_OOM();
++		}
 +		while (p < e) {
 +			add_next_index_stringl(return_value, (const char*)p, MIN(chunk_len, e - p));
 +			p += chunk_len;
++			if (ZEND_HAS_OOM()) {
++				RETURN_OOM();
++			}
 +		}
 +	} else if (enc->mblen_table) {
 +		unsigned char const *mbtab = enc->mblen_table;

 -		/* assume that we have 1-bytes characters */
 -		if (UNEXPECTED(!array_finit_size_packed(return_value, (string.len + split_length) / split_length /* round up */))) {
 -			return;
 +		/* Assume that we have 1-byte characters */
- 		array_init_size(return_value, (ZSTR_LEN(str) + split_len - 1) / split_len);
++		if (UNEXPECTED(!array_finit_size_packed(return_value, (ZSTR_LEN(str) + split_len - 1) / split_len))) {
++			RETURN_OOM();
+ 		}

 -		while (p < last) { /* split cycle work until the cursor has reached the last byte */
 -			char const *chunk_p = p; /* chunk first byte pointer */
 -			chunk_len = 0; /* chunk length in bytes */
 -			zend_long char_count;
 +		while (p < e) {
 +			unsigned char *chunk = p; /* start of chunk */

 -			for (char_count = 0; char_count < split_length && p < last; ++char_count) {
 -				char unsigned const m = mbtab[*(const unsigned char *)p]; /* single character length table */
 -				chunk_len += m;
 -				p += m;
 +			for (int char_count = 0; char_count < split_len && p < e; char_count++) {
 +				p += mbtab[*p];
  			}
 -			if (p >= last) chunk_len -= p - last; /* check if chunk is in bounds */
 -			add_next_index_stringl(return_value, chunk_p, chunk_len);
 +			if (p > e) {
 +				p = e; /* ensure chunk is in bounds */
 +			}
 +			add_next_index_stringl(return_value, (const char*)chunk, p - chunk);
+ 			if (ZEND_HAS_OOM()) {
 -				break;
++				RETURN_OOM();
+ 			}
  		}
 -		return;
  	} else {
 -		/* third scenario: other multibyte encodings */
 -		mbfl_convert_filter *filter, *decoder;
 -
 -		/* assume that we have 1-bytes characters */
 -		if (UNEXPECTED(!array_finit_size_packed(return_value, (string.len + split_length) / split_length /* round up */))) {
 -			return;
 +		/* Assume that we have 1-byte characters */
- 		array_init_size(return_value, (ZSTR_LEN(str) + split_len - 1) / split_len);
++		if (UNEXPECTED(!array_finit_size_packed(return_value, (ZSTR_LEN(str) + split_len - 1) / split_len))) {
++			RETURN_OOM();
+ 		}

 -		/* decoder filter to decode wchar to encoding */
 -		mbfl_memory_device device;
 -		/* clamp init size to avoid over allocation */
 -		size_t init_size = MIN(ZSTR_LEN(str), split_length) + 1;
 -		mbfl_memory_device_init(&device, init_size, 0);
 -
 -		decoder = mbfl_convert_filter_new(
 -				&mbfl_encoding_wchar,
 -				string.encoding,
 -				mbfl_memory_device_output,
 -				NULL,
 -				&device);
 -		/* assert that nothing is wrong with the decoder */
 -		ZEND_ASSERT(decoder != NULL);
 -
 -		/* wchar filter */
 -		mbfl_string_init(&result_string); /* mbfl_string to store chunk in the callback */
 -		struct mbfl_split_params params = { /* init callback function params structure */
 -			.return_value = return_value,
 -			.result_string = &result_string,
 -			.mb_chunk_length = 0,
 -			.split_length = (size_t)split_length,
 -			.next_filter = decoder,
 -		};
 -
 -		filter = mbfl_convert_filter_new(
 -				string.encoding,
 -				&mbfl_encoding_wchar,
 -				mbfl_split_output,
 -				NULL,
 -				&params);
 -		/* assert that nothing is wrong with the filter */
 -		ZEND_ASSERT(filter != NULL);
 -
 -		while (p < last - 1) { /* cycle each byte except last with callback function */
 -			if (UNEXPECTED((*filter->filter_function)(*p++, filter) < 0)) {
 -				return;
 +		uint32_t wchar_buf[128];
 +		size_t in_len = ZSTR_LEN(str);
 +		unsigned int state = 0, char_count = 0;
 +
 +		mb_convert_buf buf;
 +
 +		while (in_len) {
++			if (ZEND_HAS_OOM()) {
++				RETURN_OOM();
++			}
++
 +			size_t out_len = enc->to_wchar(&p, &in_len, wchar_buf, 128, &state);
 +			ZEND_ASSERT(out_len <= 128);
 +			size_t i = 0;
 +
 +			/* Is there some output remaining from the previous iteration? */
 +			if (char_count) {
 +				if (out_len >= split_len - char_count) {
 +					/* Finish off an incomplete chunk from previous iteration
 +					 * ('buf' was already initialized; we don't need to do it again) */
 +					enc->from_wchar(wchar_buf, split_len - char_count, &buf, true);
 +					i += split_len - char_count;
 +					char_count = 0;
 +					add_next_index_str(return_value, mb_convert_buf_result(&buf, enc));
 +				} else {
 +					/* Output from this iteration is not enough to finish the next chunk;
 +					 * output what we can, and leave 'buf' to be used again on next iteration */
 +					enc->from_wchar(wchar_buf, out_len, &buf, !in_len);
 +					char_count += out_len;
 +					continue;
 +				}
 +			}
 +
 +			while (i < out_len) {
++				if (ZEND_HAS_OOM()) {
++					RETURN_OOM();
++				}
++
 +				/* Prepare for the next chunk */
 +				mb_convert_buf_init(&buf, split_len, MBSTRG(current_filter_illegal_substchar), MBSTRG(current_filter_illegal_mode));
 +
 +				if (out_len - i >= split_len) {
 +					enc->from_wchar(wchar_buf + i, split_len, &buf, true);
 +					i += split_len;
 +					add_next_index_str(return_value, mb_convert_buf_result(&buf, enc));
 +				} else {
 +					/* The remaining codepoints in wchar_buf aren't enough to finish a chunk;
 +					 * leave them for the next iteration */
 +					enc->from_wchar(wchar_buf + i, out_len - i, &buf, !in_len);
 +					char_count = out_len - i;
 +					break;
 +				}
  			}
 -		}
 -		params.mb_chunk_length = split_length - 1; /* force to finish current chunk */
 -		/* process last char */
 -		if (UNEXPECTED((*filter->filter_function)(*p++, filter) < 0)) {
 -			return;
  		}

 -		mbfl_convert_filter_delete(decoder);
 -		mbfl_convert_filter_delete(filter);
 -		mbfl_memory_device_clear(&device);
 -		return;
 +		if (char_count) {
 +			/* The main loop above has finished processing the input string, but
 +			 * has left a partial chunk in 'buf' */
 +			add_next_index_str(return_value, mb_convert_buf_result(&buf, enc));
 +		}
  	}
 +}

 -	/* first scenario: 1,2,4-bytes fixed width encodings (tail part) */
 -	chunks = (mb_len + split_length - 1) / split_length; /* (round up idiom) */
 -	if (UNEXPECTED(!array_finit_size_packed(return_value, chunks))) {
 -		return;
 -	}
 -	if (chunks != 0) {
 -		zend_long i;
 +#ifdef __SSE2__
 +/* Thanks to StackOverflow user 'Paul R' (https://stackoverflow.com/users/253056/paul-r)
 + * From: https://stackoverflow.com/questions/36998538/fastest-way-to-horizontally-sum-sse-unsigned-byte-vector
 + * Takes a 128-bit XMM register, treats each byte as an 8-bit integer, and sums up all
 + * 16 of them, returning the sum in an ordinary scalar register */
 +static inline uint32_t _mm_sum_epu8(const __m128i v)
 +{
 +	/* We don't have any dedicated instruction to sum up 8-bit values from a 128-bit register
 +	 * _mm_sad_epu8 takes the differences between corresponding bytes of two different XMM registers,
 +	 * sums up those differences, and stores them as two 16-byte integers in the top and bottom
 +	 * halves of the destination XMM register
 +	 * By using a zeroed-out XMM register as one operand, we ensure the "differences" which are
 +	 * summed up will actually just be the 8-bit values from `v` */
 +	__m128i vsum = _mm_sad_epu8(v, _mm_setzero_si128());
 +	/* If _mm_sad_epu8 had stored the sum of those bytes as a single integer, we would just have
 +	 * to extract it here; but it stored the sum as two different 16-bit values
 +	 * _mm_cvtsi128_si32 extracts one of those values into a scalar register
 +	 * _mm_extract_epi16 extracts the other one into another scalar register; then we just add them */
 +	return _mm_cvtsi128_si32(vsum) + _mm_extract_epi16(vsum, 4);
 +}
 +#endif

 -		for (i = 0; i < chunks - 1; p += chunk_len, ++i) {
 -			add_next_index_stringl(return_value, p, chunk_len);
 -			if (ZEND_HAS_OOM()) {
 -				return;
 +/* This assumes that `string` is valid UTF-8
 + * In UTF-8, the only bytes which do not start a new codepoint are 0x80-0xBF (continuation bytes)
 + * Interpreted as signed integers, those are all byte values less than -64
 + * A fast way to get the length of a UTF-8 string is to start with its byte length,
 + * then subtract off the number of continuation bytes */
 +static size_t mb_fast_strlen_utf8(unsigned char *p, size_t len)
 +{
 +	unsigned char *e = p + len;
 +
 +#ifdef __SSE2__
 +	if (len >= sizeof(__m128i)) {
 +		e -= sizeof(__m128i);
 +
 +		const __m128i threshold = _mm_set1_epi8(-64);
 +		const __m128i delta = _mm_set1_epi8(1);
 +		__m128i counter = _mm_setzero_si128(); /* Vector of 16 continuation-byte counters */
 +
 +		int reset_counter = 255;
 +		do {
 +			__m128i operand = _mm_loadu_si128((__m128i*)p); /* Load 16 bytes */
 +			__m128i lt = _mm_cmplt_epi8(operand, threshold); /* Find all which are continuation bytes */
 +			counter = _mm_add_epi8(counter, _mm_and_si128(lt, delta)); /* Update the 16 counters */
 +
 +			/* The counters can only go up to 255, so every 255 iterations, fold them into `len`
 +			 * and reset them to zero */
 +			if (--reset_counter == 0) {
 +				len -= _mm_sum_epu8(counter);
 +				counter = _mm_setzero_si128();
 +				reset_counter = 255;
  			}
 +
 +			p += sizeof(__m128i);
 +		} while (p <= e);
 +
 +		e += sizeof(__m128i);
 +		len -= _mm_sum_epu8(counter); /* Fold in any remaining non-zero values in the 16 counters */
 +	}
 +#endif
 +
 +	/* Check for continuation bytes in the 0-15 remaining bytes at the end of the string */
 +	while (p < e) {
 +		signed char c = *p++;
 +		if (c < -64) {
 +			len--;
  		}
 -		add_next_index_stringl(return_value, p, last - p);
  	}
 +
 +	return len;
  }
 -/* }}} */

  static size_t mb_get_strlen(zend_string *string, const mbfl_encoding *encoding)
  {
@@@ -2589,7 -2528,9 +2615,9 @@@ dont_restart_conversion

  append_trim_marker:
  	if (ZSTR_LEN(marker) > 0) {
- 		MB_CONVERT_BUF_ENSURE((&buf), buf.out, buf.limit, ZSTR_LEN(marker));
 -		if (UNEXPECTED(mb_convert_buf_ensure(&buf, &buf.out, &buf.limit, ZSTR_LEN(marker)) == FAILURE)) {
 -			return mb_convert_buf_result(&buf);
++		if (!MB_CONVERT_BUF_ENSURE_EX(&buf, buf.out, buf.limit, ZSTR_LEN(marker))) {
++			return mb_convert_buf_result(&buf, enc);
+ 		}
  		memcpy(buf.out, ZSTR_VAL(marker), ZSTR_LEN(marker));
  		buf.out += ZSTR_LEN(marker);
  	}
@@@ -2647,7 -2584,7 +2675,12 @@@ PHP_FUNCTION(mb_strimwidth
  		}
  	}

--	RETVAL_STR(mb_trim_string(str, trimmarker, enc, from, width));
++	zend_string *ret = mb_trim_string(str, trimmarker, enc, from, width);
++	if (UNEXPECTED(ret == NULL)) {
++		RETURN_OOM();
++	}
++
++	RETVAL_STR(ret);
  }


@@@ -3482,50 -3352,7 +3515,53 @@@ static bool mb_recursive_find_strings(z
  			Z_PROTECT_RECURSION_P(var);
  		}

 -		ht = HASH_OF(var);
 +		HashTable *ht = HASH_OF(var);
 +		if (ht != NULL) {
 +			zval *entry;
 +			ZEND_HASH_FOREACH_VAL_IND(ht, entry) {
 +				if (mb_recursive_find_strings(entry, val_list, len_list, count)) {
 +					if (Z_REFCOUNTED_P(var)) {
 +						Z_UNPROTECT_RECURSION_P(var);
 +						return true;
 +					}
 +				}
 +			} ZEND_HASH_FOREACH_END();
 +		}
 +
 +		if (Z_REFCOUNTED_P(var)) {
 +			Z_UNPROTECT_RECURSION_P(var);
 +		}
 +	}
 +
 +	return false;
 +}
 +
 +static bool mb_recursive_convert_variable(zval *var, const mbfl_encoding* from_encoding, const mbfl_encoding* to_encoding)
 +{
 +	zval *entry, *orig_var;
 +
 +	orig_var = var;
 +	ZVAL_DEREF(var);
 +
 +	if (Z_TYPE_P(var) == IS_STRING) {
 +		zend_string *ret = php_mb_convert_encoding_ex(Z_STRVAL_P(var), Z_STRLEN_P(var), to_encoding, from_encoding);
 +		zval_ptr_dtor(orig_var);
 +		ZVAL_STR(orig_var, ret);
++		if (ZEND_HAS_OOM()) {
++			return true;
++		}
 +	} else if (Z_TYPE_P(var) == IS_ARRAY || Z_TYPE_P(var) == IS_OBJECT) {
 +		if (Z_TYPE_P(var) == IS_ARRAY) {
 +			SEPARATE_ARRAY(var);
 +		}
 +		if (Z_REFCOUNTED_P(var)) {
 +			if (Z_IS_RECURSIVE_P(var)) {
 +				return true;
 +			}
 +			Z_PROTECT_RECURSION_P(var);
 +		}
 +
 +		HashTable *ht = HASH_OF(var);
  		if (ht != NULL) {
  			ZEND_HASH_FOREACH_VAL(ht, entry) {
  				/* Can be a typed property declaration, in which case we need to remove the reference from the source list.
@@@ -4350,20 -4209,34 +4386,23 @@@ PHP_FUNCTION(mb_send_mail
  	}
  	const char *line_sep = PG(mail_mixed_lf_and_crlf) ? "\n" : CRLF;
  	size_t line_sep_len = strlen(line_sep);
 -	pstr = mbfl_mime_header_encode(&orig_str, &conv_str, tran_cs, head_enc, line_sep, strlen("Subject: [PHP-jp nnnnnnnn]") + line_sep_len);
 -	if (pstr != NULL) {
 -		subject_buf = subject = (char *)pstr->val;
 +
 +	subject = mb_mime_header_encode(subject, enc, tran_cs, head_enc == &mbfl_encoding_base64, (char*)line_sep, line_sep_len, strlen("Subject: [PHP-jp nnnnnnnn]") + line_sep_len);
++	if (UNEXPECTED(!subject)) {
++		RETURN_OOM();
+ 	}

  	/* message body */
 -	orig_str.val = (unsigned char *)message;
 -	orig_str.len = message_len;
 -	orig_str.encoding = MBSTRG(current_internal_encoding);
 -
 -	if (orig_str.encoding->no_encoding == mbfl_no_encoding_invalid
 -			|| orig_str.encoding->no_encoding == mbfl_no_encoding_pass) {
 -		orig_str.encoding = mbfl_identify_encoding(&orig_str, MBSTRG(current_detect_order_list), MBSTRG(current_detect_order_list_size), MBSTRG(strict_detection));
 +	const mbfl_encoding *msg_enc = MBSTRG(current_internal_encoding);
 +	if (msg_enc == &mbfl_encoding_pass) {
 +		msg_enc = mb_guess_encoding((unsigned char*)message, message_len, MBSTRG(current_detect_order_list), MBSTRG(current_detect_order_list_size), MBSTRG(strict_detection), false);
  	}

 -	pstr = NULL;
 -	{
 -		mbfl_string tmpstr;
 -
 -		if (mbfl_convert_encoding(&orig_str, &tmpstr, tran_cs) != NULL) {
 -			tmpstr.encoding = &mbfl_encoding_8bit;
 -			pstr = mbfl_convert_encoding(&tmpstr, &conv_str, body_enc);
 -			efree(tmpstr.val);
 -		}
 -	}
 -	if (pstr != NULL) {
 -		message_buf = message = (char *)pstr->val;
 -	}
 +	unsigned int num_errors = 0;
 +	zend_string *tmpstr = mb_fast_convert((unsigned char*)message, message_len, msg_enc, tran_cs, '?', MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR, &num_errors);
 +	zend_string *conv = mb_fast_convert((unsigned char*)ZSTR_VAL(tmpstr), ZSTR_LEN(tmpstr), &mbfl_encoding_8bit, body_enc, '?', MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR, &num_errors);
 +	zend_string_free(tmpstr);
 +	message = ZSTR_VAL(conv);

  	/* other headers */
  #define PHP_MBSTR_MAIL_MIME_HEADER1 "MIME-Version: 1.0"
@@@ -5542,132 -4750,6 +5581,137 @@@ PHP_FUNCTION(mb_chr
  }
  /* }}} */

 +PHP_FUNCTION(mb_str_pad)
 +{
 +	zend_string *input, *encoding_str = NULL, *pad = ZSTR_CHAR(' ');
 +	zend_long pad_to_length;
 +	zend_long pad_type_val = PHP_STR_PAD_RIGHT;
 +
 +	ZEND_PARSE_PARAMETERS_START(2, 5)
 +		Z_PARAM_STR(input)
 +		Z_PARAM_LONG(pad_to_length)
 +		Z_PARAM_OPTIONAL
 +		Z_PARAM_STR(pad)
 +		Z_PARAM_LONG(pad_type_val)
 +		Z_PARAM_STR_OR_NULL(encoding_str)
 +	ZEND_PARSE_PARAMETERS_END();
 +
 +	const mbfl_encoding *encoding = php_mb_get_encoding(encoding_str, 5);
 +	if (!encoding) {
 +		RETURN_THROWS();
 +	}
 +
 +	size_t input_length = mb_get_strlen(input, encoding);
 +
 +	/* If resulting string turns out to be shorter than input string,
 +	   we simply copy the input and return. */
 +	if (pad_to_length < 0 || (size_t)pad_to_length <= input_length) {
 +		RETURN_STR_COPY(input);
 +	}
 +
 +	if (ZSTR_LEN(pad) == 0) {
 +		zend_argument_value_error(3, "must be a non-empty string");
 +		RETURN_THROWS();
 +	}
 +
 +	if (pad_type_val < PHP_STR_PAD_LEFT || pad_type_val > PHP_STR_PAD_BOTH) {
 +		zend_argument_value_error(4, "must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH");
 +		RETURN_THROWS();
 +	}
 +
 +	size_t pad_length = mb_get_strlen(pad, encoding);
 +
 +	size_t num_mb_pad_chars = pad_to_length - input_length;
 +
 +	/* We need to figure out the left/right padding lengths. */
 +	size_t left_pad = 0, right_pad = 0; /* Initialize here to silence compiler warnings. */
 +	switch (pad_type_val) {
 +		case PHP_STR_PAD_RIGHT:
 +			right_pad = num_mb_pad_chars;
 +			break;
 +
 +		case PHP_STR_PAD_LEFT:
 +			left_pad = num_mb_pad_chars;
 +			break;
 +
 +		case PHP_STR_PAD_BOTH:
 +			left_pad = num_mb_pad_chars / 2;
 +			right_pad = num_mb_pad_chars - left_pad;
 +			break;
 +	}
 +
 +	/* How many full block copies need to happen, and how many characters are then left over? */
 +	size_t full_left_pad_copies = left_pad / pad_length;
 +	size_t full_right_pad_copies = right_pad / pad_length;
 +	size_t remaining_left_pad_chars = left_pad % pad_length;
 +	size_t remaining_right_pad_chars = right_pad % pad_length;
 +
 +	if (UNEXPECTED(full_left_pad_copies > SIZE_MAX / ZSTR_LEN(pad) || full_right_pad_copies > SIZE_MAX / ZSTR_LEN(pad))) {
 +		goto overflow_no_release;
 +	}
 +
 +	/* Compute the number of bytes required for the padding */
 +	size_t full_left_pad_bytes = full_left_pad_copies * ZSTR_LEN(pad);
 +	size_t full_right_pad_bytes = full_right_pad_copies * ZSTR_LEN(pad);
 +
 +	/* No special fast-path handling necessary for zero-length pads because these functions will not
 +	 * allocate memory in case a zero-length pad is required. */
 +	zend_string *remaining_left_pad_str = mb_get_substr(pad, 0, remaining_left_pad_chars, encoding);
 +	zend_string *remaining_right_pad_str = mb_get_substr(pad, 0, remaining_right_pad_chars, encoding);
 +
 +	if (UNEXPECTED(full_left_pad_bytes > ZSTR_MAX_LEN - ZSTR_LEN(remaining_left_pad_str)
 +		|| full_right_pad_bytes > ZSTR_MAX_LEN - ZSTR_LEN(remaining_right_pad_str))) {
 +		goto overflow;
 +	}
 +
 +	size_t left_pad_bytes = full_left_pad_bytes + ZSTR_LEN(remaining_left_pad_str);
 +	size_t right_pad_bytes = full_right_pad_bytes + ZSTR_LEN(remaining_right_pad_str);
 +
 +	if (UNEXPECTED(left_pad_bytes > ZSTR_MAX_LEN - right_pad_bytes
 +		|| ZSTR_LEN(input) > ZSTR_MAX_LEN - left_pad_bytes - right_pad_bytes)) {
 +		goto overflow;
 +	}
 +
- 	zend_string *result = zend_string_alloc(ZSTR_LEN(input) + left_pad_bytes + right_pad_bytes, false);
++	zend_string *result = zend_string_safe_falloc(1, ZSTR_LEN(input), left_pad_bytes + right_pad_bytes, false, /* fallible */ true);
++	if (UNEXPECTED(!result)) {
++		zend_string_release_ex(remaining_left_pad_str, false);
++		zend_string_release_ex(remaining_right_pad_str, false);
++		RETURN_OOM();
++	}
 +	char *buffer = ZSTR_VAL(result);
 +
 +	/* First we pad the left. */
 +	for (size_t i = 0; i < full_left_pad_copies; i++, buffer += ZSTR_LEN(pad)) {
 +		memcpy(buffer, ZSTR_VAL(pad), ZSTR_LEN(pad));
 +	}
 +	memcpy(buffer, ZSTR_VAL(remaining_left_pad_str), ZSTR_LEN(remaining_left_pad_str));
 +	buffer += ZSTR_LEN(remaining_left_pad_str);
 +
 +	/* Then we copy the input string. */
 +	memcpy(buffer, ZSTR_VAL(input), ZSTR_LEN(input));
 +	buffer += ZSTR_LEN(input);
 +
 +	/* Finally, we pad on the right. */
 +	for (size_t i = 0; i < full_right_pad_copies; i++, buffer += ZSTR_LEN(pad)) {
 +		memcpy(buffer, ZSTR_VAL(pad), ZSTR_LEN(pad));
 +	}
 +	memcpy(buffer, ZSTR_VAL(remaining_right_pad_str), ZSTR_LEN(remaining_right_pad_str));
 +
 +	ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
 +
 +	zend_string_release_ex(remaining_left_pad_str, false);
 +	zend_string_release_ex(remaining_right_pad_str, false);
 +
 +	RETURN_NEW_STR(result);
 +
 +overflow:
 +	zend_string_release_ex(remaining_left_pad_str, false);
 +	zend_string_release_ex(remaining_right_pad_str, false);
 +overflow_no_release:
 +	zend_throw_error(NULL, "String size overflow");
 +	RETURN_THROWS();
 +}
 +
  /* {{{ */
  PHP_FUNCTION(mb_scrub)
  {
@@@ -5779,684 -4859,62 +5823,707 @@@ MBSTRING_API char *php_mb_safe_strrchr(
  	return last;
  }

 -/* {{{ MBSTRING_API int php_mb_stripos() */
 -MBSTRING_API size_t php_mb_stripos(int mode, const char *old_haystack, size_t old_haystack_len, const char *old_needle, size_t old_needle_len, zend_long offset, const mbfl_encoding *enc)
 +MBSTRING_API size_t php_mb_stripos(bool mode, zend_string *haystack, zend_string *needle, zend_long offset, const mbfl_encoding *enc)
  {
 -	size_t n = (size_t) -1;
 -	mbfl_string haystack, needle;
 +	/* We're using simple case-folding here, because we'd have to deal with remapping of
 +	 * offsets otherwise. */
 +	zend_string *haystack_conv = php_unicode_convert_case(PHP_UNICODE_CASE_FOLD_SIMPLE, ZSTR_VAL(haystack), ZSTR_LEN(haystack), enc, &mbfl_encoding_utf8, MBFL_OUTPUTFILTER_ILLEGAL_MODE_BADUTF8, 0);
 +	zend_string *needle_conv = php_unicode_convert_case(PHP_UNICODE_CASE_FOLD_SIMPLE, ZSTR_VAL(needle), ZSTR_LEN(needle), enc, &mbfl_encoding_utf8, MBFL_OUTPUTFILTER_ILLEGAL_MODE_BADUTF8, 0);

 -	mbfl_string_init_set(&haystack, enc);
 -	mbfl_string_init_set(&needle, enc);
 +	size_t n = mb_find_strpos(haystack_conv, needle_conv, &mbfl_encoding_utf8, offset, mode);

 -	do {
 -		/* We're using simple case-folding here, because we'd have to deal with remapping of
 -		 * offsets otherwise. */
 +	zend_string_free(haystack_conv);
 +	zend_string_free(needle_conv);

 -		size_t len = 0;
 -		haystack.val = (unsigned char *)mbstring_convert_case(PHP_UNICODE_CASE_FOLD_SIMPLE, (char *)old_haystack, old_haystack_len, &len, enc);
 -		haystack.len = len;
 +	return n;
 +}

 -		if (!haystack.val) {
 -			break;
 +static void php_mb_gpc_get_detect_order(const zend_encoding ***list, size_t *list_size) /* {{{ */
 +{
 +	*list = (const zend_encoding **)MBSTRG(http_input_list);
 +	*list_size = MBSTRG(http_input_list_size);
 +}
 +/* }}} */
 +
 +static void php_mb_gpc_set_input_encoding(const zend_encoding *encoding) /* {{{ */
 +{
 +	MBSTRG(http_input_identify) = (const mbfl_encoding*)encoding;
 +}
 +/* }}} */
 +
 +static const unsigned char base64_table[] = {
 + /* 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', */
 +   0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,
 + /* 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', */
 +   0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,
 + /* 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', */
 +   0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,
 + /* 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', */
 +   0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,
 + /* '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0' */
 +   0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x2b,0x2f,0x00
 +};
 +
 +static size_t transfer_encoded_size(mb_convert_buf *tmpbuf, bool base64)
 +{
 +	if (base64) {
 +		return ((mb_convert_buf_len(tmpbuf) + 2) / 3) * 4;
 +	} else {
 +		size_t enc_size = 0;
 +		unsigned char *p = (unsigned char*)ZSTR_VAL(tmpbuf->str);
 +		while (p < tmpbuf->out) {
 +			unsigned char c = *p++;
 +			enc_size += (c > 0x7F || c == '=' || mime_char_needs_qencode[c]) ? 3 : 1;
  		}
 +		return enc_size;
 +	}
 +}

 -		if (haystack.len == 0) {
 -			break;
 +static void transfer_encode_mime_bytes(mb_convert_buf *tmpbuf, mb_convert_buf *outbuf, bool base64)
 +{
 +	unsigned char *out, *limit;
 +	MB_CONVERT_BUF_LOAD(outbuf, out, limit);
 +	unsigned char *p = (unsigned char*)ZSTR_VAL(tmpbuf->str), *e = tmpbuf->out;
 +
 +	if (base64) {
 +		MB_CONVERT_BUF_ENSURE(outbuf, out, limit, ((e - p) + 2) / 3 * 4);
 +		while ((e - p) >= 3) {
 +			unsigned char a = *p++;
 +			unsigned char b = *p++;
 +			unsigned char c = *p++;
 +			uint32_t bits = (a << 16) | (b << 8) | c;
 +			out = mb_convert_buf_add4(out,
 +				base64_table[(bits >> 18) & 0x3F],
 +				base64_table[(bits >> 12) & 0x3F],
 +				base64_table[(bits >> 6) & 0x3F],
 +				base64_table[bits & 0x3F]);
 +		}
 +		if (p != e) {
 +			if ((e - p) == 1) {
 +				uint32_t bits = *p++;
 +				out = mb_convert_buf_add4(out, base64_table[(bits >> 2) & 0x3F], base64_table[(bits & 0x3) << 4], '=', '=');
 +			} else {
 +				unsigned char a = *p++;
 +				unsigned char b = *p++;
 +				uint32_t bits = (a << 8) | b;
 +				out = mb_convert_buf_add4(out, base64_table[(bits >> 10) & 0x3F], base64_table[(bits >> 4) & 0x3F], base64_table[(bits & 0xF) << 2], '=');
 +			}
  		}
 +	} else {
 +		MB_CONVERT_BUF_ENSURE(outbuf, out, limit, (e - p) * 3);
 +		while (p < e) {
 +			unsigned char c = *p++;
 +			if (c > 0x7F || c == '=' || mime_char_needs_qencode[c]) {
 +				out = mb_convert_buf_add3(out, '=', "0123456789ABCDEF"[(c >> 4) & 0xF], "0123456789ABCDEF"[c & 0xF]);
 +			} else {
 +				out = mb_convert_buf_add(out, c);
 +			}
 +		}
 +	}

 -		needle.val = (unsigned char *)mbstring_convert_case(PHP_UNICODE_CASE_FOLD_SIMPLE, (char *)old_needle, old_needle_len, &len, enc);
 -		needle.len = len;
 +	mb_convert_buf_reset(tmpbuf, 0);
 +	MB_CONVERT_BUF_STORE(outbuf, out, limit);
 +}
 +
 +#define MBSTRING_HEADER_ENC_WCHAR_BUFSIZE 90
 +
 +static zend_string* mb_mime_header_encode(zend_string *input, const mbfl_encoding *incode, const mbfl_encoding *outcode, bool base64, char *linefeed, size_t linefeed_len, zend_long indent)
 +{
 +	unsigned char *in = (unsigned char*)ZSTR_VAL(input);
 +	size_t in_len = ZSTR_LEN(input);

 -		if (!needle.val) {
 +	ZEND_ASSERT(outcode->mime_name != NULL);
 +	ZEND_ASSERT(outcode->mime_name[0] != '\0');
 +
 +	if (!in_len) {
 +		return zend_empty_string;
 +	}
 +
 +	if (indent < 0 || indent >= 74) {
 +		indent = 0;
 +	}
 +
 +	if (linefeed_len > 8) {
 +		linefeed_len = 8;
 +	}
 +	/* Maintain legacy behavior as regards embedded NUL (zero) bytes in linefeed string */
 +	for (size_t i = 0; i < linefeed_len; i++) {
 +		if (linefeed[i] == '\0') {
 +			linefeed_len = i;
  			break;
  		}
 +	}
 +
 +	unsigned int state = 0;
 +	/* wchar_buf should be big enough that when it is full, we definitely have enough
 +	 * wchars to fill an entire line of output */
 +	uint32_t wchar_buf[MBSTRING_HEADER_ENC_WCHAR_BUFSIZE];
 +	uint32_t *p, *e;
 +	/* What part of wchar_buf is filled with still-unprocessed data which should not
 +	 * be overwritten? */
 +	unsigned int offset = 0;
 +	size_t line_start = 0;
 +
 +	/* If the entire input string is ASCII with no spaces (except possibly leading
 +	 * spaces), just pass it through unchanged */
 +	bool checking_leading_spaces = true;
 +	while (in_len) {
 +		size_t out_len = incode->to_wchar(&in, &in_len, wchar_buf, MBSTRING_HEADER_ENC_WCHAR_BUFSIZE, &state);
 +		p = wchar_buf;
 +		e = wchar_buf + out_len;
 +
 +		while (p < e) {
 +			uint32_t w = *p++;
 +			if (checking_leading_spaces) {
 +				if (w == ' ') {
 +					continue;
 +				} else {
 +					checking_leading_spaces = false;
 +				}
 +			}
 +			if (w < 0x21 || w > 0x7E || w == '=' || w == '?' || w == '_') {
 +				/* We cannot simply pass input string through unchanged; start again */
 +				in = (unsigned char*)ZSTR_VAL(input);
 +				in_len = ZSTR_LEN(input);
 +				goto no_passthrough;
 +			}
 +		}
 +	}
 +
 +	return zend_string_copy(input); /* This just increments refcount */

 -		n = mbfl_strpos(&haystack, &needle, offset, mode);
 -	} while(0);
 +no_passthrough: ;

 -	if (haystack.val) {
 -		efree(haystack.val);
 +	mb_convert_buf buf;
 +	mb_convert_buf_init(&buf, in_len, '?', MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR);
 +
 +	/* Encode some prefix of the input string as plain ASCII if possible
 +	 * If we find it necessary to switch to Base64/QPrint encoding, we will
 +	 * do so all the way to the end of the string */
 +	while (in_len) {
 +		/* Decode part of the input string, refill wchar_buf */
 +		ZEND_ASSERT(offset + MBSTRING_MIN_WCHAR_BUFSIZE <= MBSTRING_HEADER_ENC_WCHAR_BUFSIZE);
 +		size_t out_len = incode->to_wchar(&in, &in_len, wchar_buf + offset, MBSTRING_HEADER_ENC_WCHAR_BUFSIZE - offset, &state);
 +		ZEND_ASSERT(out_len <= MBSTRING_HEADER_ENC_WCHAR_BUFSIZE - offset);
 +		p = wchar_buf;
 +		e = wchar_buf + offset + out_len;
 +		/* ASCII output is broken into space-delimited 'words'
 +		 * If we find a non-ASCII character in the middle of a word, we will
 +		 * transfer-encode the entire word */
 +		uint32_t *word_start = p;
 +
 +		/* Don't consider adding line feed for spaces at the beginning of a word */
 +		while (p < e && *p == ' ' && (p - word_start) <= 74) {
 +			p++;
 +		}
 +
 +		while (p < e) {
 +			uint32_t w = *p++;
 +
 +			if (w < 0x20 || w > 0x7E || w == '?' || w == '=' || w == '_' || (w == ' ' && (p - word_start) > 74)) {
 +				/* Non-ASCII character (or line too long); switch to Base64/QPrint encoding
 +				 * If we are already too far along on a line to include Base64/QPrint encoded data
 +				 * on the same line (without overrunning max line length), then add a line feed
 +				 * right now */
 +feed_and_mime_encode:
 +				if (mb_convert_buf_len(&buf) - line_start + indent + strlen(outcode->mime_name) > 55) {
- 					MB_CONVERT_BUF_ENSURE(&buf, buf.out, buf.limit, (e - word_start) + linefeed_len + 1);
++					if (!MB_CONVERT_BUF_ENSURE_EX(&buf, buf.out, buf.limit, (e - word_start) + linefeed_len + 1)) {
++						return NULL;
++					}
 +					buf.out = mb_convert_buf_appendn(buf.out, linefeed, linefeed_len);
 +					buf.out = mb_convert_buf_add(buf.out, ' ');
 +					indent = 0;
 +					line_start = mb_convert_buf_len(&buf);
 +				} else if (mb_convert_buf_len(&buf) > 0) {
- 					MB_CONVERT_BUF_ENSURE(&buf, buf.out, buf.limit, 1);
++					if (!MB_CONVERT_BUF_ENSURE_EX(&buf, buf.out, buf.limit, 1)) {
++						return NULL;
++					}
 +					buf.out = mb_convert_buf_add(buf.out, ' ');
 +				}
 +				p = word_start; /* Back up to where MIME encoding of input chars should start */
 +				goto mime_encoding_needed;
 +			} else if (w == ' ') {
 +				/* When we see a space, check whether we should insert a line break */
 +				if (mb_convert_buf_len(&buf) - line_start + (p - word_start) + indent > 75) {
- 					MB_CONVERT_BUF_ENSURE(&buf, buf.out, buf.limit, (e - word_start) + linefeed_len + 1);
++					if (!MB_CONVERT_BUF_ENSURE_EX(&buf, buf.out, buf.limit, (e - word_start) + linefeed_len + 1)) {
++						return NULL;
++					}
 +					buf.out = mb_convert_buf_appendn(buf.out, linefeed, linefeed_len);
 +					buf.out = mb_convert_buf_add(buf.out, ' ');
 +					indent = 0;
 +					line_start = mb_convert_buf_len(&buf);
 +				} else if (mb_convert_buf_len(&buf) > 0) {
- 					MB_CONVERT_BUF_ENSURE(&buf, buf.out, buf.limit, (e - word_start) + 1);
++					if (!MB_CONVERT_BUF_ENSURE_EX(&buf, buf.out, buf.limit, (e - word_start) + 1)) {
++						return NULL;
++					}
 +					buf.out = mb_convert_buf_add(buf.out, ' ');
 +				}
 +				/* Output one (space-delimited) word as plain ASCII */
 +				while (word_start < p-1) {
 +					buf.out = mb_convert_buf_add(buf.out, *word_start++ & 0xFF);
 +				}
 +				word_start++;
 +				while (p < e && *p == ' ') {
 +					p++;
 +				}
 +			}
 +		}
 +
 +		if (in_len) {
 +			/* Copy chars which are part of an incomplete 'word' to the beginning
 +			 * of wchar_buf and reprocess them on the next iteration.
 +			 * But first make sure that the incomplete 'word' isn't so big that
 +			 * there will be no space to add any more decoded wchars in the buffer
 +			 * (which could lead to an infinite loop) */
 +			if ((word_start - wchar_buf) < MBSTRING_MIN_WCHAR_BUFSIZE) {
 +				goto feed_and_mime_encode;
 +			}
 +			offset = e - word_start;
 +			if (offset) {
 +				memmove(wchar_buf, word_start, offset * sizeof(uint32_t));
 +			}
 +		} else {
 +			/* We have reached the end of the input string while still in 'ASCII mode';
 +			 * process any trailing ASCII chars which were not followed by a space */
 +			if (word_start < e && mb_convert_buf_len(&buf) > 0) {
 +				/* The whole input string was not just one big ASCII 'word' with no spaces
 +				 * consider adding a line feed if necessary to prevent output lines from
 +				 * being too long */
 +				if (mb_convert_buf_len(&buf) - line_start + (p - word_start) + indent > 74) {
- 					MB_CONVERT_BUF_ENSURE(&buf, buf.out, buf.limit, (e - word_start) + linefeed_len + 1);
++					if (!MB_CONVERT_BUF_ENSURE_EX(&buf, buf.out, buf.limit, (e - word_start) + linefeed_len + 1)) {
++						return NULL;
++					}
 +					buf.out = mb_convert_buf_appendn(buf.out, linefeed, linefeed_len);
 +					buf.out = mb_convert_buf_add(buf.out, ' ');
 +				} else {
- 					MB_CONVERT_BUF_ENSURE(&buf, buf.out, buf.limit, (e - word_start) + 1);
++					if (!MB_CONVERT_BUF_ENSURE_EX(&buf, buf.out, buf.limit, (e - word_start) + 1)) {
++						return NULL;
++					}
 +					buf.out = mb_convert_buf_add(buf.out, ' ');
 +				}
 +			}
 +			while (word_start < e) {
 +				buf.out = mb_convert_buf_add(buf.out, *word_start++ & 0xFF);
 +			}
 +		}
  	}

 -	if (needle.val) {
 -		efree(needle.val);
 +	/* Ensure output string is marked as valid UTF-8 (ASCII strings are always 'valid UTF-8') */
 +	return mb_convert_buf_result(&buf, &mbfl_encoding_utf8);
 +
 +mime_encoding_needed: ;
 +
 +	/* We will generate the output line by line, first converting wchars to bytes
 +	 * in the requested output encoding, then transfer-encoding those bytes as
 +	 * Base64 or QPrint
 +	 * 'tmpbuf' will receive the bytes which need to be transfer-encoded before
 +	 * sending them to 'buf' */
 +	mb_convert_buf tmpbuf;
 +	mb_convert_buf_init(&tmpbuf, in_len, '?', MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR);
 +
 +	/* Do we need to refill wchar_buf to make sure we don't run out of wchars
 +	 * in the middle of a line? */
 +	offset = e - p;
 +	if (MBSTRING_HEADER_ENC_WCHAR_BUFSIZE - offset < MBSTRING_MIN_WCHAR_BUFSIZE) {
 +		goto start_new_line;
  	}
 +	memmove(wchar_buf, p, offset * sizeof(uint32_t));

 -	return n;
 +	while(true) {
 +refill_wchar_buf: ;
 +		ZEND_ASSERT(offset + MBSTRING_MIN_WCHAR_BUFSIZE <= MBSTRING_HEADER_ENC_WCHAR_BUFSIZE);
 +		size_t out_len = incode->to_wchar(&in, &in_len, wchar_buf + offset, MBSTRING_HEADER_ENC_WCHAR_BUFSIZE - offset, &state);
 +		ZEND_ASSERT(out_len <= MBSTRING_HEADER_ENC_WCHAR_BUFSIZE - offset);
 +		p = wchar_buf;
 +		e = wchar_buf + offset + out_len;
 +
 +start_new_line: ;
- 		MB_CONVERT_BUF_ENSURE(&buf, buf.out, buf.limit, strlen(outcode->mime_name) + 5);
++		if (!MB_CONVERT_BUF_ENSURE_EX(&buf, buf.out, buf.limit, strlen(outcode->mime_name) + 5)) {
++			return NULL;
++		}
 +		buf.out = mb_convert_buf_add2(buf.out, '=', '?');
 +		buf.out = mb_convert_buf_appends(buf.out, outcode->mime_name);
 +		buf.out = mb_convert_buf_add3(buf.out, '?', base64 ? 'B' : 'Q', '?');
 +
 +		/* How many wchars should we try converting to Base64/QPrint-encoded bytes?
 +		 * We do something like a 'binary search' to find the greatest number which
 +		 * can be included on this line without exceeding max line length */
 +		unsigned int n = 12;
 +		size_t space_available = 73 - indent - (mb_convert_buf_len(&buf) - line_start);
 +
 +		while (true) {
 +			ZEND_ASSERT(p < e);
 +
 +			/* Remember where we were in process of generating output, so we can back
 +			 * up if necessary */
 +			size_t tmppos = mb_convert_buf_len(&tmpbuf);
 +			unsigned int tmpstate = tmpbuf.state;
 +
 +			/* Try encoding 'n' wchars in output text encoding and sending output
 +			 * bytes to 'tmpbuf'. Hopefully this is not too many to fit on the
 +			 * current line. */
 +			n = MIN(n, e - p);
 +			outcode->from_wchar(p, n, &tmpbuf, false);
 +
 +			/* For some output text encodings, there may be a few ending bytes
 +			 * which need to be emitted to output before we break a line.
 +			 * Again, remember where we were so we can back up */
 +			size_t tmppos2 = mb_convert_buf_len(&tmpbuf);
 +			unsigned int tmpstate2 = tmpbuf.state;
 +			outcode->from_wchar(NULL, 0, &tmpbuf, true);
 +
 +			if (transfer_encoded_size(&tmpbuf, base64) <= space_available || (n == 1 && tmppos == 0)) {
 +				/* If we convert 'n' more wchars on the current line, it will not
 +				 * overflow the maximum line length */
 +				p += n;
 +
 +				if (p == e) {
 +					/* We are done; we shouldn't reach here if there is more remaining
 +					 * of the input string which needs to be processed */
 +					ZEND_ASSERT(!in_len);
 +					transfer_encode_mime_bytes(&tmpbuf, &buf, base64);
- 					MB_CONVERT_BUF_ENSURE(&buf, buf.out, buf.limit, 2);
++					if (!MB_CONVERT_BUF_ENSURE_EX(&buf, buf.out, buf.limit, 2)) {
++						return NULL;
++					}
 +					buf.out = mb_convert_buf_add2(buf.out, '?', '=');
 +					mb_convert_buf_free(&tmpbuf);
 +					return mb_convert_buf_result(&buf, &mbfl_encoding_utf8);
 +				} else {
 +					/* It's possible that more chars might fit on the current line,
 +					 * so back up to where we were before emitting any ending bytes */
 +					mb_convert_buf_reset(&tmpbuf, tmppos2);
 +					tmpbuf.state = tmpstate2;
 +				}
 +			} else {
 +				/* Converting 'n' more wchars on this line would be too much.
 +				 * Back up to where we were before we tried that. */
 +				mb_convert_buf_reset(&tmpbuf, tmppos);
 +				tmpbuf.state = tmpstate;
 +
 +				if (n == 1) {
 +					/* We have found the exact number of chars which will fit on the
 +					 * current line. Finish up and move to a new line. */
 +					outcode->from_wchar(NULL, 0, &tmpbuf, true);
 +					transfer_encode_mime_bytes(&tmpbuf, &buf, base64);
 +					tmpbuf.state = 0;
 +
- 					MB_CONVERT_BUF_ENSURE(&buf, buf.out, buf.limit, 3 + linefeed_len);
++					if (!MB_CONVERT_BUF_ENSURE_EX(&buf, buf.out, buf.limit, 3 + linefeed_len)) {
++						return NULL;
++					}
 +					buf.out = mb_convert_buf_add2(buf.out, '?', '=');
 +
 +					indent = 0; /* Indent argument must only affect the first line */
 +
 +					if (in_len || p < e) {
 +						/* We still have more input to process */
 +						buf.out = mb_convert_buf_appendn(buf.out, linefeed, linefeed_len);
 +						buf.out = mb_convert_buf_add(buf.out, ' ');
 +						line_start = mb_convert_buf_len(&buf);
 +						offset = e - p;
 +						if (in_len && (MBSTRING_HEADER_ENC_WCHAR_BUFSIZE - offset >= MBSTRING_MIN_WCHAR_BUFSIZE)) {
 +							/* Copy any remaining wchars to beginning of buffer and refill
 +							 * the rest of the buffer */
 +							memmove(wchar_buf, p, offset * sizeof(uint32_t));
 +							goto refill_wchar_buf;
 +						}
 +						goto start_new_line;
 +					} else {
 +						/* We are done! */
 +						mb_convert_buf_free(&tmpbuf);
 +						return mb_convert_buf_result(&buf, &mbfl_encoding_utf8);
 +					}
 +				} else {
 +					/* Try a smaller number of wchars */
 +					n = MAX(n >> 1, 1);
 +				}
 +			}
 +		}
 +	}
  }
 -/* }}} */

 -static void php_mb_gpc_get_detect_order(const zend_encoding ***list, size_t *list_size) /* {{{ */
 +PHP_FUNCTION(mb_encode_mimeheader)
  {
 -	*list = (const zend_encoding **)MBSTRG(http_input_list);
 -	*list_size = MBSTRG(http_input_list_size);
 +	const mbfl_encoding *charset = &mbfl_encoding_pass;
 +	zend_string *str, *charset_name = NULL, *transenc_name = NULL;
 +	char *linefeed = "\r\n";
 +	size_t linefeed_len = 2;
 +	zend_long indent = 0;
 +	bool base64 = true;
 +
 +	ZEND_PARSE_PARAMETERS_START(1, 5)
 +		Z_PARAM_STR(str)
 +		Z_PARAM_OPTIONAL
 +		Z_PARAM_STR(charset_name)
 +		Z_PARAM_STR(transenc_name)
 +		Z_PARAM_STRING(linefeed, linefeed_len)
 +		Z_PARAM_LONG(indent)
 +	ZEND_PARSE_PARAMETERS_END();
 +
 +	if (charset_name != NULL) {
 +		charset = php_mb_get_encoding(charset_name, 2);
 +		if (!charset) {
 +			RETURN_THROWS();
 +		} else if (charset->mime_name == NULL || charset->mime_name[0] == '\0' || charset == &mbfl_encoding_qprint) {
 +			zend_argument_value_error(2, "\"%s\" cannot be used for MIME header encoding", ZSTR_VAL(charset_name));
 +			RETURN_THROWS();
 +		}
 +	} else {
 +		const mbfl_language *lang = mbfl_no2language(MBSTRG(language));
 +		if (lang != NULL) {
 +			charset = mbfl_no2encoding(lang->mail_charset);
 +			const mbfl_encoding *transenc = mbfl_no2encoding(lang->mail_header_encoding);
 +			char t = transenc->name[0];
 +			if (t == 'Q' || t == 'q') {
 +				base64 = false;
 +			}
 +		}
 +	}
 +
 +	if (transenc_name != NULL && ZSTR_LEN(transenc_name) > 0) {
 +		char t = ZSTR_VAL(transenc_name)[0];
 +		if (t == 'Q' || t == 'q') {
 +			base64 = false;
 +		}
 +	}
 +
- 	RETURN_STR(mb_mime_header_encode(str, MBSTRG(current_internal_encoding), charset, base64, linefeed, linefeed_len, indent));
++	zend_string *ret = mb_mime_header_encode(str, MBSTRG(current_internal_encoding), charset, base64, linefeed, linefeed_len, indent);
++	if (UNEXPECTED(ret == NULL)) {
++		RETURN_OOM();
++	}
++
++	RETURN_STR(ret);
  }
 -/* }}} */

 -static void php_mb_gpc_set_input_encoding(const zend_encoding *encoding) /* {{{ */
 +static int8_t decode_base64(unsigned char c)
  {
 -	MBSTRG(http_input_identify) = (const mbfl_encoding*)encoding;
 +	if (c >= 'A' && c <= 'Z') {
 +		return c - 'A';
 +	} else if (c >= 'a' && c <= 'z') {
 +		return c - 'a' + 26;
 +	} else if (c >= '0' && c <= '9') {
 +		return c - '0' + 52;
 +	} else if (c == '+') {
 +		return 62;
 +	} else if (c == '/') {
 +		return 63;
 +	}
 +	return -1;
 +}
 +
 +static int8_t qprint_map[] = {
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
 +	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 +	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
 +};
 +
 +/* Decode MIME encoded word as defined in RFC 2047 */
 +static unsigned char* mime_header_decode_encoded_word(unsigned char *p, unsigned char *e, const mbfl_encoding *outcode, mb_convert_buf *outbuf, unsigned int *state)
 +{
 +	if ((e - p) < 6) {
 +		return NULL;
 +	}
 +
 +	ZEND_ASSERT(p[0] == '=');
 +	ZEND_ASSERT(p[1] == '?');
 +	p += 2;
 +
 +	unsigned char *charset = p;
 +	unsigned char *charset_end = memchr(charset, '?', e - charset);
 +	if (charset_end == NULL) {
 +		return NULL;
 +	}
 +
 +	unsigned char *encoding = charset_end + 1;
 +	p = encoding + 1;
 +	if (p >= e || *p++ != '?') {
 +		return NULL;
 +	}
 +
 +	char *charset_name = estrndup((const char*)charset, charset_end - charset);
 +	const mbfl_encoding *incode = mbfl_name2encoding(charset_name);
 +	efree(charset_name);
 +	if (incode == NULL) {
 +		return NULL;
 +	}
 +
 +	unsigned char *end_marker = (unsigned char*)zend_memnstr((const char*)p, "?=", 2, (const char*)e);
 +	if (end_marker) {
 +		e = end_marker;
 +	} else if (p < e && *(e-1) == '?') {
 +		/* If encoded word is not properly terminated, but last byte is '?',
 +		 * take that as a terminator (legacy behavior) */
 +		e--;
 +	}
 +
 +	unsigned char *buf = emalloc(e - p), *bufp = buf;
 +	if (*encoding == 'Q' || *encoding == 'q') {
 +		/* Fill `buf` with bytes from decoding QPrint */
 +		while (p < e) {
 +			unsigned char c = *p++;
 +			if (c == '_') {
 +				*bufp++ = ' ';
 +				continue;
 +			} else if (c == '=' && (e - p) >= 2) {
 +				unsigned char c2 = *p++;
 +				unsigned char c3 = *p++;
 +				if (qprint_map[c2] >= 0 && qprint_map[c3] >= 0) {
 +					*bufp++ = (qprint_map[c2] << 4) | (qprint_map[c3] & 0xF);
 +					continue;
 +				} else if (c2 == '\r') {
 +					if (c3 != '\n') {
 +						p--;
 +					}
 +					continue;
 +				} else if (c2 == '\n') {
 +					p--;
 +					continue;
 +				}
 +			}
 +			*bufp++ = c;
 +		}
 +	} else if (*encoding == 'B' || *encoding == 'b') {
 +		/* Fill `buf` with bytes from decoding Base64 */
 +		unsigned int bits = 0, cache = 0;
 +		while (p < e) {
 +			unsigned char c = *p++;
 +			if (c == '\r' || c == '\n' || c == ' ' || c == '\t' || c == '=') {
 +				continue;
 +			}
 +			int8_t decoded = decode_base64(c);
 +			if (decoded == -1) {
 +				*bufp++ = '?';
 +				continue;
 +			}
 +			bits += 6;
 +			cache = (cache << 6) | (decoded & 0x3F);
 +			if (bits == 24) {
 +				*bufp++ = (cache >> 16) & 0xFF;
 +				*bufp++ = (cache >> 8) & 0xFF;
 +				*bufp++ = cache & 0xFF;
 +				bits = cache = 0;
 +			}
 +		}
 +		if (bits == 18) {
 +			*bufp++ = (cache >> 10) & 0xFF;
 +			*bufp++ = (cache >> 2) & 0xFF;
 +		} else if (bits == 12) {
 +			*bufp++ = (cache >> 4) & 0xFF;
 +		}
 +	} else {
 +		efree(buf);
 +		return NULL;
 +	}
 +
 +	size_t in_len = bufp - buf;
 +	uint32_t wchar_buf[128];
 +
 +	bufp = buf;
 +	while (in_len) {
 +		size_t out_len = incode->to_wchar(&bufp, &in_len, wchar_buf, 128, state);
 +		ZEND_ASSERT(out_len <= 128);
 +		outcode->from_wchar(wchar_buf, out_len, outbuf, false);
 +	}
 +
 +	efree(buf);
 +	return e + 2;
 +}
 +
 +static zend_string* mb_mime_header_decode(zend_string *input, const mbfl_encoding *outcode)
 +{
 +	unsigned char *p = (unsigned char*)ZSTR_VAL(input), *e = p + ZSTR_LEN(input);
 +	unsigned int state = 0;
 +	bool space_pending = false;
 +
 +	mb_convert_buf buf;
 +	mb_convert_buf_init(&buf, ZSTR_LEN(input), '?', MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR);
 +
 +	while (p < e) {
 +		unsigned char c = *p;
 +
 +		if (c == '=' && *(p + 1) == '?' && (e - p) >= 6) {
 +			/* Does this look like a MIME encoded word? If so, try to decode it as one */
 +			unsigned char *incode_end = memchr(p + 2, '?', e - p - 2);
 +			if (incode_end && (e - incode_end) >= 3) {
 +				unsigned char *temp = mime_header_decode_encoded_word(p, e, outcode, &buf, &state);
 +				if (temp) {
 +					p = temp;
 +					/* Decoding of MIME encoded word was successful;
 +					 * Try to collapse a run of whitespace */
 +					if (p < e && (*p == '\n' || *p == '\r' || *p == '\t' || *p == ' ')) {
 +						do {
 +							p++;
 +						} while (p < e && (*p == '\n' || *p == '\r' || *p == '\t' || *p == ' '));
 +						/* We will only actually output a space if this is not immediately followed
 +						 * by another valid encoded word */
 +						space_pending = true;
 +					} else {
 +						space_pending = false;
 +					}
 +					continue;
 +				}
 +			}
 +		}
 +
 +		if (space_pending) {
 +			uint32_t space = ' ';
 +			outcode->from_wchar(&space, 1, &buf, false);
 +			space_pending = false;
 +		}
 +
 +		/* Consume a run of plain ASCII characters */
 +		if (c != '\n' && c != '\r') {
 +			unsigned char *end = p + 1;
 +			while (end < e && (*end != '=' && *end != '\n' && *end != '\r')) {
 +				end++;
 +			}
 +			uint32_t wchar_buf[128];
 +			size_t in_len = end - p;
 +			while (in_len) {
 +				size_t out_len = mbfl_encoding_ascii.to_wchar(&p, &in_len, wchar_buf, 128, &state);
 +				ZEND_ASSERT(out_len <= 128);
 +				outcode->from_wchar(wchar_buf, out_len, &buf, false);
 +			}
 +		}
 +		/* Collapse a run of whitespace into a single space */
 +		if (p < e && (*p == '\n' || *p == '\r')) {
 +			do {
 +				p++;
 +			} while (p < e && (*p == '\n' || *p == '\r' || *p == '\t' || *p == ' '));
 +			if (p < e) {
 +				/* Emulating legacy behavior of mb_decode_mimeheader here;
 +				 * a run of whitespace is not converted to a space at the very
 +				 * end of the input string */
 +				uint32_t space = ' ';
 +				outcode->from_wchar(&space, 1, &buf, false);
 +			}
 +		}
 +	}
 +
 +	outcode->from_wchar(NULL, 0, &buf, true);
 +
 +	return mb_convert_buf_result(&buf, outcode);
 +}
 +
 +PHP_FUNCTION(mb_decode_mimeheader)
 +{
 +	zend_string *str;
 +
 +	ZEND_PARSE_PARAMETERS_START(1, 1)
 +		Z_PARAM_STR(str)
 +	ZEND_PARSE_PARAMETERS_END();
 +
 +	RETURN_STR(mb_mime_header_decode(str, MBSTRG(current_internal_encoding)));
  }
 -/* }}} */
diff --cc ext/mbstring/tests/oom_mb_decode_mimeheader.phpt
index 00000000000,85bbddd7297..c0678184139
mode 000000,100644..100644
--- a/ext/mbstring/tests/oom_mb_decode_mimeheader.phpt
+++ b/ext/mbstring/tests/oom_mb_decode_mimeheader.phpt
@@@ -1,0 -1,15 +1,14 @@@
+ --TEST--
+ mb_decode_mimeheader() doesn't reach hard limit
+ --EXTENSIONS--
+ mbstring
+ --INI--
+ memory_limit=8M
 -memory_hard_limit_percent=125
+ --FILE--
+ <?php
+ $encoded = str_repeat("=?UTF-8?B?w6nDqcOp?= ", (int) (5500 * 1024 / 21));
+ mb_decode_mimeheader($encoded);
+ echo "not reached\n";
+ ?>
+ --EXPECTF--
+ Fatal error: Allowed memory size of 8388608 bytes exhausted %S(tried to allocate %d bytes) in %s on line 3
diff --cc ext/mbstring/tests/oom_mb_parse_str_vars.phpt
index 00000000000,00000000000..8c1a91049b6
new file mode 100644
--- /dev/null
+++ b/ext/mbstring/tests/oom_mb_parse_str_vars.phpt
@@@ -1,0 -1,0 +1,22 @@@
++--TEST--
++mb_parse_str() doesn't reach hard limit - variable registration
++--EXTENSIONS--
++mbstring
++--INI--
++memory_limit=8M
++max_input_vars=10000000
++--FILE--
++<?php
++/* 100000 variables, under 1 MB of input: the name/value tables fit, but
++ * registering every variable does not. */
++$query = '';
++for ($i = 0; $i < 100000; $i++) {
++    $query .= ($i ? '&' : '') . "k$i=v";
++}
++
++mb_parse_str($query, $result);
++
++echo "not reached\n";
++?>
++--EXPECTF--
++Fatal error: Allowed memory size of 8388608 bytes exhausted %S(tried to allocate %d bytes) in %s on line %d
diff --cc ext/mbstring/tests/oom_mb_str_pad.phpt
index 00000000000,00000000000..ea8e0c5e3d7
new file mode 100644
--- /dev/null
+++ b/ext/mbstring/tests/oom_mb_str_pad.phpt
@@@ -1,0 -1,0 +1,14 @@@
++--TEST--
++mb_str_pad() doesn't reach hard limit
++--EXTENSIONS--
++mbstring
++--INI--
++memory_limit=8M
++--FILE--
++<?php
++mb_str_pad("a", 100 * 1024 * 1024);
++
++echo "not reached\n";
++?>
++--EXPECTF--
++Fatal error: Allowed memory size of 8388608 bytes exhausted %S(tried to allocate %d bytes) in %s on line %d
diff --cc ext/mbstring/tests/oom_mb_substr_count.phpt
index 00000000000,9e346ed86f3..838555a8756
mode 000000,100644..100644
--- a/ext/mbstring/tests/oom_mb_substr_count.phpt
+++ b/ext/mbstring/tests/oom_mb_substr_count.phpt
@@@ -1,0 -1,14 +1,17 @@@
+ --TEST--
+ mb_substr_count() doesn't reach hard limit
+ --EXTENSIONS--
+ mbstring
+ --INI--
+ memory_limit=8M
+ --FILE--
+ <?php
 -// 3 MB needle -> 12 MB wchar buffer, memory_limit is 8 MB
 -mb_substr_count("abc", str_repeat("a", 3 * 1024 * 1024));
++/* The needle is converted to UTF-8 before counting. 0x80 is U+20AC in CP1252,
++ * which takes 3 bytes in UTF-8, so a 2 MB needle (memory_limit / 4) grows into
++ * a 6 MB buffer. */
++mb_substr_count("abc", str_repeat("\x80", 2 * 1024 * 1024), "CP1252");
++
+ echo "not reached\n";
+ ?>
+ --EXPECTF--
+ Fatal error: Allowed memory size of 8388608 bytes exhausted %S(tried to allocate %d bytes) in %s on line %d
diff --cc ext/opcache/ZendAccelerator.c
index f8564999eef,586e3aaa47d..9355604141b
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@@ -4656,243 -4601,206 +4664,244 @@@ static void preload_send_header(sapi_he
  {
  }

 -static int accel_finish_startup(void)
 +#ifndef ZEND_WIN32
 +static zend_result accel_finish_startup_preload(bool in_child)
  {
 -	if (!ZCG(enabled) || !accel_startup_ok) {
 -		return SUCCESS;
 -	}
 +	zend_result ret = SUCCESS;
 +	int orig_error_reporting;
 +
 +	int (*orig_activate)(void) = sapi_module.activate;
 +	int (*orig_deactivate)(void) = sapi_module.deactivate;
 +	void (*orig_register_server_variables)(zval *track_vars_array) = sapi_module.register_server_variables;
 +	int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers) = sapi_module.header_handler;
 +	int (*orig_send_headers)(sapi_headers_struct *sapi_headers) = sapi_module.send_headers;
 +	void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context)= sapi_module.send_header;
 +	char *(*orig_getenv)(const char *name, size_t name_len) = sapi_module.getenv;
 +	size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write;
 +	void (*orig_flush)(void *server_context) = sapi_module.flush;
 +#ifdef ZEND_SIGNALS
 +	bool old_reset_signals = SIGG(reset);
 +#endif
 +
 +	sapi_module.activate = NULL;
 +	sapi_module.deactivate = NULL;
 +	sapi_module.register_server_variables = NULL;
 +	sapi_module.header_handler = preload_header_handler;
 +	sapi_module.send_headers = preload_send_headers;
 +	sapi_module.send_header = preload_send_header;
 +	sapi_module.getenv = NULL;
 +	sapi_module.ub_write = preload_ub_write;
 +	sapi_module.flush = preload_flush;
 +
 +	zend_interned_strings_switch_storage(1);

 -	if (ZCG(accel_directives).preload && *ZCG(accel_directives).preload) {
 -#ifdef ZEND_WIN32
 -		zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Preloading is not supported on Windows");
 -		return FAILURE;
 -#else
 -		bool in_child = false;
 -		int ret = SUCCESS;
 -		int rc;
 -		int orig_error_reporting;
 -
 -		int (*orig_activate)(void) = sapi_module.activate;
 -		int (*orig_deactivate)(void) = sapi_module.deactivate;
 -		void (*orig_register_server_variables)(zval *track_vars_array) = sapi_module.register_server_variables;
 -		int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers) = sapi_module.header_handler;
 -		int (*orig_send_headers)(sapi_headers_struct *sapi_headers) = sapi_module.send_headers;
 -		void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context)= sapi_module.send_header;
 -		char *(*orig_getenv)(const char *name, size_t name_len) = sapi_module.getenv;
 -		size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write;
 -		void (*orig_flush)(void *server_context) = sapi_module.flush;
  #ifdef ZEND_SIGNALS
 -		bool old_reset_signals = SIGG(reset);
 +	SIGG(reset) = false;
  #endif

 -		if (UNEXPECTED(file_cache_only)) {
 -			zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode");
 -			return SUCCESS;
 +	orig_error_reporting = EG(error_reporting);
 +	EG(error_reporting) = 0;
 +
 +	const zend_result rc = php_request_startup();
++	zend_mm_set_max_allocs(0);
 +
 +	EG(error_reporting) = orig_error_reporting;
 +
 +	if (rc == SUCCESS) {
 +		bool orig_report_memleaks;
 +
 +		/* don't send headers */
 +		SG(headers_sent) = true;
 +		SG(request_info).no_headers = true;
 +		php_output_set_status(0);
 +
 +		ZCG(auto_globals_mask) = 0;
 +		ZCG(request_time) = (time_t)sapi_get_request_time();
 +		ZCG(cache_opline) = NULL;
 +		ZCG(cache_persistent_script) = NULL;
 +		ZCG(include_path_key_len) = 0;
 +		ZCG(include_path_check) = true;
 +
 +		ZCG(cwd) = NULL;
 +		ZCG(cwd_key_len) = 0;
 +		ZCG(cwd_check) = true;
 +
 +		if (accel_preload(ZCG(accel_directives).preload, in_child) != SUCCESS) {
 +			ret = FAILURE;
  		}
 +		preload_flush(NULL);

 -		/* exclusive lock */
 -		zend_shared_alloc_lock();
 +		orig_report_memleaks = PG(report_memleaks);
 +		PG(report_memleaks) = false;
 +#ifdef ZEND_SIGNALS
 +		/* We may not have registered signal handlers due to SIGG(reset)=0, so
 +		 * also disable the check that they are registered. */
 +		SIGG(check) = false;
 +#endif
 +		php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */
 +		EG(class_table) = NULL;
 +		EG(function_table) = NULL;
 +		PG(report_memleaks) = orig_report_memleaks;
 +#ifdef ZTS
 +		/* Reset the virtual CWD state back to the original state created by virtual_cwd_startup().
 +		 * This is necessary because the normal startup code assumes the CWD state is active. */
 +		virtual_cwd_activate();
 +#endif
 +	} else {
 +		zend_shared_alloc_unlock();
 +		ret = FAILURE;
 +	}
 +#ifdef ZEND_SIGNALS
 +	SIGG(reset) = old_reset_signals;
 +#endif

 -		if (ZCSG(preload_script)) {
 -			/* Preloading was done in another process */
 -			preload_load();
 -			zend_shared_alloc_unlock();
 -			return SUCCESS;
 +	sapi_module.activate = orig_activate;
 +	sapi_module.deactivate = orig_deactivate;
 +	sapi_module.register_server_variables = orig_register_server_variables;
 +	sapi_module.header_handler = orig_header_handler;
 +	sapi_module.send_headers = orig_send_headers;
 +	sapi_module.send_header = orig_send_header;
 +	sapi_module.getenv = orig_getenv;
 +	sapi_module.ub_write = orig_ub_write;
 +	sapi_module.flush = orig_flush;
 +
 +	sapi_activate();
 +
 +	return ret;
 +}
 +
 +static zend_result accel_finish_startup_preload_subprocess(pid_t *pid)
 +{
 +	uid_t euid = geteuid();
 +	if (euid != 0) {
 +		if (ZCG(accel_directives).preload_user
 +		 && *ZCG(accel_directives).preload_user) {
 +			zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored because the current user is not \"root\"");
  		}

 -		if (geteuid() == 0) {
 -			pid_t pid;
 -			struct passwd *pw;
 +		*pid = -1;
 +		return SUCCESS;
 +	}

 -			if (!ZCG(accel_directives).preload_user
 -			 || !*ZCG(accel_directives).preload_user) {
 -				zend_shared_alloc_unlock();
 -				zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload_user\" has not been defined");
 -				return FAILURE;
 -			}
 +	if (!ZCG(accel_directives).preload_user
 +	 || !*ZCG(accel_directives).preload_user) {

 -			pw = getpwnam(ZCG(accel_directives).preload_user);
 -			if (pw == NULL) {
 -				zend_shared_alloc_unlock();
 -				zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user);
 -				return FAILURE;
 -			}
 +		bool sapi_requires_preload_user = !(strcmp(sapi_module.name, "cli") == 0
 +		  || strcmp(sapi_module.name, "phpdbg") == 0);

 -			pid = fork();
 -			if (pid == -1) {
 -				zend_shared_alloc_unlock();
 -				zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to fork()");
 -				return FAILURE;
 -			} else if (pid == 0) { /* children */
 -				if (setgid(pw->pw_gid) < 0) {
 -					zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setgid(%d)", pw->pw_gid);
 -					exit(1);
 -				}
 -				if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
 -					zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to initgroups(\"%s\", %d)", pw->pw_name, pw->pw_uid);
 -					exit(1);
 -				}
 -				if (setuid(pw->pw_uid) < 0) {
 -					zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setuid(%d)", pw->pw_uid);
 -					exit(1);
 -				}
 -				in_child = true;
 -			} else { /* parent */
 -				int status;
 +		if (!sapi_requires_preload_user) {
 +			*pid = -1;
 +			return SUCCESS;
 +		}

 -				if (waitpid(pid, &status, 0) < 0) {
 -					zend_shared_alloc_unlock();
 -					zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid);
 -					return FAILURE;
 -				}
 +		zend_shared_alloc_unlock();
 +		zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload\" requires \"opcache.preload_user\" when running under uid 0");
 +		return FAILURE;
 +	}

 -				if (ZCSG(preload_script)) {
 -					preload_load();
 -				}
 +	struct passwd *pw = getpwnam(ZCG(accel_directives).preload_user);
 +	if (pw == NULL) {
 +		zend_shared_alloc_unlock();
 +		zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user);
 +		return FAILURE;
 +	}

 -				zend_shared_alloc_unlock();
 -				if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
 -					return SUCCESS;
 -				} else {
 -					return FAILURE;
 -				}
 -			}
 -		} else {
 -			if (ZCG(accel_directives).preload_user
 -			 && *ZCG(accel_directives).preload_user) {
 -				zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored");
 -			}
 +	if (pw->pw_uid == euid) {
 +		*pid = -1;
 +		return SUCCESS;
 +	}
 +
 +	*pid = fork();
 +	if (*pid == -1) {
 +		zend_shared_alloc_unlock();
 +		zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to fork()");
 +		return FAILURE;
 +	}
 +
 +	if (*pid == 0) { /* children */
 +		if (setgid(pw->pw_gid) < 0) {
 +			zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setgid(%d)", pw->pw_gid);
 +			exit(1);
 +		}
 +		if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
 +			zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to initgroups(\"%s\", %d)", pw->pw_name, pw->pw_uid);
 +			exit(1);
  		}
 +		if (setuid(pw->pw_uid) < 0) {
 +			zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setuid(%d)", pw->pw_uid);
 +			exit(1);
 +		}
 +	}

 -		sapi_module.activate = NULL;
 -		sapi_module.deactivate = NULL;
 -		sapi_module.register_server_variables = NULL;
 -		sapi_module.header_handler = preload_header_handler;
 -		sapi_module.send_headers = preload_send_headers;
 -		sapi_module.send_header = preload_send_header;
 -		sapi_module.getenv = NULL;
 -		sapi_module.ub_write = preload_ub_write;
 -		sapi_module.flush = preload_flush;
 +	return SUCCESS;
 +}
 +#endif /* ZEND_WIN32 */

 -		zend_interned_strings_switch_storage(1);
 +static zend_result accel_finish_startup(void)
 +{
 +	if (!ZCG(enabled) || !accel_startup_ok) {
 +		return SUCCESS;
 +	}

 -#ifdef ZEND_SIGNALS
 -		SIGG(reset) = false;
 -#endif
 +	if (!(ZCG(accel_directives).preload && *ZCG(accel_directives).preload)) {
 +		return SUCCESS;
 +	}

 -		orig_error_reporting = EG(error_reporting);
 -		EG(error_reporting) = 0;
 +#ifdef ZEND_WIN32
 +	zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Preloading is not supported on Windows");
 +	return FAILURE;
 +#else /* ZEND_WIN32 */

 -		rc = php_request_startup();
 -		zend_mm_set_max_allocs(0);
 +	if (UNEXPECTED(file_cache_only)) {
 +		zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode");
 +		return SUCCESS;
 +	}

 -		EG(error_reporting) = orig_error_reporting;
 +	/* exclusive lock */
 +	zend_shared_alloc_lock();

 -		if (rc == SUCCESS) {
 -			bool orig_report_memleaks;
 +	if (ZCSG(preload_script)) {
 +		/* Preloading was done in another process */
 +		preload_load();
 +		zend_shared_alloc_unlock();
 +		return SUCCESS;
 +	}

 -			/* don't send headers */
 -			SG(headers_sent) = true;
 -			SG(request_info).no_headers = true;
 -			php_output_set_status(0);

 -			ZCG(auto_globals_mask) = 0;
 -			ZCG(request_time) = (time_t)sapi_get_request_time();
 -			ZCG(cache_opline) = NULL;
 -			ZCG(cache_persistent_script) = NULL;
 -			ZCG(include_path_key_len) = 0;
 -			ZCG(include_path_check) = true;
 +	pid_t pid;
 +	if (accel_finish_startup_preload_subprocess(&pid) == FAILURE) {
 +		zend_shared_alloc_unlock();
 +		return FAILURE;
 +	}

 -			ZCG(cwd) = NULL;
 -			ZCG(cwd_key_len) = 0;
 -			ZCG(cwd_check) = true;
 +	if (pid == -1) { /* no subprocess was needed */
 +		/* The called function unlocks the shared alloc lock */
 +		return accel_finish_startup_preload(false);
 +	} else if (pid == 0) { /* subprocess */
 +		const zend_result ret = accel_finish_startup_preload(true);

 -			if (accel_preload(ZCG(accel_directives).preload, in_child) != SUCCESS) {
 -				ret = FAILURE;
 -			}
 -			preload_flush(NULL);
 +		exit(ret == SUCCESS ? 0 : 1);
 +	} else { /* parent */
 +		int status;

 -			orig_report_memleaks = PG(report_memleaks);
 -			PG(report_memleaks) = false;
 -#ifdef ZEND_SIGNALS
 -			/* We may not have registered signal handlers due to SIGG(reset)=0, so
 -			 * also disable the check that they are registered. */
 -			SIGG(check) = false;
 -#endif
 -			php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */
 -			EG(class_table) = NULL;
 -			EG(function_table) = NULL;
 -			PG(report_memleaks) = orig_report_memleaks;
 -		} else {
 +		if (waitpid(pid, &status, 0) < 0) {
  			zend_shared_alloc_unlock();
 -			ret = FAILURE;
 +			zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid);
  		}
 -#ifdef ZEND_SIGNALS
 -		SIGG(reset) = old_reset_signals;
 -#endif

 -		sapi_module.activate = orig_activate;
 -		sapi_module.deactivate = orig_deactivate;
 -		sapi_module.register_server_variables = orig_register_server_variables;
 -		sapi_module.header_handler = orig_header_handler;
 -		sapi_module.send_headers = orig_send_headers;
 -		sapi_module.send_header = orig_send_header;
 -		sapi_module.getenv = orig_getenv;
 -		sapi_module.ub_write = orig_ub_write;
 -		sapi_module.flush = orig_flush;
 -
 -		sapi_activate();
 -
 -		if (in_child) {
 -			if (ret == SUCCESS) {
 -				exit(0);
 -			} else {
 -				exit(2);
 -			}
 +		if (ZCSG(preload_script)) {
 +			preload_load();
  		}

 -		return ret;
 -#endif
 -	}
 +		zend_shared_alloc_unlock();

 -	return SUCCESS;
 +		if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
 +			return SUCCESS;
 +		} else {
 +			return FAILURE;
 +		}
 +	}
 +#endif /* ZEND_WIN32 */
  }

  ZEND_EXT_API zend_extension zend_extension_entry = {
diff --cc ext/opcache/ZendAccelerator.h
index a25f9a1bdf0,d29c30bc06f..a10ae41aaa6
--- a/ext/opcache/ZendAccelerator.h
+++ b/ext/opcache/ZendAccelerator.h
@@@ -223,7 -228,14 +223,13 @@@ typedef struct _zend_accel_globals
  	const zend_op          *cache_opline;
  	zend_persistent_script *cache_persistent_script;
  	/* preallocated buffer for keys */
 -	zend_string             key;
 -	char                    _key[MAXPATHLEN * 8];
 +	zend_string            *key;
+
+ 	/* 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 {
diff --cc ext/opcache/zend_file_cache.c
index defc3b9942a,b3161b51fd7..8afc4a43ac5
--- a/ext/opcache/zend_file_cache.c
+++ b/ext/opcache/zend_file_cache.c
@@@ -1935,6 -1937,8 +1942,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);

  		zend_accel_hash_update(&ZCSG(hash), script->script.filename, 0, script);
diff --cc ext/session/session.c
index ccf01d093b6,c19d85b9c18..1b35bf46e7e
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@@ -886,10 -887,10 +908,10 @@@ PS_SERIALIZER_DECODE_FUNC(php_serialize
  	zval session_vars;
  	php_unserialize_data_t var_hash;
  	bool result;
 -	zend_string *var_name = zend_string_init("_SESSION", sizeof("_SESSION") - 1, 0);
 +	zend_string *var_name = ZSTR_INIT_LITERAL("_SESSION", 0);

  	ZVAL_NULL(&session_vars);
- 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
+ 	php_session_unserialize_init(&var_hash);
  	result = php_var_unserialize(
  		&session_vars, (const unsigned char **)&val, (const unsigned char *)endptr, &var_hash);
  	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
@@@ -946,12 -947,13 +968,12 @@@ PS_SERIALIZER_DECODE_FUNC(php_binary) /
  	php_unserialize_data_t var_hash;
  	zval *current, rv;

- 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
+ 	php_session_unserialize_init(&var_hash);

  	for (p = val; p < endptr; ) {
 -		// Can this be changed to size_t?
 -		int namelen = ((unsigned char)(*p)) & (~PS_BIN_UNDEF);
 +		size_t namelen = ((unsigned char)(*p)) & (~PS_BIN_UNDEF);

 -		if (namelen < 0 || namelen > PS_BIN_MAX || (p + namelen) >= endptr) {
 +		if (namelen > PS_BIN_MAX || (p + namelen) >= endptr) {
  			PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
  			return FAILURE;
  		}
diff --cc ext/simplexml/simplexml.c
index 186db442ffa,6ad25143974..7ac685e36fc
--- a/ext/simplexml/simplexml.c
+++ b/ext/simplexml/simplexml.c
@@@ -1114,8 -1108,12 +1111,12 @@@ static HashTable *sxe_get_prop_hash(zen
  			ZVAL_UNDEF(&zattr);
  			test = sxe->iter.name && sxe->iter.type == SXE_ITER_ATTRLIST;
  			while (attr) {
 -				if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr)attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
 +				if ((!test || xmlStrEqual(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr)attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
- 					ZVAL_STR(&value, sxe_xmlNodeListGetString((xmlDocPtr) sxe->document->ptr, attr->children, 1));
+ 					zend_string *str = sxe_xmlNodeListGetString((xmlDocPtr) sxe->document->ptr, attr->children, 1);
+ 					if (UNEXPECTED(!str)) {
+ 						goto oom;
+ 					}
+ 					ZVAL_STR(&value, str);
  					namelen = xmlStrlen(attr->name);
  					if (Z_ISUNDEF(zattr)) {
  						array_init(&zattr);
diff --cc ext/spl/spl_fixedarray.c
index 53dba1b727c,a1f848c4a03..1dc0f6334a7
--- a/ext/spl/spl_fixedarray.c
+++ b/ext/spl/spl_fixedarray.c
@@@ -103,18 -105,29 +103,28 @@@ static void spl_fixedarray_init_elems(s
  	}
  }

- static void spl_fixedarray_init_non_empty_struct(spl_fixedarray *array, zend_long size)
+ static zend_result spl_fixedarray_init_non_empty_struct(spl_fixedarray *array, zend_long size)
  {
  	array->size = 0; /* reset size in case ecalloc() fails */
- 	array->elements = size ? safe_emalloc(size, sizeof(zval), 0) : NULL;
- 	array->size = size;
 -	array->should_rebuild_properties = true;
  	array->cached_resize = -1;
+ 	if (size) {
+ 		array->elements = safe_femalloc(size, sizeof(zval), 0, /* fallible */ true);
+ 		if (UNEXPECTED(!array->elements)) {
+ 			return FAILURE;
+ 		}
+ 		array->size = size;
+ 	} else {
+ 		array->elements = NULL;
+ 	}
+ 	return SUCCESS;
  }

- static void spl_fixedarray_init(spl_fixedarray *array, zend_long size)
+ static zend_result spl_fixedarray_init(spl_fixedarray *array, zend_long size)
  {
  	if (size > 0) {
- 		spl_fixedarray_init_non_empty_struct(array, size);
+ 		if (UNEXPECTED(spl_fixedarray_init_non_empty_struct(array, size) == FAILURE)) {
+ 			return FAILURE;
+ 		}
  		spl_fixedarray_init_elems(array, 0, size);
  	} else {
  		spl_fixedarray_default_ctor(array);
@@@ -177,8 -194,9 +191,8 @@@ static zend_result spl_fixedarray_resiz
  {
  	if (size == array->size) {
  		/* nothing to do */
- 		return;
+ 		return SUCCESS;
  	}
 -	array->should_rebuild_properties = true;

  	/* first initialization */
  	if (array->size == 0) {
@@@ -286,13 -316,9 +306,8 @@@ static zend_object *spl_fixedarray_obje
  	zend_object_std_init(&intern->std, class_type);
  	object_properties_init(&intern->std, class_type);

- 	if (orig && clone_orig) {
- 		spl_fixedarray_object *other = spl_fixed_array_from_obj(orig);
- 		spl_fixedarray_copy_ctor(&intern->array, &other->array);
- 	}
-
  	while (parent) {
  		if (parent == spl_ce_SplFixedArray) {
 -			intern->std.handlers = &spl_handler_SplFixedArray;
  			break;
  		}

diff --cc ext/standard/array.c
index 6021f88b379,7183c8b3852..ce3f8fe9ade
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@@ -2758,93 -2672,25 +2760,97 @@@ PHP_FUNCTION(array_fill_keys
  		} \
  		size = (uint32_t)_php_math_round(__calc_size, 0, PHP_ROUND_HALF_UP); \
  		array_init_size(return_value, size); \
- 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
+ 		if (UNEXPECTED(zend_hash_real_finit_packed(Z_ARRVAL_P(return_value), /* fallible */ true) == FAILURE)) { \
+ 			RETURN_OOM(); \
+ 		} \
  	} while (0)

 -#define RANGE_CHECK_LONG_INIT_ARRAY(start, end) do { \
 -		zend_ulong __calc_size = ((zend_ulong) start - end) / lstep; \
 +#define RANGE_CHECK_LONG_INIT_ARRAY(start, end, _step) do { \
 +		zend_ulong __calc_size = ((zend_ulong) start - end) / (_step); \
  		if (__calc_size >= HT_MAX_SIZE - 1) { \
  			zend_value_error(\
 -					"The supplied range exceeds the maximum array size: start=" ZEND_LONG_FMT " end=" ZEND_LONG_FMT, end, start); \
 +					"The supplied range exceeds the maximum array size: start=" ZEND_LONG_FMT " end=" ZEND_LONG_FMT " step=" ZEND_LONG_FMT, end, start, (_step)); \
  			RETURN_THROWS(); \
  		} \
  		size = (uint32_t)(__calc_size + 1); \
  		array_init_size(return_value, size); \
- 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
+ 		if (UNEXPECTED(zend_hash_real_finit_packed(Z_ARRVAL_P(return_value), /* fallible */ true) == FAILURE)) { \
+ 			RETURN_OOM(); \
+ 		} \
  	} while (0)

 +/* Process input for the range() function
 + * 0 on exceptions
 + * IS_LONG if only interpretable as int
 + * IS_DOUBLE if only interpretable as float
 + * IS_STRING if only interpretable as string
 + * IS_ARRAY (as IS_LONG < IS_STRING < IS_ARRAY) for ambiguity of single byte strings which contains a digit */
 +static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend_long /* restrict */ *lval, double /* restrict */ *dval)
 +{
 +	switch (Z_TYPE_P(input)) {
 +		case IS_LONG:
 +			*lval = Z_LVAL_P(input);
 +			*dval = (double) Z_LVAL_P(input);
 +			return IS_LONG;
 +		case IS_DOUBLE:
 +			*dval = Z_DVAL_P(input);
 +			check_dval_value:
 +			if (zend_isinf(*dval)) {
 +				zend_argument_value_error(arg_num, "must be a finite number, INF provided");
 +				return 0;
 +			}
 +			if (zend_isnan(*dval)) {
 +				zend_argument_value_error(arg_num, "must be a finite number, NAN provided");
 +				return 0;
 +			}
 +			return IS_DOUBLE;
 +		case IS_STRING: {
 +			/* Process strings:
 +			 * - Empty strings are converted to 0 with a diagnostic
 +			 * - Check if string is numeric and store the values in passed pointer
 +			 * - If numeric float, this means it cannot be a numeric string with only one byte GOTO IS_DOUBLE
 +			 * - If numeric int, check it is one byte or not
 +			 *   - If it one byte, return IS_ARRAY as IS_LONG < IS_STRING < IS_ARRAY
 +			 *   - If not should only be interpreted as int, return IS_LONG;
 +			 * - Otherwise is a string and return IS_STRING */
 +			if (Z_STRLEN_P(input) == 0) {
 +				const char *arg_name = get_active_function_arg_name(arg_num);
 +				php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must not be empty, casted to 0", arg_num, arg_name);
 +				if (UNEXPECTED(EG(exception))) {
 +					return 0;
 +				}
 +				*lval = 0;
 +				*dval = 0.0;
 +				return IS_LONG;
 +			}
 +			uint8_t type = is_numeric_str_function(Z_STR_P(input), lval, dval);
 +			if (type == IS_DOUBLE) {
 +				goto check_dval_value;
 +			}
 +			if (type == IS_LONG) {
 +				*dval = (double) *lval;
 +				if (Z_STRLEN_P(input) == 1) {
 +					return IS_ARRAY;
 +				} else {
 +					return IS_LONG;
 +				}
 +			}
 +			if (Z_STRLEN_P(input) != 1) {
 +				const char *arg_name = get_active_function_arg_name(arg_num);
 +				php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must be a single byte, subsequent bytes are ignored", arg_num, arg_name);
 +				if (UNEXPECTED(EG(exception))) {
 +					return 0;
 +				}
 +			}
 +			/* Set fall back values to 0 in case the other argument is not a string */
 +			*lval = 0;
 +			*dval = 0.0;
 +			return IS_STRING;
 +		}
 +		EMPTY_SWITCH_DEFAULT_CASE();
 +	}
 +}
 +
  /* {{{ Create an array containing the range of integers or characters from low to high (inclusive) */
  PHP_FUNCTION(range)
  {
diff --cc ext/standard/basic_functions.c
index 35715c6abe5,6edfb5d653f..f72fed258b2
mode 100644,100755..100644
--- a/ext/standard/basic_functions.c
+++ b/ext/standard/basic_functions.c
diff --cc ext/standard/file.c
index 20d0b897045,0e378db53ee..6d04569da64
--- a/ext/standard/file.c
+++ b/ext/standard/file.c
@@@ -675,8 -681,8 +681,8 @@@ parse_eol
  			} while ((p = memchr(p, eol_marker, (e-p))));
  		}

 -		/* handle any left overs of files without new lines */
 +		/* handle any leftovers of files without new lines */
- 		if (s != e) {
+ 		if (s != e && !ZEND_HAS_OOM()) {
  			p = e;
  			goto parse_eol;
  		}
diff --cc ext/standard/string.c
index f7df2894f90,19b6e9d92ea..779dd6359d5
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@@ -987,8 -1182,18 +1015,19 @@@ PHPAPI void php_implode(const zend_stri
  	} ZEND_HASH_FOREACH_END();

  	/* numelems cannot be 0, we checked above */
- 	str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0);
+ 	/* The glue is repeated once per element, so the result can dwarf the input. */
+ 	str = zend_string_safe_falloc(numelems - 1, ZSTR_LEN(glue), len, 0, /* fallible */ true);
+ 	if (UNEXPECTED(!str)) {
+ 		while (ptr > strings) {
+ 			ptr--;
+ 			if (ptr->str && ptr->lval) {
+ 				zend_string_release_ex(ptr->str, 0);
+ 			}
+ 		}
+ 		free_alloca(strings, use_heap);
+ 		RETURN_OOM();
+ 	}
 +	GC_ADD_FLAGS(str, flags);
  	cptr = ZSTR_VAL(str) + ZSTR_LEN(str);
  	*cptr = 0;

@@@ -5174,9 -5365,12 +5281,13 @@@ PHP_FUNCTION(str_repeat
  		RETURN_EMPTY_STRING();

  	/* Initialize the result string */
- 	result = zend_string_safe_alloc(ZSTR_LEN(input_str), mult, 0, 0);
+ 	result = zend_string_safe_falloc(ZSTR_LEN(input_str), mult, 0, /*persistent */ false, /* fallible */ true);
+ 	if (UNEXPECTED(!result)) {
+ 		RETURN_OOM();
+ 	}
+
  	result_len = ZSTR_LEN(input_str) * mult;
 +	ZSTR_COPY_CONCAT_PROPERTIES(result, input_str);

  	/* Heavy optimization for situations where input string is 1 byte long */
  	if (ZSTR_LEN(input_str) == 1) {
diff --cc ext/standard/var.c
index d1ae076992d,429e90d48f1..400d88c39a1
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@@ -777,15 -770,80 +780,65 @@@ static inline bool php_var_serialize_cl
  }
  /* }}} */

+ static bool php_var_serialize_incomplete_raw(smart_str *buf, zval *struc)
+ {
+ 	HashTable *props = Z_OBJ_P(struc)->properties;
+ 	zend_string *class_name;
+ 	zval *member;
+
+ 	if (!props) {
+ 		return false;
+ 	}
+
+ 	member = zend_hash_str_find(props, MAGIC_MEMBER_SERIALIZED, sizeof(MAGIC_MEMBER_SERIALIZED) - 1);
+ 	if (member && Z_TYPE_P(member) == IS_STRING) {
+ 		class_name = php_lookup_class_name(Z_OBJ_P(struc));
+ 		if (!class_name) {
+ 			return false;
+ 		}
+ 		smart_str_appendl(buf, "C:", 2);
+ 		smart_str_append_unsigned(buf, ZSTR_LEN(class_name));
+ 		smart_str_appendl(buf, ":\"", 2);
+ 		smart_str_append(buf, class_name);
+ 		smart_str_appendl(buf, "\":", 2);
+ 		smart_str_append_unsigned(buf, Z_STRLEN_P(member));
+ 		smart_str_appendl(buf, ":{", 2);
+ 		smart_str_append(buf, Z_STR_P(member));
+ 		smart_str_appendc(buf, '}');
+ 		zend_string_release_ex(class_name, 0);
+ 		return true;
+ 	}
+
+ 	member = zend_hash_str_find(props, MAGIC_MEMBER_ENUM_CASE, sizeof(MAGIC_MEMBER_ENUM_CASE) - 1);
+ 	if (member && Z_TYPE_P(member) == IS_STRING) {
+ 		class_name = php_lookup_class_name(Z_OBJ_P(struc));
+ 		if (!class_name) {
+ 			return false;
+ 		}
+ 		smart_str_appendl(buf, "E:", 2);
+ 		smart_str_append_unsigned(buf, ZSTR_LEN(class_name) + strlen(":") + Z_STRLEN_P(member));
+ 		smart_str_appendl(buf, ":\"", 2);
+ 		smart_str_append(buf, class_name);
+ 		smart_str_appendc(buf, ':');
+ 		smart_str_append(buf, Z_STR_P(member));
+ 		smart_str_appendl(buf, "\";", 2);
+ 		zend_string_release_ex(class_name, 0);
+ 		return true;
+ 	}
+
+ 	/* Other incomplete classes are handled using the normal path */
+ 	return false;
+ }
+
  static HashTable* php_var_serialize_call_sleep(zend_object *obj, zend_function *fn) /* {{{ */
  {
 -	zend_result res;
 -	zend_fcall_info fci;
 -	zend_fcall_info_cache fci_cache;
  	zval retval;

 -	fci.size = sizeof(fci);
 -	fci.object = obj;
 -	fci.retval = &retval;
 -	fci.param_count = 0;
 -	fci.params = NULL;
 -	fci.named_params = NULL;
 -	ZVAL_UNDEF(&fci.function_name);
 -
 -	fci_cache.function_handler = fn;
 -	fci_cache.object = obj;
 -	fci_cache.called_scope = obj->ce;
 -
  	BG(serialize_lock)++;
 -	res = zend_call_function(&fci, &fci_cache);
 +	zend_call_known_instance_method(fn, obj, &retval, /* param_count */ 0, /* params */ NULL);
  	BG(serialize_lock)--;

 -	if (res == FAILURE || Z_ISUNDEF(retval)) {
 +	if (Z_ISUNDEF(retval) || EG(exception)) {
  		zval_ptr_dtor(&retval);
  		return NULL;
  	}
diff --cc ext/standard/var_unserializer.re
index 1d23269ef03,f60dfbb63f7..954ad08dbf8
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@@ -827,7 -882,9 +882,9 @@@ static inline int object_common(UNSERIA
  		return 0;
  	}

- 	zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, HT_IS_PACKED(ht));
 -	if (UNEXPECTED(zend_hash_fextend(ht, zend_hash_num_elements(ht) + elements, HT_FLAGS(ht) & HASH_FLAG_PACKED) == FAILURE)) {
++	if (UNEXPECTED(zend_hash_fextend(ht, zend_hash_num_elements(ht) + elements, HT_IS_PACKED(ht)) == FAILURE)) {
+ 		return 0;
+ 	}
  	if (!process_nested_object_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJ_P(rval))) {
  		if (has_wakeup) {
  			ZVAL_DEREF(rval);
diff --cc ext/xml/xml.c
index c4c1ac31a4b,451b4f3da40..62286575904
--- a/ext/xml/xml.c
+++ b/ext/xml/xml.c
@@@ -588,28 -592,11 +591,33 @@@ static zend_string *_xml_decode_tag(xml
  }
  /* }}} */

 +static zval *xml_get_separated_data(xml_parser *parser)
 +{
 +	if (EXPECTED(Z_TYPE_P(Z_REFVAL(parser->data)) == IS_ARRAY)) {
 +		SEPARATE_ARRAY(Z_REFVAL(parser->data));
 +		return Z_REFVAL(parser->data);
 +	}
 +	return NULL;
 +}
 +
 +static zval *xml_get_ctag(xml_parser *parser)
 +{
 +	zval *data = xml_get_separated_data(parser);
 +	if (EXPECTED(data)) {
 +		zval *zv = zend_hash_index_find_deref(Z_ARRVAL_P(data), parser->ctag_index);
 +		if (EXPECTED(zv && Z_TYPE_P(zv) == IS_ARRAY)) {
 +			SEPARATE_ARRAY(zv);
 +			return zv;
 +		}
 +	}
 +	return NULL;
 +}
 +
+ static zend_always_inline bool xml_handler_aborted(void)
+ {
+ 	return EG(exception) != NULL || ZEND_HAS_OOM();
+ }
+
  /* {{{ _xml_startElementHandler() */
  void _xml_startElementHandler(void *userData, const XML_Char *name, const XML_Char **attributes)
  {
diff --cc ext/xmlwriter/php_xmlwriter.c
index aacbfdb6447,6baa51ce53f..5eae605802b
--- a/ext/xmlwriter/php_xmlwriter.c
+++ b/ext/xmlwriter/php_xmlwriter.c
@@@ -894,20 -997,29 +894,24 @@@ static void php_xmlwriter_flush(INTERNA
  	}
  	XMLWRITER_FROM_OBJECT(ptr, self);

 -	if (ptr) {
 -		buffer = Z_XMLWRITER_P(self)->output;
 -		if (force_string == 1 && buffer == NULL) {
 -			RETURN_EMPTY_STRING();
 +	buffer = Z_XMLWRITER_P(self)->output;
 +	if (force_string == 1 && buffer == NULL) {
 +		RETURN_EMPTY_STRING();
 +	}
 +	output_bytes = xmlTextWriterFlush(ptr);
 +	if (buffer) {
 +		const xmlChar *content = xmlBufferContent(buffer);
- 		RETVAL_STRING((const char *) content);
++		zend_string *ret = zend_string_finit((const char *) content, strlen((const char *) content), /* persistent */ false, /* fallible */ true);
++		if (UNEXPECTED(!ret)) {
++			RETURN_OOM();
+ 		}
 -		output_bytes = xmlTextWriterFlush(ptr);
 -		if (buffer) {
 -			const xmlChar *content = xmlBufferContent(buffer);
 -			zend_string *ret = zend_string_finit((const char *) content, strlen((const char *) content), /* persistent */ false, /* fallible */ true);
 -			if (UNEXPECTED(!ret)) {
 -				RETURN_OOM();
 -			}
 -			RETVAL_NEW_STR(ret);
 -			if (empty) {
 -				xmlBufferEmpty(buffer);
 -			}
 -		} else {
 -			RETVAL_LONG(output_bytes);
++		RETVAL_NEW_STR(ret);
 +		if (empty) {
 +			xmlBufferEmpty(buffer);
  		}
 -		return;
 +	} else {
 +		RETVAL_LONG(output_bytes);
  	}
 -
 -	RETURN_EMPTY_STRING();
  }
  /* }}} */

diff --cc ext/zend_test/test.c
index 67150d47b04,a6cba8ddf40..0d493eeecf0
--- a/ext/zend_test/test.c
+++ b/ext/zend_test/test.c
@@@ -32,9 -31,10 +32,11 @@@
  #include "zend_weakrefs.h"
  #include "Zend/Optimizer/zend_optimizer.h"
  #include "Zend/zend_alloc.h"
+ #include "SAPI.h"
+ #include "rfc1867.h"
 -#include "test.h"
  #include "test_arginfo.h"
 +#include "zend_call_stack.h"
 +#include "zend_exceptions.h"

  // `php.h` sets `NDEBUG` when not `PHP_DEBUG` which will make `assert()` from
  // assert.h a no-op. In order to have `assert()` working on NDEBUG builds, we
diff --cc ext/zend_test/test.stub.php
index 290f55976d1,2df7d547e45..02ba9d52a01
--- a/ext/zend_test/test.stub.php
+++ b/ext/zend_test/test.stub.php
@@@ -263,10 -203,10 +267,15 @@@ function zend_test_override_libxml_glob

      function zend_test_log_err_debug(string $str): void {}

 +    function zend_test_gh18756(): void {}
 +
 +    /** @compile-time-eval */
 +    function zend_test_gh19792(): void {}
++
+     /* @param resource $stream */
+     function zend_test_handle_post_data(mixed $stream, string $content_type, int $content_length): void {}
+
+     function zend_new_singleton_array(): array {}
  }

  namespace ZendTestNS {
diff --cc ext/zend_test/test_arginfo.h
index cccd4608d98,32583414195..e8cc9c23544
Binary files differ
diff --cc main/rfc1867.c
index 129e16d5f10,83591e8466e..eabc8fb9984
--- a/main/rfc1867.c
+++ b/main/rfc1867.c
@@@ -49,9 -49,9 +49,9 @@@ static php_rfc1867_getword_t php_rfc186
  static php_rfc1867_getword_conf_t php_rfc1867_getword_conf = php_ap_getword_conf;
  static php_rfc1867_basename_t php_rfc1867_basename = NULL;

 -PHPAPI int (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra) = NULL;
 +PHPAPI zend_result (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra) = NULL;

- static void safe_php_register_variable(char *var, char *strval, size_t val_len, zval *track_vars_array, bool override_protection);
+ static bool safe_php_register_variable(char *var, char *strval, size_t val_len, zval *track_vars_array, bool override_protection);

  /* The longest property name we use in an uploaded file array */
  #define MAX_SIZE_OF_INDEX sizeof("[full_path]")
@@@ -1013,9 -1140,13 +1117,13 @@@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867
  					event_file_data.length = blen;
  					event_file_data.newlength = &blen;
  					if (php_rfc1867_callback(MULTIPART_EVENT_FILE_DATA, &event_file_data, &event_extra_data) == FAILURE) {
 -						cancel_upload = UPLOAD_ERROR_X;
 +						cancel_upload = PHP_UPLOAD_ERROR_X;
  						continue;
  					}
+ 					if (ZEND_HAS_OOM()) {
 -						cancel_upload = UPLOAD_ERROR_X;
++						cancel_upload = PHP_UPLOAD_ERROR_X;
+ 						continue;
+ 					}
  				}

  				if (PG(upload_max_filesize) > 0 && (zend_long)(total_bytes+blen) > PG(upload_max_filesize)) {
@@@ -1080,8 -1210,11 +1188,11 @@@
  				event_file_end.temp_filename = temp_filename ? ZSTR_VAL(temp_filename) : NULL;
  				event_file_end.cancel_upload = cancel_upload;
  				if (php_rfc1867_callback(MULTIPART_EVENT_FILE_END, &event_file_end, &event_extra_data) == FAILURE) {
 -					cancel_upload = UPLOAD_ERROR_X;
 +					cancel_upload = PHP_UPLOAD_ERROR_X;
  				}
+ 				if (ZEND_HAS_OOM()) {
 -					cancel_upload = UPLOAD_ERROR_X;
++					cancel_upload = PHP_UPLOAD_ERROR_X;
+ 				}
  			}

  			if (cancel_upload) {
diff --cc main/streams/memory.c
index 785109db658,15b31154c9e..33d4b8eae56
--- a/main/streams/memory.c
+++ b/main/streams/memory.c
@@@ -49,17 -49,15 +49,21 @@@ static ssize_t php_stream_memory_write(

  	if (ms->mode & TEMP_STREAM_READONLY) {
  		return (ssize_t) -1;
 -	} else if (ms->mode & TEMP_STREAM_APPEND) {
 -		ms->fpos = ZSTR_LEN(ms->data);
  	}
 -	if (ms->fpos + count > ZSTR_LEN(ms->data)) {
 +	size_t data_len = ZSTR_LEN(ms->data);
 +	if (ms->mode & TEMP_STREAM_APPEND) {
 +		ms->fpos = data_len;
 +	}
 +	if (ms->fpos + count > data_len) {
- 		ms->data = zend_string_realloc(ms->data, ms->fpos + count, 0);
+ 		zend_string *grown = zend_string_frealloc(ms->data, ms->fpos + count, 0, /* fallible */ true);
+ 		if (UNEXPECTED(!grown)) {
+ 			return (ssize_t) -1;
+ 		}
+ 		ms->data = grown;
 +		if (ms->fpos > data_len) {
 +			/* zero the bytes added due to seek past end position */
 +			memset(ZSTR_VAL(ms->data) + data_len, 0, ms->fpos - data_len);
 +		}
  	} else {
  		ms->data = zend_string_separate(ms->data, 0);
  	}
diff --cc main/streams/streams.c
index ba4ef2f5190,1186f449080..5cab95d148e
--- a/main/streams/streams.c
+++ b/main/streams/streams.c
@@@ -1562,8 -1537,11 +1596,11 @@@ PHPAPI zend_string *_php_stream_copy_to
  		maxlen = 0;
  	}

 -	if (maxlen > 0) {
 +	if (maxlen > 0 && maxlen < 4 * CHUNK_SIZE) {
- 		result = zend_string_alloc(maxlen, persistent);
+ 		result = zend_string_falloc(maxlen, persistent, /* fallible */ true);
+ 		if (UNEXPECTED(!result)) {
+ 			return NULL;
+ 		}
  		ptr = ZSTR_VAL(result);
  		while ((len < maxlen) && !php_stream_eof(src)) {
  			ret = php_stream_read(src, ptr, maxlen - len);
@@@ -1596,30 -1574,28 +1633,38 @@@
  	 * by a downsize of the buffer, overestimate by the step size (which is
  	 * 8K).  */
  	if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) {
 -		max_len = MAX(ssbuf.sb.st_size - src->position, 0) + step;
 +		buflen = MAX(ssbuf.sb.st_size - src->position, 0) + step;
 +		if (maxlen > 0 && buflen > maxlen) {
 +			buflen = maxlen;
 +		}
  	} else {
 -		max_len = step;
 +		buflen = step;
  	}

- 	result = zend_string_alloc(buflen, persistent);
 -	result = zend_string_falloc(max_len, persistent, /* fallible */ true);
++	result = zend_string_falloc(buflen, persistent, /* fallible */ true);
+ 	if (UNEXPECTED(!result)) {
+ 		return NULL;
+ 	}
  	ptr = ZSTR_VAL(result);

  	// TODO: Propagate error?
 -	while ((ret = php_stream_read(src, ptr, max_len - len)) > 0){
 +	while ((ret = php_stream_read(src, ptr, buflen - len)) > 0) {
  		len += ret;
 -		if (len + min_room >= max_len) {
 -			zend_string *grown = zend_string_fextend(result, max_len + step, persistent, /* fallible */ true);
 +		if (len + min_room >= buflen) {
 +			if (maxlen == len) {
 +				break;
 +			}
 +			if (maxlen > 0 && buflen + step > maxlen) {
 +				buflen = maxlen;
 +			} else {
 +				buflen += step;
 +			}
- 			result = zend_string_extend(result, buflen, persistent);
++			zend_string *grown = zend_string_fextend(result, buflen, persistent, /* fallible */ true);
+ 			if (UNEXPECTED(!grown)) {
+ 				zend_string_free(result);
+ 				return NULL;
+ 			}
+ 			result = grown;
 -			max_len += step;
  			ptr = ZSTR_VAL(result) + len;
  		} else {
  			ptr += ret;