]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
tar: Keep block alignment after pax error 2637/head
authorTobias Stoeckmann <tobias@stoeckmann.org>
Sun, 25 May 2025 10:03:55 +0000 (12:03 +0200)
committerTobias Stoeckmann <tobias@stoeckmann.org>
Sun, 25 May 2025 10:03:55 +0000 (12:03 +0200)
If a pax attribute has a 0 length value and no newline, the tar reader
gets out of sync with block alignment.

This happens because the pax parser assumes that variable value_length
(which includes the terminating newline) is at least 1. To get the
real value length, 1 is subtracted. This result is subtracted from
extsize, which in this case would lead to `extsize -= -1`, i.e.
the remaining byte count is increased.

Such an unexpected calculation leads to an off-by-one when skipping
to the next block. In supplied test case, bsdtar complains that the
checksum of the next block is wrong. Since the tar parser was not
properly 512 bytes aligned, this is no surprise.

Gracefully handle such a case like GNU tar does and warn the user that
an invalid attribute has been encountered.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Makefile.am
libarchive/archive_read_support_format_tar.c
libarchive/test/CMakeLists.txt
libarchive/test/test_read_pax_empty_val_no_nl.c [new file with mode: 0644]
libarchive/test/test_read_pax_empty_val_no_nl.tar.uu [new file with mode: 0644]

index d159f29708469ab34da7fb1725c53b968bbeaf7c..22253d70996112053e4dd8781109fe19b1330353 100644 (file)
@@ -566,6 +566,7 @@ libarchive_test_SOURCES= \
        libarchive/test/test_read_format_zip_zip64.c \
        libarchive/test/test_read_format_zip_with_invalid_traditional_eocd.c \
        libarchive/test/test_read_large.c \
+       libarchive/test/test_read_pax_empty_val_no_nl.c \
        libarchive/test/test_read_pax_xattr_rht_security_selinux.c \
        libarchive/test/test_read_pax_xattr_schily.c \
        libarchive/test/test_read_pax_truncated.c \
@@ -1043,6 +1044,7 @@ libarchive_test_EXTRA_DIST=\
        libarchive/test/test_read_large_splitted_rar_ac.uu \
        libarchive/test/test_read_large_splitted_rar_ad.uu \
        libarchive/test/test_read_large_splitted_rar_ae.uu \
+       libarchive/test/test_read_pax_empty_val_no_nl.tar.uu \
        libarchive/test/test_read_pax_xattr_rht_security_selinux.tar.uu \
        libarchive/test/test_read_pax_xattr_schily.tar.uu \
        libarchive/test/test_read_splitted_rar_aa.uu \
index 4ead50c7dc9fcf9906dceb7b95bbc2e94bc48e90..1cc66716904510560cad482501615173afd80fae 100644 (file)
@@ -2001,6 +2001,13 @@ header_pax_extension(struct archive_read *a, struct tar *tar,
                *unconsumed += p - attr_start;
                tar_flush_unconsumed(a, unconsumed);
 
+               if (value_length == 0) {
+                       archive_set_error(&a->archive, EINVAL,
+                                         "Malformed pax attributes");
+                       *unconsumed += ext_size + ext_padding;
+                       return (ARCHIVE_WARN);
+               }
+
                /* pax_attribute will consume value_length - 1 */
                r = pax_attribute(a, tar, entry, attr_name.s, archive_strlen(&attr_name), value_length - 1, unconsumed);
                ext_size -= value_length - 1;
index 2fa8b01c86486b712dc466c60b935d785183921b..1443f1c9df394838c347ae53fe3b7209d130feb1 100644 (file)
@@ -209,6 +209,7 @@ IF(ENABLE_TEST)
     test_read_format_zip_zip64.c
     test_read_format_zip_with_invalid_traditional_eocd.c
     test_read_large.c
+    test_read_pax_empty_val_no_nl.c
     test_read_pax_xattr_rht_security_selinux.c
     test_read_pax_xattr_schily.c
     test_read_pax_truncated.c
diff --git a/libarchive/test/test_read_pax_empty_val_no_nl.c b/libarchive/test/test_read_pax_empty_val_no_nl.c
new file mode 100644 (file)
index 0000000..f985488
--- /dev/null
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2025 Tobias Stoeckmann
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+
+/*
+ * Read a pax formatted tar archive that contains an invalid attribute,
+ * because it does not end in a newline. Additionally, value is empty.
+ * The pax reader should stop and tar reader should continue with warning.
+ */
+DEFINE_TEST(test_read_pax_empty_val_no_nl)
+{
+       char name[] = "test_read_pax_empty_val_no_nl.tar";
+       struct archive_entry *ae;
+       struct archive *a;
+
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       extract_reference_file(name);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240));
+
+       /* Read first entry. */
+       assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae));
+       assertEqualString("empty", archive_entry_pathname(ae));
+       assertEqualInt(1748163748, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_uid(ae));
+       assertEqualString("root", archive_entry_uname(ae));
+       assertEqualInt(0, archive_entry_gid(ae));
+       assertEqualString("root", archive_entry_gname(ae));
+       assertEqualInt(0100600, archive_entry_mode(ae));
+       assertEqualInt(archive_entry_is_encrypted(ae), 0);
+       assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED);
+
+       /* Verify the end-of-archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+       /* Verify that the format detection worked. */
+       assertEqualInt(archive_filter_code(a, 0), ARCHIVE_FILTER_NONE);
+       assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE);
+
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_read_pax_empty_val_no_nl.tar.uu b/libarchive/test/test_read_pax_empty_val_no_nl.tar.uu
new file mode 100644 (file)
index 0000000..5de8b25
--- /dev/null
@@ -0,0 +1,60 @@
+begin 600 test_read_pax_empty_val_no_nl.tar
+M4&%X2&5A9&5R+V5M<'1Y````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#8P,"``,#`P,#`P(``P,#`P,#`@`#`P,#`P,#`P,#`T
+M(#$U,#$T-34V,C0T(#`Q-#`U-@`@>```````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<@`P,')O;W0`
+M````````````````````````````````````<F]O=```````````````````
+M```````````````````P,#`P,#`@`#`P,#`P,"``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````T('@]````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````H`````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````&5M<'1Y````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````P
+M,#`V,#`@`#`P,#`P,"``,#`P,#`P(``P,#`P,#`P,#`P,"`Q-3`Q-#4U-C(T
+M-"`P,3(Q,#$`(#``````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````=7-T87(`,#!R;V]T````````````````
+M`````````````````````')O;W0`````````````````````````````````
+M````,#`P,#`P(``P,#`P,#`@````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+H````````````````````````````````````````````````````````
+`
+end