php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #72321 invalid free in phar_extract_file()
Submitted: 2016-06-03 06:43 UTC Modified: 2016-09-21 12:17 UTC
From: hji at dyntopia dot com Assigned: stas (profile)
Status: Closed Package: PHAR related
PHP Version: 5.6.22 OS:
Private report: No CVE-ID: 2016-4473
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: hji at dyntopia dot com
New email:
PHP Version: OS:

 

 [2016-06-03 06:43 UTC] hji at dyntopia dot com
Description:
------------
An invalid free (assigned CVE-2016-4473) may occur under certain
conditions when processing phar-compatible archives in php 5.6.22, 7.0.7
and git head:

php-7.0.7/ext/phar/phar_object.c
,----
| 4063 static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *dest, int dest_len, char **error) /* {{{ */
| 4064 {
| ....
| 4071     cwd_state new_state;
| ....
| 4084     new_state.cwd = (char*)emalloc(2);    // (1)
| 4085     new_state.cwd[0] = DEFAULT_SLASH;
| 4086     new_state.cwd[1] = '\0';
| 4087     new_state.cwd_length = 1;
| 4088     if (virtual_file_ex(&new_state, entry->filename, NULL, CWD_EXPAND) != 0 ||
| 4089             new_state.cwd_length <= 1) {
| ....
| 4099     }
| ....
| 4163
| 4164     if (FAILURE == php_stream_stat_path(fullpath, &ssb)) {
| 4165         if (entry->is_dir) {
| 4166             if (!php_stream_mkdir(fullpath, entry->flags & PHAR_ENT_PERM_MASK,  PHP_STREAM_MKDIR_RECURSIVE, NULL)) { // (2)
| ....
| 4169                 free(new_state.cwd);      // (3)
| ....
| 4171             }
| 4172         } else {
| 4173             if (!php_stream_mkdir(fullpath, 0777,  PHP_STREAM_MKDIR_RECURSIVE, NULL)) { // (4)
| ....
| 4176                 free(new_state.cwd);      // (5)
| ....
| 4178             }
| 4179         }
| 4180     }
| ....
| 4246 }
`----

`new_state.cwd' is initially allocated through the internal zend
allocator in (1) and is later reallocated as the file path is resolved
in `virtual_file_ex':

php-7.0.7/Zend/zend_virtual_cwd.c
,----
| 1178 CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath) /* {{{ */
| 1179 {
| ....
| 1336     if (verify_path) {
| ....
| 1342         tmp = erealloc(state->cwd, state->cwd_length+1);
| ....
| 1349         state->cwd = (char *) tmp;
| 1350
| 1351         memcpy(state->cwd, resolved_path, state->cwd_length+1);
| ....
| 1360     } else {
| ....
| 1362         tmp = erealloc(state->cwd, state->cwd_length+1);
| ....
| 1369         state->cwd = (char *) tmp;
| 1370
| 1371         memcpy(state->cwd, resolved_path, state->cwd_length+1);
| ....
| 1373     }
| ....
| 1379 }
`----

However, should `php_stream_mkdir' fail in (2) or (4), `cwd' is freed by
the underlying libc allocator in (3) or (5).


Test script:
---------------
mkzip.py: https://gist.github.com/dyntopia/9e54d61674ca89cd1381fbb017791e39
phar.php: https://gist.github.com/dyntopia/b7133c623bafe3ff7b7f57e38d7b2f90


Actual result:
--------------
On FreeBSD (ie. jemalloc) with mkdir() failing due to a directory
already existing as a regular file:

$ python mkzip.py
$ gdb711 --args php phar.php out/ 1.zip 2.zip
(gdb) r
Starting program: /usr/home/php/php/bin/php phar.php out/ 1.zip 2.zip

Warning: PharData::extractTo(): Not a directory in /usr/home/php/phar.php on line 14

Program received signal SIGBUS, Bus error.
0x00000008025bde2c in __jemalloc_arena_dalloc_bin_locked (arena=<optimized out>, chunk=<optimized out>, ptr=<optimized out>, mapelm=<optimized out>) at jemalloc_arena.c:1717

1717        bin->stats.allocated -= size;

