xref: /aosp_15_r20/external/zlib/contrib/tests/fuzzers/inflate_with_header_fuzzer.cc (revision 86ee64e75fa5f8bce2c8c356138035642429cd05)
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