
Proof of Concept für CVE-2026-86476: GStreamer GstVideoMeta Heap-Out-of-Bounds-Read/Write über unixfd-Deserialisierung, mit ASan-Repro und RCE-Kette.
Ein Heap-Pufferüberlauf in GStreamers libgstvideo (gst-plugins-base). Eine GstVideoMeta beschreibt, wie die Pixel eines Videobildes im Speicher angeordnet sind. Wenn diese Beschreibung von einem nicht vertrauenswürdigen unixfd-Peer deserialisiert wird, prüft nichts, ob die angegebene Geometrie in den zugrunde liegenden Puffer passt. Ein Peer kann überdimensionierte Plane-Strides setzen, und der Konsument liest oder schreibt über die Allokation hinaus.
| Feld | Wert |
|---|---|
| CVE | CVE-2026-86476 |
| Klasse | CWE-125 (Out-of-Bounds-Read), CWE-787 (Out-of-Bounds-Write), erreicht über CWE-502 (Deserialisierung nicht vertrauenswürdiger Daten) |
| Komponente | libgstvideo: gstvideometa.c, video-frame.c |
| Auswirkung | Out-of-Bounds-Read (bestätigt), Out-of-Bounds-Write bis zur Codeausführung (demonstriert) |
| Auslöser | Eine GstVideoMeta mit gefälschten Plane-Strides, deserialisiert von einem unixfd-Peer |
| Fehlerstelle | video_orc_convert_I420_BGRA, der ORC-Kernel von videoconvert |
Der Fehler besteht aus drei fehlenden Prüfungen, die sich gegenseitig verstärken.
Keine Größenprüfung im Meta-Zweig der Frame-Map. gst_video_frame_map_id() in video-frame.c hat zwei Zweige. Der Zweig ohne Meta endet mit if (frame->map[0].size < info->size) goto invalid_size;. Der Meta-Zweig, der immer dann genommen wird, wenn eine GstVideoMeta vorhanden ist, erzwingt nur info->width <= meta->width und info->height <= meta->height. Das sind Untergrenzen. Nichts vergleicht die Plane-Geometrie mit der Größe des tatsächlichen Speichers.
default_map() prüft ein Byte pro Plane. In gstvideometa.c wird der Plane-Speicher mit gst_buffer_find_memory(buffer, offset, 1, ...) aufgelöst. Das Größenargument ist 1. Der Code bestätigt, dass ein einzelnes Byte am Startoffset der Plane existiert, und kopiert dann den Stride unverändert heraus. Die tatsächliche Ausdehnung der Plane, stride * height, wird nie mit der Allokation verglichen. Ein Kommentar FIXME use plane size steht direkt über der Prüfung.
Der Deserializer prüft keine Bereiche. video_meta_deserialize() validiert nur, dass dem Byte-Reader der Input nicht ausgegangen ist, dass n_planes <= GST_VIDEO_MAX_PLANES gilt und dass version != 0 ist. Offsets und Strides werden direkt aus dem Stream kopiert. Jeder Stride wird als vorzeichenbehafteter gint32 gelesen, sodass ein negativer Stride akzeptiert wird.
Auf der Empfängerseite deserialisiert gst_unix_fd_src_create() die Metas gegen einen leeren Puffer und hängt den Speicher des Peers erst danach an. Der Stride, den die Meta trägt, und die Größe des Speichers, der den Puffer stützt, werden nie verglichen.
Ein feindlicher Peer serialisiert eine GstVideoMeta für ein 320x240-I420-Bild mit jedem Plane-Stride auf 1000000 gesetzt. Der Konsument deserialisiert sie ohne Bereichsprüfung, hängt den kleinen tatsächlichen Speicher an und mappt das Bild. default_map() ist zufrieden, weil das erste Byte jeder Plane existiert. videoconvert liest dann jede Eingabe-Plane für stride * height Bytes und läuft über die Allokation hinaus.
Das ist der Lesezugriff. Der Schreibzugriff ist das Spiegelbild. gst_video_frame_copy_plane() führt ein memcpy von stride * height Bytes aus, sodass, wenn ein kleiner Puffer mit der lügenden Meta das Ziel einer Kopie ist, das memcpy über seinen zugrunde liegenden Speicher hinaus schreibt.
Der unixfd-Transport kopiert Nicht-FD-Puffer in ein memfd, sodass die Standard-Pipeline den Lesezugriff an einer mmap-Mapping-Grenze landen lässt (Absturz und Informationsleck). Treibt man denselben Deserializer mit denselben nicht vertrauenswürdigen Bytes gegen ein heap-gestütztes Ziel, wird das Schreib-Primitiv ausgeführt. Der Transport entscheidet, ob der Überlauf eine Mapping-Grenze oder den Heap trifft. Er entscheidet nicht, ob der Fehler existiert.
Mit dem Heap-Schreibzugriff überschreibt der Proof of Concept den free-Funktionszeiger im Header eines benachbarten GstMiniObject, platziert den Befehlsstring dort, wo er im ersten Argumentregister landet, und löst einen Unref aus, sodass der gefälschte Zeiger als system() aufgerufen wird.
Jede Pipeline, die Videopuffer über unixfd von einem Prozess entgegennimmt, dem sie nicht vollständig vertraut. Sandboxed Media Decoder, Portal-artige aufgeteilte Pipelines und alles, was unixfdsrc verwendet, um Frames über eine Privilegien- oder Vertrauensgrenze hinweg zu empfangen.
Bestätigt:
libgstreamer-1.0.so.0.2804.0 (1.28.4-2) mit glibc 2.42.Zwei Prozesse über einen Unix-Socket, ausschließlich Standard-Elemente.
fakesrc ! capsfilter ! unixfdsink, der einen gültigen 320x240-I420-Puffer mit echten Strides 320/160/160, aber einer GstVideoMeta mit Plane-Strides von 1000000 überträgt.unixfdsrc ! videoconvert ! capsfilter ! fakesink, wobei der capsfilter I420 zu BGRA erzwingt, sodass videoconvert den Frame anhand der Meta-Strides lesen muss.| Lauf | Meta-Strides | Ergebnis |
|---|---|---|
| 1 | 1000000 / 1000000 / 1000000 | Out-of-Bounds-Lesen unter ASan in video_orc_convert_I420_BGRA |
| 2 | 320 / 160 / 160 | normale Beendigung, Puffer erreicht fakesink |
Baue GStreamer mit -Db_sanitize=address für den Nachweis auf der Leseseite. Der RCE-Proof-of-Concept läuft auf einem Standard-Build, da ein ASan-Build beim ersten überlaufenden memcpy abbricht.
default_map() den Ein-Byte-Aufruf gst_buffer_find_memory(..., 1, ...) durch eine Prüfung gegen die Plane-Ausdehnung stride * height ersetzen.gst_video_frame_map_id() dieselbe invalid_size-Prüfung geben, die der Zweig ohne Meta hat.video_meta_deserialize() Offsets und Strides gegen den Puffer auf ihren Wertebereich prüfen und negative Strides ablehnen.Jede einzelne dieser Maßnahmen unterbricht die Kette. Alle drei zusammen sind die korrekte Fehlerbehebung für eine Struktur, die eine Vertrauensgrenze überschreitet.