Skip to content

Commit 488d7bf

Browse files
committed
Use fallible in sprintf and bin2hex
1 parent b4ca62e commit 488d7bf

14 files changed

Lines changed: 304 additions & 5 deletions

‎ext/standard/formatted_print.c‎

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ php_sprintf_appendchar(zend_string **buffer, size_t *pos, char add)
5050
{
5151
if ((*pos + 1) >= ZSTR_LEN(*buffer)) {
5252
PRINTF_DEBUG(("%s(): ereallocing buffer to %zu bytes\n", get_active_function_name(), ZSTR_LEN(*buffer)));
53-
*buffer = zend_string_extend(*buffer, ZSTR_LEN(*buffer) << 1, 0);
53+
zend_string *extended_buffer = zend_string_fextend(*buffer, ZSTR_LEN(*buffer) << 1, 0, /* fallible */ true);
54+
if (UNEXPECTED(!extended_buffer)) {
55+
return;
56+
}
57+
*buffer = extended_buffer;
5458
}
5559
PRINTF_DEBUG(("sprintf: appending '%c', pos=%zu\n", add, *pos));
5660
ZSTR_VAL(*buffer)[(*pos)++] = add;
@@ -68,7 +72,11 @@ php_sprintf_appendchars(zend_string **buffer, size_t *pos, char *add, size_t len
6872
do {
6973
nlen = nlen << 1;
7074
} while ((*pos + len) >= nlen);
71-
*buffer = zend_string_extend(*buffer, nlen, 0);
75+
zend_string *extended_buffer = zend_string_fextend(*buffer, nlen, 0, /* fallible */ true);
76+
if (UNEXPECTED(!extended_buffer)) {
77+
return;
78+
}
79+
*buffer = extended_buffer;
7280
}
7381
PRINTF_DEBUG(("sprintf: appending \"%s\", pos=%zu\n", add, *pos));
7482
memcpy(ZSTR_VAL(*buffer) + (*pos), add, len);
@@ -109,7 +117,11 @@ php_sprintf_appendstring(zend_string **buffer, size_t *pos, char *add,
109117
size <<= 1;
110118
}
111119
PRINTF_DEBUG(("sprintf ereallocing buffer to %zu bytes\n", size));
112-
*buffer = zend_string_extend(*buffer, size, 0);
120+
zend_string *extended_buffer = zend_string_fextend(*buffer, size, 0, /* fallible */ true);
121+
if (UNEXPECTED(!extended_buffer)) {
122+
return;
123+
}
124+
*buffer = extended_buffer;
113125
}
114126
if (alignment == ALIGN_RIGHT) {
115127
if ((neg || always_sign) && padding=='0') {
@@ -246,7 +258,7 @@ php_sprintf_appenddouble(zend_string **buffer, size_t *pos,
246258
}
247259

248260
if (zend_isinf(number)) {
249-
is_negative = (number<0);
261+
is_negative = (number<0);
250262
char *str = is_negative ? "-INF" : "INF";
251263
php_sprintf_appendstring(buffer, pos, str, strlen(str), 0, padding,
252264
alignment, strlen(str), is_negative, 0, always_sign);
@@ -442,6 +454,10 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n
442454
int expprec;
443455
zval *tmp;
444456

457+
if (UNEXPECTED(ZEND_HAS_OOM())) {
458+
goto fail;
459+
}
460+
445461
temppos = memchr(format, '%', format_len);
446462
if (!temppos) {
447463
php_sprintf_appendchars(&result, &outpos, format, format_len);
@@ -717,6 +733,10 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n
717733
}
718734
}
719735

736+
if (UNEXPECTED(ZEND_HAS_OOM())) {
737+
goto fail;
738+
}
739+
720740
if (max_missing_argnum >= 0) {
721741
if (nb_additional_parameters == -1) {
722742
zend_value_error("The arguments array must contain %d items, %d given", max_missing_argnum + 1, argc);
@@ -772,6 +792,9 @@ PHP_FUNCTION(sprintf)
772792

773793
result = php_formatted_print(format, format_len, args, argc, 1);
774794
if (result == NULL) {
795+
if (ZEND_HAS_OOM()) {
796+
RETURN_OOM();
797+
}
775798
RETURN_THROWS();
776799
}
777800
RETVAL_STR(result);
@@ -798,6 +821,9 @@ PHP_FUNCTION(vsprintf)
798821
result = php_formatted_print(format, format_len, args, argc, -1);
799822
efree(args);
800823
if (result == NULL) {
824+
if (ZEND_HAS_OOM()) {
825+
RETURN_OOM();
826+
}
801827
RETURN_THROWS();
802828
}
803829
RETVAL_STR(result);
@@ -821,6 +847,9 @@ PHP_FUNCTION(printf)
821847

822848
result = php_formatted_print(format, format_len, args, argc, 1);
823849
if (result == NULL) {
850+
if (ZEND_HAS_OOM()) {
851+
RETURN_OOM();
852+
}
824853
RETURN_THROWS();
825854
}
826855
rlen = PHPWRITE(ZSTR_VAL(result), ZSTR_LEN(result));
@@ -850,6 +879,9 @@ PHP_FUNCTION(vprintf)
850879
result = php_formatted_print(format, format_len, args, argc, -1);
851880
efree(args);
852881
if (result == NULL) {
882+
if (ZEND_HAS_OOM()) {
883+
RETURN_OOM();
884+
}
853885
RETURN_THROWS();
854886
}
855887
rlen = PHPWRITE(ZSTR_VAL(result), ZSTR_LEN(result));
@@ -878,6 +910,9 @@ PHP_FUNCTION(fprintf)
878910

879911
result = php_formatted_print(format, format_len, args, argc, 2);
880912
if (result == NULL) {
913+
if (ZEND_HAS_OOM()) {
914+
RETURN_OOM();
915+
}
881916
RETURN_THROWS();
882917
}
883918

@@ -912,6 +947,9 @@ PHP_FUNCTION(vfprintf)
912947
result = php_formatted_print(format, format_len, args, argc, -1);
913948
efree(args);
914949
if (result == NULL) {
950+
if (ZEND_HAS_OOM()) {
951+
RETURN_OOM();
952+
}
915953
RETURN_THROWS();
916954
}
917955

‎ext/standard/string.c‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ static zend_string *php_bin2hex(const unsigned char *old, const size_t oldlen)
6464
zend_string *result;
6565
size_t i, j;
6666

67-
result = zend_string_safe_alloc(oldlen, 2 * sizeof(char), 0, 0);
67+
result = zend_string_safe_falloc(oldlen, 2 * sizeof(char), 0, 0, /* fallible */ true);
68+
if (UNEXPECTED(!result)) {
69+
return NULL;
70+
}
6871

6972
for (i = j = 0; i < oldlen; i++) {
7073
ZSTR_VAL(result)[j++] = hexconvtab[old[i] >> 4];
@@ -177,6 +180,9 @@ PHP_FUNCTION(bin2hex)
177180
ZEND_PARSE_PARAMETERS_END();
178181

179182
result = php_bin2hex((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
183+
if (UNEXPECTED(!result)) {
184+
RETURN_OOM();
185+
}
180186

181187
RETURN_STR(result);
182188
}

‎ext/standard/tests/file/bug81145_src.bin‎

Whitespace-only changes.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
OOM bin2hex()
3+
--INI--
4+
memory_limit=20M
5+
--SKIPIF--
6+
<?php
7+
if (getenv("USE_ZEND_ALLOC") === "0") die("skip Zend MM disabled");
8+
?>
9+
--FILE--
10+
<?php
11+
12+
// The string fits into the limit. The hex result is twice as long, and
13+
// together they pass the hard limit.
14+
$s = str_repeat("a", 15 * 1024 * 1024);
15+
16+
echo "Setup done", PHP_EOL;
17+
18+
$result = bin2hex($s);
19+
20+
echo "Not reached", PHP_EOL;
21+
?>
22+
--EXPECTF--
23+
Setup done
24+
25+
Fatal error: Allowed memory size of %d bytes exhausted %S(tried to allocate %d bytes) in %s on line 9
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
OOM fprintf() with a huge field width
3+
--INI--
4+
memory_limit=64M
5+
--SKIPIF--
6+
<?php
7+
if (getenv("USE_ZEND_ALLOC") === "0") die("skip Zend MM disabled");
8+
?>
9+
--FILE--
10+
<?php
11+
12+
$stream = fopen("php://memory", "w+");
13+
14+
echo "Setup done", PHP_EOL;
15+
16+
$result = fprintf($stream, "%1000000000d", 1);
17+
18+
echo "Not reached", PHP_EOL;
19+
?>
20+
--EXPECTF--
21+
Setup done
22+
23+
Fatal error: Allowed memory size of %d bytes exhausted %S(tried to allocate %d bytes) in %s on line 7
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
OOM printf() with a huge field width
3+
--INI--
4+
memory_limit=64M
5+
--SKIPIF--
6+
<?php
7+
if (getenv("USE_ZEND_ALLOC") === "0") die("skip Zend MM disabled");
8+
?>
9+
--FILE--
10+
<?php
11+
12+
echo "Setup done", PHP_EOL;
13+
14+
$result = printf("%1000000000d", 1);
15+
16+
echo "Not reached", PHP_EOL;
17+
?>
18+
--EXPECTF--
19+
Setup done
20+
21+
Fatal error: Allowed memory size of %d bytes exhausted %S(tried to allocate %d bytes) in %s on line 5
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
OOM printf() does not print the partial result
3+
--INI--
4+
memory_limit=64M
5+
--SKIPIF--
6+
<?php
7+
if (getenv("USE_ZEND_ALLOC") === "0") die("skip Zend MM disabled");
8+
?>
9+
--FILE--
10+
<?php
11+
12+
// The text before the specifier is already in the buffer when the growth for
13+
// the field width fails. It must not be printed.
14+
$format = "abc%1000000000d";
15+
16+
echo "Setup done", PHP_EOL;
17+
18+
$result = printf($format, 1);
19+
20+
echo "Not reached", PHP_EOL;
21+
?>
22+
--EXPECTF--
23+
Setup done
24+
25+
Fatal error: Allowed memory size of %d bytes exhausted %S(tried to allocate %d bytes) in %s on line 9
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
OOM sprintf() with a huge field width
3+
--INI--
4+
memory_limit=64M
5+
--SKIPIF--
6+
<?php
7+
if (getenv("USE_ZEND_ALLOC") === "0") die("skip Zend MM disabled");
8+
?>
9+
--FILE--
10+
<?php
11+
12+
echo "Setup done", PHP_EOL;
13+
14+
$result = sprintf("%1000000000d", 1);
15+
16+
echo "Not reached", PHP_EOL;
17+
?>
18+
--EXPECTF--
19+
Setup done
20+
21+
Fatal error: Allowed memory size of %d bytes exhausted %S(tried to allocate %d bytes) in %s on line 5
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
OOM sprintf() growing the buffer one character at a time
3+
--INI--
4+
memory_limit=72M
5+
--SKIPIF--
6+
<?php
7+
if (getenv("USE_ZEND_ALLOC") === "0") die("skip Zend MM disabled");
8+
?>
9+
--FILE--
10+
<?php
11+
12+
// Each %% adds one character. The output reaches the length of the buffer
13+
// (240 bytes times 2^17), and the buffer doubles to about 60 MB.
14+
$format = str_repeat("%%", 31457280);
15+
16+
echo "Setup done", PHP_EOL;
17+
18+
$result = sprintf($format);
19+
20+
echo "Not reached", PHP_EOL;
21+
?>
22+
--EXPECTF--
23+
Setup done
24+
25+
Fatal error: Allowed memory size of %d bytes exhausted %S(tried to allocate %d bytes) in %s on line 9
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
OOM sprintf() growing the buffer for literal text
3+
--INI--
4+
memory_limit=40M
5+
--SKIPIF--
6+
<?php
7+
if (getenv("USE_ZEND_ALLOC") === "0") die("skip Zend MM disabled");
8+
?>
9+
--FILE--
10+
<?php
11+
12+
// The format is longer than the 240 byte start buffer times 2^17, so the buffer
13+
// grows to about 60 MB in one step.
14+
$format = str_repeat("a", 31457281);
15+
16+
echo "Setup done", PHP_EOL;
17+
18+
$result = sprintf($format);
19+
20+
echo "Not reached", PHP_EOL;
21+
?>
22+
--EXPECTF--
23+
Setup done
24+
25+
Fatal error: Allowed memory size of %d bytes exhausted %S(tried to allocate %d bytes) in %s on line 9

0 commit comments

Comments
 (0)