Skip to content

Commit fd5d6ad

Browse files
committed
Fix GH-17650: realloc with size 0 in user_filters.c
If the returned buffer string is of length 0, then a realloc can happen with length 0. However, the behaviour is implementation-defined. From 7.20.3.1 of C11 spec: > If the size of the space requested is zero, the behavior is > implementation-defined: either a null pointer is returned, > or the behavior is as if the size were some nonzero value, > except that the returned pointer shall not be used to access an object This is problematic for the test case on my system as it returns NULL, causing a memleak and later using it in memcpy causing UB. The bucket code is not prepared to handle a NULL pointer. To solve this, we use MAX to clamp the size to 1 at the least. Closes GH-17656.
1 parent 00d4390 commit fd5d6ad

File tree

3 files changed

+39
-1
lines changed

3 files changed

+39
-1
lines changed

NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ PHP NEWS
3131
. Partially fixed bug GH-17387 (Trivial crash in phpdbg lexer). (nielsdos)
3232
. Fix memory leak in phpdbg calling registered function. (nielsdos)
3333

34+
- Streams:
35+
. Fixed bug GH-17650 (realloc with size 0 in user_filters.c). (nielsdos)
36+
3437
13 Feb 2025, PHP 8.3.17
3538

3639
- Core:
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
GH-17650 (realloc with size 0 in user_filters.c)
3+
--FILE--
4+
<?php
5+
class testfilter extends php_user_filter {
6+
function filter($in, $out, &$consumed, $closing): int {
7+
while ($bucket = stream_bucket_make_writeable($in)) {
8+
$bucket->data = '';
9+
$consumed += strlen($bucket->data);
10+
stream_bucket_append($out, $bucket);
11+
}
12+
return PSFS_PASS_ON;
13+
}
14+
}
15+
16+
stream_filter_register('testfilter','testfilter');
17+
18+
$text = "Hello There!";
19+
20+
$fp = fopen('php://memory', 'w+');
21+
fwrite($fp, $text);
22+
23+
rewind($fp);
24+
stream_filter_append($fp, 'testfilter', STREAM_FILTER_READ, 'testuserfilter');
25+
26+
while ($x = fgets($fp)) {
27+
var_dump($x);
28+
}
29+
30+
fclose($fp);
31+
32+
echo "Done\n";
33+
?>
34+
--EXPECT--
35+
Done

ext/standard/user_filters.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ static void php_stream_bucket_attach(int append, INTERNAL_FUNCTION_PARAMETERS)
398398
bucket = php_stream_bucket_make_writeable(bucket);
399399
}
400400
if (bucket->buflen != Z_STRLEN_P(pzdata)) {
401-
bucket->buf = perealloc(bucket->buf, Z_STRLEN_P(pzdata), bucket->is_persistent);
401+
bucket->buf = perealloc(bucket->buf, MAX(Z_STRLEN_P(pzdata), 1), bucket->is_persistent);
402402
bucket->buflen = Z_STRLEN_P(pzdata);
403403
}
404404
memcpy(bucket->buf, Z_STRVAL_P(pzdata), bucket->buflen);

0 commit comments

Comments
 (0)