Merge branch 'PHP-8.2'

* PHP-8.2:
  Fix use-after-free when unregistering user stream wrapper from itself
This commit is contained in:
Ilija Tovilo 2023-07-19 11:20:18 +02:00
commit b0037eda26
No known key found for this signature in database
GPG key ID: A4F5D403F118200A
4 changed files with 42 additions and 4 deletions

3
NEWS
View file

@ -2,6 +2,9 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.3.0beta2
- Streams:
. Fixed bug GH-11735 (Use-after-free when unregistering user stream wrapper
from itself). (ilutov)
20 Jul 2023, PHP 8.3.0beta1

16
Zend/tests/gh11735_1.phpt Normal file
View file

@ -0,0 +1,16 @@
--TEST--
GH-11735: Use-after-free when unregistering user stream wrapper from user stream wrapper
--FILE--
<?php
class FooWrapper {
public $context;
public function stream_open($path, $mode, $options, &$opened_path) {
stream_wrapper_unregister('foo');
return true;
}
}
stream_wrapper_register('foo', 'FooWrapper');
var_dump(fopen('foo://bar', 'r'));
?>
--EXPECTF--
resource(%d) of type (stream)

17
Zend/tests/gh11735_2.phpt Normal file
View file

@ -0,0 +1,17 @@
--TEST--
GH-11735: Use-after-free when unregistering user stream wrapper from user stream wrapper
--FILE--
<?php
class FooWrapper {
public $context;
public function stream_open($path, $mode, $options, &$opened_path) {
stream_wrapper_unregister('foo');
return false;
}
}
stream_wrapper_register('foo', 'FooWrapper');
var_dump(fopen('foo://bar', 'r'));
?>
--EXPECTF--
Warning: fopen(foo://bar): Failed to open stream: "FooWrapper::stream_open" call failed in %s on line %d
bool(false)

View file

@ -315,6 +315,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
us = emalloc(sizeof(*us));
us->wrapper = uwrap;
/* call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
GC_ADDREF(us->wrapper->resource);
user_stream_create_object(uwrap, context, &us->object);
if (Z_TYPE(us->object) == IS_UNDEF) {
@ -350,8 +352,6 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
/* set wrapper data to be a reference to our object */
ZVAL_COPY(&stream->wrapperdata, &us->object);
GC_ADDREF(us->wrapper->resource);
} else {
php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" call failed",
ZSTR_VAL(us->wrapper->ce->name));
@ -361,6 +361,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
if (stream == NULL) {
zval_ptr_dtor(&us->object);
ZVAL_UNDEF(&us->object);
zend_list_delete(us->wrapper->resource);
efree(us);
}
zval_ptr_dtor(&zretval);
@ -403,6 +404,8 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
us = emalloc(sizeof(*us));
us->wrapper = uwrap;
/* call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
GC_ADDREF(us->wrapper->resource);
user_stream_create_object(uwrap, context, &us->object);
if (Z_TYPE(us->object) == IS_UNDEF) {
@ -425,8 +428,6 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
/* set wrapper data to be a reference to our object */
ZVAL_COPY(&stream->wrapperdata, &us->object);
GC_ADDREF(us->wrapper->resource);
} else {
php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed",
ZSTR_VAL(us->wrapper->ce->name));
@ -436,6 +437,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
if (stream == NULL) {
zval_ptr_dtor(&us->object);
ZVAL_UNDEF(&us->object);
zend_list_delete(us->wrapper->resource);
efree(us);
}
zval_ptr_dtor(&zretval);