Expand the muxer fuzzer.

And fix a few bugs it already found.

Bug: 497882857

Change-Id: I4af63d7e7fece0686dac931d54a5e855353aaa3a
This commit is contained in:
Vincent Rabaud
2026-04-01 11:11:04 +02:00
parent b8814a57f0
commit 0c9546f7ef
4 changed files with 47 additions and 9 deletions

View File

@@ -493,6 +493,7 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
} }
case MKFOURCC('A', 'N', 'M', 'F'): { case MKFOURCC('A', 'N', 'M', 'F'): {
if (anim_chunks == 0) return PARSE_ERROR; // 'ANIM' precedes frames. if (anim_chunks == 0) return PARSE_ERROR; // 'ANIM' precedes frames.
if (chunk_size_padded < ANMF_CHUNK_SIZE) return PARSE_ERROR;
status = ParseAnimationFrame(dmux, chunk_size_padded); status = ParseAnimationFrame(dmux, chunk_size_padded);
break; break;
} }

View File

@@ -91,7 +91,7 @@ static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag,
WebPMuxError err = WEBP_MUX_NOT_FOUND; WebPMuxError err = WEBP_MUX_NOT_FOUND;
const CHUNK_INDEX idx = ChunkGetIndexFromTag(tag); const CHUNK_INDEX idx = ChunkGetIndexFromTag(tag);
assert(mux != NULL); assert(mux != NULL);
assert(!IsWPI(kChunks[idx].id)); if (IsWPI(kChunks[idx].id)) return WEBP_MUX_INVALID_ARGUMENT;
ChunkInit(&chunk); ChunkInit(&chunk);
SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x); SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x);
@@ -402,8 +402,8 @@ static WebPMuxError GetFrameInfo(const WebPChunk* const frame_chunk,
int* const duration) { int* const duration) {
const WebPData* const data = &frame_chunk->data; const WebPData* const data = &frame_chunk->data;
const size_t expected_data_size = ANMF_CHUNK_SIZE; const size_t expected_data_size = ANMF_CHUNK_SIZE;
assert(frame_chunk->tag == kChunks[IDX_ANMF].tag);
assert(frame_chunk != NULL); assert(frame_chunk != NULL);
assert(frame_chunk->tag == kChunks[IDX_ANMF].tag);
if (data->size != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT; if (data->size != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT;
*x_offset = 2 * GetLE24(data->bytes + 0); *x_offset = 2 * GetLE24(data->bytes + 0);
@@ -454,8 +454,9 @@ static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux,
const int max_x_pos = x_offset + w; const int max_x_pos = x_offset + w;
const int max_y_pos = y_offset + h; const int max_y_pos = y_offset + h;
if (err != WEBP_MUX_OK) return err; if (err != WEBP_MUX_OK) return err;
assert(x_offset < MAX_POSITION_OFFSET); if (x_offset >= MAX_POSITION_OFFSET || y_offset >= MAX_POSITION_OFFSET) {
assert(y_offset < MAX_POSITION_OFFSET); return WEBP_MUX_INVALID_ARGUMENT;
}
if (max_x_pos > max_x) max_x = max_x_pos; if (max_x_pos > max_x) max_x = max_x_pos;
if (max_y_pos > max_y) max_y = max_y_pos; if (max_y_pos > max_y) max_y = max_y_pos;

View File

@@ -196,7 +196,7 @@ void ChunkListDelete(WebPChunk** const chunk_list) {
static uint8_t* ChunkEmit(const WebPChunk* const chunk, uint8_t* dst) { static uint8_t* ChunkEmit(const WebPChunk* const chunk, uint8_t* dst) {
const size_t chunk_size = chunk->data.size; const size_t chunk_size = chunk->data.size;
assert(chunk); assert(chunk);
assert(chunk->tag != NIL_TAG); // Do not check chunk->tag != NIL as NIL_TAG could have been read from a file.
PutLE32(dst + 0, chunk->tag); PutLE32(dst + 0, chunk->tag);
PutLE32(dst + TAG_SIZE, (uint32_t)chunk_size); PutLE32(dst + TAG_SIZE, (uint32_t)chunk_size);
assert(chunk_size == (uint32_t)chunk_size); assert(chunk_size == (uint32_t)chunk_size);

View File

@@ -14,6 +14,7 @@
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <array>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <string_view> #include <string_view>
@@ -25,7 +26,8 @@
namespace { namespace {
void MuxDemuxApiTest(std::string_view data_in, bool use_mux_api) { void MuxDemuxApiTest(std::string_view data_in, bool use_mux_api,
const std::array<int, 10>& chunk_flags) {
const size_t size = data_in.size(); const size_t size = data_in.size();
WebPData webp_data; WebPData webp_data;
WebPDataInit(&webp_data); WebPDataInit(&webp_data);
@@ -48,6 +50,9 @@ void MuxDemuxApiTest(std::string_view data_in, bool use_mux_api) {
uint32_t flags; uint32_t flags;
(void)WebPMuxGetFeatures(mux, &flags); (void)WebPMuxGetFeatures(mux, &flags);
int width = 0, height = 0;
(void)WebPMuxGetCanvasSize(mux, &width, &height);
WebPMuxAnimParams params; WebPMuxAnimParams params;
(void)WebPMuxGetAnimationParams(mux, &params); (void)WebPMuxGetAnimationParams(mux, &params);
@@ -62,6 +67,36 @@ void MuxDemuxApiTest(std::string_view data_in, bool use_mux_api) {
} }
} }
// Try setting a custom chunk.
if (size > 20) {
WebPData custom_chunk;
custom_chunk.bytes = reinterpret_cast<const uint8_t*>(data_in.data()) + 4;
custom_chunk.size = size - 4;
int chunk_idx = 0;
for (std::string_view fourcc : {"VP8X", "ICCP", "ANIM", "EXIF", "XMP ",
"ANMF", "ALPH", "VP8 ", "VP8L", "FUZZ"}) {
// The last five image chunks should return WEBP_MUX_INVALID_ARGUMENT.
for (int i = 0; i < chunk_flags[chunk_idx]; i++) {
(void)WebPMuxSetChunk(mux, fourcc.data(), &custom_chunk, 0);
}
chunk_idx++;
}
}
// Try assembling the mux
WebPData assembled;
WebPDataInit(&assembled);
(void)WebPMuxAssemble(mux, &assembled);
WebPDataClear(&assembled);
// Get number of chunks of various types
for (int chunk_type = WEBP_CHUNK_VP8X; chunk_type <= WEBP_CHUNK_NIL;
++chunk_type) {
int num_chunks = 0;
(void)WebPMuxNumChunks(mux, static_cast<WebPChunkId>(chunk_type),
&num_chunks);
}
WebPMuxDelete(mux); WebPMuxDelete(mux);
} else { } else {
// Demux API // Demux API
@@ -104,6 +139,7 @@ void MuxDemuxApiTest(std::string_view data_in, bool use_mux_api) {
} // namespace } // namespace
FUZZ_TEST(MuxDemuxApi, MuxDemuxApiTest) FUZZ_TEST(MuxDemuxApi, MuxDemuxApiTest)
.WithDomains(fuzztest::String().WithMaxSize(fuzz_utils::kMaxWebPFileSize + .WithDomains(
1), fuzztest::String().WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1),
/*mux=*/fuzztest::Arbitrary<bool>()); /*mux=*/fuzztest::Arbitrary<bool>(),
/*chunk_flags=*/fuzztest::ArrayOf<10>(fuzztest::InRange(0, 2)));