php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71535 Integer overflow in zend_mm_alloc_heap()
Submitted: 2016-02-05 11:13 UTC Modified: 2016-02-22 00:21 UTC
From: hlt99 at blinkenshell dot org Assigned: dmitry (profile)
Status: Closed Package: *General Issues
PHP Version: 7.0.3 OS: Arch Linux (64-bit)
Private report: No CVE-ID: None
 [2016-02-05 11:13 UTC] hlt99 at blinkenshell dot org
Description:
------------
# Build instructions

    ../configure --enable-debug --enable-exif && make


# Description

The debug code in zend_mm_alloc_heap() contains an integer overflow bug. As shown in the code snippet below the code for memory alignment may increase the size parameter resulting in an integer overflow.


    (PHP-7.0.2) zend_alloc.c:
    1346   static zend_always_inline void *zend_mm_alloc_heap(zend_mm_heap *heap, size_t size /*...*/)
    1347   {
    [...]
    1349   #if ZEND_DEBUG
    [...]
    1355    size = ZEND_MM_ALIGNED_SIZE(size) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info));
            //                                ^-- possible int overflow

Overflowing the size parameter leads to a successful heap memory allocation.
Then a valid pointer pointing to a much smaller then requested heap memory
section is returned to the caller.

This bug can be triggered and exploited using [1].

[1] For more info on the security impact of that bug see #71534 (Type confusion
    in exif_read_data() leading to heap overflow)!


# PHP versions known to be affected

        7.0.2 (git)
        7.0.3 (git)

Versions prior to 7.0.2 have not been tested.


# PHP versions known to be not affected

        5.6.18 (git)

Versions prior to 5.6.18 have not been tested.

Test script:
---------------
/*
   exif_read_data.php
   poc.tiff:
   http://hlt99.blinkenshell.org/php/poc.tiff
*/
<?php
   $data = exif_read_data("poc.tiff", NULL, true, true);
?>

Expected result:
----------------
Fatal out of memory error

