Skip to content

Commit 27cbdd8

Browse files
committed
[standard] Fix crash when filter callback unsets StreamBucket::$data
stream_bucket_prepend()/append() assumed a successful zend_read_property() of StreamBucket::$data always yields a string, but for an unset typed property it throws while returning &EG(uninitialized_zval), so Z_STRLEN_P() dereferenced a NULL string pointer and crashed when the brigade was consumed. Reject non-string reads up front (rethrowing any pending exception) so the bucket is never re-attached with undefined data; the sibling $bucket property path is already safe because zend_fetch_resource_ex() rejects non-resources.
1 parent b2956e0 commit 27cbdd8

3 files changed

Lines changed: 35 additions & 0 deletions

File tree

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ PHP NEWS
4848
with no other live PDO handle. (iliaal)
4949

5050
- Standard:
51+
. Fixed a segfault when a stream filter callback unsets StreamBucket::$data
52+
before re-attaching the bucket. (iliaal)
5153
. Fixed a memory leak in array_merge_recursive() when the recursive merge of
5254
an object converted to an array fails. (David Carlier)
5355

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
unset(StreamBucket::$data) in filter callback must not crash when bucket is re-attached
3+
--FILE--
4+
<?php
5+
class MyFilter extends php_user_filter {
6+
public function filter($in, $out, &$consumed, bool $closing): int {
7+
while ($bucket = stream_bucket_make_writeable($in)) {
8+
unset($bucket->data);
9+
stream_bucket_prepend($out, $bucket);
10+
}
11+
return PSFS_PASS_ON;
12+
}
13+
}
14+
stream_filter_register("myfilter", "MyFilter");
15+
$fp = fopen("php://temp", "w+");
16+
fwrite($fp, str_repeat("A", 100));
17+
rewind($fp);
18+
stream_filter_append($fp, "myfilter");
19+
try {
20+
var_dump(stream_get_contents($fp));
21+
} catch (Error $e) {
22+
printf("%s: %s\n", get_class($e), $e->getMessage());
23+
}
24+
echo "DONE\n";
25+
--EXPECT--
26+
Error: Typed property StreamBucket::$data must not be accessed before initialization
27+
DONE

ext/standard/user_filters.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,12 @@ static void php_stream_bucket_attach(int append, INTERNAL_FUNCTION_PARAMETERS)
424424

425425
if (NULL != (pzdata = zend_read_property(NULL, Z_OBJ_P(zobject), "data", sizeof("data")-1, false, &rv))) {
426426
ZVAL_DEREF(pzdata);
427+
if (Z_TYPE_P(pzdata) != IS_STRING) {
428+
if (!EG(exception)) {
429+
zend_argument_value_error(2, "must have a string \"data\" property");
430+
}
431+
RETURN_THROWS();
432+
}
427433
if (!bucket->own_buf) {
428434
bucket = php_stream_bucket_make_writeable(bucket);
429435
}

0 commit comments

Comments
 (0)