A malformed APEv2 tag containing a short MP3GAIN_ALBUM_MINMAX value can trigger a heap-buffer-overflow in ReadMP3APETag().
The crash occurs while parsing APE tags before any gain calculation operation is performed.
mp3gain 1.6.2
(Debian 1.6.2-3, including debian/patches/fix-security-bugs.patch)
The MP3GAIN_ALBUM_MINMAX parser does not validate the length of the value before copying fixed-size fields.
The expected format of MP3GAIN_ALBUM_MINMAX is:
"001,153"
which requires at least 7 bytes.
The MP3GAIN_MINMAX parser already contains a length check:
} else if (!_stricmp(name,"MP3GAIN_MINMAX")) {
if(vsize < 7)
{
free(value);
free(name);
p += isize + 1 + vsize;
continue;
}
...
memcpy(tmpString,vp,3);
However, the following MP3GAIN_ALBUM_MINMAX parser performs the same parsing operations without checking vsize:
} else if (!_stricmp(name,"MP3GAIN_ALBUM_MINMAX")) {
info->haveAlbumMinMaxGain = !0;
vp = value;
memcpy(tmpString,vp,3);
...
vp = vp + 4;
memcpy(tmpString,vp,3); /* apetag.c:294 */
When a malformed MP3GAIN_ALBUM_MINMAX value is shorter than expected, the second memcpy() reads beyond the allocated value buffer, causing a heap-buffer-overflow.
Build mp3gain with AddressSanitizer enabled.
Run:
export ASAN_OPTIONS=abort_on_error=1:halt_on_error=1:detect_leaks=0:symbolize=1
mp3gain -s c poc_apetag_294_ALBUM_MINMAX.mp3
The same issue is also triggered by:
mp3gain -s i poc_apetag_294_ALBUM_MINMAX.mp3
=================================================================
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000036
READ of size 3 at 0x502000000036 thread T0
#0 __asan_memcpy
#1 ReadMP3APETag apetag.c:294
#2 ReadMP3GainAPETag apetag.c:404
#3 main mp3gain.c:1835
0x502000000036 is located 0 bytes after 6-byte region [0x502000000030,0x502000000036)
allocated by thread T0 here:
#0 malloc
#1 ReadMP3APETag apetag.c:223
#2 ReadMP3GainAPETag apetag.c:404
#3 main mp3gain.c:1835
SUMMARY:
AddressSanitizer: heap-buffer-overflow in __asan_memcpy
Attached:
poc_apetag_294_ALBUM_MINMAX.mp3
Apply the same vsize check used by MP3GAIN_MINMAX:
} else if (!_stricmp(name,"MP3GAIN_ALBUM_MINMAX")) {
if(vsize < 7)
{
free(value);
free(name);
p += isize + 1 + vsize;
continue;
}
...
This keeps MP3GAIN_ALBUM_MINMAX handling consistent with MP3GAIN_MINMAX and prevents out-of-bounds reads when processing malformed APEv2 tags.