| 
 | 1 | +// Copyright 2025 The Chromium Authors  | 
 | 2 | +// Use of this source code is governed by a BSD-style license that can be  | 
 | 3 | +// found in the LICENSE file.  | 
 | 4 | + | 
 | 5 | +#include <algorithm>  | 
 | 6 | +#include <cstdint>  | 
 | 7 | +#include <cstring>  | 
 | 8 | +#include <vector>  | 
 | 9 | + | 
 | 10 | +#include "unzip.h"  | 
 | 11 | + | 
 | 12 | +// Fuzzer builds often have NDEBUG set, so roll our own assert macro.  | 
 | 13 | +#define ASSERT(cond) \  | 
 | 14 | + do { \  | 
 | 15 | + if (!(cond)) { \  | 
 | 16 | + fprintf(stderr, "%s:%d Assert failed: %s\n", __FILE__, __LINE__, #cond); \  | 
 | 17 | + exit(1); \  | 
 | 18 | + } \  | 
 | 19 | + } while (0)  | 
 | 20 | + | 
 | 21 | +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {  | 
 | 22 | + // Mock read-only filesystem with only one file, file_data. In the calls  | 
 | 23 | + // below, 'opaque' points to file_data, and 'strm' points to the file's seek  | 
 | 24 | + // position, which is heap allocated so that failing to "close" it triggers a  | 
 | 25 | + // leak error.  | 
 | 26 | + std::vector<uint8_t> file_data(data, data + size);  | 
 | 27 | + zlib_filefunc64_def file_func = {  | 
 | 28 | + .zopen64_file = [](void* opaque, const void* filename,  | 
 | 29 | + int mode) -> void* {  | 
 | 30 | + ASSERT(mode == (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING));  | 
 | 31 | + return new size_t(0);  | 
 | 32 | + },  | 
 | 33 | + .zread_file = [](void* opaque, void* strm, void* buf,  | 
 | 34 | + uLong size) -> uLong {  | 
 | 35 | + std::vector<uint8_t>* vec = static_cast<std::vector<uint8_t>*>(opaque);  | 
 | 36 | + size_t* pos = static_cast<size_t*>(strm);  | 
 | 37 | + if (*pos >= vec->size()) {  | 
 | 38 | + return 0;  | 
 | 39 | + }  | 
 | 40 | + size = std::min(static_cast<size_t>(size), vec->size() - *pos);  | 
 | 41 | + memcpy(buf, vec->data() + *pos, size);  | 
 | 42 | + (*pos) += size;  | 
 | 43 | + return size;  | 
 | 44 | + },  | 
 | 45 | + .zwrite_file = [](void*, void*, const void*, uLong) -> uLong {  | 
 | 46 | + ASSERT(0 && "Writing is not supported.");  | 
 | 47 | + return 0;  | 
 | 48 | + },  | 
 | 49 | + .ztell64_file = [](void*, void* strm) -> ZPOS64_T {  | 
 | 50 | + return *static_cast<size_t*>(strm);  | 
 | 51 | + },  | 
 | 52 | + .zseek64_file = [](void* opaque, void* strm, ZPOS64_T offset,  | 
 | 53 | + int origin) -> long {  | 
 | 54 | + std::vector<uint8_t>* vec = static_cast<std::vector<uint8_t>*>(opaque);  | 
 | 55 | + size_t* pos = static_cast<size_t*>(strm);  | 
 | 56 | + switch (origin) {  | 
 | 57 | + case ZLIB_FILEFUNC_SEEK_SET:  | 
 | 58 | + *pos = offset;  | 
 | 59 | + break;  | 
 | 60 | + case ZLIB_FILEFUNC_SEEK_CUR:  | 
 | 61 | + *pos = *pos + offset;  | 
 | 62 | + break;  | 
 | 63 | + case ZLIB_FILEFUNC_SEEK_END:  | 
 | 64 | + *pos = vec->size() + offset;  | 
 | 65 | + break;  | 
 | 66 | + default:  | 
 | 67 | + ASSERT(0 && "Invalid origin");  | 
 | 68 | + }  | 
 | 69 | + return 0;  | 
 | 70 | + },  | 
 | 71 | + .zclose_file = [](void*, void* strm) -> int {  | 
 | 72 | + delete static_cast<size_t*>(strm);  | 
 | 73 | + return 0;  | 
 | 74 | + },  | 
 | 75 | + .zerror_file = [](void*, void*) -> int { return 0; },  | 
 | 76 | + .opaque = &file_data};  | 
 | 77 | + | 
 | 78 | + unzFile uzf = unzOpen2_64("foo.zip", &file_func);  | 
 | 79 | + if (uzf == NULL) {  | 
 | 80 | + return 0;  | 
 | 81 | + }  | 
 | 82 | + | 
 | 83 | + while (true) {  | 
 | 84 | + unz_file_info64 info = {0};  | 
 | 85 | + | 
 | 86 | + // TODO: Pass nullptrs and different buffer sizes to cover more code.  | 
 | 87 | + char filename[UINT16_MAX + 1]; // +1 for the null terminator.  | 
 | 88 | + char extra[UINT16_MAX]; // No null terminator.  | 
 | 89 | + char comment[UINT16_MAX + 1]; // +1 for the null terminator.  | 
 | 90 | + | 
 | 91 | + if (unzGetCurrentFileInfo64(uzf, &info, filename, sizeof(filename), extra,  | 
 | 92 | + sizeof(extra), comment, sizeof(comment)) == UNZ_OK) {  | 
 | 93 | + ASSERT(info.size_filename <= UINT16_MAX);  | 
 | 94 | + ASSERT(info.size_file_extra <= UINT16_MAX);  | 
 | 95 | + ASSERT(info.size_file_comment <= UINT16_MAX);  | 
 | 96 | + | 
 | 97 | + ASSERT(filename[info.size_filename] == '\0');  | 
 | 98 | + ASSERT(comment[info.size_file_comment] == '\0');  | 
 | 99 | + }  | 
 | 100 | + | 
 | 101 | + if (unzOpenCurrentFile(uzf) == UNZ_OK) {  | 
 | 102 | + while (true) {  | 
 | 103 | + char buffer[4096];  | 
 | 104 | + int num_read = unzReadCurrentFile(uzf, buffer, sizeof(buffer));  | 
 | 105 | + if (num_read <= 0) {  | 
 | 106 | + break;  | 
 | 107 | + }  | 
 | 108 | + }  | 
 | 109 | + unzCloseCurrentFile(uzf);  | 
 | 110 | + }  | 
 | 111 | + | 
 | 112 | + if (unzGoToNextFile(uzf) != UNZ_OK) {  | 
 | 113 | + break;  | 
 | 114 | + }  | 
 | 115 | + }  | 
 | 116 | + | 
 | 117 | + unzClose(uzf);  | 
 | 118 | + return 0;  | 
 | 119 | +}  | 
0 commit comments