From bed04279c3f8ef7dbb3aa0c0543d50f72248cb2c Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 16 Mar 2002 14:39:51 +0000 Subject: [PATCH] Hopefully fix resource usage so that we have no leaks and don't segfault. --- ext/zlib/zlib_fopen_wrapper.c | 13 +++++---- main/main.c | 9 +++--- main/network.c | 22 ++++++++------- main/php_streams.h | 5 ++-- main/streams.c | 53 ++++++++++++++++++++++++----------- 5 files changed, 63 insertions(+), 39 deletions(-) diff --git a/ext/zlib/zlib_fopen_wrapper.c b/ext/zlib/zlib_fopen_wrapper.c index 061326816c..31589f824b 100644 --- a/ext/zlib/zlib_fopen_wrapper.c +++ b/ext/zlib/zlib_fopen_wrapper.c @@ -32,7 +32,7 @@ static size_t php_gziop_read(php_stream *stream, char *buf, size_t count) { struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *)stream->abstract; - if (buf == NULL && count == 0) { + if (buf == NULL && count == 0) { if (gzeof(self->gz_file)) return EOF; return 0; @@ -60,13 +60,14 @@ static int php_gziop_seek(php_stream *stream, off_t offset, int whence) return gzseek(self->gz_file, offset, whence); } -static int php_gziop_close(php_stream *stream) +static int php_gziop_close(php_stream *stream, int close_handle) { struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *)stream->abstract; int ret; - ret = gzclose(self->gz_file); - php_stream_close(self->stream); + if (close_handle) + ret = gzclose(self->gz_file); + php_stream_free(self->stream, close_handle); efree(self); return ret; @@ -92,9 +93,9 @@ php_stream *php_stream_gzopen(char *path, char *mode, int options, char **opened self->stream = php_stream_open_wrapper(path, mode, options, opened_path TSRMLS_CC); - if (self->stream) { + if (self->stream) { int fd; - if (SUCCESS == php_stream_cast(self->stream, PHP_STREAM_AS_FD, (void**)&fd, REPORT_ERRORS)) { + if (SUCCESS == php_stream_cast(self->stream, PHP_STREAM_AS_FD, (void**)&fd, REPORT_ERRORS)) { self->gz_file = gzdopen(fd, mode); if (self->gz_file) { stream = php_stream_alloc(&php_stream_gzio_ops, self, 0, mode); diff --git a/main/main.c b/main/main.c index e3faf52d88..187286ad49 100644 --- a/main/main.c +++ b/main/main.c @@ -572,11 +572,10 @@ static FILE *php_fopen_wrapper_for_zend(const char *filename, char **opened_path /* no need for us to check the stream type here */ php_stream_sock_set_chunk_size(stream, 1); - if (php_stream_cast(stream, PHP_STREAM_AS_STDIO | PHP_STREAM_CAST_TRY_HARD, (void**)&retval, 1) == SUCCESS) { - /* The leak here prevents a segfault */ - /* ZEND_REGISTER_RESOURCE(NULL, stream, php_file_le_stream()); */ - } - else { + /* when this succeeds, stream either has or will be freed automatically */ + if (php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_TRY_HARD|PHP_STREAM_CAST_RELEASE, + (void**)&retval, REPORT_ERRORS) == FAILURE) + { php_stream_close(stream); if (opened_path && *opened_path) efree(*opened_path); diff --git a/main/network.c b/main/network.c index dd877bbd1d..93e0f2bbce 100644 --- a/main/network.c +++ b/main/network.c @@ -675,22 +675,24 @@ static size_t php_sockop_read(php_stream *stream, char *buf, size_t count) return ret; } -static int php_sockop_close(php_stream *stream) +static int php_sockop_close(php_stream *stream, int close_handle) { php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract; + if (close_handle) { #if HAVE_OPENSSL_EXT - if (sock->ssl_active) { - SSL_shutdown(sock->ssl_handle); - sock->ssl_active = 0; - SSL_free(sock->ssl_handle); - sock->ssl_handle = NULL; - } + if (sock->ssl_active) { + SSL_shutdown(sock->ssl_handle); + sock->ssl_active = 0; + SSL_free(sock->ssl_handle); + sock->ssl_handle = NULL; + } #endif - - shutdown(sock->socket, 0); - closesocket(sock->socket); + shutdown(sock->socket, 0); + closesocket(sock->socket); + + } if (sock->readbuf) pefree(sock->readbuf, php_stream_is_persistent(stream)); diff --git a/main/php_streams.h b/main/php_streams.h index 7ec7f6f6f7..65a83c8036 100755 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -43,7 +43,7 @@ typedef struct _php_stream_ops { /* stdio like functions - these are mandatory! */ size_t (*write)(php_stream *stream, const char *buf, size_t count); size_t (*read)(php_stream *stream, char *buf, size_t count); - int (*close)(php_stream *stream); + int (*close)(php_stream *stream, int close_handle); int (*flush)(php_stream *stream); /* these are optional */ int (*seek)(php_stream *stream, off_t offset, int whence); @@ -132,7 +132,8 @@ PHPAPI php_stream *php_stream_fopen_temporary_file(const char *dir, const char * /* try really, really hard to make sure the cast happens (socketpair) */ #define PHP_STREAM_CAST_TRY_HARD 0x80000000 - +#define PHP_STREAM_CAST_RELEASE 0x40000000 /* stream becomes invalid on success */ +#define PHP_STREAM_CAST_MASK (PHP_STREAM_CAST_TRY_HARD | PHP_STREAM_CAST_RELEASE) PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_err); /* use this to check if a stream can be cast into another form */ #define php_stream_can_cast(stream, as) php_stream_cast(stream, as, NULL, 0) diff --git a/main/streams.c b/main/streams.c index eb0c942f27..be5ae0812c 100755 --- a/main/streams.c +++ b/main/streams.c @@ -69,6 +69,7 @@ PHPAPI int php_stream_free(php_stream *stream, int call_dtor) /* {{{ */ if (stream->wrapper && stream->wrapper->destroy) { stream->wrapper->destroy(stream); + stream->wrapper = NULL; } if (call_dtor) { @@ -84,9 +85,12 @@ PHPAPI int php_stream_free(php_stream *stream, int call_dtor) /* {{{ */ } php_stream_flush(stream); - ret = stream->ops->close(stream); - stream->abstract = NULL; + } + + ret = stream->ops->close(stream, call_dtor); + stream->abstract = NULL; + if (call_dtor) { /* tidy up any FILE* that might have been fdopened */ if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FDOPEN && stream->stdiocast) { fclose(stream->stdiocast); @@ -96,6 +100,7 @@ PHPAPI int php_stream_free(php_stream *stream, int call_dtor) /* {{{ */ if (stream->wrapperdata) { FREE_ZVAL(stream->wrapperdata); + stream->wrapperdata = NULL; } pefree(stream, stream->is_persistent); @@ -494,18 +499,22 @@ static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count) return fread(buf, 1, count, data->file); } -static int php_stdiop_close(php_stream *stream) +static int php_stdiop_close(php_stream *stream, int close_handle) { int ret; php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; assert(data != NULL); - if (data->is_pipe) { - ret = pclose(data->file); - } else { - ret = fclose(data->file); + if (close_handle) { + if (data->is_pipe) { + ret = pclose(data->file); + } else { + ret = fclose(data->file); + } } + else + ret = 0; efree(data); @@ -759,16 +768,15 @@ static COOKIE_IO_FUNCTIONS_T stream_cookie_functions = PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_err) /* {{{ */ { + int flags = castas & PHP_STREAM_CAST_MASK; + castas &= ~PHP_STREAM_CAST_MASK; - /* trying hard is not yet implemented */ - castas &= ~PHP_STREAM_CAST_TRY_HARD; - - if (castas == PHP_STREAM_AS_STDIO) { - if (stream->stdiocast) { + if (castas == PHP_STREAM_AS_STDIO) { + if (stream->stdiocast) { if (ret) { *ret = stream->stdiocast; } - return SUCCESS; + goto exit_success; } if (stream->ops->cast && stream->ops->cast(stream, castas, ret) == SUCCESS) @@ -782,8 +790,8 @@ PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_ *ret = fopencookie(stream, stream->mode, stream_cookie_functions); - if (*ret != NULL) { - stream->fclose_stdiocast = 1; + if (*ret != NULL) { + stream->fclose_stdiocast = PHP_STREAM_FCLOSE_FOPENCOOKIE; goto exit_success; } @@ -807,7 +815,7 @@ PHPAPI int php_stream_cast(php_stream *stream, int castas, void **ret, int show_ exit_fail: - if (show_err) { + if (show_err) { /* these names depend on the values of the PHP_STREAM_AS_XXX defines in php_streams.h */ static const char *cast_names[3] = { "STDIO FILE*", "File Descriptor", "Socket Descriptor" @@ -827,6 +835,19 @@ exit_success: if (castas == PHP_STREAM_AS_STDIO && ret) stream->stdiocast = *ret; + if (flags & PHP_STREAM_CAST_RELEASE) { + /* Something other than php_stream_close will be closing + * the underlying handle, so we should free the stream handle/data + * here now. The stream may not be freed immediately (in the case + * of fopencookie), but the caller should still not touch their + * original stream pointer in any case. */ + if (stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE) { + /* ask the implementation to release resources other than + * the underlying handle */ + php_stream_free(stream, 0); + } + } + return SUCCESS; } /* }}} */ -- 2.40.0