[PATCH] erofs-utils: lib: fix error-path resource leaks in stream setup
Saksham
aghi.saksham5 at gmail.com
Fri Mar 27 05:14:03 AEDT 2026
Currently, erofs_iostream_open() in lib/tar.c has several resource leaks
in its error paths. Specifically:
1. In the LZMA decoder path, if lzma_auto_decoder() fails, the allocated
ios->lzma structure is leaked. Furthermore, the file descriptor 'fd'
passed to the function is not closed, despite ownership being
conceptually transferred to the stream.
2. In the GZRAN decoder path, if erofs_gzran_builder_init() fails, the
file descriptor 'fd' is leaked.
3. In the final buffer allocation loop, if malloc() fails for all
attempted buffer sizes, the function returns -ENOMEM without
cleaning up the already initialized decoder state (e.g., gzdopen'ed
handler, lzma state, or gzran builder). This also leaks the file
descriptor.
This patch refactors erofs_iostream_open() to use a unified error
cleanup path. A new 'err_close' label is introduced to handle failures
that occur before the high-level stream is fully established, ensuring
that the file descriptor is closed. For failures that occur after
the stream is partially or fully initialized (like the buffer allocation
failure), erofs_iostream_close() is called to perform a complete cleanup.
Additionally, erofs_iostream_close() is updated to ensure that the
underlying file descriptor is closed in the EROFS_IOS_DECODER_GZRAN
case, which was previously overlooked.
Furthermore, this patch fixes related resource leaks in lib/gzran.c,
ensuring that erofs_gzran_builder_init() frees its state on failure,
and erofs_gzran_builder_final() always frees its state regardless of
inflateEnd()'s return value.
These changes ensure clear resource ownership and robust error handling
during stream setup, reducing the risk of file descriptor and memory
leaks in long-running or resource-constrained environments.
Signed-off-by: Saksham <aghi.saksham5 at gmail.com>
---
lib/gzran.c | 9 +++----
lib/tar.c | 72 ++++++++++++++++++++++++++++++++++++-----------------
2 files changed, 53 insertions(+), 28 deletions(-)
diff --git a/lib/gzran.c b/lib/gzran.c
index dffb20a..e64b5b0 100644
--- a/lib/gzran.c
+++ b/lib/gzran.c
@@ -50,8 +50,10 @@ struct erofs_gzran_builder *erofs_gzran_builder_init(struct erofs_vfile *vf,
strm->avail_in = 0;
strm->next_in = Z_NULL;
ret = inflateInit2(strm, 47); /* automatic zlib or gzip decoding */
- if (ret != Z_OK)
+ if (ret != Z_OK) {
+ free(gb);
return ERR_PTR(-EFAULT);
+ }
gb->vf = vf;
gb->span_size = span_size;
gb->totout = gb->totin = 0;
@@ -187,11 +189,8 @@ int erofs_gzran_builder_export_zinfo(struct erofs_gzran_builder *gb,
int erofs_gzran_builder_final(struct erofs_gzran_builder *gb)
{
struct erofs_gzran_cutpoint_item *ci, *n;
- int ret;
- ret = inflateEnd(&gb->strm);
- if (ret != Z_OK)
- return -EFAULT;
+ (void)inflateEnd(&gb->strm);
list_for_each_entry_safe(ci, n, &gb->items, list) {
list_del(&ci->list);
free(ci);
diff --git a/lib/tar.c b/lib/tar.c
index eca29f5..dd8135b 100644
--- a/lib/tar.c
+++ b/lib/tar.c
@@ -53,80 +53,99 @@ struct erofs_iostream_liblzma {
void erofs_iostream_close(struct erofs_iostream *ios)
{
- free(ios->buffer);
+ if (ios->buffer) {
+ free(ios->buffer);
+ ios->buffer = NULL;
+ }
if (ios->decoder == EROFS_IOS_DECODER_GZIP) {
#if defined(HAVE_ZLIB)
gzclose(ios->handler);
#endif
- return;
} else if (ios->decoder == EROFS_IOS_DECODER_LIBLZMA) {
#if defined(HAVE_LIBLZMA)
lzma_end(&ios->lzma->strm);
close(ios->lzma->fd);
free(ios->lzma);
#endif
- return;
- } else if (ios->decoder == EROFS_IOS_DECODER_GZRAN) {
- erofs_gzran_builder_final(ios->gb);
- return;
+ } else {
+ if (ios->decoder == EROFS_IOS_DECODER_GZRAN)
+ erofs_gzran_builder_final(ios->gb);
+ erofs_io_close(&ios->vf);
}
- erofs_io_close(&ios->vf);
}
int erofs_iostream_open(struct erofs_iostream *ios, int fd, int decoder)
{
s64 fsz;
+ int ret;
ios->feof = false;
ios->tail = ios->head = 0;
ios->decoder = decoder;
ios->dumpfd = -1;
+ ios->buffer = NULL;
+
if (decoder == EROFS_IOS_DECODER_GZIP) {
#if defined(HAVE_ZLIB)
ios->handler = gzdopen(fd, "r");
- if (!ios->handler)
- return -ENOMEM;
+ if (!ios->handler) {
+ ret = -ENOMEM;
+ goto err_close;
+ }
ios->sz = fsz = 0;
ios->bufsize = 32768;
#else
- return -EOPNOTSUPP;
+ ret = -EOPNOTSUPP;
+ goto err_close;
#endif
} else if (decoder == EROFS_IOS_DECODER_LIBLZMA) {
#ifdef HAVE_LIBLZMA
- lzma_ret ret;
+ lzma_ret lret;
ios->lzma = malloc(sizeof(*ios->lzma));
- if (!ios->lzma)
- return -ENOMEM;
+ if (!ios->lzma) {
+ ret = -ENOMEM;
+ goto err_close;
+ }
ios->lzma->fd = fd;
ios->lzma->strm = (lzma_stream)LZMA_STREAM_INIT;
- ret = lzma_auto_decoder(&ios->lzma->strm,
+ lret = lzma_auto_decoder(&ios->lzma->strm,
UINT64_MAX, LZMA_CONCATENATED);
- if (ret != LZMA_OK)
- return -EFAULT;
+ if (lret != LZMA_OK) {
+ free(ios->lzma);
+ ret = -EFAULT;
+ goto err_close;
+ }
ios->sz = fsz = 0;
ios->bufsize = 32768;
#else
- return -EOPNOTSUPP;
+ ret = -EOPNOTSUPP;
+ goto err_close;
#endif
} else if (decoder == EROFS_IOS_DECODER_GZRAN) {
ios->vf.fd = fd;
+ ios->vf.ops = NULL;
ios->feof = false;
ios->sz = 0;
ios->bufsize = EROFS_GZRAN_WINSIZE * 2;
ios->gb = erofs_gzran_builder_init(&ios->vf, 4194304);
- if (IS_ERR(ios->gb))
- return PTR_ERR(ios->gb);
+ if (IS_ERR(ios->gb)) {
+ ret = PTR_ERR(ios->gb);
+ goto err_close;
+ }
} else {
ios->vf.fd = fd;
+ ios->vf.ops = NULL;
fsz = lseek(fd, 0, SEEK_END);
if (fsz <= 0) {
ios->feof = !fsz;
ios->sz = 0;
} else {
ios->sz = fsz;
- if (lseek(fd, 0, SEEK_SET))
- return -EIO;
+ if (lseek(fd, 0, SEEK_SET)) {
+ ret = -EIO;
+ goto err_close;
+ }
#ifdef HAVE_POSIX_FADVISE
if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL))
erofs_warn("failed to fadvise: %s, ignored.",
@@ -143,9 +162,16 @@ int erofs_iostream_open(struct erofs_iostream *ios, int fd, int decoder)
ios->bufsize >>= 1;
} while (ios->bufsize >= 1024);
- if (!ios->buffer)
- return -ENOMEM;
+ if (!ios->buffer) {
+ ret = -ENOMEM;
+ erofs_iostream_close(ios);
+ return ret;
+ }
return 0;
+
+err_close:
+ close(fd);
+ return ret;
}
int erofs_iostream_read(struct erofs_iostream *ios, void **buf, u64 bytes)
--
2.53.0
More information about the Linux-erofs
mailing list