diff --git a/fuzz/fuzz_decode_buf.c b/fuzz/fuzz_decode_buf.c new file mode 100644 index 0000000..fb0342a --- /dev/null +++ b/fuzz/fuzz_decode_buf.c @@ -0,0 +1,64 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#define MINIMP3_IMPLEMENTATION +#define MINIMP3_ALLOW_MONO_STEREO_TRANSITION +#include "minimp3_ex.h" + +static int iterate_cb(void *user_data, const uint8_t *frame, int frame_size, + int free_format_bytes, size_t buf_size, uint64_t offset, + mp3dec_frame_info_t *info) { + (void)user_data; + (void)frame; + (void)frame_size; + (void)free_format_bytes; + (void)buf_size; + (void)offset; + (void)info; + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 4) + return 0; + if (size > 128 * 1024) + size = 128 * 1024; + + /* Use first byte to select API path */ + uint8_t selector = data[0] & 0x01; + const uint8_t *buf = data + 1; + size_t buf_size = size - 1; + + if (selector == 0) { + /* Test mp3dec_load_buf: full buffer decode */ + mp3dec_t dec; + mp3dec_file_info_t info; + memset(&info, 0, sizeof(info)); + + mp3dec_load_buf(&dec, buf, buf_size, &info, NULL, NULL); + + if (info.buffer) + free(info.buffer); + } else { + /* Test mp3dec_iterate_buf: frame iteration */ + mp3dec_iterate_buf(buf, buf_size, iterate_cb, NULL); + } + + return 0; +} diff --git a/fuzz/fuzz_decode_ex.c b/fuzz/fuzz_decode_ex.c new file mode 100644 index 0000000..144caab --- /dev/null +++ b/fuzz/fuzz_decode_ex.c @@ -0,0 +1,65 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#define MINIMP3_IMPLEMENTATION +#define MINIMP3_ALLOW_MONO_STEREO_TRANSITION +#include "minimp3_ex.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 8) + return 0; + if (size > 128 * 1024) + size = 128 * 1024; + + /* Consume first 4 bytes for seek position and flags */ + uint32_t seek_pos = ((uint32_t)data[0] << 24) | ((uint32_t)data[1] << 16) | + ((uint32_t)data[2] << 8) | (uint32_t)data[3]; + int flags = (data[4] & 0x01) ? MP3D_SEEK_TO_BYTE : MP3D_SEEK_TO_SAMPLE; + flags |= MP3D_ALLOW_MONO_STEREO_TRANSITION; + + const uint8_t *buf = data + 5; + size_t buf_size = size - 5; + + mp3dec_ex_t dec; + if (mp3dec_ex_open_buf(&dec, buf, buf_size, flags) == 0) { + /* Read some samples */ + mp3d_sample_t pcm[MINIMP3_MAX_SAMPLES_PER_FRAME]; + size_t read_total = 0; + size_t readed; + + do { + readed = mp3dec_ex_read(&dec, pcm, MINIMP3_MAX_SAMPLES_PER_FRAME); + read_total += readed; + /* Safety: limit total reads to prevent excessive runtime */ + if (read_total > 1024 * 1024) + break; + } while (readed > 0); + + /* Test seek */ + mp3dec_ex_seek(&dec, (uint64_t)seek_pos); + + /* Read after seek */ + readed = mp3dec_ex_read(&dec, pcm, MINIMP3_MAX_SAMPLES_PER_FRAME); + (void)readed; + + mp3dec_ex_close(&dec); + } + + return 0; +} diff --git a/fuzz/fuzz_decode_frame.c b/fuzz/fuzz_decode_frame.c new file mode 100644 index 0000000..7814d66 --- /dev/null +++ b/fuzz/fuzz_decode_frame.c @@ -0,0 +1,55 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#define MINIMP3_IMPLEMENTATION +#include "minimp3.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 4) + return 0; + /* Cap input to avoid OOM on very large inputs */ + if (size > 128 * 1024) + size = 128 * 1024; + + mp3dec_t dec; + mp3dec_init(&dec); + + const uint8_t *buf = data; + size_t remaining = size; + + /* Decode frames until the buffer is exhausted */ + while (remaining > 0) { + mp3dec_frame_info_t info; + mp3d_sample_t pcm[MINIMP3_MAX_SAMPLES_PER_FRAME]; + + int samples = mp3dec_decode_frame(&dec, buf, (int)remaining, pcm, &info); + + if (info.frame_bytes > 0) { + if ((size_t)info.frame_bytes > remaining) + break; + buf += info.frame_bytes; + remaining -= info.frame_bytes; + } else { + /* No frame found, skip one byte */ + buf++; + remaining--; + } + } + + return 0; +} diff --git a/fuzz/mp3.dict b/fuzz/mp3.dict new file mode 100644 index 0000000..e9dafa9 --- /dev/null +++ b/fuzz/mp3.dict @@ -0,0 +1,38 @@ +# MP3 frame sync words (0xFFE0 mask) +# MPEG1 Layer3 +kw_sync_mp1l3="\xff\xfb" +kw_sync_mp1l3_2="\xff\xfa" +# MPEG1 Layer2 +kw_sync_mp1l2="\xff\xfd" +kw_sync_mp1l2_2="\xff\xfc" +# MPEG1 Layer1 +kw_sync_mp1l1="\xff\xfe" +# MPEG2 Layer3 +kw_sync_mp2l3="\xff\xf3" +kw_sync_mp2l3_2="\xff\xf2" +# MPEG2.5 Layer3 +kw_sync_mp25l3="\xff\xe3" +kw_sync_mp25l3_2="\xff\xe2" + +# ID3v2 header +kw_id3v2="ID3" +# ID3v1 header +kw_id3v1="TAG" +# APE tag +kw_ape="APETAGEX" + +# Xing VBR header +kw_xing="Xing" +kw_info="Info" +# VBRI header +kw_vbri="VBRI" + +# Common frame header bytes +kw_hdr1="\xff\xfb\x90\x00" +kw_hdr2="\xff\xfb\x90\x64" +kw_hdr3="\xff\xfb\x92\x00" +kw_hdr4="\xff\xf3\x90\x00" +kw_hdr5="\xff\xe3\x90\x00" + +# Common bitrates (128kbps stereo 44100Hz) +kw_common="\xff\xfb\x90\x04"