diff --git a/NEWS b/NEWS index cd9ddd3994cc..5e0f79f96fb5 100644 --- a/NEWS +++ b/NEWS @@ -143,6 +143,9 @@ PHP NEWS - Streams: . Applied the so_linger, so_rcvbuf and so_sndbuf context options to unix sockets. (David Carlier) + . Fixed a use-after-free when a user stream wrapper unregisters itself from + its constructor or destructor during unlink(), rename(), mkdir(), rmdir(), + touch(), chmod() or stat(). (Ilia Alshanetsky) - Standard: . Fixed bug #60110 (fclose(), file_put_contents(), copy() do not return false diff --git a/Zend/tests/streams/user_wrapper_unregister_url_ops.phpt b/Zend/tests/streams/user_wrapper_unregister_url_ops.phpt new file mode 100644 index 000000000000..9605fa58aa1a --- /dev/null +++ b/Zend/tests/streams/user_wrapper_unregister_url_ops.phpt @@ -0,0 +1,93 @@ +--TEST-- +User stream wrapper unregistered by its own constructor or destructor during a URL operation +--FILE-- + fn() => unlink('foo://a'), + 'rename' => fn() => rename('foo://a', 'foo://b'), + 'mkdir' => fn() => mkdir('foo://a'), + 'rmdir' => fn() => rmdir('foo://a'), + 'touch' => fn() => touch('foo://a'), + 'chmod' => fn() => chmod('foo://a', 0644), + 'stat' => fn() => stat('foo://a'), +]; + +foreach (['Ctor', 'Dtor'] as $class) { + foreach ($ops as $name => $op) { + stream_wrapper_register('foo', $class); + echo "$class $name: "; + var_dump($op()); + var_dump(in_array('foo', stream_get_wrappers())); + } +} +?> +--EXPECTF-- +Ctor unlink: +Warning: unlink(): Ctor::unlink is not implemented! in %s on line %d +bool(false) +bool(false) +Ctor rename: +Warning: rename(): Ctor::rename is not implemented! in %s on line %d +bool(false) +bool(false) +Ctor mkdir: +Warning: mkdir(): Ctor::mkdir is not implemented! in %s on line %d +bool(false) +bool(false) +Ctor rmdir: +Warning: rmdir(): Ctor::rmdir is not implemented! in %s on line %d +bool(false) +bool(false) +Ctor touch: +Warning: touch(): Ctor::stream_metadata is not implemented! in %s on line %d +bool(false) +bool(false) +Ctor chmod: +Warning: chmod(): Ctor::stream_metadata is not implemented! in %s on line %d +bool(false) +bool(false) +Ctor stat: +Warning: stat(): Ctor::url_stat is not implemented! in %s on line %d + +Warning: stat(): stat failed for foo://a in %s on line %d +bool(false) +bool(false) +Dtor unlink: +Warning: unlink(): Dtor::unlink is not implemented! in %s on line %d +bool(false) +bool(false) +Dtor rename: +Warning: rename(): Dtor::rename is not implemented! in %s on line %d +bool(false) +bool(false) +Dtor mkdir: +Warning: mkdir(): Dtor::mkdir is not implemented! in %s on line %d +bool(false) +bool(false) +Dtor rmdir: +Warning: rmdir(): Dtor::rmdir is not implemented! in %s on line %d +bool(false) +bool(false) +Dtor touch: +Warning: touch(): Dtor::stream_metadata is not implemented! in %s on line %d +bool(false) +bool(false) +Dtor chmod: +Warning: chmod(): Dtor::stream_metadata is not implemented! in %s on line %d +bool(false) +bool(false) +Dtor stat: +Warning: stat(): Dtor::url_stat is not implemented! in %s on line %d + +Warning: stat(): stat failed for foo://a in %s on line %d +bool(false) +bool(false) diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 8c4b43f6ce83..97885d0e3c0f 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -1096,8 +1096,10 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int int ret = 0; /* create an instance of our class */ + GC_ADDREF(uwrap->resource); zend_object *object = user_stream_create_object(uwrap, context); if (object == NULL) { + zend_list_delete(uwrap->resource); return ret; } @@ -1121,6 +1123,7 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int zval_ptr_dtor(&zretval); + zend_list_delete(uwrap->resource); return ret; } @@ -1133,8 +1136,10 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from int ret = 0; /* create an instance of our class */ + GC_ADDREF(uwrap->resource); zend_object *object = user_stream_create_object(uwrap, context); if (object == NULL) { + zend_list_delete(uwrap->resource); return ret; } @@ -1159,6 +1164,7 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from zval_ptr_dtor(&zretval); + zend_list_delete(uwrap->resource); return ret; } @@ -1171,8 +1177,10 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int int ret = 0; /* create an instance of our class */ + GC_ADDREF(uwrap->resource); zend_object *object = user_stream_create_object(uwrap, context); if (object == NULL) { + zend_list_delete(uwrap->resource); return ret; } @@ -1197,6 +1205,7 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int zval_ptr_dtor(&zretval); + zend_list_delete(uwrap->resource); return ret; } @@ -1209,8 +1218,10 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int ret = 0; /* create an instance of our class */ + GC_ADDREF(uwrap->resource); zend_object *object = user_stream_create_object(uwrap, context); if (object == NULL) { + zend_list_delete(uwrap->resource); return ret; } @@ -1234,6 +1245,7 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, zval_ptr_dtor(&zretval); + zend_list_delete(uwrap->resource); return ret; } @@ -1271,9 +1283,11 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i } /* create an instance of our class */ + GC_ADDREF(uwrap->resource); zend_object *object = user_stream_create_object(uwrap, context); if (object == NULL) { zval_ptr_dtor(&args[2]); + zend_list_delete(uwrap->resource); return ret; } @@ -1298,6 +1312,7 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i zval_ptr_dtor(&zretval); + zend_list_delete(uwrap->resource); return ret; } @@ -1311,8 +1326,10 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, i int ret = -1; /* create an instance of our class */ + GC_ADDREF(uwrap->resource); zend_object *object = user_stream_create_object(uwrap, context); if (object == NULL) { + zend_list_delete(uwrap->resource); return -1; } @@ -1329,9 +1346,11 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, i if (UNEXPECTED(call_result == FAILURE)) { php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, "%s::" USERSTREAM_STATURL " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + zend_list_delete(uwrap->resource); return -1; } if (UNEXPECTED(Z_ISUNDEF(zretval))) { + zend_list_delete(uwrap->resource); return -1; } if (EXPECTED(Z_TYPE(zretval) == IS_ARRAY)) { @@ -1342,6 +1361,7 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, i zval_ptr_dtor(&zretval); + zend_list_delete(uwrap->resource); return ret; }