Coverage Report

Created: 2026-07-30 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libvpx/examples/vpx_enc_fuzzer.cc
Line
Count
Source
1
/*
2
 *  Copyright (c) 2025 The WebM project authors. All Rights Reserved.
3
 *
4
 *  Use of this source code is governed by a BSD-style license
5
 *  that can be found in the LICENSE file in the root of the source
6
 *  tree. An additional intellectual property rights grant can be found
7
 *  in the file PATENTS.  All contributing project authors may
8
 *  be found in the AUTHORS file in the root of the source tree.
9
 */
10
11
/*
12
 * Fuzzer for libvpx encoders
13
 * ==========================
14
 * Requirements
15
 * --------------
16
 * Requires Clang 6.0 or above as -fsanitize=fuzzer is used as a linker
17
 * option.
18
19
 * Steps to build
20
 * --------------
21
 * Clone libvpx repository
22
   $git clone https://chromium.googlesource.com/webm/libvpx
23
24
 * Create a directory in parallel to libvpx and change directory
25
   $mkdir vpx_enc_fuzzer
26
   $cd vpx_enc_fuzzer/
27
28
 * Enable sanitizers (Supported: address integer memory thread undefined)
29
   $source ../libvpx/tools/set_analyzer_env.sh address
30
31
 * Configure libvpx.
32
 * Note --size-limit and VPX_MAX_ALLOCABLE_MEMORY are defined to avoid
33
 * Out of memory errors when running generated fuzzer binary
34
   $../libvpx/configure --disable-unit-tests --size-limit=12288x12288 \
35
   --extra-cflags="-fsanitize=fuzzer-no-link \
36
   -DVPX_MAX_ALLOCABLE_MEMORY=1073741824" \
37
   --disable-webm-io --enable-debug --enable-vp8-encoder \
38
   --enable-vp9-encoder --disable-examples
39
40
 * Build libvpx
41
   $make -j32
42
43
 * Build vp9 fuzzer
44
   $ $CXX $CXXFLAGS -std=gnu++17 -Wall -Wextra -Wimplicit-fallthrough \
45
   -DENCODER=vp9 -fsanitize=fuzzer -I../libvpx -I. -Wl,--start-group \
46
   ../libvpx/examples/vpx_enc_fuzzer.cc -o ./vpx_enc_fuzzer_vp9 \
47
   ./libvpx.a -Wl,--end-group
48
49
 * ENCODER should be defined as vp9 or vp8 to enable vp9/vp8
50
 *
51
 * create a corpus directory and copy some ivf files there.
52
 * Based on which codec (vp8/vp9) is being tested, it is recommended to
53
 * have corresponding ivf files in corpus directory
54
 * Empty corpus directory also is acceptable, though not recommended
55
   $mkdir CORPUS && cp some-files CORPUS
56
57
 * Run fuzzing:
58
   $./vpx_enc_fuzzer_vp9 CORPUS
59
60
 * References:
61
 * http://llvm.org/docs/LibFuzzer.html
62
 * https://github.com/google/oss-fuzz
63
 */
