Commit 60dba5a1a27 for php
commit 60dba5a1a271656123eb9c30464de1817c3cf9bf
Merge: ad0d61ae799 047c4501f28
Author: Máté Kocsis <kocsismate90@gmail.com>
Date: Sun Nov 8 22:21:20 2026 +0100
Merge branch 'PHP-8.5' into PHP-8.6
* PHP-8.5:
Fix in-place URL decoding of interned userinfo in the http and ftp wrappers
diff --cc NEWS
index ab99b600e65,4ed7877b679..943c2a8de9f
--- a/NEWS
+++ b/NEWS
@@@ -60,11 -51,9 +60,13 @@@ PH
. Fixed incorrect handling of non-ASCII pending_input bytes in readline_info(),
which could cause readline() to return false prematurely. (Weilin Du)
+- Reflection:
+ . Fixed bug GH-25266 (Misaligned read in ReflectionProperty::isReadable()).
+ (Lazizbek Ergashev)
+
- Streams:
+ . Fixed in-place URL decoding of interned strings in the HTTP and FTP
+ wrappers. (Alejandro Pacheco)
. Fixed bug GH-22063 (Stream filter chain UAF on self-removal during
callback). (Ilia Alshanetsky)
. Fixed leaks and data loss when user filter leaves buckets on input
diff --cc ext/standard/ftp_fopen_wrapper.c
index da6562f1d14,cacfa9e1013..c1341ff6266
--- a/ext/standard/ftp_fopen_wrapper.c
+++ b/ext/standard/ftp_fopen_wrapper.c
@@@ -247,24 -261,18 +265,18 @@@ static php_stream *php_ftp_fopen_connec
#endif
}
- #define PHP_FTP_CNTRL_CHK(val, val_len, err_msg) { \
- unsigned char *s = (unsigned char *) val, *e = (unsigned char *) s + val_len; \
- while (s < e) { \
- if (iscntrl((unsigned char)*s)) { \
- php_stream_wrapper_log_warn(wrapper, context, options, AuthFailed, err_msg, val); \
- goto connect_errexit; \
- } \
- s++; \
- } \
- }
-
/* send the user name */
if (resource->user != NULL) {
- ZSTR_LEN(resource->user) = php_raw_url_decode(ZSTR_VAL(resource->user), ZSTR_LEN(resource->user));
+ zend_string *user = php_ftp_url_decode(resource->user);
- PHP_FTP_CNTRL_CHK(ZSTR_VAL(resource->user), ZSTR_LEN(resource->user), "Invalid login %s")
+ if (php_ftp_has_cntrl(user)) {
- php_stream_wrapper_log_error(wrapper, options, "Invalid login %s", ZSTR_VAL(user));
++ php_stream_wrapper_log_warn(wrapper, context, options, AuthFailed, "Invalid login %s", ZSTR_VAL(user));
+ zend_string_efree(user);
+ goto connect_errexit;
+ }
- php_stream_printf(stream, "USER %pS\r\n", resource->user);
- php_stream_printf(stream, "USER %s\r\n", ZSTR_VAL(user));
++ php_stream_printf(stream, "USER %pS\r\n", user);
+ zend_string_efree(user);
} else {
php_stream_write_string(stream, "USER anonymous\r\n");
}
@@@ -277,11 -285,16 +289,16 @@@
php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_REQUIRED, tmp_line, 0);
if (resource->password != NULL) {
- ZSTR_LEN(resource->password) = php_raw_url_decode(ZSTR_VAL(resource->password), ZSTR_LEN(resource->password));
+ zend_string *password = php_ftp_url_decode(resource->password);
- PHP_FTP_CNTRL_CHK(ZSTR_VAL(resource->password), ZSTR_LEN(resource->password), "Invalid password %s")
+ if (php_ftp_has_cntrl(password)) {
- php_stream_wrapper_log_error(wrapper, options, "Invalid password %s", ZSTR_VAL(password));
++ php_stream_wrapper_log_warn(wrapper, context, options, AuthFailed, "Invalid password %s", ZSTR_VAL(password));
+ zend_string_efree(password);
+ goto connect_errexit;
+ }
- php_stream_printf(stream, "PASS %pS\r\n", resource->password);
- php_stream_printf(stream, "PASS %s\r\n", ZSTR_VAL(password));
++ php_stream_printf(stream, "PASS %pS\r\n", password);
+ zend_string_efree(password);
} else {
/* if the user has configured who they are,
send that as the password */
diff --cc ext/standard/http_fopen_wrapper.c
index f1c36defb1e,ac91e4210eb..21a2be87571
--- a/ext/standard/http_fopen_wrapper.c
+++ b/ext/standard/http_fopen_wrapper.c
@@@ -148,9 -158,35 +148,35 @@@ static bool check_has_header(const cha
}
s++;
}
- return 0;
+ return false;
}
+ /* The components of a php_uri may be interned or shared, so they are decoded into a new string. */
+ static zend_string *php_stream_http_url_decode(const zend_string *component)
+ {
+ zend_string *decoded = zend_string_alloc(ZSTR_LEN(component), false);
+ ZSTR_LEN(decoded) = php_url_decode_ex(ZSTR_VAL(decoded), ZSTR_VAL(component), ZSTR_LEN(component));
+ return decoded;
+ }
+
+ static bool php_stream_http_url_component_has_cntrl(const zend_string *component)
+ {
+ if (!component) {
+ return false;
+ }
+
+ zend_string *decoded = php_stream_http_url_decode(component);
+ bool has_cntrl = false;
+ for (size_t i = 0; i < ZSTR_LEN(decoded); i++) {
+ if (iscntrl((unsigned char) ZSTR_VAL(decoded)[i])) {
+ has_cntrl = true;
+ break;
+ }
+ }
+ zend_string_efree(decoded);
+ return has_cntrl;
+ }
+
static zend_result php_stream_handle_proxy_authorization_header(const char *s, smart_str *header)
{
const char *p;
@@@ -1157,11 -1175,9 +1173,10 @@@ finish
header_info.location = NULL;
}
+ /* check for invalid redirection URLs */
php_uri *new_resource = php_uri_parse_to_struct(uri_parser, new_path, strlen(new_path), PHP_URI_COMPONENT_READ_MODE_RAW, true);
if (new_resource == NULL) {
- php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl,
- "Invalid redirect URL! %s", new_path);
- php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path);
++ php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, "Invalid redirect URL! %s", new_path);
efree(new_path);
goto out;
}
@@@ -1177,27 -1193,15 +1192,15 @@@
php_uri_struct_free(resource);
resource = new_resource;
- #define CHECK_FOR_CNTRL_CHARS(val) { \
- if (val) { \
- unsigned char *s, *e; \
- ZSTR_LEN(val) = php_url_decode(ZSTR_VAL(val), ZSTR_LEN(val)); \
- s = (unsigned char*)ZSTR_VAL(val); e = s + ZSTR_LEN(val); \
- while (s < e) { \
- if (iscntrl(*s)) { \
- php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, \
- "Invalid redirect URL! %s", new_path); \
- efree(new_path); \
- goto out; \
- } \
- s++; \
- } \
- } \
- }
/* check for control characters in login, password & path */
if (strncasecmp(new_path, "http://", sizeof("http://") - 1) || strncasecmp(new_path, "https://", sizeof("https://") - 1)) {
- CHECK_FOR_CNTRL_CHARS(resource->user);
- CHECK_FOR_CNTRL_CHARS(resource->password);
- CHECK_FOR_CNTRL_CHARS(resource->path);
+ if (php_stream_http_url_component_has_cntrl(resource->user)
+ || php_stream_http_url_component_has_cntrl(resource->password)
+ || php_stream_http_url_component_has_cntrl(resource->path)) {
- php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path);
++ php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, "Invalid redirect URL! %s", new_path);
+ efree(new_path);
+ goto out;
+ }
}
int new_flags = HTTP_WRAPPER_REDIRECTED | (flags & HTTP_WRAPPER_STRIP_AUTH);
if (!same_origin) {