Skip to content

Commit ca914ee

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: Fix GH-20286: use-after-destroy during userland stream_close()
2 parents 56dd321 + d13b5eb commit ca914ee

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

Zend/zend_list.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,21 +214,34 @@ void zend_init_rsrc_plist(void)
214214

215215
void zend_close_rsrc_list(HashTable *ht)
216216
{
217-
/* Reload ht->arData on each iteration, as it may be reallocated. */
218217
uint32_t i = ht->nNumUsed;
218+
uint32_t num = ht->nNumUsed;
219219

220220
retry:
221221
zend_try {
222222
while (i-- > 0) {
223+
/* Reload ht->arData on each iteration, as it may be reallocated. */
223224
zval *p = ZEND_HASH_ELEMENT(ht, i);
224225
if (Z_TYPE_P(p) != IS_UNDEF) {
225226
zend_resource *res = Z_PTR_P(p);
226227
if (res->type >= 0) {
227228
zend_resource_dtor(res);
229+
230+
if (UNEXPECTED(ht->nNumUsed != num)) {
231+
/* New resources were added, reloop from the start.
232+
* We need to keep the top->down order to avoid freeing resources
233+
* in use by the newly created resources. */
234+
i = num = ht->nNumUsed;
235+
}
228236
}
229237
}
230238
}
231239
} zend_catch {
240+
if (UNEXPECTED(ht->nNumUsed != num)) {
241+
/* See above */
242+
i = num = ht->nNumUsed;
243+
}
244+
232245
/* If we have bailed, we probably executed user code (e.g. user stream
233246
* API). Keep closing resources so they don't leak. User handlers must be
234247
* called now so they aren't called in zend_deactivate() on
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--TEST--
2+
GH-20286 use after destroy on userland stream_close
3+
--CREDITS--
4+
vi3tL0u1s
5+
--SKIPIF--
6+
<?php
7+
if (substr(PHP_OS, 0, 3) == 'WIN') die('skip Aborts with STATUS_BAD_FUNCTION_TABLE on Windows');
8+
?>
9+
--FILE--
10+
<?php
11+
class lib {
12+
public $context;
13+
function stream_set() {}
14+
function stream_set_option() {}
15+
function stream_stat() {
16+
return true;
17+
}
18+
function stream_open() {
19+
return true;
20+
}
21+
22+
function stream_read($count) {
23+
function a() {}
24+
include('lib://');
25+
}
26+
27+
function stream_close() {
28+
static $count = 0;
29+
if ($count++ < 3) // Prevent infinite loop
30+
include('lib://');
31+
}
32+
}
33+
stream_wrapper_register('lib', lib::class);
34+
include('lib://test.php');
35+
?>
36+
--EXPECTF--
37+
Fatal error: Cannot redeclare function a() (previously declared in %s:%d) in %s on line %d
38+
39+
Fatal error: Cannot redeclare function a() (previously declared in %s:%d) in %s on line %d
40+
41+
Fatal error: Cannot redeclare function a() (previously declared in %s:%d) in %s on line %d
42+
43+
Fatal error: Cannot redeclare function a() (previously declared in %s:%d) in %s on line %d

0 commit comments

Comments
 (0)