Skip to content

Commit 22d1292

Browse files
Avoid using OBJPROP
1 parent fe4961a commit 22d1292

File tree

2 files changed

+37
-11
lines changed

2 files changed

+37
-11
lines changed

ext/standard/tests/filters/gh20370.phpt

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,19 @@ stream_filter_append($fp, "pass");
2626
try {
2727
fwrite($fp, "data");
2828
} catch (TypeError $e) {
29-
echo "TypeError: Cannot assign resource to typed property\n";
29+
echo "Threw TypeError on fwrite\n";
3030
}
3131

32+
try {
33+
fclose($fp);
34+
} catch (TypeError $e) {
35+
echo "Threw TypeError on fclose\n";
36+
}
37+
38+
unset($fp); // prevent cleanup at shutdown
39+
3240
?>
3341
--EXPECTF--
3442
Warning: fwrite(): Unprocessed filter buckets remaining on input brigade in %s on line %d
35-
TypeError: Cannot assign resource to typed property
36-
37-
Fatal error: Uncaught TypeError: Cannot assign resource to property pass_filter::$stream of type int in %s
38-
Stack trace:
39-
#0 {main}
40-
thrown in %s
43+
Threw TypeError on fwrite
44+
Threw TypeError on fclose

ext/standard/user_filters.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,21 @@ php_stream_filter_status_t userfilter_filter(
148148
stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
149149

150150
/* Give the userfilter class a hook back to the stream */
151-
zval *stream_prop = zend_hash_str_find(Z_OBJPROP_P(obj), "stream", sizeof("stream")-1);
152-
if (stream_prop) {
151+
zend_property_info *prop_info = zend_hash_str_find_ptr(&Z_OBJCE_P(obj)->properties_info, "stream", sizeof("stream")-1);
152+
153+
if (prop_info) {
154+
/* Declared property (may be uninitialized typed property) - always update */
153155
zval stream_zval;
154156
php_stream_to_zval(stream, &stream_zval);
155157
zend_update_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), "stream", sizeof("stream")-1, &stream_zval);
158+
} else {
159+
/* Check if it's a dynamic property - only access OBJPROP as fallback */
160+
zval *stream_prop = zend_hash_str_find(Z_OBJPROP_P(obj), "stream", sizeof("stream")-1);
161+
if (stream_prop) {
162+
zval stream_zval;
163+
php_stream_to_zval(stream, &stream_zval);
164+
zend_update_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), "stream", sizeof("stream")-1, &stream_zval);
165+
}
156166
}
157167

158168
ZVAL_STRINGL(&func_name, "filter", sizeof("filter")-1);
@@ -196,8 +206,20 @@ php_stream_filter_status_t userfilter_filter(
196206
/* filter resources are cleaned up by the stream destructor,
197207
* keeping a reference to the stream resource here would prevent it
198208
* from being destroyed properly */
199-
if (stream_prop) {
200-
zend_update_property_null(Z_OBJCE_P(obj), Z_OBJ_P(obj), "stream", sizeof("stream")-1);
209+
if (prop_info) {
210+
/* Declared property - find the actual value to clean it up */
211+
zval *stream_prop = zend_hash_str_find(Z_OBJPROP_P(obj), "stream", sizeof("stream")-1);
212+
if (stream_prop) {
213+
/* Use direct manipulation to avoid type checking during cleanup.
214+
* We need to break the resource reference regardless of property type constraints. */
215+
convert_to_null(stream_prop);
216+
}
217+
} else {
218+
/* Dynamic property - check if it exists */
219+
zval *stream_prop = zend_hash_str_find(Z_OBJPROP_P(obj), "stream", sizeof("stream")-1);
220+
if (stream_prop) {
221+
convert_to_null(stream_prop);
222+
}
201223
}
202224

203225
zval_ptr_dtor(&args[3]);

0 commit comments

Comments
 (0)