Skip to content
Open
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
4 changes: 4 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,10 @@ PHP 8.6 UPGRADE NOTES
endianness modifier. A name starting with these characters is unaffected
when a repeater precedes it, as in "s1<value".
RFC: https://wiki.php.net/rfc/pack-unpack-endianness-signed-integers-support
. stream_is_local() now returns false for zlib and bzip2 wrappers around
non-local streams, e.g. the following are now no longer considered local:
- `compress.bzip2://http://127.0.0.1/example.html`
- `compress.zlib://http://127.0.0.1/example.html`

- Sysvshm:
. shm_attach() now raises a ValueError when the $key argument is outside the
Expand Down
14 changes: 14 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,19 @@ PHP 8.6 INTERNALS UPGRADE NOTES
zval* that is IS_UNDEF when the closure is unbound.
. object_properties_load() now verifies that the given value is assignable
to typed properties. The check is performed in strict mode.
. `php_stream_wrapper.is_url` switched from a binary flag (stored as an `int`)
to an instance of the new `php_stream_wrapper_is_url` enum:
* the value `0` maintains the same meaning of a stream never being a URL,
and is equivalent to the new `STREAM_IS_URL_NEVER` case which has the
underlying value `0`.
* the value `1` maintains the same meaning of a stream always being a URL,
and is equivalent to the new `STREAM_IS_URL_ALWAYS` case which has the
underlying value `1`.
* a value of `2` is now supported, using `STREAM_IS_URL_SOMETIMES`, which
indicates that the `stream_is_url` callback should be consulted.
. Extended `php_stream_wrapper_ops()` with a new `stream_is_url` callback,
which is required when `php_stream_wrapper.is_url` is set to
`STREAM_IS_URL_SOMETIMES`.

- Added:
. New zend_class_entry.ce_flags2 and zend_function.fn_flags2 fields were
Expand Down Expand Up @@ -236,6 +249,7 @@ PHP 8.6 INTERNALS UPGRADE NOTES
"INF" and "NAN" fail, while leading-numeric strings emit E_WARNING. When
*failed is true, the returned value must not be used and an exception may
already be pending. Passing an IS_UNDEF zval is a caller error.
. Added `php_stream_wrapper_is_url` C enum.

