Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time

libwebm Vp9HeaderParser UAF by PrintVP9Info

credit by ADLab of Venustech.

Overview

A Use-After-Free bug exists in function OutputCluster in webm_info.cc of webmproject/libwebm below git version 12b42e9. The bug exists because of the combination of process logic in Vp9HeaderParser::SetFrame and misuse of it.

The ASAN log are as follows:

./webm_info -all -i ../id00010-uaf.webm 
EBML Header:
  EBMLVersion       : 1
  EBMLReadVersion   : 1
  EBMLMaxIDLength   : 4
  EBMLMaxSizeLength : 8
  Doc Type          : webm
  DocTypeVersion    : 4
  DocTypeReadVersion: 2
Segment:  @: 43  size: 11549
  SegmentInfo:  @: 278  size: 67
    TimecodeScale : 1000000 
    Duration(secs): 2.08
    MuxingApp     : Google
    WritingApp    : Google
  Tracks:  @: 345  size: 84
    Track:  @: 357  size: 72
      TrackType   : 1
      TrackNumber : 1
      CodecID     : V_VP9
      DefaultDuration: 80000000
      PixelWidth  : 256
      PixelHeight : 144
      DisplayWidth  : 128
      DisplayHeight : 144
      DisplayUnit   : 0
  Clusters (count):1
  Cluster:  @: 429  size: 11123
    Timecode (sec) : 0
    Duration (sec) : 2
    # Blocks       : 26
    Block: type:V frame:I secs:    0 @_payload: 446 size_payload: 45 key:1 v:0 altref:0 errm:0 ct:1 fpm:0 cs:0
    Block: type:V frame:I secs: 0.08 @_payload: 493 size_payload: 44 key:1 v:0 altref:0 errm:0 ct:1 fpm:0 cs:0
=================================================================
==48021==ERROR: AddressSanitizer: heap-use-after-free on address 0x61900000558f at pc 0x000000420c5d bp 0x7ffea1c25d60 sp 0x7ffea1c25d50
READ of size 1 at 0x61900000558f thread T0
    #0 0x420c5c in vp9_parser::Vp9HeaderParser::ReadBit() /home/default/Desktop/libwebm-master-asan/common/vp9_header_parser.cc:119
    #1 0x420c5c in vp9_parser::Vp9HeaderParser::VpxReadLiteral(int) /home/default/Desktop/libwebm-master-asan/common/vp9_header_parser.cc:130
    #2 0x420c5c in vp9_parser::Vp9HeaderParser::ParseUncompressedHeader() /home/default/Desktop/libwebm-master-asan/common/vp9_header_parser.cc:37
    #3 0x43b684 in PrintVP9Info /home/default/Desktop/libwebm-master-asan/webm_info.cc:719
    #4 0x43b684 in OutputCluster /home/default/Desktop/libwebm-master-asan/webm_info.cc:1027
    #5 0x413818 in main /home/default/Desktop/libwebm-master-asan/webm_info.cc:1273
    #6 0x7f578f3d182f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #7 0x417d98 in _start (/home/default/Desktop/libwebm-master-asan/webm_info+0x417d98)

0x61900000558f is located 15 bytes inside of 1065-byte region [0x619000005580,0x6190000059a9)
freed by thread T0 here:
    #0 0x7f578fdacb2a in operator delete(void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99b2a)
    #1 0x440fbf in __gnu_cxx::new_allocator<unsigned char>::deallocate(unsigned char*, unsigned long) /usr/include/c++/5/ext/new_allocator.h:110
    #2 0x440fbf in std::allocator_traits<std::allocator<unsigned char> >::deallocate(std::allocator<unsigned char>&, unsigned char*, unsigned long) /usr/include/c++/5/bits/alloc_traits.h:517
    #3 0x440fbf in std::_Vector_base<unsigned char, std::allocator<unsigned char> >::_M_deallocate(unsigned char*, unsigned long) /usr/include/c++/5/bits/stl_vector.h:178
    #4 0x440fbf in std::vector<unsigned char, std::allocator<unsigned char> >::_M_default_append(unsigned long) /usr/include/c++/5/bits/vector.tcc:578

