1 // Copyright 2022 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 <memory>
7
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12
13 #include <fuzzer/FuzzedDataProvider.h>
14
15 #include "zlib.h"
16
17 // Fuzzer builds often have NDEBUG set, so roll our own assert macro.
18 #define ASSERT(cond) \
19 do { \
20 if (!(cond)) { \
21 fprintf(stderr, "%s:%d Assert failed: %s\n", __FILE__, __LINE__, #cond); \
22 exit(1); \
23 } \
24 } while (0)
25
chunked_inflate(gz_header * header,uint8_t * data,size_t size,size_t in_chunk_size,size_t out_chunk_size)26 static void chunked_inflate(gz_header* header,
27 uint8_t* data,
28 size_t size,
29 size_t in_chunk_size,
30 size_t out_chunk_size) {
31 z_stream stream;
32 stream.next_in = data;
33 stream.avail_in = 0;
34 stream.zalloc = Z_NULL;
35 stream.zfree = Z_NULL;
36
37 static const int kDefaultWindowBits = MAX_WBITS;
38 static const int kGzipOrZlibHeader = 32;
39 ASSERT(inflateInit2(&stream, kDefaultWindowBits + kGzipOrZlibHeader) == Z_OK);
40 ASSERT(inflateGetHeader(&stream, header) == Z_OK);
41
42 auto out_buffer = std::make_unique<uint8_t[]>(out_chunk_size);
43 while (true) {
44 stream.next_in = &data[stream.total_in];
45 stream.avail_in =
46 std::min(in_chunk_size, size - static_cast<size_t>(stream.total_in));
47 stream.next_out = out_buffer.get();
48 stream.avail_out = out_chunk_size;
49
50 if (inflate(&stream, stream.avail_in == 0 ? Z_SYNC_FLUSH : Z_NO_FLUSH) !=
51 Z_OK) {
52 break;
53 }
54 }
55
56 inflateEnd(&stream);
57 }
58
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)59 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
60 if (size > 250 * 1024) {
61 // Cap the input size so the fuzzer doesn't time out. For example,
62 // crbug.com/1362206 saw timeouts with 450 KB input, so use a limit that's
63 // well below that but still large enough to hit most code.
64 return 0;
65 }
66
67 FuzzedDataProvider fdp(data, size);
68
69 // Fuzz zlib's inflate() with inflateGetHeader() enabled, various sizes for
70 // the gz_header field sizes, and various-sized chunks for input/output. This
71 // would have found CVE-2022-37434 which was a heap buffer read overflow when
72 // filling in gz_header's extra field.
73
74 gz_header header;
75 header.extra_max = fdp.ConsumeIntegralInRange(0, 100000);
76 header.name_max = fdp.ConsumeIntegralInRange(0, 100000);
77 header.comm_max = fdp.ConsumeIntegralInRange(0, 100000);
78
79 auto extra_buf = std::make_unique<uint8_t[]>(header.extra_max);
80 auto name_buf = std::make_unique<uint8_t[]>(header.name_max);
81 auto comment_buf = std::make_unique<uint8_t[]>(header.comm_max);
82
83 header.extra = extra_buf.get();
84 header.name = name_buf.get();
85 header.comment = comment_buf.get();
86
87 size_t in_chunk_size = fdp.ConsumeIntegralInRange(1, 4097);
88 size_t out_chunk_size = fdp.ConsumeIntegralInRange(1, 4097);
89 std::vector<uint8_t> remaining_data = fdp.ConsumeRemainingBytes<uint8_t>();
90
91 chunked_inflate(&header, remaining_data.data(), remaining_data.size(),
92 in_chunk_size, out_chunk_size);
93
94 return 0;
95 }
96