Commit c7eefe191a for ffmpeg

commit c7eefe191aa5c391a72a0f5174ebd78ff8934fdc
Author: Michael Niedermayer <michael@niedermayer.cc>
Date:   Sat Oct 10 04:58:34 2026 +0200

    avformat/wtvdec: reject file allocation tables that list a sector more than once

    A sub-file holds each of its sectors once, so a table that lists a
    sector more than once is rejected when the sub-file is opened.

    Out of memory Replicated through API

    Fixes: out of memory
    Fixes: 472529274/clusterfuzz-testcase-minimized-ffmpeg_dem_WTV_fuzzer-5867226843643904
    Fixes: 12e707wVt9Ky

    Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
    Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>

diff --git a/libavformat/wtvdec.c b/libavformat/wtvdec.c
index 873ba72243..71d93936c3 100644
--- a/libavformat/wtvdec.c
+++ b/libavformat/wtvdec.c
@@ -31,6 +31,7 @@
 #include "libavutil/intreadwrite.h"
 #include "libavutil/intfloat.h"
 #include "libavutil/mem.h"
+#include "libavutil/qsort.h"
 #include "avformat.h"
 #include "avio_internal.h"
 #include "demux.h"
@@ -126,6 +127,11 @@ static int64_t wtvfile_seek(void *opaque, int64_t offset, int whence)
     return offset;
 }

+static int cmp_sector(const uint32_t *a, const uint32_t *b)
+{
+    return (*a > *b) - (*a < *b);
+}
+
 /**
  * read non-zero integers (le32) from input stream
  * @param pb
@@ -208,6 +214,24 @@ static AVIOContext * wtvfile_open_sector(unsigned first_sector, uint64_t length,
         return NULL;
     }

+    uint32_t *sorted = av_memdup(wf->sectors, wf->nb_sectors * sizeof(*wf->sectors));
+    uint32_t *tmp    = av_malloc_array(wf->nb_sectors, sizeof(*tmp));
+    int i = 0;
+    if (sorted && tmp) {
+        AV_MSORT(sorted, tmp, wf->nb_sectors, uint32_t, cmp_sector);
+        for (i = 1; i < wf->nb_sectors && sorted[i] != sorted[i - 1]; i++)
+            ;
+        if (i < wf->nb_sectors)
+            av_log(s, AV_LOG_ERROR, "file allocation table lists sector %"PRIu32" more than once\n", sorted[i]);
+    }
+    av_free(sorted);
+    av_free(tmp);
+    if (i < wf->nb_sectors) {
+        av_freep(&wf->sectors);
+        av_freep(&wf);
+        return NULL;
+    }
+
     size = avio_size(s->pb);
     if (size >= 0 && (int64_t)wf->sectors[wf->nb_sectors - 1] << WTV_SECTOR_BITS > size)
         av_log(s, AV_LOG_WARNING, "truncated file\n");