Merge pull request #3299 from datauwu/tar-read-fix-uaf

tar: fix UAF when reading GNU sparse headers
This commit is contained in:
Dustin L. Howett authored and GitHub committed 2026-07-21 13:13:03 -05:00
commit f5509ae993
4 files changed
+112 -2

No files matched your search

+1
View File
@@ -951,6 +951,7 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_format_gtar_sparse_1_17_posix01.tar.uu \
libarchive/test/test_read_format_gtar_sparse_1_17_posix10.tar.uu \
libarchive/test/test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu \
libarchive/test/test_read_format_gtar_sparse_reuse.tar.uu \
libarchive/test/test_read_format_gtar_sparse_length.tar.Z.uu \
libarchive/test/test_read_format_gtar_sparse_skip_entry.tar.Z.uu \
libarchive/test/test_read_format_huge_rpm.rpm.uu \
+9 -2
View File
@@ -3024,6 +3024,7 @@ static int
header_gnutar(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, const void *h, int64_t *unconsumed)
{
struct archive_entry_header_gnutar header_copy;
const struct archive_entry_header_gnutar *header;
int64_t t;
int err = ARCHIVE_OK;
@@ -3034,8 +3035,14 @@ header_gnutar(struct archive_read *a, struct tar *tar,
* filename is stored as in old-style archives.
*/
/*
* Sparse extension reads can reuse the callback buffer containing h,
* so preserve the main header until common fields are parsed.
*/
memcpy(&header_copy, h, sizeof(header_copy));
header = &header_copy;
/* Copy filename over (to ensure null termination). */
header = (const struct archive_entry_header_gnutar *)h;
const char *existing_pathname = archive_entry_pathname(entry);
const wchar_t *existing_wcs_pathname = archive_entry_pathname_w(entry);
if ((existing_pathname == NULL || existing_pathname[0] == '\0')
@@ -3114,7 +3121,7 @@ header_gnutar(struct archive_read *a, struct tar *tar,
}
/* Grab fields common to all tar variants. */
err = header_common(a, tar, entry, h);
err = header_common(a, tar, entry, header);
if (err == ARCHIVE_FATAL)
return (err);
@@ -169,6 +169,58 @@ static struct archive_contents {
{ NULL, NULL }
};
/* Reuse one callback buffer so the sparse extension overwrites the main header. */
struct sparse_reuse_data {
FILE *file;
unsigned char block[512];
};
static la_ssize_t
sparse_reuse_read(struct archive *a, void *client_data, const void **buffer)
{
struct sparse_reuse_data *data = client_data;
size_t size;
(void)a;
size = fread(data->block, 1, sizeof(data->block), data->file);
if (size == 0 && ferror(data->file))
return (-1);
*buffer = data->block;
return ((la_ssize_t)size);
}
static void
verify_sparse_header_reuse(void)
{
const char *name = "test_read_format_gtar_sparse_reuse.tar";
struct sparse_reuse_data data;
struct archive_entry *ae;
struct archive *a;
extract_reference_file(name);
memset(&data, 0, sizeof(data));
data.file = fopen(name, "rb");
if (!assert(data.file != NULL))
return;
assert((a = archive_read_new()) != NULL);
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_tar(a));
assertEqualIntA(a, ARCHIVE_OK,
archive_read_open(a, &data, NULL, sparse_reuse_read, NULL));
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
assertEqualString("sparse", archive_entry_pathname(ae));
assertEqualInt(0644, archive_entry_perm(ae));
assertEqualInt(123, archive_entry_uid(ae));
assertEqualInt(456, archive_entry_gid(ae));
assertEqualInt(123456789, archive_entry_mtime(ae));
assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
assertEqualInt(0, fclose(data.file));
}
static void
verify_archive_file(const char *name, struct archive_contents *ac)
{
@@ -262,6 +314,8 @@ verify_archive_file(const char *name, struct archive_contents *ac)
DEFINE_TEST(test_read_format_gtar_sparse)
{
verify_sparse_header_reuse();
/* Two archives that use the "GNU tar sparse format". */
verify_archive_file("test_read_format_gtar_sparse_1_13.tar", files);
verify_archive_file("test_read_format_gtar_sparse_1_17.tar", files);
@@ -0,0 +1,48 @@
begin 644 test_read_format_gtar_sparse_reuse.tar
M<W!A<G-E````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````#`P,#`V-#0`,#`P,#$W,P`P,#`P-S$P`#`P,#`P,#`P,#`P
M`#`P-S(V-S0V-#(U`#`R,#8S,0`@4P``````````````````````````````
M````````````````````````````````````````````````````````````
M``````````````````````````````````````````!U<W1A<B`@`'5S97(`
M````````````````````````````````````9W)O=7``````````````````
M```````````````````P,#`P,#`P`#`P,#`P,#``,#`P,#`P,#`P,#``,#`P
M,#`P,#`P,#``,#`P,#`P,#`P,#`````````P,#`P,#`P,#`P,``P,#`P,#`P
M,#`P,```````````````````````````````````````````````````````
M```````````````````````````````````````````!,#`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````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
7````````````````````````````````
end