summaryrefslogtreecommitdiff
path: root/contrib/tests/fuzzers/deflate_fuzzer.cc
blob: 6f3e45e1aef7d6213c2d6589c945e456d6ce7955 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <fuzzer/FuzzedDataProvider.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>

#include "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)

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  FuzzedDataProvider fdp(data, size);
  int level = fdp.PickValueInArray({-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
  int windowBits = fdp.PickValueInArray({9, 10, 11, 12, 13, 14, 15});
  int memLevel = fdp.PickValueInArray({1, 2, 3, 4, 5, 6, 7, 8, 9});
  int strategy = fdp.PickValueInArray(
      {Z_DEFAULT_STRATEGY, Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED});

   if (fdp.ConsumeBool()) {
    // Gzip wrapper.
    windowBits += 16;
  } else if (fdp.ConsumeBool()) {
    // Raw deflate.
    windowBits *= -1;
  } else {
    // Default: zlib wrapper.
  }

  std::vector<uint8_t> src;
  std::vector<uint8_t> compressed;
  static const int kMinChunk = 1;
  static const int kMaxChunk = 512 * 1024;

  z_stream stream;
  stream.zalloc = Z_NULL;
  stream.zfree = Z_NULL;
  int ret =
      deflateInit2(&stream, level, Z_DEFLATED, windowBits, memLevel, strategy);
  ASSERT(ret == Z_OK);

  // Stream with random-sized input and output buffers.
  while (fdp.ConsumeBool()) {
    std::vector<uint8_t> src_chunk = fdp.ConsumeBytes<uint8_t>(
        fdp.ConsumeIntegralInRange(kMinChunk, kMaxChunk));
    std::vector<uint8_t> out_chunk(
        fdp.ConsumeIntegralInRange(kMinChunk, kMaxChunk));
    stream.next_in = src_chunk.data();
    stream.avail_in = src_chunk.size();
    stream.next_out = out_chunk.data();
    stream.avail_out = out_chunk.size();
    ret = deflate(&stream, Z_NO_FLUSH);
    ASSERT(ret == Z_OK || ret == Z_BUF_ERROR);

    src.insert(src.end(), src_chunk.begin(), src_chunk.end() - stream.avail_in);
    compressed.insert(compressed.end(), out_chunk.begin(),
                      out_chunk.end() - stream.avail_out);
  }
  // Finish up.
  while (true) {
    std::vector<uint8_t> out_chunk(
        fdp.ConsumeIntegralInRange(kMinChunk, kMaxChunk));
    stream.next_in = Z_NULL;
    stream.avail_in = 0;
    stream.next_out = out_chunk.data();
    stream.avail_out = out_chunk.size();
    ret = deflate(&stream, Z_FINISH);
    compressed.insert(compressed.end(), out_chunk.begin(),
                      out_chunk.end() - stream.avail_out);
    if (ret == Z_STREAM_END) {
      break;
    }
    ASSERT(ret == Z_OK || Z_BUF_ERROR);
  }

  // Check that the bound was correct.
  // size_t deflate_bound = deflateBound(&stream, src.size());
  // TODO(crbug.com/40270738): This does not always hold.
  // ASSERT(compressed.size() <= deflate_bound);

  deflateEnd(&stream);


  // Verify that the data decompresses correctly.
  ret = inflateInit2(&stream, windowBits);
  ASSERT(ret == Z_OK);
  // Make room for at least one byte so it's never empty.
  std::vector<uint8_t> decompressed(src.size() + 1);
  stream.next_in = compressed.data();
  stream.avail_in = compressed.size();
  stream.next_out = decompressed.data();
  stream.avail_out = decompressed.size();
  ret = inflate(&stream, Z_FINISH);
  ASSERT(ret == Z_STREAM_END);
  decompressed.resize(decompressed.size() - stream.avail_out);
  inflateEnd(&stream);

  ASSERT(decompressed == src);

  return 0;
}