Skip to content

Commit b0037ed

Browse files
committed
Merge branch 'PHP-8.2'
* PHP-8.2: Fix use-after-free when unregistering user stream wrapper from itself
2 parents 451cbbb + c3ccc36 commit b0037ed

File tree

4 files changed

+42
-4
lines changed

4 files changed

+42
-4
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.3.0beta2
44

5+
- Streams:
6+
. Fixed bug GH-11735 (Use-after-free when unregistering user stream wrapper
7+
from itself). (ilutov)
58

69
20 Jul 2023, PHP 8.3.0beta1
710

Zend/tests/gh11735_1.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
GH-11735: Use-after-free when unregistering user stream wrapper from user stream wrapper
3+
--FILE--
4+
<?php
5+
class FooWrapper {
6+
public $context;
7+
public function stream_open($path, $mode, $options, &$opened_path) {
8+
stream_wrapper_unregister('foo');
9+
return true;
10+
}
11+
}
12+
stream_wrapper_register('foo', 'FooWrapper');
13+
var_dump(fopen('foo://bar', 'r'));
14+
?>
15+
--EXPECTF--
16+
resource(%d) of type (stream)

Zend/tests/gh11735_2.phpt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
GH-11735: Use-after-free when unregistering user stream wrapper from user stream wrapper
3+
--FILE--
4+
<?php
5+
class FooWrapper {
6+
public $context;
7+
public function stream_open($path, $mode, $options, &$opened_path) {
8+
stream_wrapper_unregister('foo');
9+
return false;
10+
}
11+
}
12+
stream_wrapper_register('foo', 'FooWrapper');
13+
var_dump(fopen('foo://bar', 'r'));
14+
?>
15+
--EXPECTF--
16+
Warning: fopen(foo://bar): Failed to open stream: "FooWrapper::stream_open" call failed in %s on line %d
17+
bool(false)

main/streams/userspace.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
315315

316316
us = emalloc(sizeof(*us));
317317
us->wrapper = uwrap;
318+
/* call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
319+
GC_ADDREF(us->wrapper->resource);
318320

319321
user_stream_create_object(uwrap, context, &us->object);
320322
if (Z_TYPE(us->object) == IS_UNDEF) {
@@ -350,8 +352,6 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
350352

351353
/* set wrapper data to be a reference to our object */
352354
ZVAL_COPY(&stream->wrapperdata, &us->object);
353-
354-
GC_ADDREF(us->wrapper->resource);
355355
} else {
356356
php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" call failed",
357357
ZSTR_VAL(us->wrapper->ce->name));
@@ -361,6 +361,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
361361
if (stream == NULL) {
362362
zval_ptr_dtor(&us->object);
363363
ZVAL_UNDEF(&us->object);
364+
zend_list_delete(us->wrapper->resource);
364365
efree(us);
365366
}
366367
zval_ptr_dtor(&zretval);
@@ -403,6 +404,8 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
403404

404405
us = emalloc(sizeof(*us));
405406
us->wrapper = uwrap;
407+
/* call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
408+
GC_ADDREF(us->wrapper->resource);
406409

407410
user_stream_create_object(uwrap, context, &us->object);
408411
if (Z_TYPE(us->object) == IS_UNDEF) {
@@ -425,8 +428,6 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
425428

426429
/* set wrapper data to be a reference to our object */
427430
ZVAL_COPY(&stream->wrapperdata, &us->object);
428-
429-
GC_ADDREF(us->wrapper->resource);
430431
} else {
431432
php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed",
432433
ZSTR_VAL(us->wrapper->ce->name));
@@ -436,6 +437,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
436437
if (stream == NULL) {
437438
zval_ptr_dtor(&us->object);
438439
ZVAL_UNDEF(&us->object);
440+
zend_list_delete(us->wrapper->resource);
439441
efree(us);
440442
}
441443
zval_ptr_dtor(&zretval);

0 commit comments

Comments
 (0)