Skip to content

Commit 3d5358f

Browse files
Avoid using OBJPROP
1 parent fe4961a commit 3d5358f

File tree

2 files changed

+44
-11
lines changed

2 files changed

+44
-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 $e::class, ": ", $e->getMessage(), "\n";
3030
}
3131

32+
try {
33+
fclose($fp);
34+
} catch (TypeError $e) {
35+
echo $e::class, ": ", $e->getMessage(), "\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+
TypeError: Cannot assign resource to property pass_filter::$stream of type int
44+
TypeError: Cannot assign resource to property pass_filter::$stream of type int

ext/standard/user_filters.c

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "ext/standard/basic_functions.h"
2222
#include "ext/standard/file.h"
2323
#include "ext/standard/user_filters_arginfo.h"
24+
#include "zend_exceptions.h"
2425

2526
#define PHP_STREAM_BRIGADE_RES_NAME "userfilter.bucket brigade"
2627
#define PHP_STREAM_BUCKET_RES_NAME "userfilter.bucket"
@@ -148,12 +149,29 @@ php_stream_filter_status_t userfilter_filter(
148149
stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
149150

150151
/* 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) {
152+
zend_class_entry *old_scope = EG(fake_scope);
153+
EG(fake_scope) = Z_OBJCE_P(obj);
154+
155+
zend_string *stream_name = ZSTR_INIT_LITERAL("stream", 0);
156+
if (Z_OBJ_HT_P(obj)->has_property(Z_OBJ_P(obj), stream_name, ZEND_PROPERTY_EXISTS, NULL)) {
153157
zval stream_zval;
154158
php_stream_to_zval(stream, &stream_zval);
155-
zend_update_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), "stream", sizeof("stream")-1, &stream_zval);
159+
zend_update_property_ex(Z_OBJCE_P(obj), Z_OBJ_P(obj), stream_name, &stream_zval);
160+
/* If property update threw an exception, skip filter execution */
161+
if (EG(exception)) {
162+
if (buckets_in->head) {
163+
php_error_docref(NULL, E_WARNING, "Unprocessed filter buckets remaining on input brigade");
164+
}
165+
zend_string_release(stream_name);
166+
EG(fake_scope) = old_scope;
167+
stream->flags &= ~PHP_STREAM_FLAG_NO_FCLOSE;
168+
stream->flags |= orig_no_fclose;
169+
return PSFS_ERR_FATAL;
170+
}
156171
}
172+
zend_string_release(stream_name);
173+
174+
EG(fake_scope) = old_scope;
157175

158176
ZVAL_STRINGL(&func_name, "filter", sizeof("filter")-1);
159177

@@ -196,8 +214,19 @@ php_stream_filter_status_t userfilter_filter(
196214
/* filter resources are cleaned up by the stream destructor,
197215
* keeping a reference to the stream resource here would prevent it
198216
* from being destroyed properly */
217+
zend_property_info *prop_info = zend_hash_str_find_ptr(&Z_OBJCE_P(obj)->properties_info, "stream", sizeof("stream")-1);
218+
zval *stream_prop = zend_hash_str_find(Z_OBJPROP_P(obj), "stream", sizeof("stream")-1);
219+
199220
if (stream_prop) {
200-
zend_update_property_null(Z_OBJCE_P(obj), Z_OBJ_P(obj), "stream", sizeof("stream")-1);
221+
if (prop_info) {
222+
/* Declared property: set to UNDEF to make it uninitialized */
223+
zval_ptr_dtor(stream_prop);
224+
ZVAL_UNDEF(stream_prop);
225+
} else {
226+
/* Dynamic property: set to null */
227+
zval_ptr_dtor(stream_prop);
228+
ZVAL_NULL(stream_prop);
229+
}
201230
}
202231

203232
zval_ptr_dtor(&args[3]);

0 commit comments

Comments
 (0)