php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #69100 Bus error from stream_copy_to_stream (file -> SSL stream) with invalid length
Submitted: 2015-02-22 01:16 UTC Modified: 2019-07-17 13:35 UTC
From: zingaburga at hotmail dot com Assigned: nikic (profile)
Status: Closed Package: Streams related
PHP Version: 5.6.6 OS: Debian 7 amd64
Private report: No CVE-ID: None
 [2015-02-22 01:16 UTC] zingaburga at hotmail dot com
Description:
------------
It seems that a Bus error can be generated by stream_copy_to_stream, when copying to a SSL stream if the given length exceeds the available remaining data.

I've successfully replicated the problem on a few machines, using the exact parameters below, all running Debian 7 x86-64, on both PHP 5.5 and 5.6, although it doesn't seem to occur _everywhere_.

I haven't experimented, but I suspect it may be something to do with mmap and the range check.

From main/streams/plain_wrapper.c:

	case PHP_STREAM_MMAP_MAP_RANGE:
		do_fstat(data, 1);
		if (range->length == 0 && range->offset > 0 && range->offset < data->sb.st_size) {
			range->length = data->sb.st_size - range->offset;
		}
		if (range->length == 0 || range->length > data->sb.st_size) {
			range->length = data->sb.st_size;
		}
		if (range->offset >= data->sb.st_size) {
			range->offset = data->sb.st_size;
			range->length = 0;
		}

It doesn't look like range->length is ever checked against range->offset+data->sb.st_size, hence it's possible for php_stdiop_set_option to claim that more memory was mapped than actually was.  This can cause invalid memory errors when the buffer is read.

And for reference's sake, it seems that sending the file over HTTP is also problematic, though you won't get a crash:

Notice: stream_copy_to_stream(): send of 8192 bytes failed with errno=14 Bad address in /root/b.php on line 9
int(7340032)
bool(false)


Test script:
---------------
<?php

// create an TLS stream to localhost:443 (this means you need a server listening there)
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'verify_peer', false);
$fw = stream_socket_client('tls://localhost:443', $null, $null, 5, STREAM_CLIENT_CONNECT, $context);
fwrite($fw, "POST /null.php HTTP/1.1\r\nHost: localhost\r\nContent-Length: 1073741824\r\n\r\n");

// the file './data' needs to be 8383276 bytes long, can be created via 'dd if=/dev/zero of=data count=1 bs=8383276'
$fr = fopen('./data', 'rb');

// copy
var_dump(stream_copy_to_stream($fr, $fw, 7340032));
var_dump(stream_copy_to_stream($fr, $fw, 1048576)); // causes Bus Error as it overflows the file's size
fclose($fr);
fclose($fw);


Expected result:
----------------
int(7340032)
int(1043244)


Actual result:
--------------
int(7340032)

Program received signal SIGBUS, Bus error.
__memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:36
36      ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S: No such file or directory.

----- BACKTRACE -----

#0  __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:36
#1  0x00007ffff70e9c7c in memcpy (__len=8192, __src=0x7fffec514000, __dest=<optimized out>)
    at /usr/include/x86_64-linux-gnu/bits/string3.h:51
#2  do_ssl3_write (s=s@entry=0x1229b40, type=type@entry=23, buf=buf@entry=0x7fffec514000 "", len=8192,
    create_empty_fragment=create_empty_fragment@entry=0) at s3_pkt.c:836
#3  0x00007ffff70e9e24 in ssl3_write_bytes (s=0x1229b40, type=23, buf_=0x7fffec514000, len=<optimized out>)
    at s3_pkt.c:646
#4  0x0000000000479878 in php_openssl_sockop_write (stream=0x7ffff7fceb18, buf=0x7fffec514000 "",
    count=<optimized out>) at /usr/src/php5.6/nonzts/source/dotdeb-php5/ext/openssl/xp_ssl.c:1786
#5  0x000000000068d25e in _php_stream_write_buffer (stream=0x7ffff7fceb18, buf=0x7fffec514000 "", count=8192)
    at /usr/src/php5.6/nonzts/source/dotdeb-php5/main/streams/streams.c:1133
#6  0x000000000068f8df in _php_stream_copy_to_stream_ex (src=src@entry=0x7ffff7fcff10, dest=dest@entry=0x7ffff7fceb18,
    maxlen=1048576, len=len@entry=0x7fffffffac98)
    at /usr/src/php5.6/nonzts/source/dotdeb-php5/main/streams/streams.c:1536
#7  0x000000000065015f in zif_stream_copy_to_stream (ht=<optimized out>, return_value=0x7ffff7fcd768,
    return_value_ptr=<optimized out>, this_ptr=<optimized out>, return_value_used=<optimized out>)
    at /usr/src/php5.6/nonzts/source/dotdeb-php5/ext/standard/streamsfuncs.c:477
#8  0x00000000006c9919 in dtrace_execute_internal (execute_data_ptr=<optimized out>, fci=<optimized out>,
    return_value_used=<optimized out>) at /usr/src/php5.6/nonzts/source/dotdeb-php5/Zend/zend_dtrace.c:97
#9  0x000000000077d1e1 in zend_do_fcall_common_helper_SPEC (execute_data=0x7ffff7f9b268)
    at /usr/src/php5.6/nonzts/source/dotdeb-php5/Zend/zend_vm_execute.h:560
#10 0x0000000000743a88 in execute_ex (execute_data=0x7ffff7f9b268)
    at /usr/src/php5.6/nonzts/source/dotdeb-php5/Zend/zend_vm_execute.h:363
#11 0x00000000006c97ed in dtrace_execute_ex (execute_data=<optimized out>)
    at /usr/src/php5.6/nonzts/source/dotdeb-php5/Zend/zend_dtrace.c:73
#12 0x00000000006dc028 in zend_execute_scripts (type=type@entry=8, retval=retval@entry=0x0,
    file_count=file_count@entry=3) at /usr/src/php5.6/nonzts/source/dotdeb-php5/Zend/zend.c:1341
#13 0x000000000067796c in php_execute_script (primary_file=primary_file@entry=0x7fffffffd250)
    at /usr/src/php5.6/nonzts/source/dotdeb-php5/main/main.c:2584
#14 0x0000000000780783 in do_cli (argc=2, argv=0xedd990)
    at /usr/src/php5.6/nonzts/source/dotdeb-php5/sapi/cli/php_cli.c:994
#15 0x0000000000433b7f in main (argc=2, argv=0xedd990)
    at /usr/src/php5.6/nonzts/source/dotdeb-php5/sapi/cli/php_cli.c:1378


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-07-17 13:35 UTC] nikic@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: nikic
 [2019-07-17 13:35 UTC] nikic@php.net
This looks like a pretty severe bug. I'm too lazy to do the SSL setup, but just doing appropriately sized copies between files triggers this in the form of a false return value from the stream_copy_to_stream() call -- it is our luck that the access to the out of range memory will usually happen during a syscall, so it is ends up failing "gracefully" rather than with a SIGBUS.
 [2019-07-17 13:59 UTC] nikic@php.net
Automatic comment on behalf of nikita.ppv@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=b864abfe23fde5d79a303519674ba83062f89361
Log: Fixed bug #69100
 [2019-07-17 13:59 UTC] nikic@php.net
-Status: Assigned +Status: Closed
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Sun Sep 15 12:01:26 2019 UTC