From b9355c41fba87ecf465cdb3cb14146b610d93f9a Mon Sep 17 00:00:00 2001 From: lazerg Date: Wed, 28 Oct 2026 06:52:13 +0500 Subject: [PATCH] Fix GH-24802: guard request_parse_body() against reentrancy --- ext/standard/http.c | 7 ++++ .../http/request_parse_body/gh24802.phpt | 39 +++++++++++++++++++ main/SAPI.c | 1 + main/SAPI.h | 1 + sapi/cli/php_cli_server.c | 2 + 5 files changed, 50 insertions(+) create mode 100644 ext/standard/tests/http/request_parse_body/gh24802.phpt diff --git a/ext/standard/http.c b/ext/standard/http.c index ce54a17f7659..b82514542fb4 100644 --- a/ext/standard/http.c +++ b/ext/standard/http.c @@ -355,6 +355,12 @@ PHP_FUNCTION(request_parse_body) Z_PARAM_ARRAY_HT_OR_NULL(options) ZEND_PARSE_PARAMETERS_END(); + if (SG(request_parse_body_context).in_progress) { + zend_throw_error(zend_ce_request_parse_body_exception, "Cannot call request_parse_body() while the request body is already being parsed"); + RETURN_THROWS(); + } + + SG(request_parse_body_context).in_progress = true; SG(request_parse_body_context).throw_exceptions = true; if (options) { if (cache_request_parse_body_options(options) == FAILURE) { @@ -390,6 +396,7 @@ PHP_FUNCTION(request_parse_body) RETVAL_ARR(zend_new_pair(&post, &files)); exit: + SG(request_parse_body_context).in_progress = false; SG(request_parse_body_context).throw_exceptions = false; memset(&SG(request_parse_body_context).options_cache, 0, sizeof(SG(request_parse_body_context).options_cache)); } diff --git a/ext/standard/tests/http/request_parse_body/gh24802.phpt b/ext/standard/tests/http/request_parse_body/gh24802.phpt new file mode 100644 index 000000000000..b70daf4cada2 --- /dev/null +++ b/ext/standard/tests/http/request_parse_body/gh24802.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-24802 (request_parse_body() reentrancy double-frees protected variables) +--INI-- +display_errors=0 +--ENV-- +REQUEST_METHOD=PUT +--POST_RAW-- +Content-Type: multipart/form-data; boundary=----X +------X +Content-Disposition: form-data; name="a[x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x]" + +val +------X-- +--FILE-- +getMessage(), "\n"; + } + } + return true; +}); + +[$_POST, $_FILES] = request_parse_body(); +echo "outer done\n"; +var_dump($_POST, $_FILES); +?> +--EXPECT-- +nested: RequestParseBodyException: Cannot call request_parse_body() while the request body is already being parsed +outer done +array(0) { +} +array(0) { +} diff --git a/main/SAPI.c b/main/SAPI.c index 4b6b7b0c90e6..bb6161bafc2c 100644 --- a/main/SAPI.c +++ b/main/SAPI.c @@ -460,6 +460,7 @@ SAPI_API void sapi_activate(void) } SG(rfc1867_uploaded_files) = NULL; SG(request_parse_body_context).throw_exceptions = false; + SG(request_parse_body_context).in_progress = false; memset(&SG(request_parse_body_context).options_cache, 0, sizeof(SG(request_parse_body_context).options_cache)); /* Handle request method */ diff --git a/main/SAPI.h b/main/SAPI.h index 284f4cb96f1f..3742ff350f0e 100644 --- a/main/SAPI.h +++ b/main/SAPI.h @@ -110,6 +110,7 @@ typedef struct { typedef struct { bool throw_exceptions; + bool in_progress; struct { bool set; zend_long value; diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 6b17f083c125..ed5e7fe224d9 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -2259,6 +2259,7 @@ static void php_cli_server_request_shutdown(php_cli_server *server, php_cli_serv SG(server_context) = NULL; SG(rfc1867_uploaded_files) = NULL; SG(request_parse_body_context).throw_exceptions = false; + SG(request_parse_body_context).in_progress = false; memset(&SG(request_parse_body_context).options_cache, 0, sizeof(SG(request_parse_body_context).options_cache)); } /* }}} */ @@ -2355,6 +2356,7 @@ static zend_result php_cli_server_dispatch(php_cli_server *server, php_cli_serve SG(sapi_headers).send_default_content_type = 1; SG(rfc1867_uploaded_files) = NULL; SG(request_parse_body_context).throw_exceptions = false; + SG(request_parse_body_context).in_progress = false; memset(&SG(request_parse_body_context).options_cache, 0, sizeof(SG(request_parse_body_context).options_cache)); } if (FAILURE == php_cli_server_begin_send_static(server, client)) {