Commit 9705fda1bbb for php
commit 9705fda1bbb4e9458fb79bccf63bdcdb731dcd22
Merge: b9ea38ef639 d6373dfe150
Author: Ilija Tovilo <ilija.tovilo@me.com>
Date: Wed Oct 21 16:34:56 2026 +0200
Merge branch 'PHP-8.6' into master
* PHP-8.6:
Add NEWS entry
Avoid state corruption via bailout
diff --cc Zend/zend_API.h
index cc6ed3c8e10,ba67948c12b..d1064970dcd
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@@ -514,11 -549,71 +514,71 @@@ static zend_always_inline void array_in
array_init_size(arg, 0);
}
+ static zend_always_inline void array_init_singleton(zval *arg)
+ {
+ ZVAL_ARR(arg, zend_new_array_singleton());
+ }
+
+ /* Fallible array_init()/object_init(): on failure return false and leave arg as
+ * IS_NULL, so arg stays safe to destroy. */
+ static zend_always_inline bool _array_finit(zval *arg, size_t size)
+ {
+ zend_array *ht = zend_fnew_array(size);
+ if (UNEXPECTED(ht == NULL)) {
+ ZVAL_NULL(arg);
+ return false;
+ }
+ ZVAL_ARR(arg, ht);
+ return true;
+ }
+
+ static zend_always_inline bool _array_finit_ex(zval *arg, size_t size, bool packed)
+ {
+ zend_array *ht = zend_fnew_array(size);
+ if (UNEXPECTED(ht == NULL)) {
+ ZVAL_NULL(arg);
+ return false;
+ }
+ if (UNEXPECTED(zend_hash_real_finit(ht, packed, /* fallible */ true) == FAILURE)) {
+ efree(ht);
+ ZVAL_NULL(arg);
+ return false;
+ }
+ ZVAL_ARR(arg, ht);
+ return true;
+ }
+
+ static zend_always_inline bool array_finit(zval *arg)
+ {
+ return _array_finit(arg, 0);
+ }
+
+ static zend_always_inline bool array_finit_size(zval *arg, size_t size)
+ {
+ return _array_finit(arg, size);
+ }
+
+ static zend_always_inline bool array_finit_size_ex(zval *arg, size_t size, bool packed)
+ {
+ return _array_finit_ex(arg, size, packed);
+ }
+
+ static zend_always_inline bool array_finit_size_packed(zval *arg, size_t size)
+ {
+ return _array_finit_ex(arg, size, /* packed */ true);
+ }
+
+ static zend_always_inline bool array_finit_size_mixed(zval *arg, size_t size)
+ {
+ return _array_finit_ex(arg, size, /* packed */ false);
+ }
+
ZEND_API void object_init(zval *arg);
+ ZEND_API bool object_finit(zval *arg);
ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *ce);
-ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *class_type, uint32_t param_count, zval *params, HashTable *named_params);
+ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *ce, uint32_t param_count, zval *params, HashTable *named_params);
ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_entry *ce, HashTable *properties);
-ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type);
+ZEND_API void object_properties_init(zend_object *object, zend_class_entry *ce);
ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properties);
ZEND_API void object_properties_load(zend_object *object, const HashTable *properties);
diff --cc Zend/zend_alloc.c
index e3317b3a9a3,050fb252e78..b293ee1a51d
--- a/Zend/zend_alloc.c
+++ b/Zend/zend_alloc.c
@@@ -393,31 -405,160 +405,160 @@@ ZEND_NORETURN static ZEND_COLD void zen
abort();
}
- ZEND_NORETURN static ZEND_COLD void zend_mm_safe_error(zend_mm_heap *heap,
- const char *format,
- size_t limit,
- #if ZEND_DEBUG
- const char *filename,
- uint32_t lineno,
- #endif
- size_t size)
-static ZEND_COLD ZEND_NORETURN void zend_out_of_memory(void);
++ZEND_NORETURN static ZEND_COLD void zend_out_of_memory(void);
+
+ static ZEND_COLD void zend_mm_delayed_verror(const char *format, va_list args)
{
+ if (ZEND_HAS_OOM()) {
+ return;
+ }
- heap->overflow = 1;
- zend_try {
- zend_error_noreturn(E_ERROR,
- format,
- limit,
- #if ZEND_DEBUG
- filename,
- lineno,
- #endif
- size);
- } zend_catch {
- } zend_end_try();
- heap->overflow = 0;
- zend_bailout();
- exit(1);
+ zend_string *script_filename = NULL;
+ uint32_t script_lineno = 0;
+
+ if (zend_is_executing()) {
+ script_filename = zend_get_executed_filename_ex();
+ script_lineno = zend_get_executed_lineno();
+ } else if (zend_is_compiling()) {
+ script_filename = zend_get_compiled_filename();
+ script_lineno = zend_get_compiled_lineno();
+ }
+
+ if (script_filename == NULL) {
+ script_filename = ZSTR_KNOWN(ZEND_STR_UNKNOWN_CAPITALIZED);
+ script_lineno = 0;
+ }
+
+ char buf[1024];
+ vsnprintf(buf, sizeof(buf), format, args);
+
+ EG(delayed_oom).message = zend_string_init(buf, strlen(buf), /* persistent */ true);
+ GC_MAKE_PERSISTENT_LOCAL(EG(delayed_oom).message);
+ zend_string_addref(script_filename);
+ EG(delayed_oom).filename = script_filename;
+ EG(delayed_oom).lineno = script_lineno;
+ EG(delayed_oom).state = ZEND_OOM_STATE_PENDING;
+
- zend_atomic_bool_store_ex(&EG(vm_interrupt), true);
++ atomic_store(&EG(vm_interrupt), true);
+ }
+
+ ZEND_ATTRIBUTE_FORMAT(printf, 1, 2)
+ static ZEND_COLD void zend_mm_delayed_error(const char *format, ...)
+ {
+ va_list args;
+ va_start(args, format);
+ zend_mm_delayed_verror(format, args);
+ va_end(args);
+ }
+
+ ZEND_ATTRIBUTE_FORMAT(printf, 2, 3)
+ ZEND_API ZEND_COLD void zend_mm_delayed_error_or_panic(bool delay, const char *format, ...)
+ {
+ va_list args;
+ va_start(args, format);
+
+ if (delay) {
+ zend_mm_delayed_verror(format, args);
+ } else {
+ char buf[1024];
+ vsnprintf(buf, sizeof(buf), format, args);
+ zend_mm_panic(buf);
+ }
+
+ va_end(args);
+ }
+
+ ZEND_COLD void zend_mm_oom_info_dtor(zend_oom_info *info)
+ {
+ if (info->message) {
+ zend_string_release(info->message);
+ info->message = NULL;
+ }
+ if (info->filename) {
+ zend_string_release(info->filename);
+ info->filename = NULL;
+ }
+ }
+
+ #ifdef ZEND_MM_LIMIT
-static ZEND_COLD ZEND_NORETURN void zend_mm_hard_limit_error(zend_mm_heap *heap,
++ZEND_NORETURN static ZEND_COLD void zend_mm_hard_limit_error(zend_mm_heap *heap,
+ size_t add_size ZEND_FILE_LINE_DC)
+ {
+ zend_string *script_filename = NULL;
+ uint32_t script_lineno = 0;
+
+ if (zend_is_executing()) {
+ script_filename = zend_get_executed_filename_ex();
+ script_lineno = zend_get_executed_lineno();
+ } else if (zend_is_compiling()) {
+ script_filename = zend_get_compiled_filename();
+ script_lineno = zend_get_compiled_lineno();
+ }
+
+ if (script_filename == NULL) {
+ script_filename = ZSTR_KNOWN(ZEND_STR_UNKNOWN_CAPITALIZED);
+ script_lineno = 0;
+ }
+
+ fprintf(stderr,
+ # if ZEND_DEBUG
+ "Hard memory limit of %zu bytes exhausted at %s:%d (tried to allocate %zu bytes) in %s on line %d\n",
+ # else
+ "Hard memory limit of %zu bytes exhausted (tried to allocate %zu bytes) in %s on line %d\n",
+ # endif
+ heap->hard_limit
+ ZEND_FILE_LINE_RELAY_CC,
+ add_size,
+ ZSTR_VAL(script_filename),
+ script_lineno);
+
+ /* See http://support.microsoft.com/kb/190351 */
+ # ifdef ZEND_WIN32
+ fflush(stderr);
+ # endif
+
+ _exit(137);
+ }
+ #endif /* ZEND_MM_LIMIT */
+
+ /* Returns FAILURE if allocation should fail (in case the heap is fallible).
+ * When the soft limit is to be exceeded, a delayed OOM error is queued.
+ * When the hard limit is to be exceeded, the function does not return.
+ */
+ static zend_always_inline zend_result zend_mm_check_limit(zend_mm_heap *heap, size_t add_size, bool gc, bool *did_gc ZEND_FILE_LINE_DC)
+ {
+ #if ZEND_MM_LIMIT
+ /* Is the soft limit exceeded or going to be exceeded? */
+ if (UNEXPECTED(heap->limit < heap->real_size || add_size > heap->limit - heap->real_size)) {
+ if (gc && zend_mm_gc(heap)) {
+ if (did_gc) {
+ *did_gc = true;
+ }
+ }
+ ZEND_ASSERT(heap->hard_limit >= heap->limit && heap->hard_limit >= heap->real_size);
+ if (!heap->fallible && add_size > heap->hard_limit - heap->real_size) {
+ zend_mm_hard_limit_error(heap, add_size ZEND_FILE_LINE_RELAY_CC);
+ }
+ /* Check soft limit again as gc may have reduced memory usage */
+ if (!gc || (heap->limit < heap->real_size || add_size > heap->limit - heap->real_size)) {
+ if (!ZEND_HAS_OOM()) {
+ zend_mm_delayed_error(
+ # if ZEND_DEBUG
+ "Allowed memory size of %zu bytes exhausted at %s:%d (tried to allocate %zu bytes)",
+ # else
+ "Allowed memory size of %zu bytes exhausted (tried to allocate %zu bytes)",
+ # endif
+ heap->limit
+ ZEND_FILE_LINE_RELAY_CC,
+ add_size);
+ }
+
+ return heap->fallible ? FAILURE : SUCCESS;
+ }
+ }
+ #endif /* ZEND_MM_LIMIT */
+
+ return SUCCESS;
}
#ifdef _WIN32
diff --cc Zend/zend_execute.c
index e7923d76996,4e82fa20759..a5700627043
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@@ -4344,9 -4344,11 +4344,11 @@@ ZEND_API void ZEND_FASTCALL zend_free_c
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_fcall_interrupt(zend_execute_data *call)
{
- zend_atomic_bool_store_ex(&EG(vm_interrupt), false);
- if (zend_atomic_bool_load_ex(&EG(timed_out))) {
+ atomic_store(&EG(vm_interrupt), false);
+ if (atomic_load(&EG(timed_out))) {
zend_timeout();
+ } else if (ZEND_HAS_OOM()) {
+ zend_handle_delayed_oom();
} else if (zend_interrupt_function) {
zend_interrupt_function(call);
}
diff --cc Zend/zend_execute_API.c
index 09cf6c18cd1,75126daa55f..cfb78f1b14f
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@@ -1099,21 -1122,24 +1119,24 @@@ cleanup_args
zval_ptr_dtor(fci->retval);
ZVAL_UNDEF(fci->retval);
}
+ }
- /* This flag is regularly checked while running user functions, but not internal
- * So see whether interrupt flag was set while the function was running... */
- if (atomic_exchange(&EG(vm_interrupt), false)) {
- if (atomic_load(&EG(timed_out))) {
- zend_timeout();
- } else if (zend_interrupt_function) {
- zend_interrupt_function(EG(current_execute_data));
- }
- if (UNEXPECTED(zend_atomic_bool_exchange_ex(&EG(vm_interrupt), false))) {
- if (zend_atomic_bool_load_ex(&EG(timed_out))) {
++ if (UNEXPECTED(atomic_exchange(&EG(vm_interrupt), false))) {
++ if (atomic_load(&EG(timed_out))) {
+ zend_timeout();
+ } else if (ZEND_HAS_OOM()) {
+ zend_handle_delayed_oom();
+ zval_ptr_dtor(fci->retval);
+ ZVAL_UNDEF(fci->retval);
+ } else if (zend_interrupt_function) {
+ zend_interrupt_function(EG(current_execute_data));
}
+ }
- if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) {
- OBJ_RELEASE(Z_OBJ(call->This));
- }
+ if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) {
+ OBJ_RELEASE(Z_OBJ(call->This));
}
+
EG(fake_scope) = orig_fake_scope;
if (pinned_this) {
diff --cc Zend/zend_globals.h
index c3d1ce3c67a,c3034f8c8ed..419f9cc07c7
--- a/Zend/zend_globals.h
+++ b/Zend/zend_globals.h
@@@ -26,6 -26,8 +26,7 @@@
#include "zend_globals_macros.h"
+ #include "zend_alloc.h"
-#include "zend_atomic.h"
#include "zend_stack.h"
#include "zend_ptr_stack.h"
#include "zend_hash.h"
diff --cc Zend/zend_hash.h
index 3bd71bab8dc,e7c86808182..cb8a2256aa6
--- a/Zend/zend_hash.h
+++ b/Zend/zend_hash.h
@@@ -100,6 -93,17 +101,8 @@@ extern ZEND_API const HashTable zend_em
Z_TYPE_INFO_P(__z) = IS_ARRAY; \
} while (0)
+ #define HASH_NOMEM ((zval*)-1)
+
-typedef struct _zend_hash_key {
- zend_ulong h;
- zend_string *key;
-} zend_hash_key;
-
-typedef bool (*merge_checker_func_t)(HashTable *target_ht, zval *source_data, zend_hash_key *hash_key, void *pParam);
-
-BEGIN_EXTERN_C()
-
/* startup/shutdown */
ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent);
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht);
diff --cc Zend/zend_vm_def.h
index cf8d626bbdb,3ea6d7250ab..871edf3aed7
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@@ -10724,8 -10729,11 +10731,11 @@@ ZEND_VM_HELPER(zend_interrupt_helper, A
#else
SAVE_OPLINE();
#endif
- if (zend_atomic_bool_load_ex(&EG(timed_out))) {
+ if (atomic_load(&EG(timed_out))) {
zend_timeout();
+ } else if (ZEND_HAS_OOM()) {
+ zend_handle_delayed_oom();
+ HANDLE_EXCEPTION();
} else if (zend_interrupt_function) {
zend_interrupt_function(execute_data);
if (EG(exception)) {
diff --cc Zend/zend_vm_execute.h
index 88dab2fd86c,8b6a5533fa3..bdd7bea9bc7
Binary files differ
diff --cc ext/mbstring/libmbfl/mbfl/mbfl_encoding.h
index 620d8e07198,e71737318bf..6abc042ff09
--- a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h
+++ b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h
@@@ -162,17 -162,34 +162,31 @@@ 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 ((size_t)(*limit - *out) < needed) {
+ size_t oldsize = *limit - (unsigned char*)ZSTR_VAL((buf)->str);
+ size_t newsize = oldsize + MAX(oldsize >> 1, needed);
+ zend_string *newstr = safe_ferealloc((buf)->str, 1, newsize, _ZSTR_STRUCT_SIZE(0), /* fallible */ true);
+ 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));
+ *limit = (unsigned char*)ZSTR_VAL(newstr) + newsize;
+ (buf)->str = newstr;
+ }
+ return SUCCESS;
+ }
+
#define MB_CONVERT_BUF_ENSURE(buf, out, limit, needed) \
- ZEND_ASSERT(out <= limit); \
- if ((size_t)(limit - out) < (needed)) { \
- size_t oldsize = limit - (unsigned char*)ZSTR_VAL((buf)->str); \
- size_t newsize = oldsize + MAX(oldsize >> 1, needed); \
- zend_string *newstr = 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 (!MB_CONVERT_BUF_ENSURE_EX(buf, out, limit, needed)) { \
+ return; \
}
+ #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
diff --cc ext/opcache/ZendAccelerator.c
index 2317bc4a12e,8f88ce4bea2..2318f5b28c8
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@@ -2721,9 -2725,11 +2725,11 @@@ static zend_class_entry* zend_accel_inh
entry->next = proto->inheritance_cache;
ZCSG(map_ptr_last) = CG(map_ptr_last);
- ZEND_ATOMIC_FENCE_RELEASE();
+ atomic_thread_fence(memory_order_release);
proto->inheritance_cache = entry;
+ zend_accel_pending_ce_cache_commit();
+
zend_shared_alloc_destroy_xlat_table();
zend_shared_alloc_unlock();
diff --cc ext/pcre/php_pcre.c
index 256b67e9e4c,fd239eb887f..4f49b6fbf69
--- a/ext/pcre/php_pcre.c
+++ b/ext/pcre/php_pcre.c
@@@ -1310,9 -1340,12 +1339,12 @@@ matched
add_offset_pair(
match_sets[i], subject, offsets[2*i], offsets[2*i+1],
NULL, unmatched_as_null);
+ if (ZEND_HAS_OOM()) {
+ goto oom;
+ }
}
} else {
- for (i = 0; i < count; i++) {
+ for (int i = 0; i < count; i++) {
zval val;
populate_match_value(
&val, subject, offsets[2*i], offsets[2*i+1], unmatched_as_null);
@@@ -1472,6 -1518,25 +1517,25 @@@ error
} else {
RETVAL_FALSE;
}
+ return;
+
+ oom:
+ if (match_data != mdata) {
+ pcre2_match_data_free(match_data);
+ }
+ if (match_sets) {
- for (i = 0; i < num_subpats; i++) {
++ for (uint32_t i = 0; i < num_subpats; i++) {
+ zend_array_destroy(match_sets[i]);
+ }
+ efree(match_sets);
+ }
+ if (marks) {
+ zend_array_destroy(marks);
+ }
+ if (subpats) {
+ zend_hash_clean(Z_ARRVAL_P(subpats));
+ }
+ RETURN_OOM();
}
/* }}} */
@@@ -1560,9 -1625,17 +1624,13 @@@ static zend_string *preg_do_repl_func(z
zval arg; /* Argument to pass to function */
array_init_size(&arg, count + (mark ? 1 : 0));
- populate_subpat_array(Z_ARRVAL(arg), ZSTR_VAL(subject), offsets, subpat_names, num_subpats, count, mark, flags);
- zend_result result = populate_subpat_array(Z_ARRVAL(arg), subject, offsets, subpat_names, num_subpats, count, mark, flags);
++ zend_result result = populate_subpat_array(Z_ARRVAL(arg), ZSTR_VAL(subject), offsets, subpat_names, num_subpats, count, mark, flags);
+ if (UNEXPECTED(result == FAILURE)) {
+ zval_ptr_dtor(&arg);
+ return NULL;
+ }
- fci->retval = &retval;
- fci->param_count = 1;
- fci->params = &arg;
- fci->consumed_args = zend_fci_consumed_arg(0);
- zend_call_function(fci, fcc);
+ zend_call_known_fcc_ex(fcc, &retval, 1, &arg, NULL, zend_fci_consumed_arg(0));
zval_ptr_dtor(&arg);
if (EXPECTED(Z_TYPE(retval) == IS_STRING)) {
return Z_STR(retval);
@@@ -1948,10 -2030,10 +2025,10 @@@ matched
/* Use custom function to get replacement string and its length. */
zend_string *eval_result = preg_do_repl_func(
- fci, fcc, ZSTR_VAL(subject_str), offsets, subpat_names, num_subpats, count,
+ fcc, subject_str, offsets, subpat_names, num_subpats, count,
pcre2_get_mark(match_data), flags);
- if (UNEXPECTED(eval_result == NULL)) {
+ if (UNEXPECTED(!eval_result)) {
goto error;
}
new_len = zend_safe_address_guarded(1, ZSTR_LEN(eval_result) + ZSTR_MAX_OVERHEAD, new_len) -ZSTR_MAX_OVERHEAD;
diff --cc ext/session/tests/rfc1867_sid_invalid.phpt
index 687fbcd96b5,53c2c0e5e6e..5f410883aba
--- a/ext/session/tests/rfc1867_sid_invalid.phpt
+++ b/ext/session/tests/rfc1867_sid_invalid.phpt
@@@ -43,9 -43,13 +43,9 @@@ session_start()
var_dump(session_id());
var_dump(basename(__FILE__) == $_POST[ini_get("session.upload_progress.name")]);
var_dump($_FILES);
- var_dump($_SESSION["upload_progress_" . basename(__FILE__)]);
+ var_dump($_SESSION);
session_destroy();
?>
---CLEAN--
-<?php
-@unlink(__DIR__ . DIRECTORY_SEPARATOR . "rfc1867_sid_invalid.post.txt");
-?>
--EXPECTF--
Warning: PHP Request Startup: Session ID is too long or contains illegal characters. Only the A-Z, a-z, 0-9, "-", and "," characters are allowed in Unknown on line 0
diff --cc ext/standard/array.c
index 0b184efc399,39422639418..39e23bf8738
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@@ -7124,127 -7156,120 +7155,132 @@@ PHP_FUNCTION(array_map
}
}
+ if (!maxlen) {
+ RETURN_EMPTY_ARRAY();
+ }
+
- array_init_size(return_value, maxlen);
- zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
+ if (UNEXPECTED(!array_finit_size_packed(return_value, maxlen))) {
+ RETURN_OOM();
+ }
- if (!ZEND_FCI_INITIALIZED(fci)) {
- uint32_t *array_pos = ecalloc(n_arrays, sizeof(HashPosition));
- zval zv;
-
- /* We iterate through all the arrays at once. */
- for (k = 0; k < maxlen; k++) {
-
- /* If no callback, the result will be an array, consisting of current
- * entries from all arrays. */
- if (ZEND_HAS_OOM()) {
- efree(array_pos);
- RETURN_OOM();
- }
- array_init_size(&result, n_arrays);
-
- for (i = 0; i < n_arrays; i++) {
- /* If this array still has elements, add the current one to the
- * parameter list, otherwise use null value. */
- uint32_t pos = array_pos[i];
- if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
- while (1) {
- if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
- ZVAL_NULL(&zv);
- break;
- } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
- ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
- array_pos[i] = pos + 1;
- break;
- }
- pos++;
- }
- } else {
- while (1) {
- if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
- ZVAL_NULL(&zv);
- break;
- } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
- ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
- array_pos[i] = pos + 1;
- break;
+ ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
+ if (!ZEND_FCI_INITIALIZED(fci)) {
+ uint32_t *array_pos = ecalloc(n_arrays, sizeof(HashPosition));
+ zval zv;
+
+ /* We iterate through all the arrays at once. */
+ for (k = 0; k < maxlen; k++) {
+
+ /* If no callback, the result will be an array, consisting of current
+ * entries from all arrays. */
++ if (ZEND_HAS_OOM()) {
++ efree(array_pos);
++ RETURN_OOM();
++ }
+ array_init_size(&result, n_arrays);
+
+ zend_hash_real_init_packed(Z_ARRVAL(result));
+ ZEND_HASH_FILL_PACKED(Z_ARRVAL(result)) {
+ for (i = 0; i < n_arrays; i++) {
+ /* If this array still has elements, add the current one to the
+ * parameter list, otherwise use null value. */
+ uint32_t pos = array_pos[i];
+ if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
+ while (1) {
+ if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
+ ZEND_HASH_FILL_SET_NULL();
+ break;
+ } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
+ ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arPacked[pos]);
+ ZEND_HASH_FILL_SET(&zv);
+ array_pos[i] = pos + 1;
+ break;
+ }
+ pos++;
+ }
+ } else {
+ while (1) {
+ if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
+ ZEND_HASH_FILL_SET_NULL();
+ break;
+ } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
+ ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
+ ZEND_HASH_FILL_SET(&zv);
+ array_pos[i] = pos + 1;
+ break;
+ }
+ pos++;
+ }
}
- pos++;
+ ZEND_HASH_FILL_NEXT();
}
- }
- zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
- }
-
- zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
- }
+ } ZEND_HASH_FILL_END();
- efree(array_pos);
- } else {
- zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
-
- /* Remember next starting point in the array, initialize those as zeros. */
- for (i = 0; i < n_arrays; i++) {
- Z_EXTRA(params[i]) = 0;
- }
+ ZEND_HASH_FILL_ADD(&result);
+ }
- fci.retval = &result;
- fci.param_count = n_arrays;
- fci.params = params;
+ efree(array_pos);
+ } else {
+ zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
- /* We iterate through all the arrays at once. */
- for (k = 0; k < maxlen; k++) {
+ /* Remember next starting point in the array, initialize those as zeros. */
for (i = 0; i < n_arrays; i++) {
- /* If this array still has elements, add the current one to the
- * parameter list, otherwise use null value. */
- uint32_t pos = Z_EXTRA(params[i]);
- if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
- while (1) {
- if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
- ZVAL_NULL(¶ms[i]);
- break;
- } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
- ZVAL_COPY_VALUE(¶ms[i], &Z_ARRVAL(arrays[i])->arPacked[pos]);
- Z_EXTRA(params[i]) = pos + 1;
- break;
+ Z_EXTRA(params[i]) = 0;
+ }
+
+ fci.retval = &result;
+ fci.param_count = n_arrays;
+ fci.params = params;
+
+ /* We iterate through all the arrays at once. */
+ for (k = 0; k < maxlen; k++) {
+ for (i = 0; i < n_arrays; i++) {
+ /* If this array still has elements, add the current one to the
+ * parameter list, otherwise use null value. */
+ uint32_t pos = Z_EXTRA(params[i]);
+ if (HT_IS_PACKED(Z_ARRVAL(arrays[i]))) {
+ while (1) {
+ if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
+ ZVAL_NULL(¶ms[i]);
+ break;
+ } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arPacked[pos]) != IS_UNDEF) {
+ ZVAL_COPY_VALUE(¶ms[i], &Z_ARRVAL(arrays[i])->arPacked[pos]);
+ Z_EXTRA(params[i]) = pos + 1;
+ break;
+ }
+ pos++;
}
- pos++;
- }
- } else {
- while (1) {
- if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
- ZVAL_NULL(¶ms[i]);
- break;
- } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
- ZVAL_COPY_VALUE(¶ms[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
- Z_EXTRA(params[i]) = pos + 1;
- break;
+ } else {
+ while (1) {
+ if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
+ ZVAL_NULL(¶ms[i]);
+ break;
+ } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
+ ZVAL_COPY_VALUE(¶ms[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
+ Z_EXTRA(params[i]) = pos + 1;
+ break;
+ }
+ pos++;
}
- pos++;
}
}
- }
- zend_result ret = zend_call_function(&fci, &fci_cache);
- ZEND_ASSERT(ret == SUCCESS);
- ZEND_IGNORE_VALUE(ret);
+ zend_result ret = zend_call_function(&fci, &fci_cache);
+ ZEND_ASSERT(ret == SUCCESS);
+ ZEND_IGNORE_VALUE(ret);
- if (Z_TYPE(result) == IS_UNDEF) {
- efree(params);
- RETURN_THROWS();
+ if (Z_TYPE(result) == IS_UNDEF) {
+ ZEND_HASH_FILL_FINISH();
+ efree(params);
+ RETURN_THROWS();
+ }
+
+ ZEND_HASH_FILL_ADD(&result);
}
- zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
+ efree(params);
}
-
- efree(params);
- }
+ } ZEND_HASH_FILL_END();
}
}
/* }}} */
@@@ -7325,45 -7350,10 +7361,46 @@@ PHP_FUNCTION(array_chunk
size = num_in;
}
- array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
- zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
+ if (UNEXPECTED(!array_finit_size_packed(return_value, (uint32_t)(((num_in - 1) / size) + 1)))) {
+ RETURN_OOM();
+ }
+ if (!preserve_keys) {
+ /* Every chunk is a list of exactly `size` elements (the last one possibly
+ * shorter), so each one can be filled directly. The input is walked by
+ * element pointer, so the packed/hash stride is computed only once. */
+ HashTable *ht = Z_ARRVAL_P(input);
+ uint32_t elem_size = ZEND_HASH_ELEMENT_SIZE(ht);
+ zval *zv = ht->arPacked;
+ uint32_t remaining = (uint32_t)num_in;
+
+ while (remaining > 0) {
+ uint32_t chunk_size = MIN((uint32_t)size, remaining);
+
+ array_init_size(&chunk, chunk_size);
+ zend_hash_real_init_packed(Z_ARRVAL(chunk));
+ ZEND_HASH_FILL_PACKED(Z_ARRVAL(chunk)) {
+ uint32_t n = 0;
+ while (n < chunk_size) {
+ entry = zv;
+ zv = ZEND_HASH_NEXT_ELEMENT(zv, elem_size);
+ if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
+ continue;
+ }
+ if (UNEXPECTED(Z_ISREF_P(entry)) && Z_REFCOUNT_P(entry) == 1) {
+ entry = Z_REFVAL_P(entry);
+ }
+ Z_TRY_ADDREF_P(entry);
+ ZEND_HASH_FILL_ADD(entry);
+ n++;
+ }
+ } ZEND_HASH_FILL_END();
+ zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &chunk);
+ remaining -= chunk_size;
+ }
+ return;
+ }
+
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
/* If new chunk, create and initialize it. */
if (current == 0) {
diff --cc ext/standard/html.c
index 075d629f10b,d47326f4a64..4631b980a0b
--- a/ext/standard/html.c
+++ b/ext/standard/html.c
@@@ -1329,9 -1175,12 +1329,12 @@@ static zend_string *escape_html_entitie
maxlen = zend_safe_addmult(oldlen, 2, 0, "html_entities");
}
- replaced = zend_string_alloc(maxlen, 0);
+ replaced = zend_string_falloc(maxlen, /* persistent */ false, /* fallible */ true);
+ if (!replaced) {
+ return ZSTR_EMPTY_ALLOC();
+ }
- len = 0;
- cursor = 0;
+ memcpy(ZSTR_VAL(replaced), old, cursor);
+ len = cursor;
while (cursor < oldlen) {
const unsigned char *mbsequence = NULL;
size_t mbseqlen = 0,
diff --cc ext/standard/levenshtein.c
index 9ac7689d3bf,399554fab98..0b0dd08fc75
--- a/ext/standard/levenshtein.c
+++ b/ext/standard/levenshtein.c
@@@ -80,15 -91,15 +91,19 @@@ PHP_FUNCTION(levenshtein
zend_long cost_rep = 1;
zend_long cost_del = 1;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|lll", &string1, &string2, &cost_ins, &cost_rep, &cost_del) == FAILURE) {
- RETURN_THROWS();
- }
-
+ ZEND_PARSE_PARAMETERS_START(2, 5)
+ Z_PARAM_STR(string1)
+ Z_PARAM_STR(string2)
+ Z_PARAM_OPTIONAL
+ Z_PARAM_LONG(cost_ins)
+ Z_PARAM_LONG(cost_rep)
+ Z_PARAM_LONG(cost_del)
+ ZEND_PARSE_PARAMETERS_END();
- RETURN_LONG(reference_levdist(string1, string2, cost_ins, cost_rep, cost_del));
+ zend_long distance;
+ if (UNEXPECTED(!reference_levdist(string1, string2, cost_ins, cost_rep, cost_del, &distance))) {
+ RETURN_OOM();
+ }
+ RETURN_LONG(distance);
}
/* }}} */
diff --cc ext/standard/string.c
index 3775074b590,0a05a1ae36f..59f93854368
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@@ -884,11 -882,16 +899,16 @@@ PHPAPI void php_explode(const zend_stri
if (p2 == NULL) {
ZVAL_STR_COPY(&tmp, str);
- zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
+ zend_hash_next_index_insert_new(parts, &tmp);
} else {
- zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
- ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
+ zend_hash_real_init_packed(parts);
+ ZEND_HASH_FILL_PACKED(parts) {
do {
+ /* Each piece costs a zval plus a zend_string, so stop once over the limit. */
+ if (ZEND_HAS_OOM()) {
+ ZEND_HASH_FILL_FINISH();
+ return;
+ }
ZEND_HASH_FILL_GROW();
ZEND_HASH_FILL_SET_STR(zend_string_init_fast(p1, p2 - p1));
ZEND_HASH_FILL_NEXT();
@@@ -938,8 -946,11 +963,11 @@@ static void php_explode_negative_limit(
to_return = limit + found;
/* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
for (i = 0; i < to_return; i++) { /* this checks also for to_return > 0 */
+ if (ZEND_HAS_OOM()) {
+ break;
+ }
ZVAL_STRINGL_FAST(&tmp, positions[i], (positions[i+1] - ZSTR_LEN(delim)) - positions[i]);
- zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
+ zend_hash_next_index_insert_new(parts, &tmp);
}
efree((void *)positions);
}
diff --cc ext/zend_test/observer.c
index 77ce0cc2525,799320357e3..a03c89fc260
--- a/ext/zend_test/observer.c
+++ b/ext/zend_test/observer.c
@@@ -75,8 -75,9 +75,9 @@@ static void observer_begin(zend_execute
{
assert_observer_opline(execute_data);
- if (ZT_G(observer_set_vm_interrupt_on_begin)) {
+ if (ZT_G(observer_set_vm_interrupt_on_begin)
+ || ZT_G(observer_set_vm_interrupt_on_begin_without_callback)) {
- zend_atomic_bool_store_ex(&EG(vm_interrupt), true);
+ atomic_store(&EG(vm_interrupt), true);
}
if (!ZT_G(observer_show_output)) {