Skip to content
Merged
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
7 changes: 7 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.5.0RC1

- Core:
. Fixed bug GH-19765 (object_properties_load() bypasses readonly property
checks). (timwolla)

- URI:
. Fixed bug GH-19780 (InvalidUrlException should check $errors argument).
(nielsdos)

11 Sep 2025, PHP 8.5.0beta3

Expand Down
8 changes: 8 additions & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -1769,6 +1769,14 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties)
property_info &&
(property_info->flags & ZEND_ACC_STATIC) == 0) {
zval *slot = OBJ_PROP(object, property_info->offset);
if (UNEXPECTED((property_info->flags & ZEND_ACC_READONLY) && !Z_ISUNDEF_P(slot))) {
if (Z_PROP_FLAG_P(slot) & IS_PROP_REINITABLE) {
Z_PROP_FLAG_P(slot) &= ~IS_PROP_REINITABLE;
} else {
zend_readonly_property_modification_error(property_info);
return;
}
}
zval_ptr_dtor(slot);
ZVAL_COPY_VALUE(slot, prop);
zval_add_ref(slot);
Expand Down
21 changes: 21 additions & 0 deletions ext/random/tests/03_randomizer/gh_19765_unserialize.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
GH-19765: object_properties_load() bypasses readonly property checks
--FILE--
<?php

use Random\Engine\Mt19937;
use Random\Engine\PcgOneseq128XslRr64;
use Random\Randomizer;

try {
$r = new Randomizer(new Mt19937());
$r->__unserialize([['engine' => new PcgOneseq128XslRr64()]]);
} catch (Exception $error) {
echo $error->getMessage() . "\n";
}
var_dump($r->engine::class);

?>
--EXPECT--
Invalid serialization data for Random\Randomizer object
string(21) "Random\Engine\Mt19937"
2 changes: 1 addition & 1 deletion ext/random/tests/03_randomizer/gh_9186_unserialize.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
--TEST--
Fix GH-9186 @strict-properties can be bypassed using unserialization
GH-9186: @strict-properties can be bypassed using unserialization
--FILE--
<?php

Expand Down
2 changes: 1 addition & 1 deletion ext/soap/php_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ static bool in_domain(const zend_string *host, const zend_string *domain)

int make_http_soap_request(
zval *this_ptr, zend_string *buf, zend_string *location, char *soapaction,
int soap_version, const zend_string *uri_parser_class, zval *return_value
int soap_version, zend_string *uri_parser_class, zval *return_value
) {
zend_string *request;
smart_str soap_headers = {0};
Expand Down
2 changes: 1 addition & 1 deletion ext/soap/php_http.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

int make_http_soap_request(
zval *this_ptr, zend_string *buf, zend_string *location, char *soapaction,
int soap_version, const zend_string *uri_parser_class, zval *return_value
int soap_version, zend_string *uri_parser_class, zval *return_value
);

int proxy_authentication(zval* this_ptr, smart_str* soap_headers);
Expand Down
37 changes: 29 additions & 8 deletions ext/uri/php_uri.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,6 @@ static const zend_module_dep uri_deps[] = {

static zend_array uri_parsers;

static const uri_parser_t *uri_parser_by_name(const char *uri_parser_name, size_t uri_parser_name_len)
{
return zend_hash_str_find_ptr(&uri_parsers, uri_parser_name, uri_parser_name_len);
}

static HashTable *uri_get_debug_properties(zend_object *object)
{
uri_internal_t *internal_uri = uri_internal_from_obj(object);
Expand Down Expand Up @@ -105,13 +100,13 @@ static HashTable *uri_get_debug_properties(zend_object *object)
return result;
}

PHPAPI const uri_parser_t *php_uri_get_parser(const zend_string *uri_parser_name)
PHPAPI const uri_parser_t *php_uri_get_parser(zend_string *uri_parser_name)
{
if (uri_parser_name == NULL) {
return uri_parser_by_name(PHP_URI_PARSER_PHP_PARSE_URL, sizeof(PHP_URI_PARSER_PHP_PARSE_URL) - 1);
return zend_hash_str_find_ptr(&uri_parsers, PHP_URI_PARSER_PHP_PARSE_URL, sizeof(PHP_URI_PARSER_PHP_PARSE_URL) - 1);
}

return uri_parser_by_name(ZSTR_VAL(uri_parser_name), ZSTR_LEN(uri_parser_name));
return zend_hash_find_ptr(&uri_parsers, uri_parser_name);
}

ZEND_ATTRIBUTE_NONNULL PHPAPI uri_internal_t *php_uri_parse(const uri_parser_t *uri_parser, const char *uri_str, size_t uri_str_len, bool silent)
Expand Down Expand Up @@ -396,6 +391,27 @@ static void create_rfc3986_uri(INTERNAL_FUNCTION_PARAMETERS, bool is_constructor
php_uri_instantiate_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, uri_str, base_url_object, is_constructor, is_constructor, NULL);
}

static bool is_list_of_whatwg_validation_errors(const HashTable *array)
{
if (!zend_array_is_list(array)) {
return false;
}

ZEND_HASH_FOREACH_VAL(array, zval *val) {
/* Do not allow references as they may change types after checking. */

if (Z_TYPE_P(val) != IS_OBJECT) {
return false;
}

if (!instanceof_function(Z_OBJCE_P(val), uri_whatwg_url_validation_error_ce)) {
return false;
}
} ZEND_HASH_FOREACH_END();

return true;
}

PHP_METHOD(Uri_Rfc3986_Uri, parse)
{
create_rfc3986_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
Expand Down Expand Up @@ -430,6 +446,11 @@ PHP_METHOD(Uri_WhatWg_InvalidUrlException, __construct)
ZVAL_EMPTY_ARRAY(&tmp);
zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), &tmp);
} else {
if (!is_list_of_whatwg_validation_errors(Z_ARR_P(errors))) {
zend_argument_value_error(2, "must be a list of %s", ZSTR_VAL(uri_whatwg_url_validation_error_ce->name));
RETURN_THROWS();
}

zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), errors);
}
if (EG(exception)) {
Expand Down
2 changes: 1 addition & 1 deletion ext/uri/php_uri.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ PHPAPI zend_result php_uri_parser_register(const uri_parser_t *uri_parser);
* @param uri_parser_name The URI parser name
* @return The URI parser
*/
PHPAPI const uri_parser_t *php_uri_get_parser(const zend_string *uri_parser_name);
PHPAPI const uri_parser_t *php_uri_get_parser(zend_string *uri_parser_name);

ZEND_ATTRIBUTE_NONNULL PHPAPI uri_internal_t *php_uri_parse(const uri_parser_t *uri_parser, const char *uri_str, size_t uri_str_len, bool silent);

Expand Down
29 changes: 29 additions & 0 deletions ext/uri/tests/gh19780.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
GH-19780 (InvalidUrlException should check $errors argument)
--EXTENSIONS--
uri
--FILE--
<?php

use Uri\WhatWg\InvalidUrlException;
use Uri\WhatWg\UrlValidationError;
use Uri\WhatWg\UrlValidationErrorType;

try {
new InvalidUrlException('message', ['foo']);
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}

try {
new InvalidUrlException('message', [
1 => new UrlValidationError('context', UrlValidationErrorType::HostMissing, true)
]);
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Uri\WhatWg\InvalidUrlException::__construct(): Argument #2 ($errors) must be a list of Uri\WhatWg\UrlValidationError
Uri\WhatWg\InvalidUrlException::__construct(): Argument #2 ($errors) must be a list of Uri\WhatWg\UrlValidationError
91 changes: 66 additions & 25 deletions ext/uri/uri_parser_rfc3986.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,17 @@ static zend_result php_uri_parser_rfc3986_scheme_write(struct uri_internal_t *in
result = uriSetSchemeMmA(uriparser_uri, Z_STRVAL_P(value), Z_STRVAL_P(value) + Z_STRLEN_P(value), mm);
}

if (result != URI_SUCCESS) {
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified scheme is malformed", 0);
return FAILURE;
switch (result) {
case URI_SUCCESS:
return SUCCESS;
case URI_ERROR_SYNTAX:
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified scheme is malformed", 0);
return FAILURE;
default:
/* This should be unreachable in practice. */
zend_throw_exception(uri_invalid_uri_exception_ce, "Failed to update the scheme", 0);
return FAILURE;
}

return SUCCESS;
}

ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_userinfo_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
Expand Down Expand Up @@ -168,9 +173,13 @@ zend_result php_uri_parser_rfc3986_userinfo_write(struct uri_internal_t *interna
case URI_ERROR_SETUSERINFO_HOST_NOT_SET:
zend_throw_exception(uri_invalid_uri_exception_ce, "Cannot set a userinfo without having a host", 0);
return FAILURE;
default:
case URI_ERROR_SYNTAX:
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified userinfo is malformed", 0);
return FAILURE;
default:
/* This should be unreachable in practice. */
zend_throw_exception(uri_invalid_uri_exception_ce, "Failed to update the userinfo", 0);
return FAILURE;
}
}

Expand Down Expand Up @@ -259,9 +268,13 @@ static zend_result php_uri_parser_rfc3986_host_write(struct uri_internal_t *inte
case URI_ERROR_SETHOST_USERINFO_SET:
zend_throw_exception(uri_invalid_uri_exception_ce, "Cannot remove the host from a URI that has a userinfo", 0);
return FAILURE;
default:
case URI_ERROR_SYNTAX:
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified host is malformed", 0);
return FAILURE;
default:
/* This should be unreachable in practice. */
zend_throw_exception(uri_invalid_uri_exception_ce, "Failed to update the host", 0);
return FAILURE;
}
}

