From 0c9546f7efc61eac7f79ae115c3f99c91c21c443 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Wed, 1 Apr 2026 11:11:04 +0200 Subject: [PATCH] Expand the muxer fuzzer. And fix a few bugs it already found. Bug: 497882857 Change-Id: I4af63d7e7fece0686dac931d54a5e855353aaa3a --- src/demux/demux.c | 1 + src/mux/muxedit.c | 9 +++--- src/mux/muxinternal.c | 2 +- tests/fuzzer/mux_demux_api_fuzzer.cc | 44 +++++++++++++++++++++++++--- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/demux/demux.c b/src/demux/demux.c index c4f8d1a2..4b2f1a1d 100644 --- a/src/demux/demux.c +++ b/src/demux/demux.c @@ -493,6 +493,7 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) { } case MKFOURCC('A', 'N', 'M', 'F'): { 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); break; } diff --git a/src/mux/muxedit.c b/src/mux/muxedit.c index 9c755163..464e224b 100644 --- a/src/mux/muxedit.c +++ b/src/mux/muxedit.c @@ -91,7 +91,7 @@ static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, WebPMuxError err = WEBP_MUX_NOT_FOUND; const CHUNK_INDEX idx = ChunkGetIndexFromTag(tag); assert(mux != NULL); - assert(!IsWPI(kChunks[idx].id)); + if (IsWPI(kChunks[idx].id)) return WEBP_MUX_INVALID_ARGUMENT; ChunkInit(&chunk); SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x); @@ -402,8 +402,8 @@ static WebPMuxError GetFrameInfo(const WebPChunk* const frame_chunk, int* const duration) { const WebPData* const data = &frame_chunk->data; const size_t expected_data_size = ANMF_CHUNK_SIZE; - assert(frame_chunk->tag == kChunks[IDX_ANMF].tag); assert(frame_chunk != NULL); + assert(frame_chunk->tag == kChunks[IDX_ANMF].tag); if (data->size != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT; *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_y_pos = y_offset + h; if (err != WEBP_MUX_OK) return err; - assert(x_offset < MAX_POSITION_OFFSET); - assert(y_offset < MAX_POSITION_OFFSET); + if (x_offset >= MAX_POSITION_OFFSET || y_offset >= MAX_POSITION_OFFSET) { + return WEBP_MUX_INVALID_ARGUMENT; + } if (max_x_pos > max_x) max_x = max_x_pos; if (max_y_pos > max_y) max_y = max_y_pos; diff --git a/src/mux/muxinternal.c b/src/mux/muxinternal.c index 3048445c..35224fee 100644 --- a/src/mux/muxinternal.c +++ b/src/mux/muxinternal.c @@ -196,7 +196,7 @@ void ChunkListDelete(WebPChunk** const chunk_list) { static uint8_t* ChunkEmit(const WebPChunk* const chunk, uint8_t* dst) { const size_t chunk_size = chunk->data.size; 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 + TAG_SIZE, (uint32_t)chunk_size); assert(chunk_size == (uint32_t)chunk_size); diff --git a/tests/fuzzer/mux_demux_api_fuzzer.cc b/tests/fuzzer/mux_demux_api_fuzzer.cc index f8ad0e17..013469e0 100644 --- a/tests/fuzzer/mux_demux_api_fuzzer.cc +++ b/tests/fuzzer/mux_demux_api_fuzzer.cc @@ -14,6 +14,7 @@ // //////////////////////////////////////////////////////////////////////////////// +#include #include #include #include @@ -25,7 +26,8 @@ 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& chunk_flags) { const size_t size = data_in.size(); WebPData webp_data; WebPDataInit(&webp_data); @@ -48,6 +50,9 @@ void MuxDemuxApiTest(std::string_view data_in, bool use_mux_api) { uint32_t flags; (void)WebPMuxGetFeatures(mux, &flags); + int width = 0, height = 0; + (void)WebPMuxGetCanvasSize(mux, &width, &height); + WebPMuxAnimParams params; (void)WebPMuxGetAnimationParams(mux, ¶ms); @@ -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(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(chunk_type), + &num_chunks); + } + WebPMuxDelete(mux); } else { // Demux API @@ -104,6 +139,7 @@ void MuxDemuxApiTest(std::string_view data_in, bool use_mux_api) { } // namespace FUZZ_TEST(MuxDemuxApi, MuxDemuxApiTest) - .WithDomains(fuzztest::String().WithMaxSize(fuzz_utils::kMaxWebPFileSize + - 1), - /*mux=*/fuzztest::Arbitrary()); + .WithDomains( + fuzztest::String().WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1), + /*mux=*/fuzztest::Arbitrary(), + /*chunk_flags=*/fuzztest::ArrayOf<10>(fuzztest::InRange(0, 2)));