Skip to content

Commit bac9666

Browse files
author
OracleNep
committed
Validate request_parse_body() limit option ranges
1 parent 425cd3d commit bac9666

4 files changed

Lines changed: 75 additions & 9 deletions

File tree

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ PHP NEWS
226226
contains null bytes. (Weilin Du)
227227
. parse_str() now raises a ValueError when the $string argument contains
228228
null bytes. (Weilin Du)
229+
. request_parse_body() now raises a ValueError for invalid limit option
230+
ranges. (OracleNep)
229231
. proc_open() now raises a ValueError when the $cwd argument contains
230232
null bytes. (Weilin Du)
231233
. ini_get_all() now includes the built-in default value in the details.

UPGRADING

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ PHP 8.6 UPGRADE NOTES
142142
contains null bytes.
143143
. parse_str() now raises a ValueError when the $string argument contains
144144
null bytes.
145+
. request_parse_body() now raises a ValueError when the $options argument
146+
contains negative values for max_file_uploads or max_input_vars, or
147+
non-positive values for post_max_size or upload_max_filesize.
148+
Negative values for max_multipart_body_parts continue to derive the limit
149+
from max_input_vars + max_file_uploads.
145150
. linkinfo() now raises a ValueError when the $path argument is empty.
146151
. pathinfo() now raises a ValueError when an invalid $flag
147152
argument value is passed.

ext/standard/http.c

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ PHP_FUNCTION(http_build_query)
247247
}
248248
/* }}} */
249249

250-
static zend_result cache_request_parse_body_option(HashTable *options, zval *option, int cache_offset)
250+
static zend_result cache_request_parse_body_option(zval *option, int cache_offset, const char *option_name, zend_long min_value)
251251
{
252252
if (option) {
253253
zend_long result;
@@ -265,6 +265,14 @@ static zend_result cache_request_parse_body_option(HashTable *options, zval *opt
265265
zend_value_error("Invalid %s value in $options argument", zend_zval_value_name(option));
266266
return FAILURE;
267267
}
268+
if (result < min_value) {
269+
if (min_value == 0) {
270+
zend_value_error("\"%s\" option in $options argument must be greater than or equal to 0", option_name);
271+
} else {
272+
zend_value_error("\"%s\" option in $options argument must be greater than 0", option_name);
273+
}
274+
return FAILURE;
275+
}
268276
SG(request_parse_body_context).options_cache[cache_offset].set = true;
269277
SG(request_parse_body_context).options_cache[cache_offset].value = result;
270278
} else {
@@ -288,9 +296,9 @@ static zend_result cache_request_parse_body_options(HashTable *options)
288296
return FAILURE;
289297
}
290298

291-
#define CHECK_OPTION(name) \
299+
#define CHECK_OPTION(name, min_value) \
292300
if (zend_string_equals_literal_ci(key, #name)) { \
293-
if (cache_request_parse_body_option(options, value, REQUEST_PARSE_BODY_OPTION_ ## name) == FAILURE) { \
301+
if (cache_request_parse_body_option(value, REQUEST_PARSE_BODY_OPTION_ ## name, #name, min_value) == FAILURE) { \
294302
return FAILURE; \
295303
} \
296304
continue; \
@@ -299,25 +307,25 @@ static zend_result cache_request_parse_body_options(HashTable *options)
299307
switch (ZSTR_VAL(key)[0]) {
300308
case 'm':
301309
case 'M':
302-
CHECK_OPTION(max_file_uploads);
303-
CHECK_OPTION(max_input_vars);
304-
CHECK_OPTION(max_multipart_body_parts);
310+
CHECK_OPTION(max_file_uploads, 0);
311+
CHECK_OPTION(max_input_vars, 0);
312+
CHECK_OPTION(max_multipart_body_parts, ZEND_LONG_MIN);
305313
break;
306314
case 'p':
307315
case 'P':
308-
CHECK_OPTION(post_max_size);
316+
CHECK_OPTION(post_max_size, 1);
309317
break;
310318
case 'u':
311319
case 'U':
312-
CHECK_OPTION(upload_max_filesize);
320+
CHECK_OPTION(upload_max_filesize, 1);
313321
break;
314322
}
315323

316324
zend_value_error("Invalid key \"%s\" in $options argument", ZSTR_VAL(key));
317325
return FAILURE;
318326
} ZEND_HASH_FOREACH_END();
319327

320-
#undef CACHE_OPTION
328+
#undef CHECK_OPTION
321329

322330
return SUCCESS;
323331
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--TEST--
2+
request_parse_body() invalid limit option ranges
3+
--FILE--
4+
<?php
5+
6+
$invalidOptions = [
7+
['max_file_uploads', -1],
8+
['max_input_vars', '-1M'],
9+
['max_input_vars', '-1.0'],
10+
['post_max_size', -1],
11+
['post_max_size', 0],
12+
['upload_max_filesize', '-1M'],
13+
['upload_max_filesize', 0],
14+
];
15+
16+
foreach ($invalidOptions as [$name, $value]) {
17+
try {
18+
request_parse_body([$name => $value]);
19+
} catch (Throwable $e) {
20+
echo $name, ': ', get_class($e), ': ', $e->getMessage(), "\n";
21+
}
22+
}
23+
24+
$validOptions = [
25+
['max_file_uploads', 0],
26+
['max_input_vars', 0],
27+
['max_multipart_body_parts', -1],
28+
];
29+
30+
foreach ($validOptions as [$name, $value]) {
31+
try {
32+
request_parse_body([$name => $value]);
33+
} catch (Throwable $e) {
34+
echo $name, ': ', get_class($e), ': ', $e->getMessage(), "\n";
35+
}
36+
}
37+
38+
?>
39+
--EXPECTF--
40+
max_file_uploads: ValueError: "max_file_uploads" option in $options argument must be greater than or equal to 0
41+
max_input_vars: ValueError: "max_input_vars" option in $options argument must be greater than or equal to 0
42+
43+
Warning: Invalid quantity "-1.0": unknown multiplier "0", interpreting as "-1" for backwards compatibility in %s on line %d
44+
max_input_vars: ValueError: "max_input_vars" option in $options argument must be greater than or equal to 0
45+
post_max_size: ValueError: "post_max_size" option in $options argument must be greater than 0
46+
post_max_size: ValueError: "post_max_size" option in $options argument must be greater than 0
47+
upload_max_filesize: ValueError: "upload_max_filesize" option in $options argument must be greater than 0
48+
upload_max_filesize: ValueError: "upload_max_filesize" option in $options argument must be greater than 0
49+
max_file_uploads: RequestParseBodyException: Request does not provide a content type
50+
max_input_vars: RequestParseBodyException: Request does not provide a content type
51+
max_multipart_body_parts: RequestParseBodyException: Request does not provide a content type

0 commit comments

Comments
 (0)