Skip to content

GPMF-parser: Heap-buffer-overflow in GPMF_FormattedData due to integer truncation in size validation #210

@xnqjns

Description

@xnqjns

Vulnerability Summary

A heap overflow vulnerability exists in the GPMF_FormattedData function. This vulnerability stems from an overly coarse validation logic for the remaining size of the buffer (using a right shift >> 2), which allows the validation to be bypassed and an out-of-bounds read (OOB Read) to be triggered when the remaining bytes are not a multiple of 4.

GPMF_ERR GPMF_FormattedData(GPMF_stream *ms, void *buffer, uint32_t buffersize, uint32_t sample_offset, uint32_t read_samples)
{
	if (ms && buffer)
	{
		uint8_t *data = (uint8_t *)&ms->buffer[ms->pos + 2];
		uint8_t *output = (uint8_t *)buffer;
		uint32_t sample_size = GPMF_SAMPLE_SIZE(ms->buffer[ms->pos + 1]);
		uint32_t remaining_sample_size = GPMF_DATA_PACKEDSIZE(ms->buffer[ms->pos + 1]);
		uint8_t type = GPMF_SAMPLE_TYPE(ms->buffer[ms->pos + 1]);
		uint32_t typesize = 1;
		uint32_t elements = 0;
		uint32_t typestringlength = 1;
		char complextype[64] = "L";

		if (type == GPMF_TYPE_NEST)
			return GPMF_ERROR_BAD_STRUCTURE;
		
		if (GPMF_OK != IsValidSize(ms, remaining_sample_size>>2)) //Affected code
			return GPMF_ERROR_BAD_STRUCTURE;
 

The use of bitwise right-shift (>> 2) performs a floor division by 4. If the buffer has remaining bytes that are not a multiple of 4 (e.g., 5 or 6 bytes), the check may pass even if a subsequent 2-byte or 4-byte read exceeds the actual physical boundary. In this specific crash, the logic enters case 2

case 2:
{
    uint16_t *data16 = (uint16_t *)data;
    *output16 = BYTESWAP16(*data16); // OOB Read occurs here
}

This issue also occurs in the default branch, but I've lost the corresponding crash file, but they both involve the same vulnerability.

default: //1, 16 or more not byteswapped
					for (j = 0; j < typesize; j++)
						*output++ = *data++; // OOB Read occurs here
					break;
==547763==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5120000005dc at pc 0x559d4f9804d6 bp 0x7ffdcc69b6f0 sp 0x7ffdcc69b6e8
READ of size 2 at 0x5120000005dc thread T0
    #0 0x559d4f9804d5 in GPMF_FormattedData /workspace/gpmf-parser/GPMF_parser.c:1262:18
    #1 0x559d4f96f915 in TestOneProtoInput(GPMFfuzzer::GPMF_Node const&) /workspace/gpmf-parser/harness.cc:130:21
    #2 0x559d4f96f915 in LLVMFuzzerTestOneInput /workspace/gpmf-parser/harness.cc:91:1
    #3 0x559d4f86fd9a in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/workspace/gpmf-parser/gpmf_libfuzzer+0x6fd9a) (BuildId: e737819d00e3fa2d493ce01c8a162b141ec22731)
    #4 0x559d4f857a33 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/workspace/gpmf-parser/gpmf_libfuzzer+0x57a33) (BuildId: e737819d00e3fa2d493ce01c8a162b141ec22731)
    #5 0x559d4f85dbf1 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/workspace/gpmf-parser/gpmf_libfuzzer+0x5dbf1) (BuildId: e737819d00e3fa2d493ce01c8a162b141ec22731)
    #6 0x559d4f88a1c6 in main (/workspace/gpmf-parser/gpmf_libfuzzer+0x8a1c6) (BuildId: e737819d00e3fa2d493ce01c8a162b141ec22731)
    #7 0x7f29b402a1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    #8 0x7f29b402a28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    #9 0x559d4f8521c4 in _start (/workspace/gpmf-parser/gpmf_libfuzzer+0x521c4) (BuildId: e737819d00e3fa2d493ce01c8a162b141ec22731)

0x5120000005dc is located 0 bytes after 284-byte region [0x5120000004c0,0x5120000005dc)
allocated by thread T0 here:
    #0 0x559d4f96b591 in operator new(unsigned long) (/workspace/gpmf-parser/gpmf_libfuzzer+0x16b591) (BuildId: e737819d00e3fa2d493ce01c8a162b141ec22731)
    #1 0x559d4f9702df in std::__new_allocator<unsigned char>::allocate(unsigned long, void const*) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/new_allocator.h:151:27
    #2 0x559d4f9702df in std::allocator_traits<std::allocator<unsigned char>>::allocate(std::allocator<unsigned char>&, unsigned long) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/alloc_traits.h:482:20
    #3 0x559d4f9702df in std::_Vector_base<unsigned char, std::allocator<unsigned char>>::_M_allocate(unsigned long) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:381:20
    #4 0x559d4f9702df in std::_Vector_base<unsigned char, std::allocator<unsigned char>>::_M_create_storage(unsigned long) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:398:33
    #5 0x559d4f9702df in std::_Vector_base<unsigned char, std::allocator<unsigned char>>::_Vector_base(unsigned long, std::allocator<unsigned char> const&) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:335:9
    #6 0x559d4f9702df in std::vector<unsigned char, std::allocator<unsigned char>>::vector(unsigned long, unsigned char const&, std::allocator<unsigned char> const&) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/stl_vector.h:571:9
    #7 0x559d4f96f6d6 in TestOneProtoInput(GPMFfuzzer::GPMF_Node const&) /workspace/gpmf-parser/harness.cc:101:26
    #8 0x559d4f96f6d6 in LLVMFuzzerTestOneInput /workspace/gpmf-parser/harness.cc:91:1
    #9 0x559d4f86fd9a in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/workspace/gpmf-parser/gpmf_libfuzzer+0x6fd9a) (BuildId: e737819d00e3fa2d493ce01c8a162b141ec22731)
    #10 0x559d4f857a33 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/workspace/gpmf-parser/gpmf_libfuzzer+0x57a33) (BuildId: e737819d00e3fa2d493ce01c8a162b141ec22731)
    #11 0x559d4f85dbf1 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/workspace/gpmf-parser/gpmf_libfuzzer+0x5dbf1) (BuildId: e737819d00e3fa2d493ce01c8a162b141ec22731)
    #12 0x559d4f88a1c6 in main (/workspace/gpmf-parser/gpmf_libfuzzer+0x8a1c6) (BuildId: e737819d00e3fa2d493ce01c8a162b141ec22731)
    #13 0x7f29b402a1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    #14 0x7f29b402a28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    #15 0x559d4f8521c4 in _start (/workspace/gpmf-parser/gpmf_libfuzzer+0x521c4) (BuildId: e737819d00e3fa2d493ce01c8a162b141ec22731)

SUMMARY: AddressSanitizer: heap-buffer-overflow /workspace/gpmf-parser/GPMF_parser.c:1262:18 in GPMF_FormattedData
Shadow bytes around the buggy address:
  0x512000000300: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x512000000380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x512000000400: 00 00 00 00 00 00 00 00 00 00 00 05 fa fa fa fa
  0x512000000480: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x512000000500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x512000000580: 00 00 00 00 00 00 00 00 00 00 00[04]fa fa fa fa
  0x512000000600: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x512000000680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x512000000700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x512000000780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x512000000800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==547763==ABORTING

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions