Merge pull request #3299 from datauwu/tar-read-fix-uaf
tar: fix UAF when reading GNU sparse headers
This commit is contained in:
4 files changed
+112
-2
No files matched your search
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user