From: François Degros Date: Fri, 24 Apr 2026 07:09:13 +0000 (+1000) Subject: Fix SIGSEGV in compress filter when appended before open X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3d4871e4d217b4eb395c00faf38cedfaa85fe8a3;p=thirdparty%2Flibarchive.git Fix SIGSEGV in compress filter when appended before open Calling archive_read_append_filter(a, ARCHIVE_FILTER_COMPRESS) would previously trigger a crash because compress_bidder_init() attempted to read header bits from the upstream filter immediately. If the archive was not yet opened (common when setting up filters), the upstream filter state was not ready for reading. This commit defers the header reading and decompressor initialization until the first read operation (lazy initialization), consistent with other filter implementations in libarchive. Added test_read_append_compress_filter. Bug: https://github.com/libarchive/libarchive/issues/2514 Test: ./libarchive_test test_read_append_compress_filter --- diff --git a/libarchive/archive_read_support_filter_compress.c b/libarchive/archive_read_support_filter_compress.c index b89eaabe5..57f320e83 100644 --- a/libarchive/archive_read_support_filter_compress.c +++ b/libarchive/archive_read_support_filter_compress.c @@ -104,6 +104,7 @@ struct private_data { void *out_block; /* Decompression status variables. */ + int initialized; int use_reset_code; int end_of_stream; /* EOF status. */ int maxcode; /* Largest code. */ @@ -212,7 +213,6 @@ compress_bidder_init(struct archive_read_filter *self) struct private_data *state; static const size_t out_block_size = 64 * 1024; void *out_block; - int code; self->code = ARCHIVE_FILTER_COMPRESS; self->name = "compress (.Z)"; @@ -233,14 +233,23 @@ compress_bidder_init(struct archive_read_filter *self) state->out_block = out_block; self->vtable = &compress_reader_vtable; - /* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */ + return (ARCHIVE_OK); +} + +static int +compress_filter_init(struct archive_read_filter *self) +{ + struct private_data *state = (struct private_data *)self->data; + int code; + + state->initialized = 1; (void)getbits(self, 8); /* Skip first signature byte. */ (void)getbits(self, 8); /* Skip second signature byte. */ /* Get compression parameters. */ code = getbits(self, 8); - if ((code & 0x1f) > 16) { + if (code < 0 || (code & 0x1f) > 16) { archive_set_error(&self->archive->archive, -1, "Invalid compressed data"); return (ARCHIVE_FATAL); @@ -278,6 +287,11 @@ compress_filter_read(struct archive_read_filter *self, const void **pblock) int ret; state = (struct private_data *)self->data; + if (!state->initialized) { + ret = compress_filter_init(self); + if (ret != ARCHIVE_OK) + return (ret); + } if (state->end_of_stream) { *pblock = NULL; return (0); diff --git a/libarchive/test/test_read_set_format.c b/libarchive/test/test_read_set_format.c index 2ea390452..2bac5acc5 100644 --- a/libarchive/test/test_read_set_format.c +++ b/libarchive/test/test_read_set_format.c @@ -211,6 +211,18 @@ DEFINE_TEST(test_read_append_grzip_filter) archive_read_free(a); } +DEFINE_TEST(test_read_append_compress_filter) +{ + struct archive *a; + int r; + + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_TAR)); + r = archive_read_append_filter(a, ARCHIVE_FILTER_COMPRESS); + assertEqualIntA(a, ARCHIVE_OK, r); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + DEFINE_TEST(test_read_append_filter_program) { struct archive_entry *ae;