Commit 3be6c2d943 for ffmpeg
commit 3be6c2d9438d9198284c96d7f961a8c1d79b7e2c
Author: Furkan Efe Can <caglarf646@gmail.com>
Date: Tue Oct 13 00:39:45 2026 +0300
avformat/mov: Check sample_rate in stsd version 2 audio
The QuickTime SoundDescription version 2 layout stores the sample rate as
a 64 bit float. Converting it to int is undefined behaviour when the value
does not fit into int, NaN included -- and NaN slips through every
integer-style range check because all comparisons against it are false.
The sample_rate < 0 check in ff_mov_read_stsd_entries() runs after the
conversion has already happened, so it cannot prevent this.
Validate the double before converting it, matching soxdec.
Zero is deliberately still accepted: mov_finalize_stsd_codec() substitutes
sc->time_scale for a zero audio sample rate, and rejecting zero here would
remove that existing recovery without being needed for the conversion fix.
Negative, NaN, infinite and out-of-range values are rejected before the
conversion.
Fixes: mov.c:3031 runtime error: nan is outside the range of
representable values of type 'int'
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Furkan Efe Can <caglarf646@gmail.com>
diff --git a/libavformat/mov.c b/libavformat/mov.c
index a89747666b..5660d2a07a 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -2995,8 +2995,8 @@ static void mov_parse_stsd_video(MOVContext *c, AVIOContext *pb,
}
}
-static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
- AVStream *st, MOVStreamContext *sc)
+static int mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
+ AVStream *st, MOVStreamContext *sc)
{
int bits_per_sample, flags;
uint16_t version = avio_rb16(pb);
@@ -3035,8 +3035,18 @@ static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
sc->bytes_per_frame = avio_rb32(pb);
avio_rb32(pb); /* bytes per sample */
} else if (version == 2) {
+ double sample_rate;
+
avio_rb32(pb); /* sizeof struct only */
- st->codecpar->sample_rate = av_int2double(avio_rb64(pb));
+ sample_rate = av_int2double(avio_rb64(pb));
+
+ if (sample_rate < 0 || sample_rate > INT_MAX ||
+ isnan(sample_rate)) {
+ av_log(c->fc, AV_LOG_ERROR, "Invalid sample rate %g\n",
+ sample_rate);
+ return AVERROR_INVALIDDATA;
+ }
+ st->codecpar->sample_rate = sample_rate;
channel_count = avio_rb32(pb);
st->codecpar->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC;
st->codecpar->ch_layout.nb_channels = channel_count;
@@ -3114,6 +3124,8 @@ static void mov_parse_stsd_audio(MOVContext *c, AVIOContext *pb,
st->codecpar->bits_per_coded_sample = bits_per_sample;
sc->sample_size = (bits_per_sample >> 3) * st->codecpar->ch_layout.nb_channels;
}
+
+ return 0;
}
static void mov_parse_stsd_subtitle(MOVContext *c, AVIOContext *pb,
@@ -3407,7 +3419,9 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
if (st->codecpar->codec_type==AVMEDIA_TYPE_VIDEO) {
mov_parse_stsd_video(c, pb, st, sc);
} else if (st->codecpar->codec_type==AVMEDIA_TYPE_AUDIO) {
- mov_parse_stsd_audio(c, pb, st, sc);
+ ret = mov_parse_stsd_audio(c, pb, st, sc);
+ if (ret < 0)
+ return ret;
if (st->codecpar->sample_rate < 0) {
av_log(c->fc, AV_LOG_ERROR, "Invalid sample rate %d\n", st->codecpar->sample_rate);
return AVERROR_INVALIDDATA;