Fix use-after-free when unregistering user stream wrapper from itself

Fixes GH-11735
Closes GH-11737
This commit is contained in:
Ilija Tovilo 2023-07-18 12:01:57 +02:00
parent ad01b6e8c4
commit c3ccc363c6
No known key found for this signature in database
GPG key ID: A4F5D403F118200A
4 changed files with 43 additions and 4 deletions

4
NEWS
View file

@ -6,6 +6,10 @@ PHP NEWS
. Fix use-of-uninitialized-value in hash_pbkdf2(), fix missing $options
parameter in signature. (ilutov)
- Streams:
. Fixed bug GH-11735 (Use-after-free when unregistering user stream wrapper
from itself). (ilutov)
03 Aug 2023, PHP 8.2.9
- Build:

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

@ -341,6 +341,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) {
@ -376,8 +378,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));
@ -387,6 +387,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);
@ -429,6 +430,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) {
@ -451,8 +454,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));
@ -462,6 +463,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);