64
65
#include <assert.h>
66
#include <stddef.h>
67
#include <stdint.h>
68
#include <stdio.h>
69
#include <stdlib.h>
70
#include <string.h>
71
72
#include "vpx/vp8cx.h"
73
#include "vpx/vpx_encoder.h"
74
#include "vpx_ports/mem_ops.h"
75
#include "third_party/nalloc/nalloc.h"
76
77
// fuzz header to have config options, before raw image data
78
71.2k
#define FUZZ_HDR_SZ 32
79
80
23.7k
#define VPXC_INTERFACE(name) VPXC_INTERFACE_(name)
81
23.7k
#define VPXC_INTERFACE_(name) vpx_codec_##name##_cx()
82
83
0
extern "C" void usage_exit(void) { exit(EXIT_FAILURE); }
84
85
2.20M
static int vpx_img_plane_width(const vpx_image_t *img, int plane) {
86
2.20M
  if (plane > 0 && img->x_chroma_shift > 0)
87
1.46M
    return (img->d_w + 1) >> img->x_chroma_shift;
88
733k
  else
89
733k
    return img->d_w;
90
2.20M
}
91
92
2.20M
static int vpx_img_plane_height(const vpx_image_t *img, int plane) {
93
2.20M
  if (plane > 0 && img->y_chroma_shift > 0)
94
1.46M
    return (img->d_h + 1) >> img->y_chroma_shift;
95
733k
  else
96
733k
    return img->d_h;
97
2.20M
}
98
99
static int fuzz_vpx_img_read(vpx_image_t *img, const uint8_t *data,
100
757k
                             size_t size) {
101
757k
  int plane;
102
  // TODO: wtc - Need to clamp the sample values so that they are in range
103
  // For example, if the bit depth is 10, the sample values must be <= 1023.
104
757k
  assert(img->bit_depth == 8);
105
757k
  const size_t bytespp = (img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
106
107
757k
  if (size == 0) return 0;
108
733k
  size_t used = 0;
109
2.93M
  for (plane = 0; plane < 3; ++plane) {
110
2.20M
    unsigned char *buf = img->planes[plane];
111
2.20M
    const int stride = img->stride[plane];
112
2.20M
    int w = vpx_img_plane_width(img, plane);
113
2.20M
    const int h = vpx_img_plane_height(img, plane);
114
2.20M
    int y;
115
116
    // Assuming that for nv12 we read all chroma data at once
117
2.20M
    if (img->fmt == VPX_IMG_FMT_NV12 && plane > 1) break;
118
    // NV12 UV plane is interleaved, so it has twice the width of a subsampled
119
    // plane.
120
2.20M
    if (img->fmt == VPX_IMG_FMT_NV12 && plane == 1) w = w * 2;
121
122
31.5M
    for (y = 0; y < h; ++y) {
123
29.3M
      size_t nb = bytespp * w;
124
29.3M
      if (nb > size - used) {
125
842k
        nb = size - used;
126
842k
      }
127
29.3M
      memcpy(buf, data, nb);
128
29.3M
      memset(buf + nb, 0, bytespp * w - nb);
129
29.3M
      buf += stride;
130
29.3M
      data += nb;
131
29.3M
      used += nb;
132
29.3M
    }
133
2.20M
  }
134
135
733k
  return used;
136
757k
}
137
138
static int encode_frame(vpx_codec_ctx_t *codec, vpx_image_t *img,
139
                        int frame_index, int flags, FILE *out,
140
773k
                        vpx_enc_deadline_t quality) {
141
773k
  int got_pkts = 0;
142
773k
  vpx_codec_iter_t iter = nullptr;
143
773k
  const vpx_codec_cx_pkt_t *pkt = nullptr;
144
773k
  const vpx_codec_err_t res =
145
773k
      vpx_codec_encode(codec, img, frame_index, 1, flags, quality);
146
773k
  if (res != VPX_CODEC_OK) return 0;
147
148
1.50M
  while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != nullptr) {
149
728k
    got_pkts = 1;
150
151
728k
    if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
152
728k
      if (fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, out) !=
153
728k
          pkt->data.frame.sz)
154
0
        return 0;
155
728k
    }
156
728k
  }
157
158
771k
  return got_pkts;
159
771k
}
160
161
23.7k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
162
23.7k
  if (size <= FUZZ_HDR_SZ) {
163
33
    return 0;
164
33
  }
165
23.7k
  nalloc_init(nullptr);
166
167
23.7k
  int keyframe_interval = 0;
168
23.7k
  int frame_count = 0;
169
23.7k
  vpx_codec_ctx_t codec;
170
23.7k
  vpx_image_t raw;
171
23.7k
  vpx_codec_enc_cfg_t cfg;
