Skip to content

Commit 0b516ae

Browse files
committed
Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1: Fix #81992: SplFixedArray::setSize() causes use-after-free
2 parents 231a1f8 + b71c6b2 commit 0b516ae

File tree

4 files changed

+123
-1
lines changed

4 files changed

+123
-1
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ PHP NEWS
6060
. Revert behaviour of receiving SIGCHLD signals back to the behaviour
6161
before 8.1.22. (nielsdos)
6262

63+
- SPL:
64+
. Fixed bug #81992 (SplFixedArray::setSize() causes use-after-free).
65+
(nielsdos)
66+
6367
- Standard:
6468
. Prevent int overflow on $decimals in number_format. (Marc Bennewitz)
6569
. Fixed bug GH-11870 (Fix off-by-one bug when truncating tempnam prefix)

ext/spl/spl_fixedarray.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ typedef struct _spl_fixedarray {
5050
zval *elements;
5151
/* True if this was modified after the last call to get_properties or the hash table wasn't rebuilt. */
5252
bool should_rebuild_properties;
53+
/* If positive, it's a resize within a resize and the value gives the desired size. If -1, it's not. */
54+
zend_long cached_resize;
5355
} spl_fixedarray;
5456

5557
typedef struct _spl_fixedarray_object {
@@ -118,6 +120,7 @@ static void spl_fixedarray_init(spl_fixedarray *array, zend_long size)
118120
} else {
119121
spl_fixedarray_default_ctor(array);
120122
}
123+
array->cached_resize = -1;
121124
}
122125

123126
/* Copies the range [begin, end) into the fixedarray, beginning at `offset`.
@@ -149,6 +152,7 @@ static void spl_fixedarray_copy_ctor(spl_fixedarray *to, spl_fixedarray *from)
149152
*/
150153
static void spl_fixedarray_dtor_range(spl_fixedarray *array, zend_long from, zend_long to)
151154
{
155+
array->size = from;
152156
zval *begin = array->elements + from, *end = array->elements + to;
153157
while (begin != end) {
154158
zval_ptr_dtor(begin++);
@@ -185,19 +189,35 @@ static void spl_fixedarray_resize(spl_fixedarray *array, zend_long size)
185189
return;
186190
}
187191

192+
if (UNEXPECTED(array->cached_resize >= 0)) {
193+
/* We're already resizing, so just remember the desired size.
194+
* The resize will happen later. */
195+
array->cached_resize = size;
196+
return;
197+
}
198+
array->cached_resize = size;
199+
188200
/* clearing the array */
189201
if (size == 0) {
190202
spl_fixedarray_dtor(array);
191203
array->elements = NULL;
204+
array->size = 0;
192205
} else if (size > array->size) {
193206
array->elements = safe_erealloc(array->elements, size, sizeof(zval), 0);
194207
spl_fixedarray_init_elems(array, array->size, size);
208+
array->size = size;
195209
} else { /* size < array->size */
210+
/* Size set in spl_fixedarray_dtor_range() */
196211
spl_fixedarray_dtor_range(array, size, array->size);
197212
array->elements = erealloc(array->elements, sizeof(zval) * size);
198213
}
199214

200-
array->size = size;
215+
/* If resized within the destructor, take the last resize command and perform it */
216+
zend_long cached_resize = array->cached_resize;
217+
array->cached_resize = -1;
218+
if (cached_resize != size) {
219+
spl_fixedarray_resize(array, cached_resize);
220+
}
201221
}
202222

203223
static HashTable* spl_fixedarray_object_get_gc(zend_object *obj, zval **table, int *n)

ext/spl/tests/bug81992.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
Bug #81992 (SplFixedArray::setSize() causes use-after-free)
3+
--FILE--
4+
<?php
5+
class InvalidDestructor {
6+
public function __destruct() {
7+
global $obj;
8+
var_dump($obj[0]);
9+
try {
10+
var_dump($obj[2]);
11+
} catch (Throwable $e) {
12+
echo $e->getMessage(), "\n";
13+
}
14+
try {
15+
var_dump($obj[4]);
16+
} catch (Throwable $e) {
17+
echo $e->getMessage(), "\n";
18+
}
19+
}
20+
}
21+
22+
$obj = new SplFixedArray(5);
23+
$obj[0] = str_repeat("A", 10);
24+
$obj[2] = str_repeat('B', 10);
25+
$obj[3] = new InvalidDestructor();
26+
$obj[4] = str_repeat('C', 10);
27+
$obj->setSize(2);
28+
?>
29+
--EXPECT--
30+
string(10) "AAAAAAAAAA"
31+
Index invalid or out of range
32+
Index invalid or out of range

ext/spl/tests/bug81992b.phpt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
--TEST--
2+
Bug #81992 (SplFixedArray::setSize() causes use-after-free) - setSize variation
3+
--FILE--
4+
<?php
5+
class InvalidDestructor {
6+
public function __construct(
7+
private int $desiredSize,
8+
private SplFixedArray $obj,
9+
) {}
10+
11+
public function __destruct() {
12+
echo "In destructor\n";
13+
$this->obj->setSize($this->desiredSize);
14+
echo "Destroyed, size is now still ", $this->obj->getSize(), "\n";
15+
}
16+
}
17+
18+
class DestructorLogger {
19+
public function __construct(private int $id) {}
20+
21+
public function __destruct() {
22+
echo "Destroyed the logger with id ", $this->id, "\n";
23+
}
24+
}
25+
26+
function test(int $desiredSize) {
27+
$obj = new SplFixedArray(5);
28+
$obj[0] = str_repeat("A", 10);
29+
$obj[1] = new DestructorLogger(1);
30+
$obj[2] = str_repeat('B', 10);
31+
$obj[3] = new InvalidDestructor($desiredSize, $obj);
32+
$obj[4] = new DestructorLogger(4);
33+
$obj->setSize(2);
34+
echo "Size is now ", $obj->getSize(), "\n";
35+
echo "Done\n";
36+
}
37+
38+
echo "--- Smaller size test ---\n";
39+
test(1);
40+
echo "--- Equal size test ---\n";
41+
test(2);
42+
echo "--- Larger size test ---\n";
43+
test(10);
44+
?>
45+
--EXPECT--
46+
--- Smaller size test ---
47+
In destructor
48+
Destroyed, size is now still 2
49+
Destroyed the logger with id 4
50+
Destroyed the logger with id 1
51+
Size is now 1
52+
Done
53+
--- Equal size test ---
54+
In destructor
55+
Destroyed, size is now still 2
56+
Destroyed the logger with id 4
57+
Size is now 2
58+
Done
59+
Destroyed the logger with id 1
60+
--- Larger size test ---
61+
In destructor
62+
Destroyed, size is now still 2
63+
Destroyed the logger with id 4
64+
Size is now 10
65+
Done
66+
Destroyed the logger with id 1

0 commit comments

Comments
 (0)