========================
2. Build system changes
Expand Down
32 changes: 30 additions & 2 deletions ext/bz2/bz2.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,33 @@ PHP_BZ2_API php_stream *_php_stream_bz2open(php_stream_wrapper *wrapper,

/* }}} */

static bool bz2_stream_is_url(php_stream_wrapper *wrapper, const char *path, php_stream_context *context)
{
const char *inner_path = path;
while (*inner_path) {
if (strncasecmp("compress.bzip2://", inner_path, strlen("compress.bzip2://")) == 0) {
inner_path += strlen("compress.bzip2://");
} else {
break;
}
}
php_stream_wrapper *inner_wrapper = php_stream_locate_url_wrapper(inner_path, NULL, 0);
Comment thread
DanielEScherzer marked this conversation as resolved.
if (inner_wrapper == NULL) {
// No actual target being compressed, so not a URL
return false;
}
switch (inner_wrapper->is_url) {
case STREAM_IS_URL_NEVER:
return false;
case STREAM_IS_URL_ALWAYS:
return true;
case STREAM_IS_URL_SOMETIMES:
ZEND_ASSERT(inner_wrapper->wops->stream_is_url != NULL);
return (inner_wrapper->wops->stream_is_url)(inner_wrapper, inner_path, context);
default: ZEND_UNREACHABLE();
}
}

static const php_stream_wrapper_ops bzip2_stream_wops = {
_php_stream_bz2open,
NULL, /* close */
Expand All @@ -262,13 +289,14 @@ static const php_stream_wrapper_ops bzip2_stream_wops = {
NULL, /* rename */
NULL, /* mkdir */
NULL, /* rmdir */
NULL
NULL,
bz2_stream_is_url,
};

static const php_stream_wrapper php_stream_bzip2_wrapper = {
&bzip2_stream_wops,
NULL,
0 /* is_url */
STREAM_IS_URL_SOMETIMES,
};

static void php_bz2_error(INTERNAL_FUNCTION_PARAMETERS, int);
Expand Down
13 changes: 12 additions & 1 deletion ext/openssl/xp_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,18 @@ static long php_openssl_load_stream_cafile(X509_STORE *cert_store, const char *c
// TODO: no stream and no wrapper, cannot use php_stream_warn(stream, ReadFailed, ...) nor php_stream_wrapper_log_error()
php_error(E_WARNING, "failed loading cafile stream: `%s'", cafile);
return 0;
} else if (stream->wrapper->is_url) {
}
bool is_url = false;
if (stream->wrapper->is_url == STREAM_IS_URL_ALWAYS) {
is_url = true;
} else if (stream->wrapper->is_url == STREAM_IS_URL_NEVER) {
is_url = false;
} else {
ZEND_ASSERT(stream->wrapper->is_url == STREAM_IS_URL_SOMETIMES);
ZEND_ASSERT(stream->wrapper->wops->stream_is_url != NULL);
is_url = (stream->wrapper->wops->stream_is_url)(stream->wrapper, cafile, NULL);
}
if (is_url) {
php_stream_warn(stream, PermissionDenied, "remote cafile streams are disabled for security purposes");
php_stream_close(stream);
return 0;
Expand Down
5 changes: 3 additions & 2 deletions ext/phar/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ static const php_stream_wrapper_ops phar_stream_wops = {
phar_wrapper_rename, /* rename */
phar_wrapper_mkdir, /* create directory */
phar_wrapper_rmdir, /* remove directory */
NULL
NULL,
NULL, /* is_url, unneeded since phars are never for URLs */
};

const php_stream_wrapper php_stream_phar_wrapper = {
&phar_stream_wops,
NULL,
0 /* is_url */
STREAM_IS_URL_NEVER,
};

/**
Expand Down
5 changes: 3 additions & 2 deletions ext/standard/ftp_fopen_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -1182,11 +1182,12 @@ static const php_stream_wrapper_ops ftp_stream_wops = {
php_stream_ftp_rename, /* rename */
php_stream_ftp_mkdir, /* mkdir */
php_stream_ftp_rmdir, /* rmdir */
NULL
NULL,
NULL, /* is_url, unneeded since FTP is always for URLs */
};

PHPAPI const php_stream_wrapper php_stream_ftp_wrapper = {
&ftp_stream_wops,
NULL,
1 /* is_url */
STREAM_IS_URL_ALWAYS,
};
5 changes: 3 additions & 2 deletions ext/standard/http_fopen_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -1245,11 +1245,12 @@ static const php_stream_wrapper_ops http_stream_wops = {
NULL, /* rename */
NULL, /* mkdir */
NULL, /* rmdir */
NULL
NULL,
NULL, /* is_url, unneeded since HTTP is always for URLs */
};

PHPAPI const php_stream_wrapper php_stream_http_wrapper = {
&http_stream_wops,
NULL,
1 /* is_url */
STREAM_IS_URL_ALWAYS,
};
5 changes: 3 additions & 2 deletions ext/standard/php_fopen_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -481,11 +481,12 @@ static const php_stream_wrapper_ops php_stdio_wops = {
NULL, /* rename */
NULL, /* mkdir */
NULL, /* rmdir */
NULL
NULL,
NULL, /* is_url, unneeded since php is never for URLs */
};

PHPAPI const php_stream_wrapper php_stream_php_wrapper = {
&php_stdio_wops,
NULL,
0, /* is_url */
STREAM_IS_URL_NEVER,
};
23 changes: 21 additions & 2 deletions ext/standard/streamsfuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1686,21 +1686,40 @@ PHP_FUNCTION(stream_is_local)
Z_PARAM_RESOURCE_OR_NULL(zcontext)
ZEND_PARSE_PARAMETERS_END();

const char *path;

if (Z_TYPE_P(zstream) == IS_RESOURCE) {
php_stream_from_zval(stream, zstream);
wrapper = stream->wrapper;
path = stream->orig_path;
} else {
if (!try_convert_to_string(zstream)) {
RETURN_THROWS();
}

php_stream_error_operation_begin();
context = php_stream_context_from_zval(zcontext, 0);
wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(zstream), NULL, 0);

path = Z_STRVAL_P(zstream);
wrapper = php_stream_locate_url_wrapper(path, NULL, 0);
php_stream_error_operation_end(context);
}

RETURN_BOOL(wrapper && wrapper->is_url == 0);
if (!wrapper) {
RETURN_FALSE;
}
switch (wrapper->is_url) {
case STREAM_IS_URL_NEVER:
RETURN_TRUE;
case STREAM_IS_URL_ALWAYS:
RETURN_FALSE;
case STREAM_IS_URL_SOMETIMES:
ZEND_ASSERT(wrapper->wops->stream_is_url != NULL);
RETURN_BOOL(
!(wrapper->wops->stream_is_url)(wrapper, path, NULL)
);
default: ZEND_UNREACHABLE();
}
}
/* }}} */

