[BUG] MXF: update_tid_lut() writes past tracks[32] onto nb_tracks, turning a file-supplied track id into the write offset

Aperta Adatta ai principianti
#2,457 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 7 giorni

@pranayr710 ci sta già lavorando.

Dal 10/11/2026.

  • #2458 di @pranayr710 — aperta

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
65/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
c
Ambito
backend, security

Direzione di ricerca

Inizia in src/lib_ccx/ccx_demuxer_mxf.c, in corrispondenza di update_tid_lut(), e verifica come viene gestito un array tracks pieno prima di indicizzarlo. Leggi i chiamanti e i cicli sul numero di track per confermare il comportamento al limite, poi crea e prova un file MXF che dichiari più di 32 track. Il lavoro è concluso quando le track in eccesso vengono ignorate in modo sicuro e il conteggio delle track e i campi adiacenti rimangono intatti.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Summary

update_tid_lut() in src/lib_ccx/ccx_demuxer_mxf.c appends to a fixed
MXFTrack tracks[32] without checking nb_tracks first. nb_tracks is the
struct field directly behind that array, so the 33rd track a file declares
overwrites the bound itself with a 32-bit value taken from the file — and the
very next statement re-reads that bound to choose where to write.

The overlap is exact
typedef struct MXFContext
{
        enum MXFCaptionType type;        /* byte   0 */
        int cap_track_id;                /* byte   4 */
        UID cap_essence_key;             /* byte   8 */
        MXFTrack tracks[32];             /* byte  24 .. 279 */
        int nb_tracks;                   /* byte 280 */
        int cap_count;                   /* byte 284 */
        struct ccx_rational edit_rate;   /* byte 288 */
} MXFContext;                            /* 296 bytes, heap allocated in ccx_mxf_init() */

sizeof(MXFTrack) is 8, so tracks[32] begins at 24 + 32*8 = 280 — the
same address as nb_tracks.

What happens on the 33rd track
ctx->tracks[ctx->nb_tracks].track_id = track_id;
memcpy(ctx->tracks[ctx->nb_tracks].track_number, track_number, 4);
ctx->edit_rate = edit_rate;
ctx->nb_tracks++;
  • statement 1 writes track_id at byte 280, i.e. into nb_tracks;
  • statement 2 re-evaluates ctx->tracks[ctx->nb_tracks] using the value just
    written, so its destination is 24 + 8*track_id + 4.

track_id is read straight from the file in mxf_read_timeline_track_metadata():

case MXF_TAG_TRACK_ID:
        track_id = buffered_get_be32(demux);

With track_id = 0x7FFFFFF0 the memcpy targets byte 17179869084 of a 296
byte allocation — about 16 GB past it. Smaller ids give smaller, more usable
offsets; the write is at 8 * track_id bytes from the block, with four bytes of
file-supplied content.

Everything afterwards is affected too: every for (i = 0; i < ctx->nb_tracks; i++) over tracks[] (here and in update_cap_essence_key()) becomes an
unbounded read, and cap_count at byte 284 is overwritten by track_number.

Reachability

ccx_probe_mxf() scans the first bytes for the 14-byte header partition pack
key and nothing else, so any file carrying that key is handed to this demuxer.
Each Timeline Track set in the header metadata calls update_tid_lut() once —
33 of them is enough. The C path is live; the Rust side only re-exports
ccx_probe_mxf / ccx_mxf_init ("shall be removed after mxf" in
src/rust/build.rs).

Demonstrating the mechanism

To be clear about what this evidence is and is not: the run below is a
standalone program that reproduces MXFContext and update_tid_lut()
verbatim, with no other CCExtractor code. It demonstrates the pointer
arithmetic and the crash; it is not CCExtractor itself crashing. My sanitizer
build is unavailable at the moment, so I have not yet put the crafted .mxf
files through the real binary end to end — I will add that as a comment rather
than claim it now. The struct offsets below are computed from the real
declaration, and the reachability argument is from the source.

Reproducing the exact struct and function (no other CCExtractor code):

MASTER build
  after 32 tracks: nb_tracks = 32
  adding 33rd track, track_id = 0x7FFFFFF0 ...
  Segmentation fault

FIXED build
  after 32 tracks: nb_tracks = 32
  adding 33rd track, track_id = 0x7FFFFFF0 ...
    [guard] table full at 32, ignoring track 0x7FFFFFF0
  survived: nb_tracks = 32, cap_count = 0x00000000

and the intermediate state, printed between the two statements:

nb_tracks before        = 32
nb_tracks after stmt 1  = 0x7FFFFFF0   <-- clobbered by the file's track_id
statement 2 will memcpy to tracks[2147483632].track_number = byte 17179869084 of a 296 byte block
Suggested fix

Refuse to append once the table is full:

if (ctx->nb_tracks >= (int)(sizeof(ctx->tracks) / sizeof(ctx->tracks[0])))
{
        mprint("MXF: more than %d tracks, ignoring the rest\n", ...);
        return;
}

I have a patch and a generator for the test files, and will open a PR
referencing this issue.

(Analysis and patch prepared with AI assistance; the reproducers are my own.)

Lingua principale
C
Stelle
913
Fork
603
Merge medio
6g 8h
PR unite (30g)
33

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di CCExtractor/ccextractor

Tutte le issue di CCExtractor/ccextractor

Issue simili

Altre issue su C

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.