Commit d00d31e9c9 for ffmpeg

commit d00d31e9c967f9033f1115cc7c19cf5e282ebe85
Author: Michael Niedermayer <michael@niedermayer.cc>
Date:   Mon Oct 12 02:09:06 2026 +0200

    avformat/mov: saturate the fragment timestamp arithmetic in mov_read_trun()

    Fixes: signed integer overflow
    Fixes: 5NVcwD9UoHmO
    Found during triage of the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24878
    Signed integer overflow Replicated through UnModified FFmpeg with UBSAN

diff --git a/libavformat/mov.c b/libavformat/mov.c
index 79bbdb0569..bb4c112f55 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -6315,7 +6315,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     frag_stream_info = get_current_frag_stream_info(&c->frag_index);
     if (frag_stream_info) {
         if (frag_stream_info->next_trun_dts != AV_NOPTS_VALUE) {
-            dts = frag_stream_info->next_trun_dts - sc->time_offset;
+            dts = av_sat_sub64(frag_stream_info->next_trun_dts, sc->time_offset);
         } else if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE &&
             c->use_mfra_for == FF_MOV_FLAG_MFRA_PTS) {
             pts = frag_stream_info->first_tfra_pts;
@@ -6340,7 +6340,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
             }

             if (has_tfdt && c->use_tfdt || fallback_tfdt) {
-                dts = frag_stream_info->tfdt_dts - sc->time_offset;
+                dts = av_sat_sub64(frag_stream_info->tfdt_dts, sc->time_offset);
                 av_log(c->fc, AV_LOG_DEBUG, "found tfdt time %"PRId64
                         ", using it for dts\n", dts);
             } else if (has_sidx && !c->use_tfdt || fallback_sidx) {
@@ -6350,13 +6350,13 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
                 av_log(c->fc, AV_LOG_DEBUG, "found sidx time %"PRId64
                         ", using it for dts\n", frag_stream_info->sidx_pts);
             } else {
-                dts = sc->track_end - sc->time_offset;
+                dts = av_sat_sub64(sc->track_end, sc->time_offset);
                 av_log(c->fc, AV_LOG_DEBUG, "found track end time %"PRId64
                         ", using it for dts\n", dts);
             }
         }
     } else {
-        dts = sc->track_end - sc->time_offset;
+        dts = av_sat_sub64(sc->track_end, sc->time_offset);
         av_log(c->fc, AV_LOG_DEBUG, "found track end time %"PRId64
                 ", using it for dts\n", dts);
     }
@@ -6446,11 +6446,11 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)

         mov_update_dts_shift(sc, ctts_duration, c->fc);
         if (pts != AV_NOPTS_VALUE) {
-            dts = pts - sc->dts_shift;
+            dts = av_sat_sub64(pts, sc->dts_shift);
             if (flags & MOV_TRUN_SAMPLE_CTS) {
-                dts -= ctts_duration;
+                dts = av_sat_sub64(dts, ctts_duration);
             } else {
-                dts -= sc->time_offset;
+                dts = av_sat_sub64(dts, sc->time_offset);
             }
             av_log(c->fc, AV_LOG_DEBUG,
                    "pts %"PRId64" calculated dts %"PRId64
@@ -6504,7 +6504,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
         }
     }
     if (frag_stream_info)
-        frag_stream_info->next_trun_dts = dts + sc->time_offset;
+        frag_stream_info->next_trun_dts = av_sat_add64(dts, sc->time_offset);
     if (i < entries) {
         // EOF found before reading all entries.  Fix the hole this would
         // leave in index_entries and tts_data
@@ -6551,7 +6551,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)

     frag->implicit_offset = offset;

-    sc->track_end = dts + sc->time_offset;
+    sc->track_end = av_sat_add64(dts, sc->time_offset);
     if (st->duration < sc->track_end)
         st->duration = sc->track_end;