diff --git a/Zend/zend_list.c b/Zend/zend_list.c index 10aa9174cfcc..3a60eb0fa843 100644 --- a/Zend/zend_list.c +++ b/Zend/zend_list.c @@ -55,7 +55,9 @@ ZEND_API zend_result ZEND_FASTCALL zend_list_delete(zend_resource *res) ZEND_API void ZEND_FASTCALL zend_list_free(zend_resource *res) { ZEND_ASSERT(GC_REFCOUNT(res) == 0); - zend_hash_index_del(&EG(regular_list), res->handle); + if (zend_hash_index_del(&EG(regular_list), res->handle) == FAILURE) { + efree_size(res, sizeof(zend_resource)); + } } static void zend_resource_dtor(zend_resource *res) @@ -177,7 +179,11 @@ void list_entry_destructor(zval *zv) ZVAL_UNDEF(zv); if (res->type >= 0) { + GC_ADDREF(res); zend_resource_dtor(res); + if (GC_DELREF(res) != 0) { + return; + } } efree_size(res, sizeof(zend_resource)); } diff --git a/ext/standard/tests/filters/gh16321.phpt b/ext/standard/tests/filters/gh16321.phpt new file mode 100644 index 000000000000..59e8e3ef3529 --- /dev/null +++ b/ext/standard/tests/filters/gh16321.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-16321 (UAF when stream filter throws during stream close) +--FILE-- +stream, "42"); + return PSFS_ERR_FATAL; + } +} + +stream_filter_register("test_filter", "TestFilter"); + +function test() { + $stream = fopen('php://memory', 'wb+'); + fwrite($stream, "data"); + fseek($stream, 0, SEEK_SET); + stream_filter_append($stream, "test_filter"); + stream_get_contents($stream); +} + +test(); +?> +--EXPECTF-- +Warning: stream_get_contents(): Unprocessed filter buckets remaining on input brigade in %s on line %d + +Fatal error: Uncaught TypeError: stream_bucket_new(): Argument #1 ($stream) must be an open stream resource in %s:%d +Stack trace: +#0 %s(%d): stream_bucket_new(Resource id #%d, '42') +#1 %s(%d): TestFilter->filter(Resource id #%d, Resource id #%d, 0, true) +#2 {main} + thrown in %s on line %d