Skip to content

Commit a6fcf76

Browse files
committed
Fix ht corruption during shutdown for bailing user stream
Fixes GH-19844 Closes GH-19849
1 parent 4ad4d54 commit a6fcf76

File tree

3 files changed

+60
-10
lines changed

3 files changed

+60
-10
lines changed

Zend/tests/gh19844.phpt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
GH-19844: Bail from stream_close() in zend_shutdown_executor_values()
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public $context;
8+
private static $nested = false;
9+
10+
function stream_open() {
11+
return true;
12+
}
13+
14+
function stream_read() {
15+
return '.';
16+
}
17+
function stream_set_option() {}
18+
function stream_stat() {}
19+
20+
function stream_eof() {
21+
if (!Test::$nested) {
22+
Test::$nested = true;
23+
include 'Test://';
24+
}
25+
@trigger_error('Bail', E_USER_ERROR);
26+
}
27+
28+
function stream_close() {
29+
@trigger_error('Bail', E_USER_ERROR);
30+
}
31+
}
32+
33+
stream_wrapper_register('Test', Test::class);
34+
include 'Test://';
35+
36+
?>
37+
--EXPECTF--
38+
Fatal error: Bail in %s on line %d
39+
40+
Fatal error: Bail in %s on line %d
41+
42+
Fatal error: Bail in %s on line %d

Zend/zend_execute_API.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,7 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown)
274274
zval *zv;
275275

276276
EG(flags) |= EG_FLAGS_IN_RESOURCE_SHUTDOWN;
277-
zend_try {
278-
zend_close_rsrc_list(&EG(regular_list));
279-
} zend_end_try();
277+
zend_close_rsrc_list(&EG(regular_list));
280278

281279
/* No PHP callback functions should be called after this point. */
282280
EG(active) = 0;

Zend/zend_list.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,25 @@ void zend_close_rsrc_list(HashTable *ht)
217217
/* Reload ht->arData on each iteration, as it may be reallocated. */
218218
uint32_t i = ht->nNumUsed;
219219

220-
while (i-- > 0) {
221-
zval *p = ZEND_HASH_ELEMENT(ht, i);
222-
if (Z_TYPE_P(p) != IS_UNDEF) {
223-
zend_resource *res = Z_PTR_P(p);
224-
if (res->type >= 0) {
225-
zend_resource_dtor(res);
220+
retry:
221+
zend_try {
222+
while (i-- > 0) {
223+
zval *p = ZEND_HASH_ELEMENT(ht, i);
224+
if (Z_TYPE_P(p) != IS_UNDEF) {
225+
zend_resource *res = Z_PTR_P(p);
226+
if (res->type >= 0) {
227+
zend_resource_dtor(res);
228+
}
226229
}
227230
}
228-
}
231+
} zend_catch {
232+
/* If we have bailed, we probably executed user code (e.g. user stream
233+
* API). Keep closing resources so they don't leak. User handlers must be
234+
* called now so they aren't called in zend_deactivate() on
235+
* zend_destroy_rsrc_list(&EG(regular_list)). At that point, the executor
236+
* has already shut down and the process would crash. */
237+
goto retry;
238+
} zend_end_try();
229239
}
230240

231241

0 commit comments

Comments
 (0)