Skip to content

Commit eefe4db

Browse files
committed
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
2 parents b4f14f4 + 33d7f34 commit eefe4db

8 files changed

Lines changed: 99 additions & 8 deletions

File tree

‎NEWS‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ PHP NEWS
77
in its previous chain. (Edmond)
88
. Fixed bug GH-23979 (Nullsafe operator must not flush delayed oplines of an
99
enclosing function). (ndossche)
10+
. Fixed memory manager keeping a block reallocated to exactly the size of
11+
the next smaller bin in its larger bin, which efree_size() then freed
12+
into the wrong one. (Marc Bennewitz)
1013

1114
- Opcache:
1215
. Fixed bug GH-17626 (JIT corrupts an opline handler when blacklisting a
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
erealloc() of a small block keeps it in the bin its new size maps to
3+
--EXTENSIONS--
4+
zend_test
5+
--SKIPIF--
6+
<?php
7+
if (getenv("USE_ZEND_ALLOC") === "0") die("skip requires zmm");
8+
?>
9+
--FILE--
10+
<?php
11+
12+
// The largest request of each small bin, found from fresh allocations.
13+
$largest = [];
14+
for ($n = 1; $n <= 3072; $n++) {
15+
[, $block] = zend_test_erealloc_block_size($n, $n);
16+
if ($block > 3072) {
17+
break;
18+
}
19+
$largest[$block] = $n;
20+
}
21+
var_dump(count($largest) > 20);
22+
23+
// Shrink a block of each bin to every smaller size, down to the size of the
24+
// bin below, which efree_size() maps to that bin.
25+
$mismatches = [];
26+
foreach ($largest as $old_size) {
27+
for ($new_size = 1; $new_size < $old_size; $new_size++) {
28+
[$block, $fresh] = zend_test_erealloc_block_size($old_size, $new_size);
29+
if ($block !== $fresh) {
30+
$mismatches[] = "$old_size -> $new_size: block of $block, expected $fresh";
31+
}
32+
}
33+
}
34+
var_dump(array_slice($mismatches, 0, 5));
35+
36+
?>
37+
--EXPECT--
38+
bool(true)
39+
array(0) {
40+
}

‎Zend/zend_alloc.c‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,8 +1786,9 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p
17861786

17871787
/* Check if requested size fits into current bin */
17881788
if (size <= old_size) {
1789-
/* Check if truncation is necessary */
1790-
if (old_bin_num > 0 && size < bin_data_size[old_bin_num - 1]) {
1789+
/* Check if truncation is necessary. A size that fits the
1790+
* bin below moves there, as efree_size() maps it to that bin. */
1791+
if (old_bin_num > 0 && size <= bin_data_size[old_bin_num - 1]) {
17911792
/* truncation */
17921793
ret = zend_mm_alloc_small(heap, ZEND_MM_SMALL_SIZE_TO_BIN(size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
17931794
copy_size = use_copy_size ? MIN(size, copy_size) : size;

‎ext/zend_test/test.c‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,37 @@ static ZEND_FUNCTION(zend_leak_bytes)
197197
emalloc(leakbytes);
198198
}
199199

200+
/* Reallocate a block of old_size bytes to new_size bytes and return its block
201+
* size, along with the block size of a fresh allocation of new_size bytes. */
202+
static ZEND_FUNCTION(zend_test_erealloc_block_size)
203+
{
204+
zend_long old_size, new_size;
205+
206+
ZEND_PARSE_PARAMETERS_START(2, 2)
207+
Z_PARAM_LONG(old_size)
208+
Z_PARAM_LONG(new_size)
209+
ZEND_PARSE_PARAMETERS_END();
210+
211+
if (old_size < 1) {
212+
zend_argument_value_error(1, "must be greater than 0");
213+
RETURN_THROWS();
214+
}
215+
if (new_size < 1) {
216+
zend_argument_value_error(2, "must be greater than 0");
217+
RETURN_THROWS();
218+
}
219+
220+
void *ptr = erealloc(emalloc(old_size), new_size);
221+
void *fresh = emalloc(new_size);
222+
223+
array_init(return_value);
224+
add_next_index_long(return_value, zend_mem_block_size(ptr));
225+
add_next_index_long(return_value, zend_mem_block_size(fresh));
226+
227+
efree(fresh);
228+
efree(ptr);
229+
}
230+
200231
/* Leak a refcounted variable */
201232
static ZEND_FUNCTION(zend_leak_variable)
202233
{

‎ext/zend_test/test.stub.php‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ function zend_leak_bytes(int $bytes = 3): void {}
299299

300300
function zend_delref(mixed $variable): void {}
301301

302+
function zend_test_erealloc_block_size(int $old_size, int $new_size): array {}
303+
302304
function zend_bool(bool $param): bool {}
303305
function zend_bool_or_null(bool|null $param): bool|null {}
304306
function zend_bool_slow_zpp(bool $param): bool {}

‎ext/zend_test/test_arginfo.h‎

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎ext/zend_test/test_decl.h‎

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎ext/zend_test/test_legacy_arginfo.h‎

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)