Expand Down Expand Up @@ -315,9 +328,13 @@ static zend_result php_uri_parser_rfc3986_port_write(struct uri_internal_t *inte
case URI_ERROR_SETPORT_HOST_NOT_SET:
zend_throw_exception(uri_invalid_uri_exception_ce, "Cannot set a port without having a host", 0);
return FAILURE;
default:
case URI_ERROR_SYNTAX:
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified port is malformed", 0);
return FAILURE;
default:
/* This should be unreachable in practice. */
zend_throw_exception(uri_invalid_uri_exception_ce, "Failed to update the port", 0);
return FAILURE;
}
}

Expand Down Expand Up @@ -360,12 +377,17 @@ static zend_result php_uri_parser_rfc3986_path_write(struct uri_internal_t *inte
result = uriSetPathMmA(uriparser_uri, Z_STRVAL_P(value), Z_STRVAL_P(value) + Z_STRLEN_P(value), mm);
}

if (result != URI_SUCCESS) {
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified path is malformed", 0);
return FAILURE;
switch (result) {
case URI_SUCCESS:
return SUCCESS;
case URI_ERROR_SYNTAX:
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified path is malformed", 0);
return FAILURE;
default:
/* This should be unreachable in practice. */
zend_throw_exception(uri_invalid_uri_exception_ce, "Failed to update the path", 0);
return FAILURE;
}

return SUCCESS;
}

ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_query_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
Expand All @@ -392,12 +414,17 @@ static zend_result php_uri_parser_rfc3986_query_write(struct uri_internal_t *int
result = uriSetQueryMmA(uriparser_uri, Z_STRVAL_P(value), Z_STRVAL_P(value) + Z_STRLEN_P(value), mm);
}

