]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
tar: Reset accumulated header state after reading macOS metadata blob
authorZhaofeng Li <hello@zhaofeng.li>
Sat, 24 May 2025 19:45:18 +0000 (13:45 -0600)
committerZhaofeng Li <hello@zhaofeng.li>
Sat, 24 May 2025 20:33:08 +0000 (14:33 -0600)
AppleDouble extension entries are present as separate files immediately
preceding the corresponding real files. In libarchive, we process the
entire metadata file (headers + data) as if it were a header in the real
file. However, the code forgets to reset the accumulated header state
before parsing the real file's headers. In one code path, this causes
the metadata file's name to be used as the real file's name.

Specifically, this can be triggered with a tar containing two files:

1. A file named `._badname` with pax header containing the `path` attribute
2. A file named `goodname` _with_ a pax header but _without_ the `path` attribute

libarchive will list one file, `._badname` containing the data of `goodname`.

This code is pretty brittle and we really should let the client deal with
it :(

Fixes #2510.

Signed-off-by: Zhaofeng Li <hello@zhaofeng.li>
libarchive/archive_read_support_format_tar.c

index e9b3312a2cbbd64f3fc812ace581841347399f50..4ead50c7dc9fcf9906dceb7b95bbc2e94bc48e90 100644 (file)
@@ -701,6 +701,22 @@ archive_read_format_tar_skip(struct archive_read *a)
        return (ARCHIVE_OK);
 }
 
+/*
+ * This function resets the accumulated state while reading
+ * a header.
+ */
+static void
+tar_reset_header_state(struct tar *tar)
+{
+       tar->pax_hdrcharset_utf8 = 1;
+       tar->sparse_gnu_attributes_seen = 0;
+       archive_string_empty(&(tar->entry_gname));
+       archive_string_empty(&(tar->entry_pathname));
+       archive_string_empty(&(tar->entry_pathname_override));
+       archive_string_empty(&(tar->entry_uname));
+       archive_string_empty(&tar->entry_linkpath);
+}
+
 /*
  * This function reads and interprets all of the headers associated
  * with a single entry.
@@ -726,13 +742,7 @@ tar_read_header(struct archive_read *a, struct tar *tar,
        static const int32_t seen_x_header = 32; /* Also X */
        static const int32_t seen_mac_metadata = 512;
 
-       tar->pax_hdrcharset_utf8 = 1;
-       tar->sparse_gnu_attributes_seen = 0;
-       archive_string_empty(&(tar->entry_gname));
-       archive_string_empty(&(tar->entry_pathname));
-       archive_string_empty(&(tar->entry_pathname_override));
-       archive_string_empty(&(tar->entry_uname));
-       archive_string_empty(&tar->entry_linkpath);
+       tar_reset_header_state(tar);
 
        /* Ensure format is set. */
        if (a->archive.archive_format_name == NULL) {
@@ -936,6 +946,7 @@ tar_read_header(struct archive_read *a, struct tar *tar,
                                err = err_combine(err, err2);
                                /* Note: Other headers can appear again. */
                                seen_headers = seen_mac_metadata;
+                               tar_reset_header_state(tar);
                                break;
                        }