/* * Archive entry link resolver fuzzer for libarchive * Targets archive_entry_linkify (complexity: 775, zero coverage) */ #include #include #include #include #include "archive.h" #include "archive_entry.h" #include "fuzz_helpers.h" static constexpr size_t kMaxInputSize = 64 * 1024; // 64KB // Simple data consumer extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) { if (len == 0 || len > kMaxInputSize) { return 0; } DataConsumer consumer(buf, len); // Create a link resolver struct archive_entry_linkresolver *resolver = archive_entry_linkresolver_new(); if (resolver == NULL) { return 0; } // Set the format strategy based on input uint8_t strategy = consumer.consume_byte() % 5; int format; switch (strategy) { case 0: format = ARCHIVE_FORMAT_TAR_GNUTAR; break; case 1: format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; break; case 2: format = ARCHIVE_FORMAT_CPIO_POSIX; break; case 3: format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC; break; default: format = ARCHIVE_FORMAT_TAR_USTAR; break; } archive_entry_linkresolver_set_strategy(resolver, format); // Create multiple entries to test linkify with hardlinks struct archive_entry *entries[32]; int num_entries = 0; while (!consumer.empty() && num_entries < 32 && consumer.remaining() > 20) { struct archive_entry *entry = archive_entry_new(); if (entry == NULL) break; // Set pathname archive_entry_set_pathname(entry, consumer.consume_string(64)); // Set inode and device for hardlink detection archive_entry_set_ino(entry, consumer.consume_i64()); archive_entry_set_dev(entry, consumer.consume_u32()); archive_entry_set_nlink(entry, (consumer.consume_byte() % 5) + 1); // Set mode (regular file or directory) uint8_t ftype = consumer.consume_byte() % 2; mode_t mode = ftype ? (S_IFDIR | 0755) : (S_IFREG | 0644); archive_entry_set_mode(entry, mode); archive_entry_set_size(entry, consumer.consume_i64() & 0xFFFF); archive_entry_set_uid(entry, consumer.consume_u32() & 0xFFFF); archive_entry_set_gid(entry, consumer.consume_u32() & 0xFFFF); entries[num_entries++] = entry; } // Now run all entries through the linkresolver for (int i = 0; i < num_entries; i++) { struct archive_entry *entry = entries[i]; struct archive_entry *spare = NULL; // This is the main function we want to fuzz (zero coverage) archive_entry_linkify(resolver, &entry, &spare); // Update entries[i] to reflect ownership changes from linkify. // If linkify cached the entry internally, entry is now NULL and the // resolver owns the object. If linkify swapped it with a previously // cached entry, entry points to that other object. entries[i] = entry; // Free any entry returned via spare (complete hardlink pair) if (spare != NULL) { archive_entry_free(spare); } } // Free remaining entries from the resolver (drain loop) struct archive_entry *entry = NULL; struct archive_entry *spare = NULL; while (1) { archive_entry_linkify(resolver, &entry, &spare); if (entry == NULL) break; archive_entry_free(entry); entry = NULL; if (spare != NULL) { archive_entry_free(spare); spare = NULL; } } // Free all our created entries that were NOT consumed by the resolver for (int i = 0; i < num_entries; i++) { if (entries[i] != NULL) { archive_entry_free(entries[i]); } } archive_entry_linkresolver_free(resolver); return 0; }