mirror of
https://github.com/php/php-src.git
synced 2024-11-24 02:15:04 +08:00
Merge branch 'PHP-8.2'
* PHP-8.2: Fix concurrent testing [ci skip] NEWS Fix GH-10370: File corruption in _php_stream_copy_to_stream_ex when using copy_file_range (#10440)
This commit is contained in:
commit
d49e41925b
@ -53,6 +53,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
|
||||
int replace_zend_execute_ex;
|
||||
int register_passes;
|
||||
bool print_stderr_mshutdown;
|
||||
zend_long limit_copy_file_range;
|
||||
zend_test_fiber *active_fiber;
|
||||
zend_long quantity_value;
|
||||
zend_string *str_test;
|
||||
|
@ -721,6 +721,9 @@ PHP_INI_BEGIN()
|
||||
STD_PHP_INI_BOOLEAN("zend_test.replace_zend_execute_ex", "0", PHP_INI_SYSTEM, OnUpdateBool, replace_zend_execute_ex, zend_zend_test_globals, zend_test_globals)
|
||||
STD_PHP_INI_BOOLEAN("zend_test.register_passes", "0", PHP_INI_SYSTEM, OnUpdateBool, register_passes, zend_zend_test_globals, zend_test_globals)
|
||||
STD_PHP_INI_BOOLEAN("zend_test.print_stderr_mshutdown", "0", PHP_INI_SYSTEM, OnUpdateBool, print_stderr_mshutdown, zend_zend_test_globals, zend_test_globals)
|
||||
#ifdef HAVE_COPY_FILE_RANGE
|
||||
STD_PHP_INI_ENTRY("zend_test.limit_copy_file_range", "-1", PHP_INI_ALL, OnUpdateLong, limit_copy_file_range, zend_zend_test_globals, zend_test_globals)
|
||||
#endif
|
||||
STD_PHP_INI_ENTRY("zend_test.quantity_value", "0", PHP_INI_ALL, OnUpdateLong, quantity_value, zend_zend_test_globals, zend_test_globals)
|
||||
STD_PHP_INI_ENTRY("zend_test.str_test", "", PHP_INI_ALL, OnUpdateStr, str_test, zend_zend_test_globals, zend_test_globals)
|
||||
STD_PHP_INI_ENTRY("zend_test.not_empty_str_test", "val", PHP_INI_ALL, OnUpdateStrNotEmpty, not_empty_str_test, zend_zend_test_globals, zend_test_globals)
|
||||
@ -952,3 +955,17 @@ PHP_ZEND_TEST_API void bug_gh9090_void_int_char_var(int i, char *fmt, ...) {
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#ifdef HAVE_COPY_FILE_RANGE
|
||||
/**
|
||||
* This function allows us to simulate early return of copy_file_range by setting the limit_copy_file_range ini setting.
|
||||
*/
|
||||
PHP_ZEND_TEST_API ssize_t copy_file_range(int fd_in, off64_t *off_in, int fd_out, off64_t *off_out, size_t len, unsigned int flags)
|
||||
{
|
||||
ssize_t (*original_copy_file_range)(int, off64_t *, int, off64_t *, size_t, unsigned int) = dlsym(RTLD_NEXT, "copy_file_range");
|
||||
if (ZT_G(limit_copy_file_range) >= Z_L(0)) {
|
||||
len = ZT_G(limit_copy_file_range);
|
||||
}
|
||||
return original_copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
|
||||
}
|
||||
#endif
|
||||
|
BIN
ext/zend_test/tests/gh10370.tar
Normal file
BIN
ext/zend_test/tests/gh10370.tar
Normal file
Binary file not shown.
29
ext/zend_test/tests/gh10370_1.phpt
Normal file
29
ext/zend_test/tests/gh10370_1.phpt
Normal file
@ -0,0 +1,29 @@
|
||||
--TEST--
|
||||
GH-10370: File corruption in _php_stream_copy_to_stream_ex when using copy_file_range - partial copy
|
||||
--EXTENSIONS--
|
||||
zend_test
|
||||
phar
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (PHP_OS != 'Linux') {
|
||||
die('skip For Linux only');
|
||||
}
|
||||
?>
|
||||
--INI--
|
||||
zend_test.limit_copy_file_range=3584
|
||||
--FILE--
|
||||
<?php
|
||||
/* Note: the value 3584 is chosen so that the mmap in _php_stream_copy_to_stream_ex() will mmap
|
||||
* at an offset of a multiple of 4096, which is the standard page size in most Linux systems. */
|
||||
$archive = new PharData(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370.tar');
|
||||
var_dump($archive->extractTo(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_001', ['testfile']));
|
||||
var_dump(sha1_file(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_001' . DIRECTORY_SEPARATOR . 'testfile'));
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
string(40) "a723ae4ec7eababff73ca961a771b794be6388d2"
|
||||
--CLEAN--
|
||||
<?php
|
||||
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_001' . DIRECTORY_SEPARATOR . 'testfile');
|
||||
@rmdir(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_001');
|
||||
?>
|
30
ext/zend_test/tests/gh10370_2.phpt
Normal file
30
ext/zend_test/tests/gh10370_2.phpt
Normal file
@ -0,0 +1,30 @@
|
||||
--TEST--
|
||||
GH-10370: File corruption in _php_stream_copy_to_stream_ex when using copy_file_range - unlimited copy
|
||||
--EXTENSIONS--
|
||||
zend_test
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (PHP_OS != 'Linux') {
|
||||
die('skip For Linux only');
|
||||
}
|
||||
?>
|
||||
--INI--
|
||||
zend_test.limit_copy_file_range=4096
|
||||
--FILE--
|
||||
<?php
|
||||
/* Note: the value 4096 is chosen so that the mmap in _php_stream_copy_to_stream_ex() will mmap
|
||||
* at an offset of a multiple of 4096, which is the standard page size in most Linux systems. */
|
||||
$input_file = fopen(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370.tar', 'r');
|
||||
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_002_out.tar', $input_file);
|
||||
fclose($input_file);
|
||||
|
||||
var_dump(sha1_file(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370.tar'));
|
||||
var_dump(sha1_file(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_002_out.tar'));
|
||||
?>
|
||||
--EXPECT--
|
||||
string(40) "edcad8cd6c276f5e318c826ad77a5604d6a6e93d"
|
||||
string(40) "edcad8cd6c276f5e318c826ad77a5604d6a6e93d"
|
||||
--CLEAN--
|
||||
<?php
|
||||
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_002_out.tar');
|
||||
?>
|
36
ext/zend_test/tests/gh10370_3.phpt
Normal file
36
ext/zend_test/tests/gh10370_3.phpt
Normal file
@ -0,0 +1,36 @@
|
||||
--TEST--
|
||||
GH-10370: File corruption in _php_stream_copy_to_stream_ex when using copy_file_range - partial copy using stream_copy_to_stream
|
||||
--EXTENSIONS--
|
||||
zend_test
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (PHP_OS != 'Linux') {
|
||||
die('skip For Linux only');
|
||||
}
|
||||
?>
|
||||
--INI--
|
||||
zend_test.limit_copy_file_range=3584
|
||||
--FILE--
|
||||
<?php
|
||||
/* Note: the value 3584 is chosen so that the mmap in _php_stream_copy_to_stream_ex() will mmap
|
||||
* at an offset of a multiple of 4096, which is the standard page size in most Linux systems. */
|
||||
mkdir(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_003');
|
||||
|
||||
$input = fopen(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370.tar', 'r');
|
||||
$output = fopen(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_003' . DIRECTORY_SEPARATOR . 'testfile', 'w');
|
||||
|
||||
var_dump(stream_copy_to_stream($input, $output, 10240, 0x200));
|
||||
|
||||
fclose($input);
|
||||
fclose($output);
|
||||
|
||||
var_dump(sha1_file(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_003' . DIRECTORY_SEPARATOR . 'testfile'));
|
||||
?>
|
||||
--EXPECT--
|
||||
int(10240)
|
||||
string(40) "a723ae4ec7eababff73ca961a771b794be6388d2"
|
||||
--CLEAN--
|
||||
<?php
|
||||
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_003' . DIRECTORY_SEPARATOR . 'testfile');
|
||||
@rmdir(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_003');
|
||||
?>
|
36
ext/zend_test/tests/gh10370_4.phpt
Normal file
36
ext/zend_test/tests/gh10370_4.phpt
Normal file
@ -0,0 +1,36 @@
|
||||
--TEST--
|
||||
GH-10370: File corruption in _php_stream_copy_to_stream_ex when using copy_file_range - unlimited copy using stream_copy_to_stream
|
||||
--EXTENSIONS--
|
||||
zend_test
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (PHP_OS != 'Linux') {
|
||||
die('skip For Linux only');
|
||||
}
|
||||
?>
|
||||
--INI--
|
||||
zend_test.limit_copy_file_range=4096
|
||||
--FILE--
|
||||
<?php
|
||||
/* Note: the value 4096 is chosen so that the mmap in _php_stream_copy_to_stream_ex() will mmap
|
||||
* at an offset of a multiple of 4096, which is the standard page size in most Linux systems. */
|
||||
|
||||
$input = fopen(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370.tar', 'r');
|
||||
$output = fopen(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_004_out.tar', 'w');
|
||||
|
||||
var_dump(stream_copy_to_stream($input, $output));
|
||||
|
||||
fclose($input);
|
||||
fclose($output);
|
||||
|
||||
var_dump(sha1_file(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370.tar'));
|
||||
var_dump(sha1_file(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_004_out.tar'));
|
||||
?>
|
||||
--EXPECT--
|
||||
int(11776)
|
||||
string(40) "edcad8cd6c276f5e318c826ad77a5604d6a6e93d"
|
||||
string(40) "edcad8cd6c276f5e318c826ad77a5604d6a6e93d"
|
||||
--CLEAN--
|
||||
<?php
|
||||
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'gh10370_004_out.tar');
|
||||
?>
|
@ -1635,8 +1635,21 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de
|
||||
char *p;
|
||||
|
||||
do {
|
||||
size_t chunk_size = (maxlen == 0 || maxlen > PHP_STREAM_MMAP_MAX) ? PHP_STREAM_MMAP_MAX : maxlen;
|
||||
size_t mapped;
|
||||
/* We must not modify maxlen here, because otherwise the file copy fallback below can fail */
|
||||
size_t chunk_size, must_read, mapped;
|
||||
if (maxlen == 0) {
|
||||
/* Unlimited read */
|
||||
must_read = chunk_size = PHP_STREAM_MMAP_MAX;
|
||||
} else {
|
||||
must_read = maxlen - haveread;
|
||||
if (must_read >= PHP_STREAM_MMAP_MAX) {
|
||||
chunk_size = PHP_STREAM_MMAP_MAX;
|
||||
} else {
|
||||
/* In case the length we still have to read from the file could be smaller than the file size,
|
||||
* chunk_size must not get bigger the size we're trying to read. */
|
||||
chunk_size = must_read;
|
||||
}
|
||||
}
|
||||
|
||||
p = php_stream_mmap_range(src, php_stream_tell(src), chunk_size, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
|
||||
|
||||
@ -1651,6 +1664,7 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de
|
||||
didwrite = php_stream_write(dest, p, mapped);
|
||||
if (didwrite < 0) {
|
||||
*len = haveread;
|
||||
php_stream_mmap_unmap(src);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
@ -1667,9 +1681,10 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de
|
||||
if (mapped < chunk_size) {
|
||||
return SUCCESS;
|
||||
}
|
||||
/* If we're not reading as much as possible, so a bounded read */
|
||||
if (maxlen != 0) {
|
||||
maxlen -= mapped;
|
||||
if (maxlen == 0) {
|
||||
must_read -= mapped;
|
||||
if (must_read == 0) {
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user