Fix seek truncation that can lead to OOB read in mp3dec_ex_read

This commit is contained in:
Andrei Chmelev authored and Lion committed 2026-03-12 22:47:55 +03:00
1 parent f50682ba30
commit 7b590fdcfa
2 files changed
+50 -5

No files matched your search

+19 -5
View File
@@ -757,8 +757,13 @@ int mp3dec_ex_seek(mp3dec_ex_t *dec, uint64_t position)
dec->cur_sample = 0;
goto do_exit;
}
if (dec->samples && position > dec->samples)
position = dec->samples;
dec->cur_sample = position;
position += dec->start_delay;
if (position > (UINT64_MAX - (uint64_t)dec->start_delay))
position = UINT64_MAX;
else
position += dec->start_delay;
if (0 == position)
{ /* optimize seek to zero, no index needed */
seek_zero:
@@ -846,7 +851,12 @@ seek_zero:
}
}
dec->offset = dec->index.frames[i].offset;
dec->to_skip = position - dec->index.frames[i].sample;
if (position > dec->index.frames[i].sample)
{
uint64_t to_skip64 = position - dec->index.frames[i].sample;
dec->to_skip = (to_skip64 > (uint64_t)INT_MAX) ? INT_MAX : (int)to_skip64;
} else
dec->to_skip = 0;
while ((i + 1) < dec->index.num_frames && !dec->index.frames[i].sample && !dec->index.frames[i + 1].sample)
{ /* skip not decodable first frames */
const uint8_t *hdr;
@@ -860,7 +870,11 @@ seek_zero:
return MP3D_E_IOERROR;
} else
hdr = dec->file.buffer + dec->index.frames[i].offset;
dec->to_skip += hdr_frame_samples(hdr)*dec->info.channels;
int frame_samples = hdr_frame_samples(hdr)*dec->info.channels;
if (dec->to_skip > (INT_MAX - frame_samples))
dec->to_skip = INT_MAX;
else
dec->to_skip += frame_samples;
i++;
}
do_exit:
@@ -938,7 +952,7 @@ return_e_decode:
if (dec->buffer_samples)
{
dec->buffer_samples *= frame_info->channels;
if (dec->to_skip)
if (dec->to_skip > 0)
{
size_t skip = MINIMP3_MIN(dec->buffer_samples, dec->to_skip);
dec->buffer_consumed += skip;
@@ -952,7 +966,7 @@ return_e_decode:
{
goto return_e_decode;
}
} else if (dec->to_skip)
} else if (dec->to_skip > 0)
{ /* In mp3 decoding not always can start decode from any frame because of bit reservoir,
count skip samples for such frames */
int frame_samples = hdr_frame_samples(dec_buf)*frame_info->channels;
+31
View File
@@ -527,6 +527,37 @@ static int self_test(const char *input_file_name)
ASSERT(0 == ret);
mp3dec_ex_close(&dec);
{
/* Regression: out-of-range sample seek must not corrupt to_skip and crash in mp3dec_ex_read(). */
static const uint8_t seek_oob_buf[] = {
0xff,0xfd,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xa5,0xce,0xa7,0x8b,0xff,0xff,0x00,0x00,0x00,0x00,
0x47,0x45,0x58,0x00,0xff,0xfd,0x18,0xd6,0x64,0x41,
0xae,0xce,0xd6,0x97,0x8a,0x4e,0x18,0x8b,0x82,0xff,
0xfd,0x18,0xd6,0x45,0x54,0x18,0x8b,0x82,0x47,0x45,
0x58,0x00,0xff,0xfd,0x18,0xd6,0x64,0x41,0x50,0x45,
0x54,0x41,0x01,0x18,0xd6,0x64,0x41,0x50,0x45,0x54,
0x41,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0xff,
0xfd,0x18,0xd6,0x64,0x41,0x50,0x45,0x54,0x41,0x47,
0x45,0x58,0x00,0xff,0xfd,0x18,0xd6
};
mp3d_sample_t pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
size_t r, total = 0;
ret = mp3dec_ex_open_buf(&dec, seek_oob_buf, sizeof(seek_oob_buf), MP3D_SEEK_TO_SAMPLE | MP3D_ALLOW_MONO_STEREO_TRANSITION);
ASSERT(0 == ret);
do
{
r = mp3dec_ex_read(&dec, pcm, MINIMP3_MAX_SAMPLES_PER_FRAME);
total += r;
} while (r);
ASSERT(2304 == total);
ret = mp3dec_ex_seek(&dec, 3888454656ULL);
ASSERT(0 == ret);
r = mp3dec_ex_read(&dec, pcm, MINIMP3_MAX_SAMPLES_PER_FRAME);
ASSERT(0 == r);
mp3dec_ex_close(&dec);
}
ret = mp3dec_ex_open_cb(0, &io, MP3D_SEEK_TO_SAMPLE);
ASSERT(MP3D_E_PARAM == ret);
ret = mp3dec_ex_open_cb(&dec, 0, MP3D_SEEK_TO_SAMPLE);