Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
93 changes: 93 additions & 0 deletions Zend/tests/streams/user_wrapper_unregister_url_ops.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
--TEST--
User stream wrapper unregistered by its own constructor or destructor during a URL operation
--FILE--
<?php
class Ctor {
public $context;
public function __construct() { stream_wrapper_unregister('foo'); }
}
class Dtor {
public $context;
public function __destruct() { stream_wrapper_unregister('foo'); }
}

$ops = [
'unlink' => 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)
20 changes: 20 additions & 0 deletions main/streams/userspace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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)) {
Expand All @@ -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;

}
Expand Down
Loading