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 |