Coverage Report

Created: 2025-06-12 06:14

/src/png_proto_fuzzer_example.cc
Line
Count
Source (jump to first uncovered line)
1
// Example fuzzer for PNG using protos.
2
#include <string>
3
#include <sstream>
4
#include <fstream>
5
#include <zlib.h>  // for crc32
6
7
#include "libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h"
8
#include "png_fuzz_proto.pb.h"
9
10
245k
static void WriteInt(std::stringstream &out, uint32_t x) {
11
245k
  x = __builtin_bswap32(x);
12
245k
  out.write((char *)&x, sizeof(x));
13
245k
}
14
15
35.5k
static void WriteByte(std::stringstream &out, uint8_t x) {
16
35.5k
  out.write((char *)&x, sizeof(x));
17
35.5k
}
18
19
18.2k
static std::string Compress(const std::string &s) {
20
18.2k
  std::string out(s.size() + 100, '\0');
21
18.2k
  size_t out_len = out.size();
22
18.2k
  compress((uint8_t *)&out[0], &out_len, (uint8_t *)s.data(), s.size());
23
18.2k
  out.resize(out_len);
24
18.2k
  return out;
25
18.2k
}
26
27
// Chunk is written as:
28
//  * 4-byte length
29
//  * 4-byte type
30
//  * the data itself
31
//  * 4-byte crc (of type and data)
32
static void WriteChunk(std::stringstream &out, const char *type,
33
95.1k
                       const std::string &chunk, bool compress = false) {
34
95.1k
  std::string compressed;
35
95.1k
  const std::string *s = &chunk;
36
95.1k
  if (compress) {
37
9.69k
    compressed = Compress(chunk);
38
9.69k
    s = &compressed;
39
9.69k
  }
40
95.1k
  uint32_t len = s->size();
41
95.1k
  uint32_t crc = crc32(crc32(0, (const unsigned char *)type, 4),
42
95.1k
                       (const unsigned char *)s->data(), s->size());
43
95.1k
  WriteInt(out, len);
44
95.1k
  out.write(type, 4);
45
95.1k
  out.write(s->data(), s->size());
46
95.1k
  WriteInt(out, crc);
47
95.1k
}
48
49
18.3k
std::string ProtoToPng(const PngProto &png_proto) {
50
18.3k
  std::stringstream all;
51
18.3k
  const unsigned char header[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
52
18.3k
  all.write((const char*)header, sizeof(header));
53
18.3k
  std::stringstream ihdr_str;
54
18.3k
  auto &ihdr = png_proto.ihdr();
55
  // Avoid large images.
56
  // They may have interesting bugs, but OOMs are going to kill fuzzing.
57
18.3k
  uint32_t w = std::min(ihdr.width(), 4096U);
58
18.3k
  uint32_t h = std::min(ihdr.height(), 4096U);
59
18.3k
  WriteInt(ihdr_str, w);
60
18.3k
  WriteInt(ihdr_str, h);
61
18.3k
  WriteInt(ihdr_str, ihdr.other1());
62
18.3k
  WriteByte(ihdr_str, ihdr.other2());
63
18.3k
  WriteChunk(all, "IHDR", ihdr_str.str());
64
65
79.5k
  for (size_t i = 0, n = png_proto.chunks_size(); i < n; i++) {
66
61.1k
    auto &chunk = png_proto.chunks(i);
67
61.1k
    if (chunk.has_plte()) {
68
4.99k
      WriteChunk(all, "PLTE", chunk.plte().data());
69
56.1k
    } else if (chunk.has_idat()) {
70
9.69k
      WriteChunk(all, "IDAT", chunk.idat().data(), true);
71
46.4k
    } else if (chunk.has_iccp()) {
72
8.58k
      std::stringstream iccp_str;
73
8.58k
      iccp_str << "xyz";  // don't fuzz iCCP name field.
74
8.58k
      WriteByte(iccp_str, 0);
75
8.58k
      WriteByte(iccp_str, 0);
76
8.58k
      auto compressed_data = Compress(chunk.iccp().data());
77
8.58k
      iccp_str.write(compressed_data.data(), compressed_data.size());
78
8.58k
      WriteChunk(all, "iCCP", iccp_str.str());
79
37.8k
    } else if (chunk.has_other_chunk()) {
80
35.7k
      auto &other_chunk = chunk.other_chunk();
81
35.7k
      char type[5] = {0};
82
35.7k
      if (other_chunk.has_known_type()) {
83
30.2k
        static const char * known_chunks[] = {
84
30.2k
            "bKGD", "cHRM", "dSIG", "eXIf", "gAMA", "hIST", "iCCP",
85
30.2k
            "iTXt", "pHYs", "sBIT", "sPLT", "sRGB", "sTER", "tEXt",
86
30.2k
            "tIME", "tRNS", "zTXt", "sCAL", "pCAL", "oFFs",
87
30.2k
        };
88
30.2k
        size_t known_chunks_size =
89
30.2k
            sizeof(known_chunks) / sizeof(known_chunks[0]);
90
30.2k
        size_t chunk_idx = other_chunk.known_type() % known_chunks_size;
91
30.2k
        memcpy(type, known_chunks[chunk_idx], 4);
92
30.2k
      } else if (other_chunk.has_unknown_type()) {
93
4.85k
        uint32_t unknown_type_int = other_chunk.unknown_type();
94
4.85k
        memcpy(type, &unknown_type_int, 4);
95
4.85k
      } else {
96
570
        continue;
97
570
      }
98
35.1k
      type[4] = 0;
99
35.1k
      WriteChunk(all, type, other_chunk.data());
100
35.1k
    }
101
61.1k
  }
102
18.3k
  WriteChunk(all, "IEND", "");
103
104
18.3k
  std::string res = all.str();
105
18.3k
  if (const char *dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) {
106
    // With libFuzzer binary run this to generate a PNG file x.png:
107
    // PROTO_FUZZER_DUMP_PATH=x.png ./a.out proto-input
108
0
    std::ofstream of(dump_path);
109
0
    of.write(res.data(), res.size());
110
0
  }
111
18.3k
  return res;
112
18.3k
}
113
114
// The actual fuzz target that consumes the PNG data.
115
extern "C" int FuzzPNG(const uint8_t* data, size_t size);
116
117
18.3k
DEFINE_PROTO_FUZZER(const PngProto &png_proto) {
118
18.3k
  auto s = ProtoToPng(png_proto);
119
18.3k
  FuzzPNG((const uint8_t*)s.data(), s.size());
120
18.3k
}