@@ -257,6 +257,12 @@ PHPAPI int _php_stream_free(php_stream *stream, int close_options) /* {{{ */
257257 int release_cast = 1 ;
258258 php_stream_context * context ;
259259
260+ /* Not using php_stream_check_in_use() as we want to allow releasing file descriptors during resource shutdown */
261+ if (UNEXPECTED ((stream -> flags & PHP_STREAM_FLAG_IN_USE ) && !(EG (flags ) & EG_FLAGS_IN_RESOURCE_SHUTDOWN ))) {
262+ php_stream_in_use_error ();
263+ return 0 ;
264+ }
265+
260266 /* During shutdown resources may be released before other resources still holding them.
261267 * When only resources are referenced this is not a problem, because they are refcounted
262268 * and will only be fully freed once the refcount drops to zero. However, if php_stream*
@@ -435,6 +441,10 @@ PHPAPI zend_result _php_stream_fill_read_buffer(php_stream *stream, size_t size)
435441{
436442 /* allocate/fill the buffer */
437443
444+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
445+ return FAILURE ;
446+ }
447+
438448 zend_result retval ;
439449 bool old_eof = stream -> eof ;
440450
@@ -607,6 +617,10 @@ PHPAPI ssize_t _php_stream_read(php_stream *stream, char *buf, size_t size)
607617{
608618 ssize_t toread = 0 , didread = 0 ;
609619
620+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
621+ return 0 ;
622+ }
623+
610624 while (size > 0 ) {
611625
612626 /* take from the read buffer first.
@@ -709,6 +723,10 @@ PHPAPI zend_string *php_stream_read_to_str(php_stream *stream, size_t len)
709723
710724PHPAPI bool _php_stream_eof (php_stream * stream )
711725{
726+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
727+ return 0 ;
728+ }
729+
712730 /* if there is data in the buffer, it's not EOF */
713731 if (stream -> writepos - stream -> readpos > 0 ) {
714732 return 0 ;
@@ -758,6 +776,10 @@ PHPAPI bool _php_stream_puts(php_stream *stream, const char *buf)
758776
759777PHPAPI int _php_stream_stat (php_stream * stream , php_stream_statbuf * ssb )
760778{
779+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
780+ return -1 ;
781+ }
782+
761783 memset (ssb , 0 , sizeof (* ssb ));
762784
763785 /* if the stream was wrapped, allow the wrapper to stat it */
@@ -782,6 +804,10 @@ PHPAPI const char *php_stream_locate_eol(php_stream *stream, zend_string *buf)
782804 const char * eol = NULL ;
783805 const char * readptr ;
784806
807+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
808+ return 0 ;
809+ }
810+
785811 if (!buf ) {
786812 readptr = (char * )stream -> readbuf + stream -> readpos ;
787813 avail = stream -> writepos - stream -> readpos ;
@@ -827,6 +853,10 @@ PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen,
827853 int grow_mode = 0 ;
828854 char * bufstart = buf ;
829855
856+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
857+ return NULL ;
858+ }
859+
830860 if (buf == NULL ) {
831861 grow_mode = 1 ;
832862 } else if (maxlen == 0 ) {
@@ -973,6 +1003,10 @@ PHPAPI zend_string *php_stream_get_record(php_stream *stream, size_t maxlen, con
9731003 tent_ret_len ; /* tentative returned length */
9741004 bool has_delim = delim_len > 0 ;
9751005
1006+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
1007+ return NULL ;
1008+ }
1009+
9761010 if (maxlen == 0 ) {
9771011 return NULL ;
9781012 }
@@ -1187,6 +1221,10 @@ PHPAPI int _php_stream_flush(php_stream *stream, int closing)
11871221{
11881222 int ret = 0 ;
11891223
1224+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
1225+ return -1 ;
1226+ }
1227+
11901228 if (stream -> writefilters .head && stream -> ops -> write ) {
11911229 _php_stream_write_filtered (stream , NULL , 0 , closing ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC );
11921230 }
@@ -1204,6 +1242,10 @@ PHPAPI ssize_t _php_stream_write(php_stream *stream, const char *buf, size_t cou
12041242{
12051243 ssize_t bytes ;
12061244
1245+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
1246+ return (ssize_t ) -1 ;
1247+ }
1248+
12071249 if (count == 0 ) {
12081250 return 0 ;
12091251 }
@@ -1306,6 +1348,10 @@ static zend_result php_stream_filters_seek_all(php_stream *stream, bool is_start
13061348
13071349PHPAPI int _php_stream_seek (php_stream * stream , zend_off_t offset , int whence )
13081350{
1351+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
1352+ return -1 ;
1353+ }
1354+
13091355 if (stream -> fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE ) {
13101356 /* flush can call seek internally so we need to prevent an infinite loop */
13111357 if (!stream -> fclose_stdiocast_flush_in_progress ) {
@@ -1415,6 +1461,10 @@ PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, voi
14151461{
14161462 int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL ;
14171463
1464+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
1465+ return PHP_STREAM_OPTION_RETURN_ERR ;
1466+ }
1467+
14181468 if (stream -> ops -> set_option ) {
14191469 ret = stream -> ops -> set_option (stream , option , value , ptrparam );
14201470 }
@@ -1465,6 +1515,10 @@ PHPAPI ssize_t _php_stream_passthru(php_stream * stream STREAMS_DC)
14651515 char buf [8192 ];
14661516 ssize_t b ;
14671517
1518+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
1519+ return -1 ;
1520+ }
1521+
14681522 if (php_stream_mmap_possible (stream )) {
14691523 char * p ;
14701524 size_t mapped ;
@@ -1506,6 +1560,10 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, bool
15061560 php_stream_statbuf ssbuf ;
15071561 zend_string * result ;
15081562
1563+ if (php_stream_check_in_use (src ) != SUCCESS ) {
1564+ return NULL ;
1565+ }
1566+
15091567 if (maxlen == 0 ) {
15101568 return ZSTR_EMPTY_ALLOC ();
15111569 }
@@ -1594,7 +1652,6 @@ static zend_result php_stream_copy_fallback(php_stream *src, php_stream *dest, s
15941652{
15951653 char buf [CHUNK_SIZE ];
15961654 size_t haveread = 0 ;
1597-
15981655 if (maxlen == PHP_STREAM_COPY_ALL ) {
15991656 maxlen = 0 ;
16001657 }
@@ -2240,6 +2297,10 @@ PHPAPI php_stream_context *php_stream_context_set(php_stream *stream, php_stream
22402297{
22412298 php_stream_context * oldcontext = PHP_STREAM_CONTEXT (stream );
22422299
2300+ if (php_stream_check_in_use (stream ) != SUCCESS ) {
2301+ return NULL ;
2302+ }
2303+
22432304 if (context ) {
22442305 stream -> ctx = context -> res ;
22452306 GC_ADDREF (context -> res );
@@ -2430,3 +2491,8 @@ PHPAPI int _php_stream_scandir(const char *dirname, zend_string **namelist[], in
24302491 return -1 ;
24312492}
24322493/* }}} */
2494+
2495+ PHPAPI ZEND_COLD void php_stream_in_use_error (void )
2496+ {
2497+ zend_throw_error (NULL , "Concurrent access to a stream" );
2498+ }
0 commit comments