[php-src] master: Merge branch 'PHP-8.5' into PHP-8.6

From: Date: Thu, 08 Oct 2026 18:42:02 +0000
Subject: [php-src] master: Merge branch 'PHP-8.5' into PHP-8.6
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-142105@lists.php.net to get a copy of this message
Author: ndossche (ndossche) Date: 2026-10-08T20:41:43+02:00 Commit: https://github.com/php/php-src/commit/eefe4db40ed2b6da4352db6510d920d2abcaead7 Raw diff: https://github.com/php/php-src/commit/eefe4db40ed2b6da4352db6510d920d2abcaead7.diff Merge branch 'PHP-8.5' into PHP-8.6 * PHP-8.5: zend_alloc: move a small block shrunk to the size of the bin below Changed paths: A Zend/tests/zend_alloc_realloc_shrink_boundary.phpt M NEWS M Zend/zend_alloc.c M ext/zend_test/test.c M ext/zend_test/test.stub.php M ext/zend_test/test_arginfo.h M ext/zend_test/test_decl.h M ext/zend_test/test_legacy_arginfo.h Diff: diff --git a/NEWS b/NEWS index fcfee542f6b6..618ecace1ae2 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,9 @@ PHP NEWS in its previous chain. (Edmond) . Fixed bug GH-23979 (Nullsafe operator must not flush delayed oplines of an enclosing function). (ndossche) + . Fixed memory manager keeping a block reallocated to exactly the size of + the next smaller bin in its larger bin, which efree_size() then freed + into the wrong one. (Marc Bennewitz) - Opcache: . Fixed bug GH-17626 (JIT corrupts an opline handler when blacklisting a diff --git a/Zend/tests/zend_alloc_realloc_shrink_boundary.phpt b/Zend/tests/zend_alloc_realloc_shrink_boundary.phpt new file mode 100644 index 000000000000..0fae7c2acda0 --- /dev/null +++ b/Zend/tests/zend_alloc_realloc_shrink_boundary.phpt @@ -0,0 +1,40 @@ +--TEST-- +erealloc() of a small block keeps it in the bin its new size maps to +--EXTENSIONS-- +zend_test +--SKIPIF-- +<?php +if (getenv("USE_ZEND_ALLOC") === "0") die("skip requires zmm"); +?> +--FILE-- +<?php + +// The largest request of each small bin, found from fresh allocations. +$largest = []; +for ($n = 1; $n <= 3072; $n++) { + [, $block] = zend_test_erealloc_block_size($n, $n); + if ($block > 3072) { + break; + } + $largest[$block] = $n; +} +var_dump(count($largest) > 20); + +// Shrink a block of each bin to every smaller size, down to the size of the +// bin below, which efree_size() maps to that bin. +$mismatches = []; +foreach ($largest as $old_size) { + for ($new_size = 1; $new_size < $old_size; $new_size++) { + [$block, $fresh] = zend_test_erealloc_block_size($old_size, $new_size); + if ($block !== $fresh) { + $mismatches[] = "$old_size -> $new_size: block of $block, expected $fresh"; + } + } +} +var_dump(array_slice($mismatches, 0, 5)); + +?> +--EXPECT-- +bool(true) +array(0) { +} diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 4fc8926c0fcc..dcee8e9b2b3f 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -1786,8 +1786,9 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p /* Check if requested size fits into current bin */ if (size <= old_size) { - /* Check if truncation is necessary */ - if (old_bin_num > 0 && size < bin_data_size[old_bin_num - 1]) { + /* Check if truncation is necessary. A size that fits the + * bin below moves there, as efree_size() maps it to that bin. */ + if (old_bin_num > 0 && size <= bin_data_size[old_bin_num - 1]) { /* truncation */ ret = zend_mm_alloc_small(heap, ZEND_MM_SMALL_SIZE_TO_BIN(size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); copy_size = use_copy_size ? MIN(size, copy_size) : size; diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 4853e1f61e70..fa9b43b68c59 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -197,6 +197,37 @@ static ZEND_FUNCTION(zend_leak_bytes) emalloc(leakbytes); } +/* Reallocate a block of old_size bytes to new_size bytes and return its block + * size, along with the block size of a fresh allocation of new_size bytes. */ +static ZEND_FUNCTION(zend_test_erealloc_block_size) +{ + zend_long old_size, new_size; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(old_size) + Z_PARAM_LONG(new_size) + ZEND_PARSE_PARAMETERS_END(); + + if (old_size < 1) { + zend_argument_value_error(1, "must be greater than 0"); + RETURN_THROWS(); + } + if (new_size < 1) { + zend_argument_value_error(2, "must be greater than 0"); + RETURN_THROWS(); + } + + void *ptr = erealloc(emalloc(old_size), new_size); + void *fresh = emalloc(new_size); + + array_init(return_value); + add_next_index_long(return_value, zend_mem_block_size(ptr)); + add_next_index_long(return_value, zend_mem_block_size(fresh)); + + efree(fresh); + efree(ptr); +} + /* Leak a refcounted variable */ static ZEND_FUNCTION(zend_leak_variable) { diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index afeae903c668..1fbacdae1093 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -299,6 +299,8 @@ function zend_leak_bytes(int $bytes = 3): void {} function zend_delref(mixed $variable): void {} + function zend_test_erealloc_block_size(int $old_size, int $new_size): array {} + function zend_bool(bool $param): bool {} function zend_bool_or_null(bool|null $param): bool|null {} function zend_bool_slow_zpp(bool $param): bool {} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index d01e3d036c7f..40f06222783f 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 2b25ec2985ed2c14165625a0603ad2a122a255c7 + * Stub hash: 0d233e99eed49cad1bb8e52c28d14ba2a6054b03 * Has decl header: yes */ #include "zend_attributes.h" @@ -59,6 +59,11 @@ ZEND_END_ARG_INFO() #define arginfo_zend_delref arginfo_zend_leak_variable +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_erealloc_block_size, 0, 2, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, old_size, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, new_size, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_bool, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, param, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -441,6 +446,7 @@ static ZEND_FUNCTION(zend_terminate_string); static ZEND_FUNCTION(zend_leak_variable); static ZEND_FUNCTION(zend_leak_bytes); static ZEND_FUNCTION(zend_delref); +static ZEND_FUNCTION(zend_test_erealloc_block_size); static ZEND_FUNCTION(zend_bool); static ZEND_FUNCTION(zend_bool_or_null); static ZEND_FUNCTION(zend_bool_slow_zpp); @@ -618,6 +624,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_leak_variable, arginfo_zend_leak_variable) ZEND_FE(zend_leak_bytes, arginfo_zend_leak_bytes) ZEND_FE(zend_delref, arginfo_zend_delref) + ZEND_FE(zend_test_erealloc_block_size, arginfo_zend_test_erealloc_block_size) ZEND_FE(zend_bool, arginfo_zend_bool) ZEND_FE(zend_bool_or_null, arginfo_zend_bool_or_null) ZEND_FE(zend_bool_slow_zpp, arginfo_zend_bool_slow_zpp) diff --git a/ext/zend_test/test_decl.h b/ext/zend_test/test_decl.h index d2bc04df8f4e..96ebfbea6fc4 100644 --- a/ext/zend_test/test_decl.h +++ b/ext/zend_test/test_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 2b25ec2985ed2c14165625a0603ad2a122a255c7 */ + * Stub hash: 0d233e99eed49cad1bb8e52c28d14ba2a6054b03 */ -#ifndef ZEND_TEST_DECL_2b25ec2985ed2c14165625a0603ad2a122a255c7_H -#define ZEND_TEST_DECL_2b25ec2985ed2c14165625a0603ad2a122a255c7_H +#ifndef ZEND_TEST_DECL_0d233e99eed49cad1bb8e52c28d14ba2a6054b03_H +#define ZEND_TEST_DECL_0d233e99eed49cad1bb8e52c28d14ba2a6054b03_H typedef enum zend_enum_ZendTestUnitEnum { ZEND_ENUM_ZendTestUnitEnum_Foo = 1, @@ -27,4 +27,4 @@ typedef enum zend_enum_ZendTestEnumWithInterface { ZEND_ENUM_ZendTestEnumWithInterface_Bar = 2, } zend_enum_ZendTestEnumWithInterface; -#endif /* ZEND_TEST_DECL_2b25ec2985ed2c14165625a0603ad2a122a255c7_H */ +#endif /* ZEND_TEST_DECL_0d233e99eed49cad1bb8e52c28d14ba2a6054b03_H */ diff --git a/ext/zend_test/test_legacy_arginfo.h b/ext/zend_test/test_legacy_arginfo.h index e12e72eb6d28..4ee3d3d083eb 100644 --- a/ext/zend_test/test_legacy_arginfo.h +++ b/ext/zend_test/test_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 2b25ec2985ed2c14165625a0603ad2a122a255c7 + * Stub hash: 0d233e99eed49cad1bb8e52c28d14ba2a6054b03 * Has decl header: yes */ #include "zend_constants.h" @@ -54,6 +54,11 @@ ZEND_END_ARG_INFO() #define arginfo_zend_delref arginfo_zend_leak_variable +ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_test_erealloc_block_size, 0, 0, 2) + ZEND_ARG_INFO(0, old_size) + ZEND_ARG_INFO(0, new_size) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_bool, 0, 0, 1) ZEND_ARG_INFO(0, param) ZEND_END_ARG_INFO() @@ -365,6 +370,7 @@ static ZEND_FUNCTION(zend_terminate_string); static ZEND_FUNCTION(zend_leak_variable); static ZEND_FUNCTION(zend_leak_bytes); static ZEND_FUNCTION(zend_delref); +static ZEND_FUNCTION(zend_test_erealloc_block_size); static ZEND_FUNCTION(zend_bool); static ZEND_FUNCTION(zend_bool_or_null); static ZEND_FUNCTION(zend_bool_slow_zpp); @@ -514,6 +520,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_leak_variable, arginfo_zend_leak_variable) ZEND_FE(zend_leak_bytes, arginfo_zend_leak_bytes) ZEND_FE(zend_delref, arginfo_zend_delref) + ZEND_FE(zend_test_erealloc_block_size, arginfo_zend_test_erealloc_block_size) ZEND_FE(zend_bool, arginfo_zend_bool) ZEND_FE(zend_bool_or_null, arginfo_zend_bool_or_null) ZEND_FE(zend_bool_slow_zpp, arginfo_zend_bool_slow_zpp)

« previous php.cvs (#142105) next »