]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Fix SIGSEGV in compress filter when appended before open
authorFrançois Degros <fdegros@chromium.org>
Fri, 24 Apr 2026 07:09:13 +0000 (17:09 +1000)
committerFrançois Degros <fdegros@chromium.org>
Fri, 24 Apr 2026 07:24:02 +0000 (17:24 +1000)
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

libarchive/archive_read_support_filter_compress.c
libarchive/test/test_read_set_format.c

index b89eaabe52595ceb7659c13f851e28cba2289d11..57f320e83d60cf98f3383e6067738ec3d6d4ad46 100644 (file)
@@ -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);
index 2ea39045201f271b9a1cfebfd13afd2d4cc57318..2bac5acc55694dd170a7df2b67aa3f0aad4b3769 100644 (file)
@@ -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;