
Proof of concept per CVE-2026-86476: lettura/scrittura fuori dai limiti dell'heap in GStreamer GstVideoMeta tramite deserializzazione unixfd, con repro ASan e catena RCE.
Un overflow del buffer heap in libgstvideo di GStreamer (gst-plugins-base). Un GstVideoMeta descrive come sono disposti in memoria i pixel di un frame video. Quando tale descrizione viene deserializzata da un peer unixfd non attendibile, nulla verifica che la geometria dichiarata rientri nel buffer che la sostiene. Un peer può impostare stride di piano sovradimensionati e il consumatore legge o scrive oltre l'allocazione.
| Campo | Valore |
|---|---|
| CVE | CVE-2026-86476 |
| Classe | CWE-125 (lettura fuori dai limiti), CWE-787 (scrittura fuori dai limiti), raggiunta tramite CWE-502 (deserializzazione di dati non attendibili) |
| Componente | libgstvideo: gstvideometa.c, video-frame.c |
| Impatto | Lettura fuori dai limiti (confermata), scrittura fuori dai limiti fino all'esecuzione di codice (dimostrata) |
| Trigger | Un GstVideoMeta con stride di piano fabbricati, deserializzato da un peer unixfd |
| Sito del fault | video_orc_convert_I420_BGRA, il kernel ORC di videoconvert |
Il bug è costituito da tre controlli mancanti che si sommano.
Nessun controllo di dimensione nel ramo meta della mappatura del frame. gst_video_frame_map_id() in video-frame.c ha due rami. Il ramo senza meta termina con if (frame->map[0].size < info->size) goto invalid_size;. Il ramo meta, preso ogni volta che è presente un GstVideoMeta, impone solo info->width <= meta->width e info->height <= meta->height. Questi sono limiti inferiori. Nulla confronta la geometria del piano con la dimensione della memoria reale.
default_map() controlla un byte per piano. In gstvideometa.c, la memoria del piano viene risolta con gst_buffer_find_memory(buffer, offset, 1, ...). L'argomento della dimensione è 1. Il codice conferma che esiste un singolo byte all'offset di inizio del piano, poi copia lo stride così com'è. L'estensione reale del piano, stride * height, non viene mai confrontata con l'allocazione. Un commento FIXME use plane size si trova direttamente sopra il controllo.
Il deserializzatore non verifica alcun intervallo. video_meta_deserialize() valida solo che il lettore di byte non abbia esaurito l'input, che n_planes <= GST_VIDEO_MAX_PLANES e che version != 0. Offset e stride vengono copiati direttamente dallo stream. Ogni stride viene letto come gint32 con segno, quindi uno stride negativo viene accettato.
Sul lato ricevente, gst_unix_fd_src_create() deserializza i meta rispetto a un buffer vuoto e allega successivamente la memoria del peer. Lo stride trasportato dal meta e la dimensione della memoria che sostiene il buffer non vengono mai confrontati.
Un peer ostile serializza un GstVideoMeta per un frame I420 320x240 con ogni stride di piano impostato a 1000000. Il consumatore lo deserializza senza un controllo di intervallo, allega la piccola memoria reale e mappa il frame. default_map() è soddisfatto perché il primo byte di ogni piano esiste. videoconvert legge quindi ogni piano di input per stride * height byte e oltrepassa l'allocazione.
Questa è la lettura. La scrittura è l'immagine speculare. gst_video_frame_copy_plane() esegue una memcpy di stride * height byte, quindi quando un piccolo buffer che trasporta il meta mendace è la destinazione di una copia, la memcpy scrive oltre il suo supporto.
Il trasporto unixfd copia i buffer non-FD in un memfd, quindi la pipeline standard fa atterrare la lettura contro un confine di mapping mmap'd (crash e fuga di informazioni). Pilotare lo stesso deserializzatore con gli stessi byte non attendibili contro una destinazione sostenuta dall'heap esercita la primitiva di scrittura. Il trasporto decide se l'overflow colpisce un confine di mapping o l'heap. Non decide se il bug esiste.
Con la scrittura sull'heap, la proof of concept sovrascrive il puntatore alla funzione free nell'header di un GstMiniObject vicino, colloca la stringa di comando dove finisce nel registro del primo argomento e attiva un unref in modo che il puntatore falsificato venga chiamato come system().
Qualsiasi pipeline che accetta buffer video tramite unixfd da un processo di cui non si fida completamente. Decoder multimediali in sandbox, pipeline suddivise in stile portal e qualsiasi cosa che utilizzi unixfdsrc per ricevere frame attraverso un confine di privilegio o di fiducia.
Confermato:
libgstreamer-1.0.so.0.2804.0 di Debian (1.28.4-2) con glibc 2.42.Due processi su un socket unix, solo elementi standard.
fakesrc ! capsfilter ! unixfdsink, che invia un buffer I420 valido da 320x240 con stride reali 320/160/160 ma un GstVideoMeta i cui stride dei piani sono 1000000.unixfdsrc ! videoconvert ! capsfilter ! fakesink, dove il capsfilter forza I420 a BGRA così che videoconvert debba leggere il frame secondo gli stride del meta.| Esecuzione | Stride del meta | Risultato |
|---|---|---|
| 1 | 1000000 / 1000000 / 1000000 | lettura fuori dai limiti sotto ASan in video_orc_convert_I420_BGRA |
| 2 | 320 / 160 / 160 | completamento normale, il buffer raggiunge fakesink |
Compilare GStreamer con -Db_sanitize=address per la prova lato lettura. La proof of concept per l'RCE viene eseguita su una build standard, perché una build ASan si interrompe al primo memcpy che va in overflow.
default_map(), sostituire il controllo di un byte gst_buffer_find_memory(..., 1, ...) con un controllo rispetto all'estensione del piano stride * height.gst_video_frame_map_id() lo stesso controllo invalid_size che ha il ramo senza meta.video_meta_deserialize(), verificare gli intervalli di offset e stride rispetto al buffer e rifiutare gli stride negativi.Una qualsiasi di queste interrompe la catena. Tutte e tre rappresentano la correzione corretta per una struttura che attraversa un confine di fiducia.