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,
- ¶ms);
- /* 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;