set_num_731(bp+3, np->dir_location);
/* Parent Directory Number */
if (type_m)
- set_num_722(bp+7, np->parent->dir_number);
+ set_num_722(bp+7,
+ np->parent != NULL ? np->parent->dir_number : 0);
else
- set_num_721(bp+7, np->parent->dir_number);
+ set_num_721(bp+7,
+ np->parent != NULL ? np->parent->dir_number : 0);
/* Directory Identifier */
if (np->identifier == NULL)
bp[9] = 0;
{
unsigned char *p;
int wnp_ext_off;
+ int new_id_len;
wnp_ext_off = wnp->isoent->ext_off;
if (wnp->noff + numsize != wnp_ext_off) {
- p = (unsigned char *)wnp->isoent->identifier;
+ new_id_len = wnp->noff + numsize + wnp->isoent->ext_len;
+ /*
+ * Reallocate the identifier buffer to fit the expanded
+ * name. Add extra space for a version suffix (";1")
+ * that may be appended later.
+ */
+ p = (unsigned char *)realloc(wnp->isoent->identifier,
+ new_id_len + nullsize + numsize + 4);
+ if (p == NULL)
+ return;
+ wnp->isoent->identifier = (char *)p;
/* Extend the filename; foo.c --> foo___.c */
memmove(p + wnp->noff + numsize, p + wnp_ext_off,
wnp->isoent->ext_len + nullsize);
p2 = *((const struct isoent **)(uintptr_t)v2);
/* Compare parent directory number */
- cmp = p1->parent->dir_number - p2->parent->dir_number;
+ if (p1->parent == NULL || p2->parent == NULL) {
+ if (p1->parent == p2->parent)
+ cmp = 0;
+ else if (p1->parent == NULL)
+ return (-1);
+ else
+ return (1);
+ } else
+ cmp = p1->parent->dir_number - p2->parent->dir_number;
if (cmp != 0)
return (cmp);
p2 = *((const struct isoent **)(uintptr_t)v2);
/* Compare parent directory number */
- cmp = p1->parent->dir_number - p2->parent->dir_number;
+ if (p1->parent == NULL || p2->parent == NULL) {
+ if (p1->parent == p2->parent)
+ cmp = 0;
+ else if (p1->parent == NULL)
+ return (-1);
+ else
+ return (1);
+ } else
+ cmp = p1->parent->dir_number - p2->parent->dir_number;
if (cmp != 0)
return (cmp);
--- /dev/null
+/*-
+ * Copyright (c) 2025 Tim Kientzle
+ * 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"
+#include "test_fuzz_consumer.h"
+
+#include <stdlib.h>
+
+/*
+ * Replay a fuzzer binary through the ISO9660 writer, matching the protocol
+ * in fuzzers/custom/fuzz_writer_iso9660.cc.
+ */
+static void
+replay_iso9660_writer(const char *refname)
+{
+ FILE *f;
+ uint8_t raw[32768];
+ size_t rawsize;
+ struct fuzz_consumer consumer;
+ uint8_t opts1, opts2, num_entries;
+ struct archive *a;
+ struct archive_entry *entry;
+ size_t used;
+ void *out_buf;
+ int i;
+
+ extract_reference_file(refname);
+ f = fopen(refname, "rb");
+ if (!assert(f != NULL))
+ return;
+ rawsize = fread(raw, 1, sizeof(raw), f);
+ fclose(f);
+ if (!assert(rawsize >= 6))
+ return;
+
+ fuzz_consumer_init(&consumer, raw, rawsize);
+ opts1 = fuzz_consume_byte(&consumer);
+ opts2 = fuzz_consume_byte(&consumer);
+ num_entries = (fuzz_consume_byte(&consumer) % 6) + 1;
+
+ a = archive_write_new();
+ if (!assert(a != NULL))
+ return;
+
+ archive_write_set_format_iso9660(a);
+
+ if (opts1 & 0x01)
+ archive_write_set_options(a, "iso9660:rockridge");
+ if (opts1 & 0x02)
+ archive_write_set_options(a, "iso9660:joliet");
+ if (opts1 & 0x04)
+ archive_write_set_options(a, "iso9660:iso-level=3");
+ if (opts1 & 0x08)
+ archive_write_set_options(a, "iso9660:iso-level=4");
+ if (opts1 & 0x10)
+ archive_write_set_options(a, "iso9660:allow-vernum");
+ if (opts1 & 0x20)
+ archive_write_set_options(a, "iso9660:allow-period");
+ if (opts1 & 0x40)
+ archive_write_set_options(a, "iso9660:allow-lowercase");
+ if (opts1 & 0x80)
+ archive_write_set_options(a, "iso9660:allow-multidot");
+
+ if (opts2 & 0x02)
+ archive_write_set_options(a,
+ "iso9660:publisher=FuzzPublisher");
+ if (opts2 & 0x04)
+ archive_write_set_options(a,
+ "iso9660:volume-id=FUZZISO");
+
+ out_buf = malloc(4 * 1024 * 1024);
+ if (!assert(out_buf != NULL)) {
+ archive_write_free(a);
+ return;
+ }
+ if (archive_write_open_memory(a, out_buf, 4 * 1024 * 1024, &used)
+ != ARCHIVE_OK) {
+ archive_write_free(a);
+ free(out_buf);
+ return;
+ }
+
+ entry = archive_entry_new();
+ if (!assert(entry != NULL)) {
+ archive_write_free(a);
+ free(out_buf);
+ return;
+ }
+
+ for (i = 0; i < num_entries && fuzz_consumer_remaining(&consumer) > 2;
+ i++) {
+ const char *name;
+ uint8_t ftype;
+ uint32_t file_size = 0;
+
+ archive_entry_clear(entry);
+
+ name = fuzz_consume_string(&consumer, 128);
+ if (name[0] == '\0')
+ name = "FILE.TXT";
+ archive_entry_set_pathname(entry, name);
+
+ ftype = fuzz_consume_byte(&consumer) % 4;
+ switch (ftype) {
+ case 0:
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_perm(entry, 0644);
+ break;
+ case 1:
+ archive_entry_set_filetype(entry, AE_IFDIR);
+ archive_entry_set_perm(entry, 0755);
+ break;
+ case 2:
+ archive_entry_set_filetype(entry, AE_IFLNK);
+ archive_entry_set_perm(entry, 0777);
+ archive_entry_set_symlink(entry,
+ fuzz_consume_string(&consumer, 64));
+ break;
+ case 3:
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_perm(entry, 0755);
+ break;
+ }
+
+ archive_entry_set_uid(entry, 1000);
+ archive_entry_set_gid(entry, 1000);
+ archive_entry_set_mtime(entry,
+ 1700000000 + fuzz_consume_u16(&consumer), 0);
+ archive_entry_set_uname(entry, "user");
+ archive_entry_set_gname(entry, "group");
+
+ if (ftype == 0 || ftype == 3) {
+ file_size = fuzz_consume_u16(&consumer) % 2048;
+ archive_entry_set_size(entry, file_size);
+ }
+
+ if (archive_write_header(a, entry) != ARCHIVE_OK)
+ continue;
+
+ if (file_size > 0 &&
+ fuzz_consumer_remaining(&consumer) > 0) {
+ size_t to_write = file_size;
+ uint8_t data[2048];
+ if (to_write > fuzz_consumer_remaining(&consumer))
+ to_write =
+ fuzz_consumer_remaining(&consumer);
+ fuzz_consume_bytes(&consumer, data, to_write);
+ archive_write_data(a, data, to_write);
+ }
+ }
+
+ archive_entry_free(entry);
+ /* Close triggers path table construction; must not crash. */
+ archive_write_close(a);
+ archive_write_free(a);
+ free(out_buf);
+}
+
+DEFINE_TEST(test_write_format_iso9660_null_deref)
+{
+ replay_iso9660_writer("test_write_format_iso9660_null_deref.bin");
+}
+
+DEFINE_TEST(test_write_format_iso9660_underflow)
+{
+ replay_iso9660_writer("test_write_format_iso9660_underflow.bin");
+}
+
+DEFINE_TEST(test_write_format_iso9660_joliet_overflow)
+{
+ replay_iso9660_writer("test_write_format_iso9660_joliet_overflow.bin");
+}