From 491322d1ba294ac072fe4c357cea2d3da83ae15d Mon Sep 17 00:00:00 2001 From: Urs Fleisch Date: Fri, 8 Dec 2023 08:30:28 +0100 Subject: [PATCH] Tolerate trailing garbage in M4A files (#1186) If an M4A file contains trailing garbage (e.g. an APE tag at the end of the file), it was considered invalid since [e21640b]. It is important to make sure that atoms which affect modifications written by TagLib are valid, otherwise the file might be damaged. However, in cases where invalid atoms do not affect modifications (which are only done inside the root level "moov" and "moof" containers), they can be safely ignored. This commit allows trailing garbage in M4A files as it is also accepted in MP3, WAV and DSF files. --- taglib/mp4/mp4file.cpp | 31 ++++++++++++++++++++++++++++++- tests/test_mp4.cpp | 5 +---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp index f8e87fdb..a14db53d 100644 --- a/taglib/mp4/mp4file.cpp +++ b/taglib/mp4/mp4file.cpp @@ -40,6 +40,35 @@ namespace return std::none_of(list.begin(), list.end(), [](const auto &a) { return a->length == 0 || !checkValid(a->children); }); } + + bool checkRootLevelAtoms(MP4::AtomList &list) + { + bool moovValid = false; + for(auto it = list.begin(); it != list.end(); ++it) { + bool invalid = (*it)->length == 0 || !checkValid((*it)->children); + if(!moovValid && !invalid && (*it)->name == "moov") { + moovValid = true; + } + if(invalid) { + if(moovValid && (*it)->name != "moof") { + // Only the root level atoms "moov" and (if present) "moof" are + // modified. If they are valid, ignore following invalid root level + // atoms as trailing garbage. + while(it != list.end()) { + delete *it; + it = list.erase(it); + } + return true; + } + else { + return false; + } + } + } + + return true; + } + } // namespace class MP4::File::FilePrivate @@ -127,7 +156,7 @@ MP4::File::read(bool readProperties) return; d->atoms = std::make_unique(this); - if(!checkValid(d->atoms->atoms)) { + if(!checkRootLevelAtoms(d->atoms->atoms)) { setValid(false); return; } diff --git a/tests/test_mp4.cpp b/tests/test_mp4.cpp index 6921a0e2..79cb8645 100644 --- a/tests/test_mp4.cpp +++ b/tests/test_mp4.cpp @@ -602,10 +602,7 @@ public: void testFuzzedFile() { MP4::File f(TEST_FILE_PATH_C("infloop.m4a")); - // The file has an invalid atom length of 2775 in the last atom - // ("free", offset 0xc521, 00000ad7 66726565), whereas the remaining file - // length is 2727 bytes, therefore the file is now considered invalid. - CPPUNIT_ASSERT(!f.isValid()); + CPPUNIT_ASSERT(f.isValid()); } void testRepeatedSave()