From e95083b711010c2a369953a9e52c18f1ffcea7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 25 Aug 2025 23:26:15 +0200 Subject: [PATCH] uri: Fix lexbor memory management in uri_parser_whatwg.c Calling `lexbor_mraw_clean()` after a specific number of parses will destroy the data for any live `Uri\WhatWg\Url` objects, effectively resulting in a use-after-free. Fix the issue by removing the periodic `lexbor_mraw_clean()` call. Instead we implement `php_uri_parser_whatwg_free()`. This also requires to move the destruction of the lexbor structures from RSHUTDOWN to POST_ZEND_DEACTIVATE to prevent a use-after-free in `php_uri_parser_whatwg_free()` since otherwise the mraw would already have been destroyed. --- ext/uri/php_uri.c | 10 ++++++---- ext/uri/tests/056.phpt | 23 +++++++++++++++++++++++ ext/uri/uri_parser_whatwg.c | 25 ++++++------------------- ext/uri/uri_parser_whatwg.h | 2 +- 4 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 ext/uri/tests/056.phpt diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index e440ba8b8829c..ce7675865c021 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -1084,9 +1084,9 @@ PHP_RINIT_FUNCTION(uri) return SUCCESS; } -PHP_RSHUTDOWN_FUNCTION(uri) +ZEND_MODULE_POST_ZEND_DEACTIVATE_D(uri) { - if (PHP_RSHUTDOWN(uri_parser_whatwg)(INIT_FUNC_ARGS_PASSTHRU) == FAILURE) { + if (ZEND_MODULE_POST_ZEND_DEACTIVATE_N(uri_parser_whatwg)() == FAILURE) { return FAILURE; } @@ -1101,8 +1101,10 @@ zend_module_entry uri_module_entry = { PHP_MINIT(uri), /* PHP_MINIT - Module initialization */ PHP_MSHUTDOWN(uri), /* PHP_MSHUTDOWN - Module shutdown */ PHP_RINIT(uri), /* PHP_RINIT - Request initialization */ - PHP_RSHUTDOWN(uri), /* PHP_RSHUTDOWN - Request shutdown */ + NULL, /* PHP_RSHUTDOWN - Request shutdown */ PHP_MINFO(uri), /* PHP_MINFO - Module info */ PHP_VERSION, /* Version */ - STANDARD_MODULE_PROPERTIES + NO_MODULE_GLOBALS, + ZEND_MODULE_POST_ZEND_DEACTIVATE_N(uri), + STANDARD_MODULE_PROPERTIES_EX }; diff --git a/ext/uri/tests/056.phpt b/ext/uri/tests/056.phpt new file mode 100644 index 0000000000000..a39d7897357d4 --- /dev/null +++ b/ext/uri/tests/056.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test Lexbor memory management +--EXTENSIONS-- +uri +--FILE-- +toAsciiString() !== "https://example.com/{$i}/") { + die("FAIL"); + } +} + +?> +==DONE== +--EXPECT-- +==DONE== diff --git a/ext/uri/uri_parser_whatwg.c b/ext/uri/uri_parser_whatwg.c index 9a7dccdb74146..5295bfe03e0be 100644 --- a/ext/uri/uri_parser_whatwg.c +++ b/ext/uri/uri_parser_whatwg.c @@ -24,10 +24,8 @@ #include #endif -ZEND_TLS lxb_url_parser_t lexbor_parser; -ZEND_TLS unsigned short int parsed_urls; +ZEND_TLS lxb_url_parser_t lexbor_parser = {0}; -static const unsigned short int maximum_parses_before_cleanup = 500; static const size_t lexbor_mraw_byte_size = 8192; static zend_always_inline void zval_string_or_null_to_lexbor_str(zval *value, lexbor_str_t *lexbor_str) @@ -539,34 +537,20 @@ PHP_RINIT_FUNCTION(uri_parser_whatwg) return FAILURE; } - parsed_urls = 0; - return SUCCESS; } -PHP_RSHUTDOWN_FUNCTION(uri_parser_whatwg) +ZEND_MODULE_POST_ZEND_DEACTIVATE_D(uri_parser_whatwg) { lxb_url_parser_memory_destroy(&lexbor_parser); lxb_url_parser_destroy(&lexbor_parser, false); - parsed_urls = 0; - return SUCCESS; } -static void reset_parser_state(void) -{ - if (++parsed_urls % maximum_parses_before_cleanup == 0) { - lexbor_mraw_clean(lexbor_parser.mraw); - parsed_urls = 0; - } - - lxb_url_parser_clean(&lexbor_parser); -} - lxb_url_t *php_uri_parser_whatwg_parse_ex(const char *uri_str, size_t uri_str_len, const lxb_url_t *lexbor_base_url, zval *errors, bool silent) { - reset_parser_state(); + lxb_url_parser_clean(&lexbor_parser); lxb_url_t *url = lxb_url_parse(&lexbor_parser, lexbor_base_url, (unsigned char *) uri_str, uri_str_len); const char *reason = fill_errors(errors); @@ -619,6 +603,9 @@ static zend_string *php_uri_parser_whatwg_to_string(void *uri, uri_recomposition static void php_uri_parser_whatwg_free(void *uri) { + lxb_url_t *lexbor_uri = uri; + + lxb_url_destroy(lexbor_uri); } const uri_parser_t php_uri_parser_whatwg = { diff --git a/ext/uri/uri_parser_whatwg.h b/ext/uri/uri_parser_whatwg.h index c05d4f7d5de14..ee63e0bdf04e5 100644 --- a/ext/uri/uri_parser_whatwg.h +++ b/ext/uri/uri_parser_whatwg.h @@ -25,6 +25,6 @@ extern const uri_parser_t php_uri_parser_whatwg; lxb_url_t *php_uri_parser_whatwg_parse_ex(const char *uri_str, size_t uri_str_len, const lxb_url_t *lexbor_base_url, zval *errors, bool silent); PHP_RINIT_FUNCTION(uri_parser_whatwg); -PHP_RSHUTDOWN_FUNCTION(uri_parser_whatwg); +ZEND_MODULE_POST_ZEND_DEACTIVATE_D(uri_parser_whatwg); #endif