POSTED BY: anestis / 22.07.2016

Android stagefright impeg2d_dec_pic_data_thread integer overflow

CENSUS ID:CENSUS-2016-0005
CVE ID:CVE-2016-0835
Android ID:26070014
Affected Products:Android OS 6.0 — 6.0.1
Class:Integer Overflow (CWE-190) / Underflow (CWE-191)
Discovered by:Anestis Bechtsoudis

Android provides a media playback engine at the native level called Stagefright that comes built-in with software-based codecs for several popular media formats. Stagefright features for audio and video playback include integration with OpenMAX codecs, session management, time-synchronized rendering, transport control, and DRM.

CENSUS engineers have discovered that the MPEG-2 software decoder invoked by libstagefright suffers from an unsigned integer overflow / underflow when processing the decoder variable that stores the number of slice MacroBlocks (MBs) to be processed for the current frame. The overflow condition can be triggered when the decoder is executed in a multi-core system, while an underflow in the same unsigned variable can be triggered on single-core systems. The vulnerability can be triggered remotely when a victim Android device starts decoding an MPEG-2 compressed video resource (MMS, chat apps, browser, etc.) using the vulnerable built-in software decoder. Successful exploitation of the vulnerability can result into executing unauthorized code with the increased permissions of the mediaserver daemon.

Details

Multi-core device (unsigned integer overflow)

In the impeg2d_dec_pic_data_thread() procedure at line 882 of file impeg2d_dec_hdr.c, the ps_dec->u2_num_mbs_left calculation parameters are not sanity checked, thus allowing an overflow to occur if ps_dec->i4_start_mb_y > ps_dec->i4_end_mb_y or when ps_dec->u2_num_horiz_mb becomes very large.

