Bug #69100 [Asn->Csd]: Bus error from stream_copy_to_stream (file -> SSL stream) with invalid length

From: Date: Wed, 17 Jul 2019 13:59:50 +0000
Subject: Bug #69100 [Asn->Csd]: Bus error from stream_copy_to_stream (file -> SSL stream) with invalid length
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-221843@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=69100&edit=1

 ID:                 69100
 Updated by:         nikic@php.net
 Reported by:        zingaburga at hotmail dot com
 Summary:            Bus error from stream_copy_to_stream (file -> SSL
                     stream) with invalid length
-Status:             Assigned
+Status:             Closed
 Type:               Bug
 Package:            Streams related
 Operating System:   Debian 7 amd64
 PHP Version:        5.6.6
 Assigned To:        nikic
 Block user comment: N
 Private report:     N

 New Comment:

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


Previous Comments:
------------------------------------------------------------------------
[2019-07-17 13:35:22] 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.

------------------------------------------------------------------------
[2015-02-22 01:16:45] 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



------------------------------------------------------------------------



--
Edit this bug report at https://bugs.php.net/bug.php?id=69100&edit=1


Thread (3 messages)

« previous php.bugs (#221843) next »