Commit d38f7e4429 for aom
commit d38f7e4429ef00309e016f14ad1b1cf337ec8b71
Author: Jerome Jiang <jianj@google.com>
Date: Mon Oct 26 17:29:59 2026 -0400
Fix timeout in av1_enc_fuzzer
av1_enc_fuzzer previously encoded 50 to 100 frames per input and filled
every pixel of every plane with uncorrelated random noise. At slow
presets (such as AOM_USAGE_GOOD_QUALITY with cpu_used = 0) and larger
resolutions, full-frame white noise prevents RD early termination and
causes 50-frame runs to exceed the 60-second OSS-Fuzz timeout.
Reduce kNumFramesForCpuUsed to 4-10 frames, bound num_frames by a
per-cpu_used 64x64 superblock budget (kMaxSuperblocksForCpuUsed, keeping
at least 3 frames so initial, switch_frame, and restore_frame are always
exercised while preserving the full 1..1024 dimension range and all
cpu_used values 0..11), and fill each plane with 16-row horizontal bands
plus two 16x16 textured patches (one random noise, one gradient) so both
flat/skip and high-variance RD paths are exercised within the fuzzer
time budget. Also preserve active frame dimensions across frames between
switch_frame and restore_frame.
Bug: 558505651
TAG=agy
CONV=f2ed7983-e7b9-43ce-bdab-6286b21e3af8
Change-Id: I6b326366f2e5aef411e213324a1c077f14908a60
diff --git a/examples/av1_enc_fuzzer.cc b/examples/av1_enc_fuzzer.cc
index 6868190fbc..4e183dd737 100644
--- a/examples/av1_enc_fuzzer.cc
+++ b/examples/av1_enc_fuzzer.cc
@@ -42,18 +42,38 @@ enum ModeFlag {
// fuzzer's execution time budget (libFuzzer defaults to one second per
// input). This can be refined based on measured fuzzer performance.
constexpr unsigned int kNumFramesForCpuUsed[12] = {
- 50, // cpu_used 0
- 50, // cpu_used 1
- 50, // cpu_used 2
- 60, // cpu_used 3
- 60, // cpu_used 4
- 70, // cpu_used 5
- 70, // cpu_used 6
- 80, // cpu_used 7
- 80, // cpu_used 8
- 90, // cpu_used 9
- 90, // cpu_used 10
- 100 // cpu_used 11
+ 4, // cpu_used 0
+ 4, // cpu_used 1
+ 4, // cpu_used 2
+ 5, // cpu_used 3
+ 5, // cpu_used 4
+ 6, // cpu_used 5
+ 6, // cpu_used 6
+ 7, // cpu_used 7
+ 8, // cpu_used 8
+ 9, // cpu_used 9
+ 9, // cpu_used 10
+ 10 // cpu_used 11
+};
+
+// Maximum number of 64x64 superblocks to encode across all frames for a
+// given cpu_used value (one 1024x1024 frame is 256 superblocks). This caps
+// worst-case execution time on large frames at slow speed presets while
+// still allowing full 1024x1024 resolution and at least 3 frames (initial,
+// switch_frame, and restore_frame) at every cpu_used.
+constexpr unsigned int kMaxSuperblocksForCpuUsed[12] = {
+ 768, // cpu_used 0
+ 768, // cpu_used 1
+ 768, // cpu_used 2
+ 1024, // cpu_used 3
+ 1024, // cpu_used 4
+ 1280, // cpu_used 5
+ 1280, // cpu_used 6
+ 1536, // cpu_used 7
+ 1792, // cpu_used 8
+ 2048, // cpu_used 9
+ 2048, // cpu_used 10
+ 2304 // cpu_used 11
};
struct FuzzReader {
@@ -143,10 +163,34 @@ uint32_t Rand(uint32_t *state) {
void FillPlane(uint8_t *plane, int stride, unsigned int width,
unsigned int height, uint32_t *state) {
+ uint8_t background = 0;
for (unsigned int row = 0; row < height; ++row) {
- for (unsigned int col = 0; col < width; ++col) {
- plane[static_cast<size_t>(row) * stride + col] =
- static_cast<uint8_t>(Rand(state) >> 24);
+ if ((row & 15u) == 0) {
+ background = static_cast<uint8_t>(Rand(state) >> 24);
+ }
+ memset(plane + static_cast<size_t>(row) * stride, background, width);
+ }
+ // Stamp two small 16x16 textured patches (one random noise, one gradient)
+ // at pseudo-random locations so that both flat/skip and high-variance RD
+ // paths are exercised without filling large frames entirely with white
+ // noise.
+ constexpr unsigned int kPatchSize = 16;
+ for (unsigned int pass = 0; pass < 2; ++pass) {
+ const unsigned int patch_x = (Rand(state) >> 16) % width;
+ const unsigned int patch_y = (Rand(state) >> 16) % height;
+ const unsigned int patch_width =
+ (width - patch_x < kPatchSize) ? (width - patch_x) : kPatchSize;
+ const unsigned int patch_height =
+ (height - patch_y < kPatchSize) ? (height - patch_y) : kPatchSize;
+ for (unsigned int row = 0; row < patch_height; ++row) {
+ uint8_t *dst_row =
+ plane + static_cast<size_t>(patch_y + row) * stride + patch_x;
+ for (unsigned int col = 0; col < patch_width; ++col) {
+ dst_row[col] =
+ (pass == 0)
+ ? static_cast<uint8_t>(Rand(state) >> 24)
+ : static_cast<uint8_t>(background + row * 8u + col * 8u);
+ }
}
}
}
@@ -269,7 +313,24 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
const int cpu_used = PickCpuUsed(usage, cpu_used_raw);
ApplyControls(&codec, usage, mode_flags, cpu_used_raw, ctl0, ctl1);
- const unsigned int num_frames = kNumFramesForCpuUsed[cpu_used];
+ const unsigned int superblocks0 =
+ ((width0 + 63u) / 64u) * ((height0 + 63u) / 64u);
+ const unsigned int superblocks1 =
+ (mode_flags & kUseSecondResolution)
+ ? (((width1 + 63u) / 64u) * ((height1 + 63u) / 64u))
+ : superblocks0;
+ const unsigned int max_frame_superblocks =
+ (superblocks0 > superblocks1) ? superblocks0 : superblocks1;
+ const unsigned int max_frames_by_superblocks =
+ kMaxSuperblocksForCpuUsed[cpu_used] / max_frame_superblocks;
+ unsigned int num_frames = kNumFramesForCpuUsed[cpu_used];
+ if (num_frames > max_frames_by_superblocks) {
+ num_frames =
+ (max_frames_by_superblocks < 3u) ? 3u : max_frames_by_superblocks;
+ }
+
+ unsigned int width = width0;
+ unsigned int height = height0;
bool config_failed = false;
for (unsigned int frame = 0; frame < num_frames; ++frame) {
aom_enc_frame_flags_t flags = 0;
@@ -277,16 +338,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
flags |= AOM_EFLAG_FORCE_KF;
}
- unsigned int width = width0;
- unsigned int height = height0;
if (mode_flags & kUseSecondResolution) {
// Vary the switch frame based on the input so different encodings
// exercise the resolution change at different points in the stream.
- // num_frames >= 50 for every cpu_used, so switch_frame and
- // restore_frame stay within [1, num_frames - 1].
- const unsigned int switch_frame = 1u + (ctl0 % (num_frames / 4u));
- const unsigned int restore_frame =
- switch_frame + 1u + (ctl1 % (num_frames / 4u));
+ // num_frames >= 3 for every input, so switch_frame and restore_frame
+ // stay within [1, num_frames - 1].
+ const unsigned int window =
+ (num_frames > 4u) ? ((num_frames - 2u) / 2u) : 1u;
+ const unsigned int switch_frame = 1u + (ctl0 % window);
+ const unsigned int restore_frame = switch_frame + 1u + (ctl1 % window);
if (frame == switch_frame) {
cfg.g_w = width1;
cfg.g_h = height1;