(gdb) bt
#0  0x00000008025bde2c in __jemalloc_arena_dalloc_bin_locked (arena=<optimized out>, chunk=<optimized out>, ptr=<optimized out>, mapelm=<optimized out>) at jemalloc_arena.c:1717
#1  0x00000008025be1cf in __jemalloc_arena_dalloc_bin (chunk=<optimized out>, pageind=<optimized out>, mapelm=<optimized out>, arena=<optimized out>, chunk=<optimized out>, ptr=<optimized out>, pageind=<optimized out>, mapelm=<optimized out>) at jemalloc_arena.c:1733
#2  __jemalloc_arena_dalloc_small (arena=0x4343434343434341, chunk=0x803800000, ptr=0x0, pageind=<optimized out>) at jemalloc_arena.c:1749
#3  0x00000008025c99c5 in __jemalloc_arena_dalloc (arena=<optimized out>, chunk=<optimized out>, ptr=<optimized out>, try_tcache=<optimized out>, arena=<optimized out>, chunk=<optimized out>, ptr=<optimized out>, try_tcache=<optimized out>) at /usr/src/lib/libc/../../contrib/jemalloc/include/jemalloc/internal/arena.h:1005
#4  __jemalloc_idallocx (ptr=<optimized out>, try_tcache=<optimized out>, ptr=<optimized out>, try_tcache=<optimized out>) at /usr/src/lib/libc/../../contrib/jemalloc/include/jemalloc/internal/jemalloc_internal.h:913
#5  __jemalloc_iqallocx (ptr=<optimized out>, try_tcache=<optimized out>, ptr=<optimized out>, try_tcache=<optimized out>) at /usr/src/lib/libc/../../contrib/jemalloc/include/jemalloc/internal/jemalloc_internal.h:932
#6  __jemalloc_iqalloc (ptr=<optimized out>) at /usr/src/lib/libc/../../contrib/jemalloc/include/jemalloc/internal/jemalloc_internal.h:939
#7  __free (ptr=0x803879060) at jemalloc_jemalloc.c:1277
#8  0x0000000000762b93 in phar_extract_file (overwrite=0 '\000', entry=0x803870540, dest=0x803861018 "out/", dest_len=4, error=0x7fffffffc188) at /home/php/php-7.0.7/ext/phar/phar_object.c:4176
#9  0x0000000000762455 in zim_Phar_extractTo (execute_data=0x803813250, return_value=0x8038131f0) at /home/php/php-7.0.7/ext/phar/phar_object.c:4373
#10 0x0000000000b19529 in ZEND_DO_FCALL_SPEC_HANDLER (execute_data=0x803813030) at Zend/zend_vm_execute.h:842
#11 0x0000000000ad22a4 in execute_ex (ex=0x803813030) at Zend/zend_vm_execute.h:417
#12 0x0000000000ad2da5 in zend_execute (op_array=0x80387b000, return_value=0x0) at Zend/zend_vm_execute.h:458
#13 0x0000000000a28609 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/php/php-7.0.7/Zend/zend.c:1427
#14 0x0000000000951045 in php_execute_script (primary_file=0x7fffffffe868) at /home/php/php-7.0.7/main/main.c:2494
#15 0x0000000000c07896 in do_cli (argc=5, argv=0x7fffffffeb48) at /home/php/php-7.0.7/sapi/cli/php_cli.c:974
#16 0x0000000000c06419 in main (argc=5, argv=0x7fffffffeb48) at /home/php/php-7.0.7/sapi/cli/php_cli.c:1344

(gdb) x/i $rip
=> 0x8025bde2c <__jemalloc_arena_dalloc_bin_locked+556 at jemalloc_arena.c:1717>:sub    QWORD PTR [rbx+0x38],rax

(gdb) i r
rax       0x8                   8
rbx       0x4141414141414141    4702111234474983745
rcx       0x42424243            1111638595
rdx       0x0                   0
rsi       0x4343434343434343    4846791580151137091
rdi       0x4343434343434341    4846791580151137089
rbp       0x7fffffffbd70        0x7fffffffbd70
rsp       0x7fffffffbd20        0x7fffffffbd20
r8        0x0                   0
r9        0x0                   0
r10       0x803879010           34418954256
r11       0x8028c12b0           34402472624
r12       0x0                   0
r13       0x803879000           34418954240
r14       0x8028adf44           34402393924
r15       0x8028c1250           34402472528
rip       0x8025bde2c           0x8025bde2c <__jemalloc_arena_dalloc_bin_locked+556 at jemalloc_arena.c:1717>
eflags    0x10206               [ PF IF RF ]
cs        0x43                  67
ss        0x3b                  59
ds        <unavailable>
es        <unavailable>
fs        <unavailable>
gs        <unavailable>
(gdb)

-- Hans Jerry Illikainen

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-06-03 06:52 UTC] hji at dyntopia dot com
phar_object.diff: https://gist.github.com/dyntopia/c3d7962e8f3ab3d85e2792066db09a19

(I'm not allowed to add patches to private tickets)
 [2016-06-04 23:09 UTC] stas@php.net
-PHP Version: 7.0.7 +PHP Version: 5.6.22
 [2016-06-13 04:36 UTC] stas@php.net
-Assigned To: +Assigned To: stas
 [2016-06-13 04:36 UTC] stas@php.net
Fixed in security repo in d144590d38fa321b46b8e199c754006318985c84, also in https://gist.github.com/91fd8c90a8852ae130eaa23cf44d41d8
 [2016-06-21 07:03 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=d144590d38fa321b46b8e199c754006318985c84
Log: Fix bug #72321 - use efree() for emalloc allocation
 [2016-06-21 07:03 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-06-21 07:26 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=d144590d38fa321b46b8e199c754006318985c84
Log: Fix bug #72321 - use efree() for emalloc allocation
 [2016-06-21 07:27 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=d144590d38fa321b46b8e199c754006318985c84
Log: Fix bug #72321 - use efree() for emalloc allocation
 [2016-06-22 05:58 UTC] krakjoe@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=d144590d38fa321b46b8e199c754006318985c84
Log: Fix bug #72321 - use efree() for emalloc allocation
 [2016-09-21 12:17 UTC] kaplan@php.net
-CVE-ID: +CVE-ID: 2016-4473
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 12:01:29 2024 UTC