From: Dustin L. Howett Date: Wed, 24 Jun 2026 14:30:41 +0000 (-0500) Subject: iso9660: add a test for the 32-bit zisofs overflow case X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=02447e94d03ce06dcf8d166b30bf08ec77b0c4c8;p=thirdparty%2Flibarchive.git iso9660: add a test for the 32-bit zisofs overflow case Writing a file near 4 GiB causes an overflow when we calculate the number of block pointers we need to store, which leads to us allocating the wrong amount of memory to store them. This is visible with ASan: Exercising: libarchive 3.9.0dev zlib/1.3.1 liblzma/5.4.4 bz2lib/1.0.8 libzstd/1.5.6 cng/2.0 libb2/bundled 706: test_write_format_iso9660_zisofs_overflow ================================================================= ==40444==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x08c004b4 at pc 0x006acb3b bp 0x002edb44 sp 0x002edb38 WRITE of size 1 at 0x08c004b4 thread T0 #0 0x006acb3a in archive_le32enc libarchive\archive_endian.h:219 #1 0x006c81d0 in zisofs_write_to_temp libarchive\archive_write_set_format_iso9660.c:7760 #2 0x006ca1bd in write_iso9660_data libarchive\archive_write_set_format_iso9660.c:1785 --- diff --git a/Makefile.am b/Makefile.am index b7c18158f..49f127320 100644 --- a/Makefile.am +++ b/Makefile.am @@ -676,6 +676,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_write_format_iso9660_joliet_id.c \ libarchive/test/test_write_format_iso9660_rockridge.c \ libarchive/test/test_write_format_iso9660_zisofs.c \ + libarchive/test/test_write_format_iso9660_zisofs_overflow.c \ libarchive/test/test_write_format_mtree.c \ libarchive/test/test_write_format_mtree_absolute.c \ libarchive/test/test_write_format_mtree_absolute_path.c \ diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index 2bff4f484..bf3ebf806 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -304,6 +304,7 @@ IF(ENABLE_TEST) test_write_format_iso9660_joliet_id.c test_write_format_iso9660_rockridge.c test_write_format_iso9660_zisofs.c + test_write_format_iso9660_zisofs_overflow.c test_write_format_iso9660_bugs.c test_write_format_mtree.c test_write_format_mtree_absolute.c diff --git a/libarchive/test/test_write_format_iso9660_zisofs_overflow.c b/libarchive/test/test_write_format_iso9660_zisofs_overflow.c new file mode 100644 index 000000000..12be811a7 --- /dev/null +++ b/libarchive/test/test_write_format_iso9660_zisofs_overflow.c @@ -0,0 +1,127 @@ +/*-SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 Dustin L. Howett + * 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" + +/* + * Writing a file to zisofs which is near the 4 GiB limit triggered an + * overflow and wraparound in calculating how much space we needed to + * store compressed block pointers, which led to an out-of-bounds write + * to the heap. + * + * This test can only fail under ASan. + */ +DEFINE_TEST(test_write_format_iso9660_zisofs_overflow) +{ + struct archive *a; + struct archive_entry *ae; + unsigned char *inbuff, *arcbuff; + size_t buffsize = 2048 * 288, writesize = 1048576, entrysize = UINT32_MAX - 32000, used; + int r; + + /* ISO9660 format: Create a new archive in memory. */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, 0, archive_write_set_format_iso9660(a)); + assertEqualIntA(a, 0, archive_write_add_filter_none(a)); + r = archive_write_set_option(a, NULL, "zisofs", "1"); + if (r == ARCHIVE_FATAL) { + skipping("zisofs option not supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + return; + } + + arcbuff = malloc(buffsize); + assert(arcbuff != NULL); + if (arcbuff == NULL) + return; + + inbuff = calloc(writesize, 1); + assert(inbuff != NULL); + if (inbuff == NULL) { + free(arcbuff); + return; + } + + assertEqualIntA(a, 0, archive_write_set_option(a, NULL, "pad", NULL)); + assertEqualIntA(a, 0, archive_write_open_memory(a, arcbuff, buffsize, &used)); + + /* + * "file1" is almost exactly 4 GiB; it's enough to trigger a block pointer counting + * overflow in zisofs. + */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_atime(ae, 2, 20); + archive_entry_set_birthtime(ae, 3, 30); + archive_entry_set_ctime(ae, 4, 40); + archive_entry_set_mtime(ae, 5, 50); + archive_entry_copy_pathname(ae, "file1"); + archive_entry_set_mode(ae, S_IFREG | 0755); + archive_entry_set_size(ae, entrysize); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + while (entrysize > writesize) { + assertEqualIntA(a, writesize, archive_write_data(a, inbuff, writesize)); + entrysize -= writesize; + } + /* remainder */ + if (0 != (writesize = entrysize)) + assertEqualIntA(a, writesize, archive_write_data(a, inbuff, writesize)); + + /* Close out the archive. */ + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + failure("The ISO image size should be 589824 bytes."); + assertEqualInt(used, 2048 * 288); + + /* + * Read ISO image (basic sanity check). + */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, 0, archive_read_support_format_all(a)); + assertEqualIntA(a, 0, archive_read_support_filter_all(a)); + assertEqualIntA(a, 0, archive_read_open_memory(a, arcbuff, used)); + + /* + * Skip the root directory + */ + assertEqualIntA(a, 0, archive_read_next_header(a, &ae)); + + /* + * file1 + */ + assertEqualIntA(a, 0, archive_read_next_header(a, &ae)); + + /* + * Verify the end of the archive. + */ + 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)); + + free(arcbuff); + free(inbuff); +}