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;