From d6c5a8d8887f15dc3b0d929a7aa328f553e5523d Mon Sep 17 00:00:00 2001 From: roshan <58213083+youhaveme9@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:37:57 +0530 Subject: [PATCH] Add fuzz target for FileRef parsing under tests/fuzzing (#1433) Adds a libFuzzer target parsing arbitrary input through TagLib::FileRef, a format magic dictionary, and a README with build and run instructions. Fully decoupled from the normal build as discussed in #1431. --- tests/fuzzing/README.md | 50 +++++++++++++ tests/fuzzing/taglib.dict | 96 +++++++++++++++++++++++++ tests/fuzzing/taglib_fileref_fuzzer.cpp | 62 ++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 tests/fuzzing/README.md create mode 100644 tests/fuzzing/taglib.dict create mode 100644 tests/fuzzing/taglib_fileref_fuzzer.cpp diff --git a/tests/fuzzing/README.md b/tests/fuzzing/README.md new file mode 100644 index 00000000..d6707081 --- /dev/null +++ b/tests/fuzzing/README.md @@ -0,0 +1,50 @@ +# Fuzzing taglib + +This directory contains a libFuzzer target for taglib's generic parsing entry +point. It is intentionally decoupled from the normal CMake build: nothing in +the main source tree references it, and it is compiled on demand with the +commands below. + +## Target + +`taglib_fileref_fuzzer.cpp` parses arbitrary in-memory input through +`TagLib::FileRef` with a `ByteVectorStream`, exercising file type detection +plus tag, audio property and complex property (picture) parsing for all +supported formats: MP3/ID3v1/ID3v2, FLAC, Ogg Vorbis/Opus/Speex, MP4/M4A, +WAV/AIFF, APE/MPC/WavPack, DSF, DSDIFF, Matroska/WebM, Shorten, TrueAudio +and the tracker module formats. + +`taglib.dict` is a libFuzzer dictionary of format magic numbers for the +supported file types. + +## Building and running + +Build taglib first, for example: + +``` +cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=OFF +cmake --build build -j$(nproc) +``` + +Then compile the fuzz target against it with the libFuzzer and +AddressSanitizer flags: + +``` +clang++ -std=c++17 -g -fsanitize=fuzzer,address -fno-omit-frame-pointer \ + -I taglib -I taglib/toolkit -I build \ + tests/fuzzing/taglib_fileref_fuzzer.cpp \ + build/taglib/libtag.a -lz \ + -o taglib_fileref_fuzzer +``` + +Run it over taglib's own test data as a seed corpus (plus a dictionary): + +``` +./taglib_fileref_fuzzer -dict=tests/fuzzing/taglib.dict tests/data/ +``` + +## OSS-Fuzz + +This target is also built continuously by Google's OSS-Fuzz service for the +taglib project, which runs it under AddressSanitizer and +UndefinedBehaviorSanitizer with the test data as seed corpus. diff --git a/tests/fuzzing/taglib.dict b/tests/fuzzing/taglib.dict new file mode 100644 index 00000000..24c72cfc --- /dev/null +++ b/tests/fuzzing/taglib.dict @@ -0,0 +1,96 @@ +# Copyright 2026 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. + +# Magic numbers and markers for the file formats taglib parses. + +# ID3v2 / ID3v1 +"ID3" +"TAG" +"APETAGEX" + +# MPEG audio frame sync +"\xFF\xFB" +"\xFF\xFA" +"\xFF\xF3" +"\xFF\xF2" + +# Ogg (Vorbis, Opus, Speex, FLAC-in-Ogg) +"OggS" +"\x01vorbis" +"OpusTags" +"\x80Speex" + +# FLAC +"fLaC" + +# MP4 / M4A / iTunes +"ftyp" +"M4A " +"mp42" +"isom" +"moov" +"udta" +"meta" +"ilst" +"covr" +"free" + +# RIFF / WAV +"RIFF" +"WAVE" +"fmt " +"data" +"LIST" +"ID3 " + +# AIFF +"FORM" +"AIFF" +"AIFC" +"COMM" +"SSND" + +# APE / MPC / WavPack +"MAC " +"MP+" +"wvpk" + +# DSF / DSDIFF +"DSD " +"FRM8" +"DSd " +"DSD" + +# Matroska / WebM (EBML) +"\x1A\x45\xDF\xA3" +"matroska" +"webm" +"Segment" +"Tags" + +# Tracker modules (IT, XM, S3M, MOD) +"IMPM" +"Extended Module" +"SCRM" +"M.K." +"M!K!" +"FLT4" +"4CHN" +"6CHN" + +# TrueAudio +"TTA1" + +# Shorten (ajkg magic) +"ajkg" diff --git a/tests/fuzzing/taglib_fileref_fuzzer.cpp b/tests/fuzzing/taglib_fileref_fuzzer.cpp new file mode 100644 index 00000000..f38beb61 --- /dev/null +++ b/tests/fuzzing/taglib_fileref_fuzzer.cpp @@ -0,0 +1,62 @@ +// Copyright 2026 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. + +// Fuzzes TagLib's generic file type detection and tag/audio-property parsing +// over all supported formats (MP3/ID3v2, FLAC, OGG/Vorbis/Opus/Speex, MP4/AAC, +// WAV, AIFF, APE, MPC, WavPack, DSF, DSDIFF, tracker formats, ...). + +#include +#include + +#include +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + const TagLib::ByteVector input(reinterpret_cast(data), + static_cast(size)); + TagLib::ByteVectorStream stream(input); + { + TagLib::FileRef ref(&stream, /*readAudioProperties=*/true, + TagLib::AudioProperties::Average); + if (!ref.isNull()) { + if (ref.tag()) { + const TagLib::Tag *tag = ref.tag(); + // Touch the standard fields to force a full tag parse. + (void)tag->title(); + (void)tag->artist(); + (void)tag->album(); + (void)tag->comment(); + (void)tag->genre(); + (void)tag->year(); + (void)tag->track(); + (void)tag->properties(); + } + // Parse embedded pictures and other complex properties. + (void)ref.complexProperties("PICTURE"); + if (ref.audioProperties()) { + const TagLib::AudioProperties *props = ref.audioProperties(); + (void)props->lengthInSeconds(); + (void)props->lengthInMilliseconds(); + (void)props->bitrate(); + (void)props->sampleRate(); + (void)props->channels(); + } + } + } // FileRef is destroyed before the stream it borrows. + return 0; +}