diff --git a/NEWS b/NEWS index 49818abb90c25..bc125117dc6ee 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 4e3cdc597bfe2..865a684e4e240 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -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); diff --git a/ext/random/tests/03_randomizer/gh_19765_unserialize.phpt b/ext/random/tests/03_randomizer/gh_19765_unserialize.phpt new file mode 100644 index 0000000000000..24abfca293fc5 --- /dev/null +++ b/ext/random/tests/03_randomizer/gh_19765_unserialize.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-19765: object_properties_load() bypasses readonly property checks +--FILE-- +__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" diff --git a/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt b/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt index fe1acb2f74be4..edadb7380bd1f 100644 --- a/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt +++ b/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt @@ -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-- name)); + RETURN_THROWS(); + } + zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), errors); } if (EG(exception)) { diff --git a/ext/uri/php_uri.h b/ext/uri/php_uri.h index f570d5150ffa6..3d0b703dafc0d 100644 --- a/ext/uri/php_uri.h +++ b/ext/uri/php_uri.h @@ -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); diff --git a/ext/uri/tests/gh19780.phpt b/ext/uri/tests/gh19780.phpt new file mode 100644 index 0000000000000..7271c60e8f938 --- /dev/null +++ b/ext/uri/tests/gh19780.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-19780 (InvalidUrlException should check $errors argument) +--EXTENSIONS-- +uri +--FILE-- +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 diff --git a/ext/uri/uri_parser_rfc3986.c b/ext/uri/uri_parser_rfc3986.c index a4507a610d735..f2e4d1e72b6ab 100644 --- a/ext/uri/uri_parser_rfc3986.c +++ b/ext/uri/uri_parser_rfc3986.c @@ -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) @@ -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; } } @@ -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; } } @@ -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; } } @@ -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) @@ -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) @@ -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) @@ -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; diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c index 75e014167f274..9d8408c613395 100644 --- a/sapi/litespeed/lsapilib.c +++ b/sapi/litespeed/lsapilib.c @@ -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;