8000 Fix stream_wrapper_unregister() resource leak · iluuu1994/php-src@d3952a1 · GitHub
[go: up one dir, main page]

8000 Skip to content

Commit d3952a1

Browse files
committed
Fix stream_wrapper_unregister() resource leak
Closes phpGH-8548
1 parent 2826550 commit d3952a1

File tree

6 files changed

+68
-1
lines changed

6 files changed

+68
-1
lines changed

Zend/tests/gh8548.phpt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
GH-8548: stream_wrapper_unregister() leaks memory
3+
--ENV--
4+
AUTO_CLOSE_RESOURCE_LIST=0
5+
--FILE--
6+
<?php
7+
8+
class Wrapper
9+
{
10+
public $context;
11+
12+
public function stream_open(string $path, string $mode, int $options): bool
13+
{
14+
return true;
15+
}
16+
}
17+
18+
if (!stream_wrapper_register('foo', Wrapper::class)) {
19+
throw new \exception('Could not unregister stream wrapper');
20+
}
21+
if (!stream_wrapper_unregister('foo')) {
22+
throw new \Exception('Could not unregister stream wrapper');
23+
}
24+
25+
?>
26+
--EXPECT--

Zend/zend_execute_API.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,9 +269,17 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown)
269269
zval *zv;
270270

271271
EG(flags) |= EG_FLAGS_IN_RESOURCE_SHUTDOWN;
272+
273+
#if ZEND_DEBUG
274+
char *tmp = getenv("AUTO_CLOSE_RESOURCE_LIST");
275+
if (tmp && !ZEND_ATOL(tmp)) {
276+
goto skip_auto_closing_resource_list;
277+
}
278+
#endif
272279
zend_try {
273280
zend_close_rsrc_list(&EG(regular_list));
274281
} zend_end_try();
282+
skip_auto_closing_resource_list:
275283

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

Zend/zend_list.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,18 @@ void zend_close_rsrc_list(HashTable *ht)
231231

232232
void zend_destroy_rsrc_list(HashTable *ht)
233233
{
234+
#if ZEND_DEBUG
235+
char *tmp = getenv("AUTO_CLOSE_RESOURCE_LIST");
236+
if (tmp && !ZEND_ATOL(tmp)) {
237+
if (!(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
238+
pefree(HT_GET_DATA_ADDR(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
239+
}
240+
return;
241+
}
242+
#endif
243+
234244
zend_hash_graceful_reverse_destroy(ht);
245+
235246
}
236247

237248
/* int return due to HashTable API */

main/php_streams.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ PHPAPI zend_result php_register_url_stream_wrapper(const char *protocol, const p
577577
PHPAPI zend_result php_unregister_url_stream_wrapper(const char *protocol);
578578
PHPAPI zend_result php_register_url_stream_wrapper_volatile(zend_string *protocol, php_stream_wrapper *wrapper);
579579
PHPAPI zend_result php_unregister_url_stream_wrapper_volatile(zend_string *protocol);
580+
PHPAPI void *php_get_url_stream_wrapper_volatile(zend_string *protocol);
580581
PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC);
581582
PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const char **path_for_open, int options);
582583
PHPAPI const char *php_stream_locate_eol(php_stream *stream, zend_string *buf);

main/streams/streams.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,6 +1882,15 @@ PHPAPI zend_result php_unregister_url_stream_wrapper_volatile(zend_string *proto
18821882
}
18831883
/* }}} */
18841884

1885+
PHPAPI void *php_get_url_stream_wrapper_volatile(zend_string *protocol)
1886+
{
1887+
if (!FG(stream_wrappers)) {
1888+
clone_wrapper_hash();
1889+
}
1890+
1891+
return zend_hash_find_ptr(FG(stream_wrappers), protocol);
1892+
}
1893+
18851894
/* {{{ php_stream_locate_url_wrapper */
18861895
PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const char **path_for_open, int options)
18871896
{

main/streams/userspace.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@
3535
static int le_protocols;
3636

3737
struct php_user_stream_wrapper {
38+
php_stream_wrapper wrapper;
3839
char * protoname;
3940
zend_class_entry *ce;
40-
php_stream_wrapper wrapper;
41+
zend_resource *resource;
4142
};
4243

4344
static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC);
@@ -481,10 +482,12 @@ PHP_FUNCTION(stream_wrapper_register)
481482
uwrap->wrapper.wops = &user_stream_wops;
482483
uwrap->wrapper.abstract = uwrap;
483484
uwrap->wrapper.is_url = ((flags & PHP_STREAM_IS_URL) != 0);
485+
uwrap->resource = NULL;
484486

485487
rsrc = zend_register_resource(uwrap, le_protocols);
486488

487489
if (php_register_url_stream_wrapper_volatile(protocol, &uwrap->wrapper) == SUCCESS) {
490+
uwrap->resource = rsrc;
488491
RETURN_TRUE;
489492
}
490493

@@ -510,12 +513,21 @@ PHP_FUNCTION(stream_wrapper_unregister)
510513
RETURN_THROWS();
511514
}
512515

516+
struct php_user_stream_wrapper *uwrap = php_get_url_stream_wrapper_volatile(protocol);
513517
if (php_unregister_url_stream_wrapper_volatile(protocol) == FAILURE) {
514518
/* We failed */
515519
php_error_docref(NULL, E_WARNING, "Unable to unregister protocol %s://", ZSTR_VAL(protocol));
516520
RETURN_FALSE;
517521
}
518522

523+
ZEND_ASSERT(uwrap != NULL);
524+
zend_resource *resource = uwrap->resource;
525+
zend_list_close(resource);
526+
if (GC_DELREF(resource) == 0) {
527+
rc_dtor_func((zend_refcounted *)resource);
528+
}
529+
// uwrap was released by resource destructor
530+
519531
RETURN_TRUE;
520532
}
521533
/* }}} */

0 commit comments

Comments
 (0)
0