Expand Down
65 changes: 65 additions & 0 deletions ext/standard/tests/streams/stream_is_local-bzip2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
--TEST--
Testing stream_is_local() with bzip2 wrappers
--EXTENSIONS--
bz2
--FILE--
<?php

$targets = [
'http://127.0.0.1/example.html',
'ftp://127.0.0.1/example.html',
'/etc/os-release',
'file://evil.example.com/x',
];
$wrappers = [
'',
'compress.bzip2://',
'compress.bzip2://compress.bzip2://',
];

foreach ($targets as $target) {
foreach ($wrappers as $wrapper) {
$case = $wrapper . $target;
echo $case . "\n";
var_dump(stream_is_local($case));
echo "\n";
}
}

?>
--EXPECT--
http://127.0.0.1/example.html
bool(false)

compress.bzip2://http://127.0.0.1/example.html
bool(false)

compress.bzip2://compress.bzip2://http://127.0.0.1/example.html
bool(false)

ftp://127.0.0.1/example.html
bool(false)

compress.bzip2://ftp://127.0.0.1/example.html
bool(false)

compress.bzip2://compress.bzip2://ftp://127.0.0.1/example.html
bool(false)

/etc/os-release
bool(true)

compress.bzip2:///etc/os-release
bool(true)

compress.bzip2://compress.bzip2:///etc/os-release
bool(true)

file://evil.example.com/x
bool(false)

compress.bzip2://file://evil.example.com/x
bool(true)

compress.bzip2://compress.bzip2://file://evil.example.com/x
bool(true)
66 changes: 66 additions & 0 deletions ext/standard/tests/streams/stream_is_local-mixed.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
--TEST--
Testing stream_is_local() with bz2 and zlib wrappers
--EXTENSIONS--
bz2
zlib
--FILE--
<?php

$targets = [
'http://127.0.0.1/example.html',
'ftp://127.0.0.1/example.html',
'/etc/os-release',
Comment thread
DanielEScherzer marked this conversation as resolved.
'file://evil.example.com/x',
];
$wrappers = [
'',
'compress.bzip2://compress.zlib://',
'compress.zlib://compress.bzip2://',
];

foreach ($targets as $target) {
foreach ($wrappers as $wrapper) {
$case = $wrapper . $target;
echo $case . "\n";
var_dump(stream_is_local($case));
echo "\n";
}
}

?>
--EXPECT--
http://127.0.0.1/example.html
bool(false)

compress.bzip2://compress.zlib://http://127.0.0.1/example.html
bool(false)

compress.zlib://compress.bzip2://http://127.0.0.1/example.html
bool(false)

ftp://127.0.0.1/example.html
bool(false)

compress.bzip2://compress.zlib://ftp://127.0.0.1/example.html
bool(false)

compress.zlib://compress.bzip2://ftp://127.0.0.1/example.html
bool(false)

/etc/os-release
bool(true)

compress.bzip2://compress.zlib:///etc/os-release
bool(true)

compress.zlib://compress.bzip2:///etc/os-release
bool(true)

file://evil.example.com/x
bool(false)

compress.bzip2://compress.zlib://file://evil.example.com/x
bool(true)

compress.zlib://compress.bzip2://file://evil.example.com/x
bool(true)
Loading
Loading