diff --git a/UPGRADING b/UPGRADING index 69b58bb7097..df170bb622b 100755 --- a/UPGRADING +++ b/UPGRADING @@ -167,6 +167,8 @@ UPGRADE NOTES - PHP X.Y - stream_set_write_buffer() no longer disables the read buffer of a plain stream when 0 is given as the second argument. - stream_set_write_buffer() no longer changes the chunk size in socket streams. +- fclose() closes streams with resource refcount > 1; it doesn't merely + decrement the resource refcount. =================================== 5. Changes made to existing methods diff --git a/ext/standard/file.c b/ext/standard/file.c index f15a0f09430..c6b585c2c75 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -920,7 +920,7 @@ PHPAPI PHP_FUNCTION(fclose) } if (!stream->is_persistent) { - zend_list_delete(stream->rsrc_id); + php_stream_close(stream); } else { php_stream_pclose(stream); } diff --git a/ext/standard/tests/file/fclose_variation1.phpt b/ext/standard/tests/file/fclose_variation1.phpt new file mode 100644 index 00000000000..43a6c343d01 --- /dev/null +++ b/ext/standard/tests/file/fclose_variation1.phpt @@ -0,0 +1,15 @@ +--TEST-- +fclose() actually closes streams with refcount > 1 +--FILE-- +context; @@ -395,15 +394,21 @@ PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC) /* #if STREAM_DEBUG fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remove_rsrc=%d\n", - stream->ops->label, stream, stream->orig_path, preserve_handle, release_cast, remove_rsrc); + stream->ops->label, stream, stream->orig_path, preserve_handle, release_cast, + (close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0); #endif /* make sure everything is saved */ _php_stream_flush(stream, 1 TSRMLS_CC); /* If not called from the resource dtor, remove the stream from the resource list. */ - if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0 && remove_rsrc) { - zend_list_delete(stream->rsrc_id); + if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0) { + /* zend_list_delete actually only decreases the refcount; if we're + * releasing the stream, we want to actually delete the resource from + * the resource list, otherwise the resource will point to invalid memory. + * In any case, let's always completely delete it from the resource list, + * not only when PHP_STREAM_FREE_RELEASE_STREAM is set */ + while (zend_list_delete(stream->rsrc_id) == SUCCESS) {} } /* Remove stream from any context link list */