+android-6.0.0_r2/external/libmpeg2/decoder/impeg2d_dec_hdr.c
839 void impeg2d_dec_pic_data_thread(dec_state_t *ps_dec)
840 {
...
877                     i4_cur_row      = s_job.i2_start_mb_y;
878                     ps_dec->i4_start_mb_y = s_job.i2_start_mb_y;
879                     ps_dec->i4_end_mb_y = s_job.i2_end_mb_y;
880                     ps_dec->u2_mb_x = 0;
881                     ps_dec->u2_mb_y = ps_dec->i4_start_mb_y;
882                     ps_dec->u2_num_mbs_left = 
           (ps_dec->i4_end_mb_y — ps_dec->i4_start_mb_y) * 
           ps_dec->u2_num_horiz_mb;  /* Can overflow u2_num_mbs_left */

The following GDB session on a multi-core device demonstrates that our PoC can trigger an integer overflow condition due to ps_dec->i4_start_mb_y being greater than ps_dec->i4_end_mb_y.

gdb$ b external/libmpeg2/decoder/impeg2d_dec_hdr.c:900 if ps_dec->i4_start_mb_y > ps_dec->i4_end_mb_y
No source file named external/libmpeg2/decoder/impeg2d_dec_hdr.c.
Breakpoint 1 (external/libmpeg2/decoder/impeg2d_dec_hdr.c:900 if ps_dec->i4_start_mb_y > ps_dec->i4_end_mb_y) pending.
gdb$ c
Continuing.
[New Thread 7454]
[New Thread 7452]
[New Thread 7455]
[New Thread 7456]
[New Thread 7458]
[New Thread 7457]
[New Thread 7459]
[Switching to Thread 7457]
--------------------------------------------------------------------------[regs]
  R0:  0xB5CA71D8  R1: 0xB5CA7208  R2:  0xB5CA7288  R3:  0xFFFFFFEC
  R4:  0xB5CA7000  R5: 0x0000001D  R6:  0xB5CA71D8  R7:  0xB5CAF000
  R8:  0x00000001  R9: 0xB5CA720E  R10: 0xB4B8A8E4  R11: 0x00000000
  R12: 0x0000001D  SP: 0xB4B8A8D8  LR:  0xFFFFFC7C  PC:  0xB5C71714  N z c v q j e a i f t
[0xB4B8A8D8]------------------------------------------------------[stack]
0xB4B8A928 : 78 A9 B8 B4 00 00 00 00 — 30 F9 9F B5 30 B9 A8 B4 x.......0...0...
0xB4B8A918 : C9 A3 B6 B6 38 A9 B8 B4 — C9 A3 B6 B6 45 4B B4 B6 ....8.......EK..
0xB4B8A908 : 00 41 3A B6 00 C0 A8 B4 — B0 16 C7 B5 E9 A3 B6 B6 .A:.............
0xB4B8A8F8 : 70 A9 B8 B4 30 A9 B8 B4 — 78 00 00 00 80 19 C6 B5 p...0...x.......
0xB4B8A8E8 : 1D 00 09 00 E0 4D 00 00 — D3 4E B9 B6 30 A9 B8 B4 .....M...N..0...
0xB4B8A8D8 : 01 00 00 00 00 00 00 00 — 00 00 00 00 00 00 00 00 ................
--------------------------------------------------------------------------[code]
=> 0xb5c71714 :	mov	r0, r4
   0xb5c71718 :	bl	0xb5c71508 
   0xb5c7171c :	cmp	r0, #0
   0xb5c71720 :	bne	0xb5c717c0 
   0xb5c71724 :	mov	r0, r6
   0xb5c71728 :	mov	r1, #32
   0xb5c7172c :	bl	0xb5c70908 
   0xb5c71730 :	lsr	r11, r0, #8
--------------------------------------------------------------------------------

Breakpoint 1, impeg2d_dec_pic_data_thread (ps_dec=0xb5ca7000) at external/libmpeg2/decoder/impeg2d_dec_hdr.c:900
900	            e_error = impeg2d_dec_slice(ps_dec);
gdb$ p ps_dec->i4_end_mb_y
$1 = 0x9
gdb$ p ps_dec->i4_start_mb_y
$2 = 0x1d
gdb$ p ps_dec->u2_num_mbs_left
$3 = 0xfc7c   # variable has overflown since i4_start_mb_y > i4_end_mb_y

The accompanying ASan report against the master branch is also demonstrating the unsigned integer overflow which later results into an OOB write heap overflow.

=================================================================
==16638==AddressSanitizer: while reporting a bug found another one. Ignoring.
==16638==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xb37ff120 at pc 0xb6d64878 bp 0xb01fe908 sp 0xb01fe904
WRITE of size 1 at 0xb37ff120 thread T3 (e.mpeg2.decoder)
    #0 0xb6d64877 in impeg2d_mc_fullx_halfy external/libmpeg2/decoder/impeg2d_mc.c:1154
    #1 0xb6d59ef7 in impeg2d_motion_comp_recon_buf external/libmpeg2/decoder/impeg2d_mc.c:190
    #2 0xb6d59ef7 in impeg2d_mc_fw_or_bk_mb external/libmpeg2/decoder/impeg2d_mc.c:262
    #3 0xb6d43117 in impeg2d_dec_p_b_slice external/libmpeg2/decoder/impeg2d_pnb_pic.c:525
    #4 0xb6d51bb7 in impeg2d_dec_slice external/libmpeg2/decoder/impeg2d_dec_hdr.c:824
    #5 0xb6d5214f in impeg2d_dec_pic_data_thread external/libmpeg2/decoder/impeg2d_dec_hdr.c:914
    #6 0xb6d553ff in impeg2d_dec_pic_data external/libmpeg2/decoder/impeg2d_dec_hdr.c:1333
    #7 0xb6d56a57 in impeg2d_process_video_bit_stream external/libmpeg2/decoder/impeg2d_dec_hdr.c:1688
    #8 0xb6d3e21b in impeg2d_dec_frm external/libmpeg2/decoder/impeg2d_decoder.c:198
    #9 0xb6d3baf3 in impeg2d_api_entity external/libmpeg2/decoder/impeg2d_api_main.c:3295
    #10 0xb6d2a9a7 in android::SoftMPEG2::onQueueFilled(unsigned int) frameworks/av/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp:639
    #11 0xb6a59a37 in android::SimpleSoftOMXComponent::onMessageReceived(android::sp const&) frameworks/av/media/libstagefright/omx/SimpleSoftOMXComponent.cpp:375
    #12 0xb6a5e4e3 in android::AHandlerReflector::onMessageReceived(android::sp const&) frameworks/av/include/media/stagefright/foundation/AHandlerReflector.h:35
    #13 0xb5457a53 in android::AHandler::deliverMessage(android::sp const&) frameworks/av/media/libstagefright/foundation/AHandler.cpp:27
    #14 0xb54640ef in android::AMessage::deliver() frameworks/av/media/libstagefright/foundation/AMessage.cpp:354
    #15 0xb545b5d3 in android::ALooper::loop() frameworks/av/media/libstagefright/foundation/ALooper.cpp:216
    #16 0xb69fced5 in android::Thread::_threadLoop(void*) system/core/libutils/Threads.cpp:752
    #17 0xb53f4adf in __pthread_start(void*) bionic/libc/bionic/pthread_create.cpp:200
    #18 0xb53c74bb in __start_thread bionic/libc/bionic/clone.cpp:41

0xb37ff120 is located 32 bytes to the right of 518400-byte region [0xb3780800,0xb37ff100)
allocated by thread T0 here:
    #19 0xb62983d3 in malloc_stats ??:?
    #20 0xb6d2609f in android::SoftMPEG2::initDecoder() frameworks/av/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp:337
    #21 0xb6d28caf in android::SoftMPEG2::reInitDecoder() frameworks/av/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp:441
    #22 0xb6d29137 in android::SoftMPEG2::internalSetParameter(OMX_INDEXTYPE, void*) frameworks/av/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp:464
    #23 0xb6a56dbf in android::SimpleSoftOMXComponent::setParameter(OMX_INDEXTYPE, void*) frameworks/av/media/libstagefright/omx/SimpleSoftOMXComponent.cpp:117
    #24 0xb6a3d297 in android::OMXNodeInstance::setParameter(OMX_INDEXTYPE, void const*, unsigned int) frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp:417
    #25 0xb6a303a3 in android::OMX::setParameter(unsigned int, OMX_INDEXTYPE, void const*, unsigned int) frameworks/av/media/libstagefright/omx/OMX.cpp:311
    #26 0xb4f609c3 in android::MuxOMX::setParameter(unsigned int, OMX_INDEXTYPE, void const*, unsigned int) frameworks/av/media/libstagefright/OMXClient.cpp:274
    #27 0xb4f70def in android::OMXCodec::setVideoOutputFormat(char const*, android::sp const&) frameworks/av/media/libstagefright/OMXCodec.cpp:1417
    #28 0xb4f68823 in android::OMXCodec::configureCodec(android::sp const&) frameworks/av/media/libstagefright/OMXCodec.cpp:662
    #29 0xb4f660d3 in android::OMXCodec::Create(android::sp const&, android::sp const&, bool, android::sp const&, char const*, unsigned int, android::sp const&) frameworks/av/media/libstagefright/OMXCodec.cpp:378
    #30 0xb6fd70c3 in
    #31 0xb53c48b5 in __libc_init bionic/libc/bionic/libc_init_dynamic.cpp:114
    #13 0x1  ()

Thread T3 (e.mpeg2.decoder) created by T0 here:
    #32 0xb627cc4f in __asan_memmove ??:?
    #33 0xb69fc9fb in androidCreateRawThreadEtc system/core/libutils/Threads.cpp:160
    #34 0xb6a5638f in SimpleSoftOMXComponent frameworks/av/media/libstagefright/omx/SimpleSoftOMXComponent.cpp:42
    #35 0xb6a602ff in SoftVideoDecoderOMXComponent frameworks/av/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp:54
    #36 0xb6d2565b in SoftMPEG2 frameworks/av/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp:66
    #37 0xb6d2ca23 in createSoftOMXComponent(char const*, OMX_CALLBACKTYPE const*, void*, OMX_COMPONENTTYPE**) frameworks/av/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp:770
    #38 0xb6a5fa3b in android::SoftOMXPlugin::makeComponentInstance(char const*, OMX_CALLBACKTYPE const*, void*, OMX_COMPONENTTYPE**) frameworks/av/media/libstagefright/omx/SoftOMXPlugin.cpp:112
    #39 0xb6a37d37 in android::OMXMaster::makeComponentInstance(char const*, OMX_CALLBACKTYPE const*, void*, OMX_COMPONENTTYPE**) frameworks/av/media/libstagefright/omx/OMXMaster.cpp:138
    #40 0xb6a2dc83 in android::OMX::allocateNode(char const*, android::sp const&, unsigned int*) frameworks/av/media/libstagefright/omx/OMX.cpp:243
    #41 0xb4f5fd0b in android::MuxOMX::allocateNode(char const*, android::sp const&, unsigned int*) frameworks/av/media/libstagefright/OMXClient.cpp:233
    #42 0xb4f65e7f in android::OMXCodec::Create(android::sp const&, android::sp const&, bool, android::sp const&, char const*, unsigned int, android::sp const&) frameworks/av/media/libstagefright/OMXCodec.cpp:367
    #43 0xb6fd70c3 in
    #44 0xb53c48b5 in __libc_init bionic/libc/bionic/libc_init_dynamic.cpp:114
    #13 0x1  ()

SUMMARY: AddressSanitizer: heap-buffer-overflow (/data/lib/libstagefright_soft_mpeg2dec.so+0x45877)
Shadow bytes around the buggy address:
  0x166ffdd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x166ffde0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x166ffdf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x166ffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x166ffe10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x166ffe20: fa fa fa fa[fa]fa fa fa fa fa fa fa fa fa fa fa
  0x166ffe30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x166ffe40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x166ffe50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x166ffe60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x166ffe70: 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
  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
  Left alloca redzone:     ca
  Right alloca redzone:    cb

Single-core device (unsigned integer underflow)

The impeg2d_dec_p_b_slice() procedure is not verifying that a slice has available MacroBlocks (ps_dec->u2_num_mbs_left != 0) before entering the processing loop at lines 469-696. If the unsigned ps_dec->u2_num_mbs_left variable is zero, subtraction at line 681 will trigger an integer underflow resulting into a UINT16_MAX (65535) value for the same variable.

+android-6.0.0_r2/external/libmpeg2/decoder/impeg2d_pnb_pic.c
442 IMPEG2D_ERROR_CODES_T impeg2d_dec_p_b_slice(dec_state_t *ps_dec)
443 {
...
469     do   /* No check if loop enters with "u2_num_mbs_left" being 0 */
470     {
...
524             PROFILE_DISABLE_MC_IF0
525             ps_dec_mb_params->pf_mc(ps_dec);
...
681         ps_dec->u2_num_mbs_left--;   
/* if "u2_num_mbs_left" == 0 this will underflow to UINT16_MAX, 
resulting into overflowing MB output buffers */
682         ps_dec->u2_first_mb = 0;
683         ps_dec->u2_mb_x++;
684
685         if(ps_dec->s_bit_stream.u4_offset > ps_dec->s_bit_stream.u4_max_offset)
686         {
687             return IMPEG2D_BITSTREAM_BUFF_EXCEEDED_ERR;
688         }
689         else if (ps_dec->u2_mb_x == ps_dec->u2_num_horiz_mb)
690         {
691             ps_dec->u2_mb_x = 0;
692             ps_dec->u2_mb_y++;
693
694         }
695     }
696     while(ps_dec->u2_num_mbs_left != 0 &&
 impeg2d_bit_stream_nxt(&ps_dec->s_bit_stream,23) != 0x0);
697     return e_error;
698 }

The following GDB session on a single-core device demonstrates that our PoC triggers the integer underflow condition due to impeg2d_dec_p_b_slice() being invoked with a zero value for u2_num_mbs_left.


gdb$ b impeg2d_api_set_num_cores
Function "impeg2d_api_set_num_cores" not defined.
Breakpoint 1 (impeg2d_api_set_num_cores) pending.
gdb$ commands 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>silent
>set ps_ip->u4_num_cores = 1  # override number of cores to emulate single-core device
>c
>end
gdb$ b impeg2d_dec_p_b_slice if ps_dec->u2_num_mbs_left == 0   # break if invoked with zero MBs
Function "impeg2d_dec_p_b_slice" not defined.
Breakpoint 2 (impeg2d_dec_p_b_slice if ps_dec->u2_num_mbs_left == 0) pending.
gdb$ c
Continuing.
[New Thread 7100]
[Switching to Thread 7100]
--------------------------------------------------------------------------[regs]
  R0:  0xB5CF1000  R1: 0xB5CA53B0  R2:  0xB5CF123C  R3:  0x00000080
  R4:  0x00000000  R5: 0xB5CF1000  R6:  0x00000080  R7:  0xB5CF9000
  R8:  0x00000001  R9: 0xB5CF120E  R10: 0xB5C9A374  R11: 0x00000001
  R12: 0x0000023E  SP: 0xB5C9A358  LR:  0xB5CAA640  PC:  0xB5CA53B0  N z c v Q j e a i f t
[0xB5C9A358]------------------------------------------------------[stack]
0xB5C9A3A8 : 00 00 00 00 00 00 00 00 — 00 00 00 00 00 00 00 00 ................
0xB5C9A398 : 00 00 00 00 D0 02 00 00 — 04 E1 CC B5 BC B4 CA B5 ................
0xB5C9A388 : 00 00 00 00 B3 01 00 00 — B5 01 00 00 B2 01 00 00 ................
0xB5C9A378 : 55 14 6A 22 E0 5D 03 00 — 00 00 00 00 00 10 CF B5 U.j".]..........
0xB5C9A368 : 00 00 1C B5 3C 00 1C B5 — A0 01 00 00 01 01 00 00 ....<...........
0xB5C9A358 : 00 10 CF B5 1C 00 00 00 — D8 11 CF B5 1C A7 CA B5 ................
--------------------------------------------------------------------------[code]
=> 0xb5ca53b0 :	strd	r4, [sp, #-36]!	; 0xffffffdc
   0xb5ca53b4 :	mov	r1, #0
   0xb5ca53b8 :	mov	r4, r0
   0xb5ca53bc :	strd	r6, [sp, #8]
   0xb5ca53c0 :	mov	r2, #16
   0xb5ca53c4 :	add	r0, r0, #576	; 0x240
   0xb5ca53c8 :	strd	r8, [sp, #16]
   0xb5ca53cc :	ldr	r8, [pc, #2204]	; 0xb5ca5c70 
--------------------------------------------------------------------------------

Breakpoint 2, impeg2d_dec_p_b_slice (ps_dec=0xb5cf1000) at external/libmpeg2/decoder/impeg2d_pnb_pic.c:443
443	{
gdb$ p ps_dec->u2_num_mbs_left
$1 = 0x0   # number of MBs to process is zero
gdb$ b external/libmpeg2/decoder/impeg2d_pnb_pic.c:682
Breakpoint 3 at 0xb5ca54c4: file external/libmpeg2/decoder/impeg2d_pnb_pic.c, line 682.
gdb$ c
Continuing.
--------------------------------------------------------------------------[regs]
  R0:  0x00035DE0  R1: 0xB5CF1288  R2:  0x00000000  R3:  0x00000000
  R4:  0xB5CF1000  R5: 0x00000000  R6:  0x00014640  R7:  0x00000001
  R8:  0xB5CB7EAC  R9: 0x00000008  R10: 0xB5CF1208  R11: 0xB5CF1494
  R12: 0x00026F8C  SP: 0xB5C9A2F8  LR:  0xB5CB63B0  PC:  0xB5CA54C4  n Z c v Q j e a i f t
[0xB5C9A2F8]------------------------------------------------------[stack]
0xB5C9A348 : 0E 12 CF B5 74 A3 C9 B5 — 01 00 00 00 40 A6 CA B5 ....t.......@...
0xB5C9A338 : 00 10 CF B5 80 00 00 00 — 00 90 CF B5 01 00 00 00 ................
0xB5C9A328 : 00 00 00 00 8C 12 CF B5 — 00 00 00 00 00 00 00 00 ................
0xB5C9A318 : 00 19 B5 B5 00 90 CF B5 — 80 10 CF B5 0A 12 CF B5 ................
0xB5C9A308 : 79 FF FF FF F0 62 CB B5 — F6 11 CF B5 A0 14 CF B5 y....b..........
0xB5C9A2F8 : 08 00 00 00 A0 05 00 00 — A0 05 00 00 73 FF FF FF ............s...
--------------------------------------------------------------------------[code]
=> 0xb5ca54c4 :	ldr	r2, [sp, #52]	; 0x34
   0xb5ca54c8 :	sub	r9, r3, #1
   0xb5ca54cc :	uxth	r5, r7
   0xb5ca54d0 :	uxth	r6, r9
   0xb5ca54d4 :	cmp	r12, r0
   0xb5ca54d8 :	strh	r6, [r1, #2]
   0xb5ca54dc :	mov	r1, #0
   0xb5ca54e0 :	strh	r1, [r2]
--------------------------------------------------------------------------------

Breakpoint 3, impeg2d_dec_p_b_slice (ps_dec=0xb5cf1000) at external/libmpeg2/decoder/impeg2d_pnb_pic.c:682
gdb$ p ps_dec->u2_num_mbs_left
$2 = 0xffff   # underflow occurred

Various Android OS devices (e.g. Nexus 6) have been identified not to implement a hardware MPEG-2 decoder and thus are directly exposed to the above issue when running vulnerable versions of the software decoder.

During testing, the vulnerability was triggered against the mediaserver daemon through the Nexus default video player.

Testing was carried out on the following devices and software setups:

  • google/shamu/shamu:6.0/MRA58N/2289998:user/release-keys
  • Android/aosp_hammerhead/hammerhead:6.0/MASTER/anestisb11211639:userdebug/test-keys (“frameworks/av “commit [8ec6ab3], “external/libmpeg2” commit [e43b011])

Discussion

The vulnerablity has been fixed for supported Android OS versions as described in the April 2016 Nexus Security bulletin. Users are advised to upgrade to the latest stable Android release.

Disclosure Timeline

Vendor Contact:December 6th, 2015
Vendor Assigned Internal ID:December 7th, 2015
Vendor Triaged Vulnablity (Critical):December 8th, 2015
Vendor Updated Release Date:January 25th, 2016
Vendor Assigned CVE:March 7th, 2016
Vendor Patch Release:April 4th, 2016
Public Advisory:July 22nd, 2016