96 lines
3.4 KiB
C++
96 lines
3.4 KiB
C++
// Copyright 2022 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <fuzzer/FuzzedDataProvider.h>
|
|
|
|
#include "third_party/zlib/zlib.h"
|
|
|
|
// Fuzzer builds often have NDEBUG set, so roll our own assert macro.
|
|
#define ASSERT(cond) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
fprintf(stderr, "%s:%d Assert failed: %s\n", __FILE__, __LINE__, #cond); \
|
|
exit(1); \
|
|
} \
|
|
} while (0)
|
|
|
|
static void chunked_inflate(gz_header* header,
|
|
uint8_t* data,
|
|
size_t size,
|
|
size_t in_chunk_size,
|
|
size_t out_chunk_size) {
|
|
z_stream stream;
|
|
stream.next_in = data;
|
|
stream.avail_in = 0;
|
|
stream.zalloc = Z_NULL;
|
|
stream.zfree = Z_NULL;
|
|
|
|
static const int kDefaultWindowBits = MAX_WBITS;
|
|
static const int kGzipOrZlibHeader = 32;
|
|
ASSERT(inflateInit2(&stream, kDefaultWindowBits + kGzipOrZlibHeader) == Z_OK);
|
|
ASSERT(inflateGetHeader(&stream, header) == Z_OK);
|
|
|
|
auto out_buffer = std::make_unique<uint8_t[]>(out_chunk_size);
|
|
while (true) {
|
|
stream.next_in = &data[stream.total_in];
|
|
stream.avail_in =
|
|
std::min(in_chunk_size, size - static_cast<size_t>(stream.total_in));
|
|
stream.next_out = out_buffer.get();
|
|
stream.avail_out = out_chunk_size;
|
|
|
|
if (inflate(&stream, stream.avail_in == 0 ? Z_SYNC_FLUSH : Z_NO_FLUSH) !=
|
|
Z_OK) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
inflateEnd(&stream);
|
|
}
|
|
|
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
|
if (size > 250 * 1024) {
|
|
// Cap the input size so the fuzzer doesn't time out. For example,
|
|
// crbug.com/1362206 saw timeouts with 450 KB input, so use a limit that's
|
|
// well below that but still large enough to hit most code.
|
|
return 0;
|
|
}
|
|
|
|
FuzzedDataProvider fdp(data, size);
|
|
|
|
// Fuzz zlib's inflate() with inflateGetHeader() enabled, various sizes for
|
|
// the gz_header field sizes, and various-sized chunks for input/output. This
|
|
// would have found CVE-2022-37434 which was a heap buffer read overflow when
|
|
// filling in gz_header's extra field.
|
|
|
|
gz_header header;
|
|
header.extra_max = fdp.ConsumeIntegralInRange(0, 100000);
|
|
header.name_max = fdp.ConsumeIntegralInRange(0, 100000);
|
|
header.comm_max = fdp.ConsumeIntegralInRange(0, 100000);
|
|
|
|
auto extra_buf = std::make_unique<uint8_t[]>(header.extra_max);
|
|
auto name_buf = std::make_unique<uint8_t[]>(header.name_max);
|
|
auto comment_buf = std::make_unique<uint8_t[]>(header.comm_max);
|
|
|
|
header.extra = extra_buf.get();
|
|
header.name = name_buf.get();
|
|
header.comment = comment_buf.get();
|
|
|
|
size_t in_chunk_size = fdp.ConsumeIntegralInRange(1, 4097);
|
|
size_t out_chunk_size = fdp.ConsumeIntegralInRange(1, 4097);
|
|
std::vector<uint8_t> remaining_data = fdp.ConsumeRemainingBytes<uint8_t>();
|
|
|
|
chunked_inflate(&header, remaining_data.data(), remaining_data.size(),
|
|
in_chunk_size, out_chunk_size);
|
|
|
|
return 0;
|
|
}
|