Skip to content

Commit d229a48

Browse files
committed
Fix GH-11876: ini_parse_quantity() accepts invalid quantities
Closes GH-11910
1 parent d5f7ffb commit d229a48

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed

NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ PHP NEWS
66
. Fixed bug GH-11937 (Constant ASTs containing objects). (ilutov)
77
. Fixed bug GH-11790 (On riscv64 require libatomic if actually needed).
88
(Jeremie Courreges-Anglas)
9+
. Fixed bug GH-11876: ini_parse_quantity() accepts invalid quantities.
10+
(Girgias)
911

1012
- DOM:
1113
. Fix memory leak when setting an invalid DOMDocument encoding. (nielsdos)

Zend/tests/zend_ini/gh11876.phpt

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--TEST--
2+
Invalid INI quantities, base prefix followed by stuff eaten by strtoull()
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
8+
var_dump(zend_test_zend_ini_parse_quantity('0x0x12'));
9+
10+
var_dump(zend_test_zend_ini_parse_quantity('0b+10'));
11+
var_dump(zend_test_zend_ini_parse_quantity('0o+10'));
12+
var_dump(zend_test_zend_ini_parse_quantity('0x+10'));
13+
14+
var_dump(zend_test_zend_ini_parse_quantity('0b 10'));
15+
var_dump(zend_test_zend_ini_parse_quantity('0o 10'));
16+
var_dump(zend_test_zend_ini_parse_quantity('0x 10'));
17+
18+
var_dump(zend_test_zend_ini_parse_quantity('0g10'));
19+
var_dump(zend_test_zend_ini_parse_quantity('0m10'));
20+
var_dump(zend_test_zend_ini_parse_quantity('0k10'));
21+
22+
--EXPECTF--
23+
Warning: Invalid quantity "0x0x12": no digits after base prefix, interpreting as "0" for backwards compatibility in %s on line %d
24+
int(0)
25+
26+
Warning: Invalid quantity "0b+10": no digits after base prefix, interpreting as "0" for backwards compatibility in %s on line %d
27+
int(0)
28+
29+
Warning: Invalid quantity "0o+10": no digits after base prefix, interpreting as "0" for backwards compatibility in %s on line %d
30+
int(0)
31+
32+
Warning: Invalid quantity "0x+10": no digits after base prefix, interpreting as "0" for backwards compatibility in %s on line %d
33+
int(0)
34+
35+
Warning: Invalid quantity "0b 10": no digits after base prefix, interpreting as "0" for backwards compatibility in %s on line %d
36+
int(0)
37+
38+
Warning: Invalid quantity "0o 10": no digits after base prefix, interpreting as "0" for backwards compatibility in %s on line %d
39+
int(0)
40+
41+
Warning: Invalid quantity "0x 10": no digits after base prefix, interpreting as "0" for backwards compatibility in %s on line %d
42+
int(0)
43+
44+
Warning: Invalid quantity "0g10": unknown multiplier "0", interpreting as "0" for backwards compatibility in %s on line %d
45+
int(0)
46+
47+
Warning: Invalid quantity "0m10": unknown multiplier "0", interpreting as "0" for backwards compatibility in %s on line %d
48+
int(0)
49+
50+
Warning: Invalid quantity "0k10": unknown multiplier "0", interpreting as "0" for backwards compatibility in %s on line %d
51+
int(0)

Zend/zend_ini.c

+40
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,34 @@ typedef enum {
547547
ZEND_INI_PARSE_QUANTITY_UNSIGNED,
548548
} zend_ini_parse_quantity_signed_result_t;
549549

550+
static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end) {
551+
const char *digits_consumed = digits;
552+
/* Ignore leading whitespace. */
553+
while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
554+
if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
555+
++digits_consumed;
556+
}
557+
558+
if (digits_consumed[0] == '0' && !isdigit(digits_consumed[1])) {
559+
/* Value is just 0 */
560+
if ((digits_consumed+1) == str_end) {
561+
return digits;
562+
}
563+
564+
switch (digits_consumed[1]) {
565+
case 'x':
566+
case 'X':
567+
case 'o':
568+
case 'O':
569+
case 'b':
570+
case 'B':
571+
digits_consumed += 2;
572+
break;
573+
}
574+
}
575+
return digits_consumed;
576+
}
577+
550578
static zend_ulong zend_ini_parse_quantity_internal(zend_string *value, zend_ini_parse_quantity_signed_result_t signed_result, zend_string **errstr) /* {{{ */
551579
{
552580
char *digits_end = NULL;
@@ -634,6 +662,18 @@ static zend_ulong zend_ini_parse_quantity_internal(zend_string *value, zend_ini_
634662
smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
635663
smart_str_0(&invalid);
636664

665+
*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
666+
ZSTR_VAL(invalid.s));
667+
668+
smart_str_free(&invalid);
669+
return 0;
670+
}
671+
if (UNEXPECTED(digits != zend_ini_consume_quantity_prefix(digits, str_end))) {
672+
/* Escape the string to avoid null bytes and to make non-printable chars
673+
* visible */
674+
smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
675+
smart_str_0(&invalid);
676+
637677
*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
638678
ZSTR_VAL(invalid.s));
639679

0 commit comments

Comments
 (0)