diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index dc3b6c3c..90befd30 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -304,8 +304,13 @@ ByteVector Frame::fieldData(const ByteVector &frameData) const frameDataOffset + frameDataLength > frameData.size()) { // The first check is needed because some "dual purpose" frame constructors // call this method with only the frame ID, i.e. without a complete header. - debug("Invalid frame data length"); - return ByteVector(); + if(frameDataOffset > frameData.size()) { + debug("Invalid frame data length"); + return ByteVector(); + } + // Per-frame ID3v2.4 unsynchronisation can shrink frameData after the + // header's declared size was set; use what's actually available. + frameDataLength = frameData.size() - frameDataOffset; } if(zlib::isAvailable() && d->header->compression() && !d->header->encryption()) { diff --git a/tests/data/unsynch24.id3 b/tests/data/unsynch24.id3 new file mode 100644 index 00000000..7969dd7e Binary files /dev/null and b/tests/data/unsynch24.id3 differ diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index 9087b54e..fc90f4aa 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -72,6 +72,7 @@ class TestID3v2 : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestID3v2); CPPUNIT_TEST(testUnsynchDecode); + CPPUNIT_TEST(testUnsynchDecodeID3v24Frame); CPPUNIT_TEST(testDowngradeUTF8ForID3v23_1); CPPUNIT_TEST(testDowngradeUTF8ForID3v23_2); CPPUNIT_TEST(testUTF16BEDelimiter); @@ -151,6 +152,13 @@ public: CPPUNIT_ASSERT_EQUAL(String("My babe just cares for me"), f.tag()->title()); } + void testUnsynchDecodeID3v24Frame() + { + MPEG::File f(TEST_FILE_PATH_C("unsynch24.id3"), false); + CPPUNIT_ASSERT(f.tag()); + CPPUNIT_ASSERT_EQUAL(String("Hi"), f.tag()->title()); + } + void testDowngradeUTF8ForID3v23_1() { ScopedFileCopy copy("xing", ".mp3");