if (result != URI_SUCCESS) {
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified query is malformed", 0);
return FAILURE;
switch (result) {
case URI_SUCCESS:
return SUCCESS;
case URI_ERROR_SYNTAX:
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified query is malformed", 0);
return FAILURE;
default:
/* This should be unreachable in practice. */
zend_throw_exception(uri_invalid_uri_exception_ce, "Failed to update the query", 0);
return FAILURE;
}

return SUCCESS;
}

ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_fragment_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval)
Expand All @@ -424,12 +451,17 @@ static zend_result php_uri_parser_rfc3986_fragment_write(struct uri_internal_t *
result = uriSetFragmentMmA(uriparser_uri, Z_STRVAL_P(value), Z_STRVAL_P(value) + Z_STRLEN_P(value), mm);
}

if (result != URI_SUCCESS) {
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified fragment is malformed", 0);
return FAILURE;
switch (result) {
case URI_SUCCESS:
return SUCCESS;
case URI_ERROR_SYNTAX:
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified fragment is malformed", 0);
return FAILURE;
default:
/* This should be unreachable in practice. */
zend_throw_exception(uri_invalid_uri_exception_ce, "Failed to update the fragment", 0);
return FAILURE;
}

return SUCCESS;
}

static php_uri_parser_rfc3986_uris *uriparser_create_uris(void)
Expand All @@ -445,9 +477,18 @@ php_uri_parser_rfc3986_uris *php_uri_parser_rfc3986_parse_ex(const char *uri_str
UriUriA uri = {0};

/* Parse the URI. */
if (uriParseSingleUriExMmA(&uri, uri_str, uri_str + uri_str_len, NULL, mm) != URI_SUCCESS) {
int result = uriParseSingleUriExMmA(&uri, uri_str, uri_str + uri_str_len, NULL, mm);
if (result != URI_SUCCESS) {
if (!silent) {
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0);
switch (result) {
case URI_ERROR_SYNTAX:
zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0);
break;
default:
/* This should be unreachable in practice. */
zend_throw_exception(uri_invalid_uri_exception_ce, "Failed to parse the specified URI", 0);
break;
}
}

goto fail;
Expand Down
4 changes: 2 additions & 2 deletions sapi/litespeed/lsapilib.c
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,8 @@ static inline int isPipe( int fd )
{
char achPeer[128];
socklen_t len = 128;
if ((getpeername(fd, (struct sockaddr *)achPeer, &len) != 0 )
&& (errno == ENOTCONN || errno == ENOTSOCK))
if (( getpeername( fd, (struct sockaddr *)achPeer, &len ) != 0 )&&
( errno == ENOTCONN ))
return 0;
else
return 1;
Expand Down