]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Merge pull request #3057 from i1011/mtree-null-deref-fix
authorDustin L. Howett <dustin@howett.net>
Sun, 24 May 2026 17:43:25 +0000 (12:43 -0500)
committerMartin Matuska <martin@matuska.de>
Tue, 23 Jun 2026 08:31:08 +0000 (10:31 +0200)
mtree: Fix null dereference for some corner cases:

- a symlink whose target is never set
- emitting a root directory entry that is actually a file

(cherry picked from commit ea7084a7356a59fe6a990a2142de807c8cd77426)

libarchive/archive_write_set_format_mtree.c
libarchive/test/test_write_format_mtree_null_deref.c

index 1c6c13fc655f2ddd2152831d2a1c0300d2acba8e..ed53849bfbbcb99a31946990fc514c8fbb74d5b9 100644 (file)
@@ -801,6 +801,8 @@ mtree_entry_new(struct archive_write *a, struct archive_entry *entry,
                archive_strcpy(&me->symlink, s);
        me->nlink = archive_entry_nlink(entry);
        me->filetype = archive_entry_filetype(entry);
+       if (me->filetype == AE_IFLNK && me->symlink.s == NULL)
+               archive_strcpy(&me->symlink, "");
        me->mode = archive_entry_mode(entry) & 07777;
        me->uid = archive_entry_uid(entry);
        me->gid = archive_entry_gid(entry);
@@ -2086,6 +2088,11 @@ mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep)
        file = *filep;
        if (file->parentdir.length == 0 && file->basename.length == 1 &&
            file->basename.s[0] == '.') {
+               if (file->filetype != AE_IFDIR) {
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                               "Root entry '.' must be a directory");
+                       return (ARCHIVE_FAILED);
+               }
                file->parent = file;
                if (mtree->root != NULL) {
                        np = mtree->root;
index 5e1dbfca9fc420c21c107771a620de6a9e097f63..843bfb5d7f7fa0128d67aa4d9228adf7f79214ff 100644 (file)
@@ -171,3 +171,63 @@ DEFINE_TEST(test_write_format_mtree_null_deref)
        archive_write_free(a);
        free(out_buf);
 }
+
+DEFINE_TEST(test_write_format_mtree_no_set_symlink)
+{
+       struct archive *a;
+       size_t buffsize = 4096;
+       char *buff;
+       size_t used;
+       assert((buff = malloc(buffsize)) != NULL);
+       assert((a = archive_write_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_mtree(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+               archive_write_open_memory(a, buff, buffsize, &used));
+
+       struct archive_entry *ae;
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_pathname(ae, "./badlink"),
+       archive_entry_set_filetype(ae, AE_IFLNK);
+       archive_entry_set_perm(ae, 0777);
+       /* archive_entry_set_symlink(ae, "target"); (omitted) */
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+       archive_entry_free(ae);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+       free(buff);
+}
+
+DEFINE_TEST(test_write_format_mtree_reg_dot_root)
+{
+       struct archive *a;
+       size_t buffsize = 4096;
+       char *buff;
+       size_t used;
+       assert((buff = malloc(buffsize)) != NULL);
+       assert((a = archive_write_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_mtree(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+               archive_write_open_memory(a, buff, buffsize, &used));
+
+       struct archive_entry *ae;
+       /* Entry 1: "." with filetype AE_IFREG (not AE_IFDIR). */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_pathname(ae, "."),
+       archive_entry_set_filetype(ae, AE_IFREG);
+       archive_entry_set_perm(ae, 0644);
+       assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae));
+       archive_entry_free(ae);
+
+       /* Entry 2: any child path. */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_pathname(ae, "./foo"),
+       archive_entry_set_filetype(ae, AE_IFREG);
+       archive_entry_set_perm(ae, 0644);
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+       archive_entry_free(ae);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+       free(buff);
+}