Coverage Report

Created: 2026-07-16 07:20

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
73.3k
#define FUZZ_HDR_SZ 32
79
80
24.4k
#define VPXC_INTERFACE(name) VPXC_INTERFACE_(name)
81
24.4k
#define VPXC_INTERFACE_(name) vpx_codec_##name##_cx()
82
83
0
extern "C" void usage_exit(void) { exit(EXIT_FAILURE); }
84
85
2.25M
static int vpx_img_plane_width(const vpx_image_t *img, int plane) {
86
2.25M
  if (plane > 0 && img->x_chroma_shift > 0)
87
1.50M
    return (img->d_w + 1) >> img->x_chroma_shift;
88
752k
  else
89
752k
    return img->d_w;
90
2.25M
}
91
92
2.25M
static int vpx_img_plane_height(const vpx_image_t *img, int plane) {
93
2.25M
  if (plane > 0 && img->y_chroma_shift > 0)
94
1.50M
    return (img->d_h + 1) >> img->y_chroma_shift;
95
752k
  else
96
752k
    return img->d_h;
97
2.25M
}
98
99
static int fuzz_vpx_img_read(vpx_image_t *img, const uint8_t *data,
100
776k
                             size_t size) {
101
776k
  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
776k
  assert(img->bit_depth == 8);
105
776k
  const size_t bytespp = (img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
106
107
776k
  if (size == 0) return 0;
108
752k
  size_t used = 0;
109
3.01M
  for (plane = 0; plane < 3; ++plane) {
110
2.25M
    unsigned char *buf = img->planes[plane];
111
2.25M
    const int stride = img->stride[plane];
112
2.25M
    int w = vpx_img_plane_width(img, plane);
113
2.25M
    const int h = vpx_img_plane_height(img, plane);
114
2.25M
    int y;
115
116
    // Assuming that for nv12 we read all chroma data at once
117
2.25M
    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.25M
    if (img->fmt == VPX_IMG_FMT_NV12 && plane == 1) w = w * 2;
121
122
31.7M
    for (y = 0; y < h; ++y) {
123
29.5M
      size_t nb = bytespp * w;
124
29.5M
      if (nb > size - used) {
125
852k
        nb = size - used;
126
852k
      }
127
29.5M
      memcpy(buf, data, nb);
128
29.5M
      memset(buf + nb, 0, bytespp * w - nb);
129
29.5M
      buf += stride;
130
29.5M
      data += nb;
131
29.5M
      used += nb;
132
29.5M
    }
133
2.25M
  }
134
135
752k
  return used;
136
776k
}
137
138
static int encode_frame(vpx_codec_ctx_t *codec, vpx_image_t *img,
139
                        int frame_index, int flags, FILE *out,
140
792k
                        vpx_enc_deadline_t quality) {
141
792k
  int got_pkts = 0;
142
792k
  vpx_codec_iter_t iter = nullptr;
143
792k
  const vpx_codec_cx_pkt_t *pkt = nullptr;
144
792k
  const vpx_codec_err_t res =
145
792k
      vpx_codec_encode(codec, img, frame_index, 1, flags, quality);
146
792k
  if (res != VPX_CODEC_OK) return 0;
147
148
1.53M
  while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != nullptr) {
149
747k
    got_pkts = 1;
150
151
747k
    if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
152
747k
      if (fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, out) !=
153
747k
          pkt->data.frame.sz)
154
0
        return 0;
155
747k
    }
156
747k
  }
157
158
790k
  return got_pkts;
159
790k
}
160
161
24.4k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
162
24.4k
  if (size <= FUZZ_HDR_SZ) {
163
33
    return 0;
164
33
  }
165
24.4k
  nalloc_init(nullptr);
166
167
24.4k
  int keyframe_interval = 0;
168
24.4k
  int frame_count = 0;
169
24.4k
  vpx_codec_ctx_t codec;
170
24.4k
  vpx_image_t raw;
171
24.4k
  vpx_codec_enc_cfg_t cfg;
172
24.4k
  vpx_enc_deadline_t quality = VPX_DL_GOOD_QUALITY;
173
174
24.4k
  if ((data[0] & 0x80) != 0) {
175
6.16k
    keyframe_interval = 8;
176
6.16k
  }
177
24.4k
  if ((data[0] & 0x40) != 0) {
178
8.70k
    quality = VPX_DL_REALTIME;
179
15.7k
  } else if ((data[0] & 0x20) != 0) {
180
7.50k
    quality = VPX_DL_BEST_QUALITY;
181
7.50k
  }
182
24.4k
  const int max_frames = (quality == VPX_DL_BEST_QUALITY) ? 150 : 300;
183
184
24.4k
  if (vpx_codec_enc_config_default(VPXC_INTERFACE(ENCODER), &cfg, 0)) abort();
185
24.4k
  FILE *out = fopen("/dev/null", "wb");
186
187
24.4k
  switch (data[0] & 0x1F) {
188
3.60k
    case 0:
189
3.60k
      cfg.g_w = 64;
190
3.60k
      cfg.g_h = 1;
191
3.60k
      break;
192
3.96k
    case 1:
193
3.96k
      cfg.g_w = 1;
194
3.96k
      cfg.g_h = 48;
195
3.96k
      break;
196
3.12k
    case 2:
197
3.12k
      cfg.g_w = 1;
198
3.12k
      cfg.g_h = 1;
199
3.12k
      break;
200
4.94k
    case 3:
201
4.94k
      cfg.g_w = 4;
202
4.94k
      cfg.g_h = 4;
203
4.94k
      break;
204
869
    case 4:
205
869
      cfg.g_w = 16;
206
869
      cfg.g_h = 16;
207
869
      break;
208
7.93k
    default:
209
7.93k
      cfg.g_w = 64;
210
7.93k
      cfg.g_h = 48;
211
7.93k
      break;
212
24.4k
  }
213
24.4k
  cfg.g_timebase.num = 1;
214
24.4k
  cfg.g_timebase.den = 30;  // fps
215
24.4k
  cfg.rc_target_bitrate = 200;
216
24.4k
  cfg.g_error_resilient = 1;
217
218
24.4k
  if (vpx_codec_enc_init(&codec, VPXC_INTERFACE(ENCODER), &cfg, 0)) {
219
0
    return 0;
220
0
  }
221
222
24.4k
  if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, cfg.g_w, cfg.g_h, 1)) {
223
0
    goto fail;
224
0
  }
225
226
24.4k
  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
24.4k
  data += FUZZ_HDR_SZ;
232
24.4k
  size -= FUZZ_HDR_SZ;
233
234
  // Encode frames.
235
776k
  for (int i = 0; i < max_frames; ++i) {
236
776k
    int flags = 0;
237
776k
    size_t size_read = fuzz_vpx_img_read(&raw, data, size);
238
776k
    if (size_read == 0) break;
239
752k
    data += size_read;
240
752k
    size -= size_read;
241
752k
    if (keyframe_interval > 0 && frame_count % keyframe_interval == 0)
242
29.0k
      flags |= VPX_EFLAG_FORCE_KF;
243
752k
    encode_frame(&codec, &raw, frame_count++, flags, out, quality);
244
752k
  }
245
246
  // Flush encoder.
247
39.6k
  while (encode_frame(&codec, nullptr, -1, 0, out, quality)) {
248
15.2k
  }
249
250
24.4k
fail:
251
24.4k
  nalloc_end();
252
24.4k
  vpx_img_free(&raw);
253
24.4k
  vpx_codec_destroy(&codec);
254
24.4k
  fclose(out);
255
24.4k
  return 0;
256
24.4k
}