previously allocated by thread T0 here:
    #0 0x7f578fdac532 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99532)
    #1 0x440ed7 in __gnu_cxx::new_allocator<unsigned char>::allocate(unsigned long, void const*) /usr/include/c++/5/ext/new_allocator.h:104
    #2 0x440ed7 in std::allocator_traits<std::allocator<unsigned char> >::allocate(std::allocator<unsigned char>&, unsigned long) /usr/include/c++/5/bits/alloc_traits.h:491
    #3 0x440ed7 in std::_Vector_base<unsigned char, std::allocator<unsigned char> >::_M_allocate(unsigned long) /usr/include/c++/5/bits/stl_vector.h:170
    #4 0x440ed7 in std::vector<unsigned char, std::allocator<unsigned char> >::_M_default_append(unsigned long) /usr/include/c++/5/bits/vector.tcc:557

SUMMARY: AddressSanitizer: heap-use-after-free /home/default/Desktop/libwebm-master-asan/common/vp9_header_parser.cc:119 vp9_parser::Vp9HeaderParser::ReadBit()
Shadow bytes around the buggy address:
  0x0c327fff8a60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c327fff8a70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c327fff8a80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c327fff8a90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c327fff8aa0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c327fff8ab0: fd[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fff8ac0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fff8ad0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fff8ae0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fff8af0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fff8b00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
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
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  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
==48021==ABORTING

Root cause

In function OutputCluster, the argument vp9_parser::Vp9HeaderParser* parser was initialized by vp9parser::Vp9HeaderParser::SetFrame in PrintVP9Info:

void PrintVP9Info(const uint8_t* data, int size, FILE* o, int64_t time_ns,
                  FrameStats* stats, vp9_parser::Vp9HeaderParser* parser,
                  vp9_parser::Vp9LevelStats* level_stats) {
  if (size < 1)
    return;

  uint32_t sizes[8];
  int i = 0, count = 0;
  ParseSuperframeIndex(data, size, sizes, &count);

  // Remove all frames that are less than window size.
  while (!stats->window.empty() &&
         stats->window.front() < (time_ns - (kNanosecondsPerSecondi - 1)))
    stats->window.pop();

  do {
    const size_t frame_length = (count > 0) ? sizes[i] : size;
    if (frame_length > std::numeric_limits<int>::max() ||
        static_cast<int>(frame_length) > size) {
      fprintf(o, " invalid VP9 frame size (%u)\n",
              static_cast<uint32_t>(frame_length));
      return;
    }
    parser->SetFrame(data, frame_length);
    if (!parser->ParseUncompressedHeader())
      return;
    ...
bool Vp9HeaderParser::SetFrame(const uint8_t* frame, size_t length) {
  if (!frame || length == 0)
    return false;

  frame_ = frame;
  frame_size_ = length;
  bit_offset_ = 0;
  profile_ = -1;
  show_existing_frame_ = 0;
  key_ = 0;
  altref_ = 0;
  error_resilient_mode_ = 0;
  intra_only_ = 0;
  reset_frame_context_ = 0;
  color_space_ = 0;
  color_range_ = 0;
  subsampling_x_ = 0;
  subsampling_y_ = 0;
  refresh_frame_flags_ = 0;
  return true;
}

If parser was initialized once before, its property frame_ would not be changed because of

if (!frame || length == 0)return false;, then frame_ was used in Vp9HeaderParser::ReadBit called by Vp9HeaderParser::VpxReadLiteral and by Vp9HeaderParser::ParseUncompressedHeader.

The initialization of parser happens in Vp9HeaderParser::SetFrame, of which the argument data was passed from vector_data in OutputCluster. While in function OutputCluster, the vector_data as a std::vector<unsigned char>, is always able to be reallocated in code:

vector_data.resize(frame.len + 1024);

But theframe_ of parser was never reinitialized. That leads to a Use-After-Free situation.

Fix suggestion

Vp9HeaderParser::SetFrame should be reconsidered to be reinitialized-supported.