Skip to content

Commit cfcc2a3

Browse files
committed
Fix GH-15034: Integer overflow on stream_notification_callback byte_max parameter with files bigger than 2GB
We were using atoi, which is only for integers. When the size does not fit in an integer this breaks. Use ZEND_STRTOUL instead. Also make sure invalid data isn't accidentally parsed into a file size. Closes GH-15035.
1 parent 8de7ccb commit cfcc2a3

File tree

3 files changed

+59
-2
lines changed

3 files changed

+59
-2
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ PHP NEWS
1616

1717
- Streams:
1818
. Fixed bug GH-15028 (Memory leak in ext/phar/stream.c). (nielsdos)
19+
. Fixed bug GH-15034 (Integer overflow on stream_notification_callback
20+
byte_max parameter with files bigger than 2GB). (nielsdos)
1921

2022
- Tidy:
2123
. Fix memory leaks in ext/tidy basedir restriction code. (nielsdos)

ext/standard/http_fopen_wrapper.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -791,8 +791,19 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
791791
} else if (!strncasecmp(http_header_line, "Content-Type:", sizeof("Content-Type:")-1)) {
792792
php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, http_header_value, 0);
793793
} else if (!strncasecmp(http_header_line, "Content-Length:", sizeof("Content-Length:")-1)) {
794-
file_size = atoi(http_header_value);
795-
php_stream_notify_file_size(context, file_size, http_header_line, 0);
794+
/* https://www.rfc-editor.org/rfc/rfc9110.html#name-content-length */
795+
const char *ptr = http_header_value;
796+
/* must contain only digits, no + or - symbols */
797+
if (*ptr >= '0' && *ptr <= '9') {
798+
char *endptr = NULL;
799+
size_t parsed = ZEND_STRTOUL(ptr, &endptr, 10);
800+
/* check whether there was no garbage in the header value and the conversion was successful */
801+
if (endptr && !*endptr) {
802+
/* truncate for 32-bit such that no negative file sizes occur */
803+
file_size = MIN(parsed, ZEND_LONG_MAX);
804+
php_stream_notify_file_size(context, file_size, http_header_line, 0);
805+
}
806+
}
796807
} else if (
797808
!strncasecmp(http_header_line, "Transfer-Encoding:", sizeof("Transfer-Encoding:")-1)
798809
&& !strncasecmp(http_header_value, "Chunked", sizeof("Chunked")-1)

ext/standard/tests/http/gh15034.phpt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
GH-15034 (Integer overflow on stream_notification_callback byte_max parameter with files bigger than 2GB)
3+
--SKIPIF--
4+
<?php
5+
require 'server.inc';
6+
http_server_skipif();
7+
if (PHP_INT_SIZE != 8) die("skip 64-bit only");
8+
?>
9+
--INI--
10+
allow_url_fopen=1
11+
--FILE--
12+
<?php
13+
require 'server.inc';
14+
15+
$responses = [
16+
"data://text/plain,HTTP/1.1 200 OK\r\n"
17+
. "Content-Type: text/plain\r\n"
18+
. "Content-Length: 3000000000\r\n\r\n"
19+
. "foo\n",
20+
];
21+
['pid' => $pid, 'uri' => $uri] = http_server($responses);
22+
23+
$params = ['notification' => function(
24+
int $notification_code,
25+
int $severity,
26+
?string $message,
27+
int $message_code,
28+
int $bytes_transferred,
29+
int $bytes_max
30+
) {
31+
global $max;
32+
$max = $bytes_max;
33+
}];
34+
$contextResource = stream_context_create([], $params);
35+
36+
$resource = fopen($uri, 'r', false, $contextResource);
37+
fclose($resource);
38+
39+
http_server_kill($pid);
40+
41+
var_dump($max);
42+
?>
43+
--EXPECT--
44+
int(3000000000)

0 commit comments

Comments
 (0)