172
23.7k
  vpx_enc_deadline_t quality = VPX_DL_GOOD_QUALITY;
173
174
23.7k
  if ((data[0] & 0x80) != 0) {
175
6.00k
    keyframe_interval = 8;
176
6.00k
  }
177
23.7k
  if ((data[0] & 0x40) != 0) {
178
8.46k
    quality = VPX_DL_REALTIME;
179
15.2k
  } else if ((data[0] & 0x20) != 0) {
180
7.20k
    quality = VPX_DL_BEST_QUALITY;
181
7.20k
  }
182
23.7k
  const int max_frames = (quality == VPX_DL_BEST_QUALITY) ? 150 : 300;
183
184
23.7k
  if (vpx_codec_enc_config_default(VPXC_INTERFACE(ENCODER), &cfg, 0)) abort();
185
23.7k
  FILE *out = fopen("/dev/null", "wb");
186
187
23.7k
  switch (data[0] & 0x1F) {
188
3.41k
    case 0:
189
3.41k
      cfg.g_w = 64;
190
3.41k
      cfg.g_h = 1;
191
3.41k
      break;
192
3.95k
    case 1:
193
3.95k
      cfg.g_w = 1;
194
3.95k
      cfg.g_h = 48;
195
3.95k
      break;
196
2.96k
    case 2:
197
2.96k
      cfg.g_w = 1;
198
2.96k
      cfg.g_h = 1;
199
2.96k
      break;
200
4.64k
    case 3:
201
4.64k
      cfg.g_w = 4;
202
4.64k
      cfg.g_h = 4;
203
4.64k
      break;
204
959
    case 4:
205
959
      cfg.g_w = 16;
206
959
      cfg.g_h = 16;
207
959
      break;
208
7.79k
    default:
209
7.79k
      cfg.g_w = 64;
210
7.79k
      cfg.g_h = 48;
211
7.79k
      break;
212
23.7k
  }
213
23.7k
  cfg.g_timebase.num = 1;
214
23.7k
  cfg.g_timebase.den = 30;  // fps
215
23.7k
  cfg.rc_target_bitrate = 200;
216
23.7k
  cfg.g_error_resilient = 1;
217
218
23.7k
  if (vpx_codec_enc_init(&codec, VPXC_INTERFACE(ENCODER), &cfg, 0)) {
219
0
    return 0;
220
0
  }
221
222
23.7k
  if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h, 1)) {
223
0
    goto fail;
224
0
  }
225
226
23.7k
  nalloc_start(data, size);
227
  // We may want to add more config options (for more complex encoders as seen
228
  // in the examples) in the future while still maintaining the same format (so
229
  // that generated corpus is still valid). So we reserve FUZZ_HDR_SZ=32 bytes
230
  // for this even if we just use one byte so far.
231
23.7k
  data += FUZZ_HDR_SZ;
232
23.7k
  size -= FUZZ_HDR_SZ;
233
234
  // Encode frames.
235
757k
  for (int i = 0; i < max_frames; ++i) {
236
757k
    int flags = 0;
237
757k
    size_t size_read = fuzz_vpx_img_read(&raw, data, size);
238
757k
    if (size_read == 0) break;
239
733k
    data += size_read;
240
733k
    size -= size_read;
241
733k
    if (keyframe_interval > 0 && frame_count % keyframe_interval == 0)
242
28.3k
      flags |= VPX_EFLAG_FORCE_KF;
243
733k
    encode_frame(&codec, &raw, frame_count++, flags, out, quality);
244
733k
  }
245
246
  // Flush encoder.
247
39.2k
  while (encode_frame(&codec, nullptr, -1, 0, out, quality)) {
248
15.5k
  }
249
250
23.7k
fail:
251
23.7k
  nalloc_end();
252
23.7k
  vpx_img_free(&raw);
253
23.7k
  vpx_codec_destroy(&codec);
254
23.7k
  fclose(out);
255
23.7k
  return 0;
256
23.7k
}