From 3a072e0bb0d6084dd0741440d31896a314d1f2ea Mon Sep 17 00:00:00 2001 From: =?utf8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Mon, 8 Sep 2025 21:20:32 +0200 Subject: [PATCH] write_add_filter_bzip2: End compression in the freer If a fatal error occurs, the closer will not be called, so neither will BZ2_bzCompressEnd(), and we will leak memory. Fix this by calling it a second time from the freer. This is harmless in the non-error case as it will see that the compression state has already been cleared and immediately return BZ_PARAM_ERROR, which we simply ignore. --- libarchive/archive_write_add_filter_bzip2.c | 4 +++ libarchive/test/test_write_filter_bzip2.c | 29 +++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/libarchive/archive_write_add_filter_bzip2.c b/libarchive/archive_write_add_filter_bzip2.c index 0726f0893..2434528d5 100644 --- a/libarchive/archive_write_add_filter_bzip2.c +++ b/libarchive/archive_write_add_filter_bzip2.c @@ -281,6 +281,10 @@ static int archive_compressor_bzip2_free(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; + + /* May already have been called, but not necessarily. */ + (void)BZ2_bzCompressEnd(&(data->stream)); + free(data->compressed); free(data); f->data = NULL; diff --git a/libarchive/test/test_write_filter_bzip2.c b/libarchive/test/test_write_filter_bzip2.c index 20ca0d9a7..7b2e4f857 100644 --- a/libarchive/test/test_write_filter_bzip2.c +++ b/libarchive/test/test_write_filter_bzip2.c @@ -267,6 +267,35 @@ DEFINE_TEST(test_write_filter_bzip2) assertEqualInt(ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + /* + * Test behavior after a fatal error (triggered by giving + * archive_write_open_memory() a very small buffer). + */ + if (!use_prog) { + used1 = 0; + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_format_ustar(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_add_filter_bzip2(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, 100, &used1)); + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_filetype(ae, AE_IFREG); + archive_entry_set_size(ae, 4000000); + archive_entry_copy_pathname(ae, "file"); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_header(a, ae)); + for (i = 0; i < 1000000; i++) { + r = archive_write_data(a, &i, 4); + if (r == ARCHIVE_FATAL) + break; + } + assertEqualIntA(a, ARCHIVE_FATAL, r); + archive_entry_free(ae); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + } + /* * Clean up. */ -- 2.47.3