CVE-2026-22554
A heap-based buffer overflow vulnerability exists in the Channel Splitting functionality of MediaInfoLib (version(s): 26.01). A specially crafted .riff file can lead to arbitrary code execution. An attacker can provide a malicious file to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
MediaInfoLib (version(s): 26.01)
MediaInfoLib - https://github.com/MediaArea/MediaInfoLib
7.8 - CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-122 - Heap-based Buffer Overflow
MediaInfo(Lib) is a convenient unified display of technical information and metadata for video and audio files.
The MediaInfo library contains a channel splitting functionality that separates multi-channel audio into distinct audio channels. When parsing the PCM stream of a RIFF audio file, we see the following code:
void File_Riff::Parser_Pcm(...)
{
...
if (Channels>=2 && BitsPerSample<=32 && SamplesPerSec==48000)
{
File_ChannelSplitting* Parser=new File_ChannelSplitting;
...
}
...
}
Specifically, if the input file has more than 2 channels and the bitdepth is less than 32 bits, and the sampling rate is exactly 48000, the code performs channel splitting to correctly parse interleaved audio data from the file.
The library copies the audio data for the current channel from Buffer to two separate audio buffers.
void File_ChannelSplitting::Read_Buffer_Continue()
{
...
while (BytesToCopy<=Buffer_Size-Buffer_Offset) (1)
{
...
switch (BitDepth)
{
case 32:
// Copy 8 bytes
SplittedChannel00->Buffer[SplittedChannel00->Buffer_Size++]=Buffer[Buffer_Offset]; (2.a)
SplittedChannel1->Buffer[SplittedChannel1->Buffer_Size++]=Buffer[Buffer_Offset++]; (2.b)
SplittedChannel00->Buffer[SplittedChannel00->Buffer_Size++]=Buffer[Buffer_Offset]; (2.c)
SplittedChannel1->Buffer[SplittedChannel1->Buffer_Size++]=Buffer[Buffer_Offset++]; (2.d)
...
break;
case 24:
// Copy 6 bytes
SplittedChannel00->Buffer[SplittedChannel00->Buffer_Size++]=Buffer[Buffer_Offset]; (3.a)
SplittedChannel1->Buffer[SplittedChannel1->Buffer_Size++]=Buffer[Buffer_Offset++]; (3.b)
SplittedChannel00->Buffer[SplittedChannel00->Buffer_Size++]=Buffer[Buffer_Offset]; (3.c)
SplittedChannel1->Buffer[SplittedChannel1->Buffer_Size++]=Buffer[Buffer_Offset++]; (3.d)
...
break;
case 20:
// Copy 5 bytes
SplittedChannel00->Buffer[SplittedChannel00->Buffer_Size++]=Buffer[Buffer_Offset]; (4.a)
SplittedChannel00->Buffer[SplittedChannel00->Buffer_Size++]=Buffer[Buffer_Offset]; (4.b)
SplittedChannel00->Buffer[SplittedChannel00->Buffer_Size++]=Buffer[Buffer_Offset]; (4.c)
SplittedChannel00->Buffer[SplittedChannel00->Buffer_Size++]=Buffer[Buffer_Offset]; (4.d)
...
break;
}
}
At (1), we see the condition for the loop. Essentially, if there are more bytes to copy, the copy operation continues. As Buffer_Offset increases, when it reaches a value close to Buffer_Size, the BytesToCopy will be larger than the remaining bytes in the Buffer. As a result, the condition will be false and the loop will exit.
At (2), for the case where the audio has a bitdepth of 32 bits, the code copies one byte from Buffer to SplittedChannel00->Buffer and SplittedChannel1->Buffer, essentially splitting the audio channel into two. Note that the Buffer_Offset variable is incremented once every two copies, at (2.b) and (2.d). We see some very similar code for the case of a bitdepth of 24 at (3.b) and (3.d).
However, when bitdepth is 20 bits, the code similarly copies bytes from Buffer but note that Buffer_Offset is not incremented as there is no ++ operator unlike the previous cases. In each line, the code copies one byte (actually the same byte) to the SplittedChannel00->Buffer, incrementing only the SplittedChannel00->Buffer_Size variable. Since there is no check for the SplittedChannel00->Buffer_Size index used for copying, this can lead to a heap buffer overflow.
Under normal circumstances, the code would continue to overwrite data in heap memory until an unmapped or unwritable memory page was encountered. In certain cases, depending on the memory layout and the heap allocator, it is possible for an attacker to perform an arbitrary write before the library crashes.
This is the definition of the common::channel object:
int8u* Buffer;
...
channel()
{
Buffer=new int8u[32768];
...
}
At the object constructor, Buffer is allocated with an initial size of 32768 bytes. If Buffer happens to be located at a memory address lower than the common::channel object then during the copy operations at (4) above, the Buffer pointer will eventually be overwritten with an attacker controlled value. As a result, for the next copy, an attacker will have control of the the destination pointer, with the ability to overwrite the Buffer_Offset variable which is used for the loop condition at (1), allowing to exit the loop. Execution will continue, with corrupted heap data that can provide greater exploitation primitives for an attacker.
ASAN report:
==591==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000b12 at pc 0x63ed840df72a bp 0x7ffcadba7ab0 sp 0x7ffcadba7aa8
WRITE of size 1 at 0x502000000b12 thread T0
#0 0x63ed840df729 in MediaInfoLib::File_ChannelSplitting::Read_Buffer_Continue() /fuzz/src/Source/MediaInfo/Audio/File_ChannelSplitting.cpp:334:80
#1 0x63ed83f69f6e in MediaInfoLib::File__Analyze::Open_Buffer_Continue_Loop() /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1503:5
#2 0x63ed83f679d4 in MediaInfoLib::File__Analyze::Open_Buffer_Continue(unsigned char const*, unsigned long) /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1115:16
#3 0x63ed83f6e7d8 in MediaInfoLib::File__Analyze::Open_Buffer_Continue(MediaInfoLib::File__Analyze*, unsigned char const*, unsigned long, bool, double) /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1469:10
#4 0x63ed83ca093f in MediaInfoLib::File_Riff::AVI__movi_xxxx() /fuzz/src/Source/MediaInfo/Multiple/File_Riff_Elements.cpp:2634:13
#5 0x63ed83c8d096 in MediaInfoLib::File_Riff::Header_Begin() /fuzz/src/Source/MediaInfo/Multiple/File_Riff.cpp:882:30
#6 0x63ed83f73349 in MediaInfoLib::File__Analyze::Header_Manage() /fuzz/src/Source/MediaInfo/File__Analyze.cpp:2648:10
#7 0x63ed83f6fb93 in MediaInfoLib::File__Analyze::Buffer_Parse() /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1958:10
#8 0x63ed83f6a1a6 in MediaInfoLib::File__Analyze::Open_Buffer_Continue_Loop() /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1528:14
#9 0x63ed83f679d4 in MediaInfoLib::File__Analyze::Open_Buffer_Continue(unsigned char const*, unsigned long) /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1115:16
#10 0x63ed84092839 in MediaInfoLib::File__MultipleParsing::Read_Buffer_Continue() /fuzz/src/Source/MediaInfo/File__MultipleParsing.cpp:931:22
#11 0x63ed83f69f6e in MediaInfoLib::File__Analyze::Open_Buffer_Continue_Loop() /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1503:5
#12 0x63ed83f679d4 in MediaInfoLib::File__Analyze::Open_Buffer_Continue(unsigned char const*, unsigned long) /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1115:16
#13 0x63ed8326c903 in MediaInfoLib::MediaInfo_Internal::Open_Buffer_Continue(unsigned char const*, unsigned long) /fuzz/src/Source/MediaInfo/MediaInfo_Internal.cpp:1723:11
#14 0x63ed8326d7b9 in MediaInfoLib::MediaInfo_Internal::Open(unsigned char const*, unsigned long, unsigned char const*, unsigned long, unsigned long long) /fuzz/src/Source/MediaInfo/MediaInfo_Internal.cpp:1511:5
#15 0x63ed831d9dd1 in LLVMFuzzerTestOneInput /fuzz/harness/harness.cpp:6:15
#16 0x63ed831d9cc9 in ExecuteFilesOnyByOne /AFLplusplus/utils/aflpp_driver/aflpp_driver.c:267:7
#17 0x63ed831d9ab9 in LLVMFuzzerRunDriver /AFLplusplus/utils/aflpp_driver/aflpp_driver.c
#18 0x63ed831d965b in main /AFLplusplus/utils/aflpp_driver/aflpp_driver.c:323:10
#19 0x7055547d61c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#20 0x7055547d628a in __libc_start_main csu/../csu/libc-start.c:360:3
#21 0x63ed830f5c24 in _start (/fuzz/harness/build/harness_v26.01_vanilla_asan+0x310c24) (BuildId: 3fbcb2f1e982dfde280a167351fa91c95b983301)
0x502000000b12 is located 0 bytes after 2-byte region [0x502000000b10,0x502000000b12)
allocated by thread T0 here:
#0 0x63ed831d71f1 in operator new[](unsigned long) (/fuzz/harness/build/harness_v26.01_vanilla_asan+0x3f21f1) (BuildId: 3fbcb2f1e982dfde280a167351fa91c95b983301)
#1 0x63ed840dd0c4 in MediaInfoLib::File_ChannelSplitting::common::channel::resize(unsigned long) /fuzz/src/Project/CMake/../../Source/MediaInfo/Audio/File_ChannelSplitting.h:71:24
#2 0x63ed840dd0c4 in MediaInfoLib::File_ChannelSplitting::Read_Buffer_Continue() /fuzz/src/Source/MediaInfo/Audio/File_ChannelSplitting.cpp:275:34
#3 0x63ed83f69f6e in MediaInfoLib::File__Analyze::Open_Buffer_Continue_Loop() /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1503:5
#4 0x63ed83f679d4 in MediaInfoLib::File__Analyze::Open_Buffer_Continue(unsigned char const*, unsigned long) /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1115:16
#5 0x63ed83f6e7d8 in MediaInfoLib::File__Analyze::Open_Buffer_Continue(MediaInfoLib::File__Analyze*, unsigned char const*, unsigned long, bool, double) /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1469:10
#6 0x63ed83ca093f in MediaInfoLib::File_Riff::AVI__movi_xxxx() /fuzz/src/Source/MediaInfo/Multiple/File_Riff_Elements.cpp:2634:13
#7 0x63ed83c8d096 in MediaInfoLib::File_Riff::Header_Begin() /fuzz/src/Source/MediaInfo/Multiple/File_Riff.cpp:882:30
#8 0x63ed83f73349 in MediaInfoLib::File__Analyze::Header_Manage() /fuzz/src/Source/MediaInfo/File__Analyze.cpp:2648:10
#9 0x63ed83f6fb93 in MediaInfoLib::File__Analyze::Buffer_Parse() /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1958:10
#10 0x63ed83f6a1a6 in MediaInfoLib::File__Analyze::Open_Buffer_Continue_Loop() /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1528:14
#11 0x63ed83f679d4 in MediaInfoLib::File__Analyze::Open_Buffer_Continue(unsigned char const*, unsigned long) /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1115:16
#12 0x63ed84092839 in MediaInfoLib::File__MultipleParsing::Read_Buffer_Continue() /fuzz/src/Source/MediaInfo/File__MultipleParsing.cpp:931:22
#13 0x63ed83f69f6e in MediaInfoLib::File__Analyze::Open_Buffer_Continue_Loop() /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1503:5
#14 0x63ed83f679d4 in MediaInfoLib::File__Analyze::Open_Buffer_Continue(unsigned char const*, unsigned long) /fuzz/src/Source/MediaInfo/File__Analyze.cpp:1115:16
#15 0x63ed8326c903 in MediaInfoLib::MediaInfo_Internal::Open_Buffer_Continue(unsigned char const*, unsigned long) /fuzz/src/Source/MediaInfo/MediaInfo_Internal.cpp:1723:11
#16 0x63ed8326d7b9 in MediaInfoLib::MediaInfo_Internal::Open(unsigned char const*, unsigned long, unsigned char const*, unsigned long, unsigned long long) /fuzz/src/Source/MediaInfo/MediaInfo_Internal.cpp:1511:5
#17 0x63ed831d9dd1 in LLVMFuzzerTestOneInput /fuzz/harness/harness.cpp:6:15
#18 0x63ed831d9cc9 in ExecuteFilesOnyByOne /AFLplusplus/utils/aflpp_driver/aflpp_driver.c:267:7
2026-03-25 - Initial Vendor Contact
2026-03-25 - Vendor Disclosure
2026-05-12 - Vendor Patch Release
2026-05-20 - Public Release
Dimitrios Tatsis of Cisco TALOS