Actual result:
--------------
# GDB Log

    $ gdb sapi/cli/php
    [...]
    gdb$ b exif.c:3675
    gdb$ r exif_read_data.php
    [...]
    gdb$ si // until _safe_emalloc
    [----------------------------------registers-----------------------------------]
    RAX: 0xfc89 
    RBX: 0x1655c20 --> 0x607e ('~`')
    RCX: 0xfa73fe ("/home/rc0r/tmp/php-src/ext/exif/exif.c")
    RDX: 0x0 
    RSI: 0x1 
    RDI: 0xffffffffffffffff 
    RBP: 0x7fffffffaa00 --> 0x7ffff2e5ea00 --> 0x16199b8 --> 0xc89d20 (<php_stdiop_write>:	push   r14)
    RSP: 0x7fffffffa730 --> 0x0 
    RIP: 0x878410 (<exif_process_IFD_in_TIFF+4560>:	call   0xcd2480 <_safe_emalloc>)
    R8 : 0xe5b 
    R9 : 0x0 
    R10: 0x1627348 --> 0x1645c20 --> 0x9200000000 
    R11: 0x7fffffffaa00 --> 0x7ffff2e5ea00 --> 0x16199b8 --> 0xc89d20 (<php_stdiop_write>:	push   r14)
    R12: 0x1 
    R13: 0xe 
    R14: 0x1655c20 --> 0x607e ('~`')
    R15: 0x1627348 --> 0x1645c20 --> 0x9200000000
    EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
    [-------------------------------------code-------------------------------------]
       0x878403 <exif_process_IFD_in_TIFF+4547>:	lea    rcx,[rip+0x72eff4]        # 0xfa73fe
       0x87840a <exif_process_IFD_in_TIFF+4554>:	mov    r15,r10
       0x87840d <exif_process_IFD_in_TIFF+4557>:	mov    rbp,r11
    => 0x878410 <exif_process_IFD_in_TIFF+4560>:	call   0xcd2480 <_safe_emalloc>
       0x878415 <exif_process_IFD_in_TIFF+4565>:	mov    QWORD PTR [rbp+0x110],rax
       0x87841c <exif_process_IFD_in_TIFF+4572>:	mov    rdi,QWORD PTR [rbp+0x0]
       0x878420 <exif_process_IFD_in_TIFF+4576>:	mov    rsi,QWORD PTR [rbp+0x108]
       0x878427 <exif_process_IFD_in_TIFF+4583>:	xor    edx,edx
    Guessed arguments:
    arg[0]: 0xffffffffffffffff 
    arg[1]: 0x1 
    arg[2]: 0x0 
    arg[3]: 0xfa73fe ("/home/rc0r/tmp/php-src/ext/exif/exif.c")
    arg[4]: 0xe5b 
    arg[5]: 0x0 
    [------------------------------------------------------------------------------]
    gdb$ si // until zend_mm_heap_alloc()
    [----------------------------------registers-----------------------------------]
    RAX: 0xffffffffffffffff 
    RBX: 0x1655c20 --> 0x4d2a ('*M')
    RCX: 0xe5b 
    RDX: 0xfa73fe ("/home/rc0r/tmp/php-src/ext/exif/exif.c")
    RSI: 0xffffffffffffffff 
    RDI: 0x7ffff2e00040 --> 0x0 
    RBP: 0x7fffffffaa00 --> 0x7ffff2e5ea00 --> 0x16199b8 --> 0xc89d20 (<php_stdiop_write>:	push   r14)
    RSP: 0x7fffffffa700 --> 0x0 
    RIP: 0xcd2536 (<_safe_emalloc+182>:	call   0xccefe0 <zend_mm_alloc_heap>)
    R8 : 0x12fca48 ("/home/rc0r/tmp/php-src/Zend/zend_alloc.c")
    R9 : 0x9ce 
    R10: 0xe5b 
    R11: 0x7ffff2e00040 --> 0x0 
    R12: 0x1 
    R13: 0xe 
    R14: 0x1627348 --> 0x1645c20 --> 0x9200000000 
    R15: 0x1627348 --> 0x1645c20 --> 0x9200000000
    EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
    [-------------------------------------code-------------------------------------]
       0xcd252d <_safe_emalloc+173>:	mov    rsi,rax
       0xcd2530 <_safe_emalloc+176>:	mov    rdx,rcx
       0xcd2533 <_safe_emalloc+179>:	mov    ecx,r10d
    => 0xcd2536 <_safe_emalloc+182>:	call   0xccefe0 <zend_mm_alloc_heap>
       0xcd253b <_safe_emalloc+187>:	movzx  ecx,WORD PTR [rbx]
       0xcd253e <_safe_emalloc+190>:	mov    rdx,QWORD PTR [r14]
       0xcd2541 <_safe_emalloc+193>:	xor    rcx,0x7ddb
       0xcd2548 <_safe_emalloc+200>:	inc    BYTE PTR [rdx+rcx*1]
    Guessed arguments:
    arg[0]: 0x7ffff2e00040 --> 0x0 
    arg[1]: 0xffffffffffffffff 
    arg[2]: 0xfa73fe ("/home/rc0r/tmp/php-src/ext/exif/exif.c")
    arg[3]: 0xe5b 
    arg[4]: 0x12fca48 ("/home/rc0r/tmp/php-src/Zend/zend_alloc.c")
    arg[5]: 0x9ce 
    [------------------------------------------------------------------------------]
    gdb$ si // until overflowing code is reached
    [----------------------------------registers-----------------------------------]
    RAX: 0x5fe9 
    RBX: 0x1655c20 --> 0x961 ('a\t')
    RCX: 0xe5b 
    RDX: 0xfa73fe ("/home/rc0r/tmp/php-src/ext/exif/exif.c")
    RSI: 0xffffffffffffffff 
    RDI: 0x7ffff2e00040 --> 0x0 
    RBP: 0xe5b 
    RSP: 0x7fffffffa670 --> 0x400007575 
    RIP: 0xccf040 (<zend_mm_alloc_heap+96>:	add    r15,0x27)
    R8 : 0x12fca48 ("/home/rc0r/tmp/php-src/Zend/zend_alloc.c")
    R9 : 0x9ce 
    R10: 0x1627348 --> 0x1645c20 --> 0x9200000000 
    R11: 0x7ffff2e00040 --> 0x0 
    R12: 0x7ffff2e00040 --> 0x0 
    R13: 0x1655c20 --> 0x961 ('a\t')
    R14: 0x1627348 --> 0x1645c20 --> 0x9200000000 
    R15: 0xffffffffffffffff                                       <-- size param
    EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
    [-------------------------------------code-------------------------------------]
       0xccf032 <zend_mm_alloc_heap+82>:	cmp    rsi,0x1
       0xccf036 <zend_mm_alloc_heap+86>:	mov    r15d,0x1
       0xccf03c <zend_mm_alloc_heap+92>:	cmova  r15,rsi
    => 0xccf040 <zend_mm_alloc_heap+96>:	add    r15,0x27       <-- overflow !!!
       0xccf044 <zend_mm_alloc_heap+100>:	and    r15,0xfffffffffffffff8
       0xccf048 <zend_mm_alloc_heap+104>:	cmp    r15,0xc00
       0xccf04f <zend_mm_alloc_heap+111>:	ja     0xccf0a5 <zend_mm_alloc_heap+197>
       0xccf051 <zend_mm_alloc_heap+113>:	movzx  eax,WORD PTR [r13+0x0]
    [------------------------------------------------------------------------------]

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-02-15 08:22 UTC] stas@php.net
-Status: Open +Status: Feedback
 [2016-02-15 08:22 UTC] stas@php.net
You seem to be talking about debug mode build and also about internal function, so I'm not sure I understand how it is a security issue. Could you please explain in some more details where the security issue comes from?
 [2016-02-17 17:11 UTC] hlt99 at blinkenshell dot org
-Status: Feedback +Status: Open
 [2016-02-17 17:11 UTC] hlt99 at blinkenshell dot org
The bug itself is located in Zend internal memory management code. It opens the door for memory corruption vulns because PHP memory checks can be circumvented. I tried to demo this by chaining this together with the other bug I reported.
However, this works only in debug mode. So I guess this is a low risk bug.

But be aware that this bug may as well be triggered by other bugs we don't know of.
 [2016-02-22 00:21 UTC] stas@php.net
-Type: Security +Type: Bug -Assigned To: +Assigned To: dmitry
 [2016-02-24 08:06 UTC] dmitry@php.net
Automatic comment on behalf of dmitry@zend.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=0b9c87a02bacfbf1d1383ad393bda78e5d65570c
Log: Fixed bug #71535 (Integer overflow in zend_mm_alloc_heap())
 [2016-02-24 08:06 UTC] dmitry@php.net
-Status: Assigned +Status: Closed
 [2016-07-20 11:33 UTC] davey@php.net
Automatic comment on behalf of dmitry@zend.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=0b9c87a02bacfbf1d1383ad393bda78e5d65570c
Log: Fixed bug #71535 (Integer overflow in zend_mm_alloc_heap())
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Mar 29 09:01:28 2024 UTC