1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix GH-8157: post_max_size evaluates .user.ini too late in php-fpm

This introduces new SAPI callback that runs before post read

Closes GH-19333
This commit is contained in:
Jakub Zelenka
2025-07-31 13:42:54 +02:00
parent 585e58138f
commit 1ac68e7b07
6 changed files with 80 additions and 4 deletions

4
NEWS
View File

@@ -24,6 +24,10 @@ PHP NEWS
. Fix OSS-Fuzz #442954659 (Crash in exif_scan_HEIF_header). (nielsdos)
. Various hardening fixes to HEIF parsing. (nielsdos)
- FPM:
. Fixed GH-8157 (post_max_size evaluates .user.ini too late in php-fpm).
(Jakub Zelenka)
- Opcache:
. Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex).
(Arnaud)

View File

@@ -458,6 +458,10 @@ SAPI_API void sapi_activate(void)
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));
if (sapi_module.pre_request_init) {
sapi_module.pre_request_init();
}
/* Handle request method */
if (SG(server_context)) {
if (PG(enable_post_data_reading)

View File

@@ -290,6 +290,8 @@ struct _sapi_module_struct {
const char *ini_entries;
const zend_function_entry *additional_functions;
unsigned int (*input_filter_init)(void);
int (*pre_request_init)(void); /* called before activate and before the post data read - used for .user.ini */
};
struct _sapi_post_entry {
@@ -340,6 +342,7 @@ END_EXTERN_C()
0, /* phpinfo_as_text; */ \
NULL, /* ini_entries; */ \
NULL, /* additional_functions */ \
NULL /* input_filter_init */
NULL, /* input_filter_init */ \
NULL /* pre_request_init */
#endif /* SAPI_H */

View File

@@ -702,7 +702,7 @@ static void php_cgi_ini_activate_user_config(char *path, int path_len, const cha
}
/* }}} */
static int sapi_cgi_activate(void) /* {{{ */
static int sapi_cgi_pre_request_init(void)
{
fcgi_request *request = (fcgi_request*) SG(server_context);
char *path, *doc_root, *server_name;
@@ -766,6 +766,11 @@ static int sapi_cgi_activate(void) /* {{{ */
return SUCCESS;
}
static int sapi_cgi_activate(void) /* {{{ */
{
return SUCCESS;
}
/* }}} */
static int sapi_cgi_deactivate(void) /* {{{ */
@@ -1600,6 +1605,7 @@ int main(int argc, char *argv[])
sapi_startup(&cgi_sapi_module);
cgi_sapi_module.php_ini_path_override = NULL;
cgi_sapi_module.php_ini_ignore_cwd = 1;
cgi_sapi_module.pre_request_init = sapi_cgi_pre_request_init;
#ifndef HAVE_ATTRIBUTE_WEAK
fcgi_set_logger(fpm_fcgi_log);

View File

@@ -0,0 +1,58 @@
--TEST--
FPM: gh8157 - post related INI settings not applied for .user.ini
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;
$code = <<<EOT
<?php
var_dump(\$_POST);
EOT;
$ini = <<<EOT
post_max_size=10K
html_errors=off
EOT;
$tester = new FPM\Tester($cfg, $code);
$tester->setUserIni($ini);
$tester->start();
$tester->expectLogStartNotices();
$tester
->request(
headers: [ 'CONTENT_TYPE' => 'application/x-www-form-urlencoded'],
stdin: 'foo=' . str_repeat('a', 20000),
method: 'POST',
)
->expectBody([
'Warning: PHP Request Startup: POST Content-Length of 20004 bytes exceeds the limit of 10240 bytes in Unknown on line 0',
'array(0) {',
'}',
], skipHeadersCheck: true);
$tester->terminate();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@@ -119,10 +119,11 @@ class Response extends BaseResponse
/**
* @param mixed $body
* @param string $contentType
* @param bool $skipHeadersCheck
*
* @return Response
*/
public function expectBody($body, $contentType = 'text/html')
public function expectBody($body, $contentType = 'text/html', bool $skipHeadersCheck = false)
{
if ($multiLine = is_array($body)) {
$body = implode("\n", $body);
@@ -130,7 +131,7 @@ class Response extends BaseResponse
if ( ! $this->checkIfValid()) {
$this->error('Response is invalid');
} elseif ( ! $this->checkDefaultHeaders($contentType)) {
} elseif ( ! $skipHeadersCheck && ! $this->checkDefaultHeaders($contentType)) {
$this->error('Response default headers not found');
} elseif ($body !== $this->rawBody) {
if ($multiLine) {