Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ jobs:
PHP_BUILD_CACHE_BASE_DIR: C:\build-cache
PHP_BUILD_OBJ_DIR: C:\obj
PHP_BUILD_CACHE_SDK_DIR: C:\build-cache\sdk
PHP_BUILD_SDK_BRANCH: php-sdk-2.7.1
PHP_BUILD_SDK_BRANCH: php-sdk-2.8.2
PHP_BUILD_CRT: ${{ fromJson(inputs.branch).jobs.WINDOWS.config.vs_crt_version }}
PLATFORM: ${{ matrix.x64 && 'x64' || 'x86' }}
THREAD_SAFE: "${{ matrix.zts && '1' || '0' }}"
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ php
# Test results generated by `./run-tests.php`
php_test_results_*.txt

# Temporary test information generated by `./run-tests.php`
# Temporary test information generated by `./run-tests.php` (kept for BC; now lives in tmp dir)
/run-test-info.php

# Temporary POST data placeholder files generated by `./run-tests.php`
Expand Down
7 changes: 7 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ PHP NEWS
operation depth. (iliaal)

- Core:
. Changed run-tests.php to run in parallel by default, using up to 10
automatically detected workers. Pass -j1 for sequential execution.
(NickSdot)
. Changed run-tests.php to run test subprocesses without a shell where
possible. (NickSdot)
. Fixed GH-23083 (SEGV build_trace_args in zend_exceptions.c with
Expand Down Expand Up @@ -54,6 +57,10 @@ PHP NEWS
. Added SpoofChecker::getBidiSkeleton(). (Weilin Du)
. Added SpoofChecker::getSkeleton(). (David Carlier)

- PDO:
. Fixed pdo_raise_impl_error() emitting a warning under ERRMODE_SILENT.
(iliaal)

- PDO_ODBC:
. Fixed bug GH-23016 (NULL values in long columns come back as garbage
binary strings). (Calvin Buckley, iliaal)
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ can be determined using `nproc`.
PHP ships with an extensive test suite, the command `make test` is used after
successful compilation of the sources to run this test suite.

It is possible to run tests using multiple cores by setting `-jN` in
`TEST_PHP_ARGS` or `TESTS`:
Tests run in parallel by default, using up to 10 detected logical processors.
Set `-jN` in `TEST_PHP_ARGS` or `TESTS` to override the worker count:

```shell
make TEST_PHP_ARGS=-j4 test
```

Shall run `make test` with a maximum of 4 concurrent jobs: Generally the maximum
number of jobs should not exceed the number of cores available.
This runs `make test` with a maximum of 4 concurrent jobs. Alternatively,
use `-j1` to run tests sequentially.

Use the `TEST_PHP_ARGS` or `TESTS` variable to test only specific directories:

Expand Down
3 changes: 3 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES
========================

- Abstract:
. run-tests.php now runs in parallel by default, using up to 10 automatically
detected workers. Pass -j1 for sequential execution. --asan, --msan, and
Valgrind default to at most two workers.
. Minimum required PHP version found on the host system for running scripts
like build/gen_stub.php during development has been updated from 7.4 to 8.1.
. build/gen_stub.php may now generate a _decl.h file in addition to
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -4649,7 +4649,7 @@ static zend_always_inline zend_generator *zend_get_running_generator(EXECUTE_DAT
}
/* }}} */

ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_execute_data *call, uint32_t op_num, zend_get_gc_buffer *buf) /* {{{ */
static void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_execute_data *call, uint32_t op_num, zend_get_gc_buffer *buf) /* {{{ */
{
zend_op *opline = EX(func)->op_array.opcodes + op_num;
int level;
Expand Down
1 change: 0 additions & 1 deletion Zend/zend_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,6 @@ ZEND_API zval *zend_get_zval_ptr(const zend_op *opline, int op_type, const znode

ZEND_API void zend_clean_and_cache_symbol_table(zend_array *symbol_table);
ZEND_API void ZEND_FASTCALL zend_free_compiled_variables(zend_execute_data *execute_data);
ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_execute_data *call, uint32_t op_num, zend_get_gc_buffer *buf);
ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num);
ZEND_API ZEND_ATTRIBUTE_DEPRECATED HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer);
ZEND_API HashTable *zend_unfinished_execution_gc_ex(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer, bool suspended_by_yield);
Expand Down
5 changes: 5 additions & 0 deletions docs/source/miscellaneous/writing-tests.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ When you are testing your test case it's really important to make sure that you
temporary resources (eg files) that you used in the test. There is a special ``--CLEAN--`` section
to help you do this — see `here <#clean>`_.

Tests run in parallel by default. Mutable resources such as files, directories, ports, database
objects, and IPC identifiers must therefore be unique to each test. Read-only fixtures may be
shared. If a resource cannot be isolated, declare the narrowest applicable conflict using
``--CONFLICTS--`` or a ``CONFLICTS`` file.

Another good check is to look at what lines of code in the PHP source your test case covers. This is
easy to do, there are some instructions on the `PHP Wiki
<https://wiki.php.net/doc/articles/writing-tests>`_.
Expand Down
2 changes: 1 addition & 1 deletion ext/gd/tests/createfromwbmp2.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ gd
?>
--FILE--
<?php
$filename = __DIR__ . '/_tmp.wbmp';
$filename = __DIR__ . '/_tmp_createfromwbmp2.wbmp';
$fp = fopen($filename,"wb");
if (!$fp) {
exit("Failed to create <$filename>");
Expand Down
4 changes: 2 additions & 2 deletions ext/gd/tests/createfromwbmp2_extern.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ imagecreatefromwbmp with invalid wbmp
gd
--FILE--
<?php
$filename = __DIR__ . '/_tmp.wbmp';
$filename = __DIR__ . '/_tmp_createfromwbmp2_extern.wbmp';
$fp = fopen($filename,"wb");
if (!$fp) {
exit("Failed to create <$filename>");
Expand Down Expand Up @@ -41,4 +41,4 @@ unlink($filename);
--EXPECTF--
Warning: imagecreatefromwbmp(): %croduct of memory allocation multiplication would exceed INT_MAX, failing operation gracefully%win %s on line %d

Warning: imagecreatefromwbmp(): "%s_tmp.wbmp" is not a valid WBMP file in %s on line %d
Warning: imagecreatefromwbmp(): "%s_tmp_createfromwbmp2_extern.wbmp" is not a valid WBMP file in %s on line %d
13 changes: 4 additions & 9 deletions ext/pdo/pdo_dbh.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,16 @@ void pdo_raise_impl_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, pdo_error_type sqlst
pdo_error_type *pdo_err = &dbh->error_code;
const char *msg;

if (dbh->error_mode == PDO_ERRMODE_SILENT) {
#if 0
/* BUG: if user is running in silent mode and hits an error at the driver level
* when they use the PDO methods to call up the error information, they may
* get bogus information */
return;
#endif
}

if (stmt) {
pdo_err = &stmt->error_code;
}

memcpy(*pdo_err, sqlstate, sizeof(pdo_error_type));

if (dbh->error_mode == PDO_ERRMODE_SILENT) {
return;
}

/* hash sqlstate to error messages */
msg = pdo_sqlstate_state_to_description(*pdo_err);
if (!msg) {
Expand Down
21 changes: 21 additions & 0 deletions ext/pdo_sqlite/tests/pdo_silent_impl_error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
pdo_raise_impl_error honors ERRMODE_SILENT (no warning)
--EXTENSIONS--
pdo_sqlite
--FILE--
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
set_error_handler(function (int $errno, string $errstr): bool {
echo "warning: $errstr\n";
return true;
});
$result = $pdo->getAttribute(123456);
echo "result: ";
var_dump($result);
echo "errorInfo: ";
var_dump($pdo->errorInfo()[0]);
?>
--EXPECT--
result: bool(false)
errorInfo: string(5) "IM001"
44 changes: 39 additions & 5 deletions ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,13 @@ static zend_result php_array_walk(
* levels of recursion. */
zend_fcall_info fci = context->fci;

#ifdef ZEND_CHECK_STACK_LIMIT
if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
zend_call_stack_size_error();
return FAILURE;
}
#endif

if (zend_hash_num_elements(target_hash) == 0) {
return result;
}
Expand Down Expand Up @@ -2562,7 +2569,7 @@ PHP_FUNCTION(extract)
}
/* }}} */

static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry, uint32_t pos) /* {{{ */
static zend_result php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry, uint32_t pos) /* {{{ */
{
zval *value_ptr, data;

Expand All @@ -2580,25 +2587,43 @@ static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_valu
}
} else {
php_error_docref(NULL, E_WARNING, "Undefined variable $%pS", Z_STR_P(entry));
/* A user error handler may have thrown. */
return EG(exception) ? FAILURE : SUCCESS;
}
} else if (Z_TYPE_P(entry) == IS_ARRAY) {
zend_result result = SUCCESS;

#ifdef ZEND_CHECK_STACK_LIMIT
if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
zend_call_stack_size_error();
return FAILURE;
}
#endif
if (Z_REFCOUNTED_P(entry)) {
if (Z_IS_RECURSIVE_P(entry)) {
zend_throw_error(NULL, "Recursion detected");
return;
return FAILURE;
}
Z_PROTECT_RECURSION_P(entry);
}
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(entry), value_ptr) {
php_compact_var(eg_active_symbol_table, return_value, value_ptr, pos);
if (UNEXPECTED(php_compact_var(eg_active_symbol_table, return_value, value_ptr, pos) == FAILURE)) {
result = FAILURE;
break;
}
} ZEND_HASH_FOREACH_END();
if (Z_REFCOUNTED_P(entry)) {
Z_UNPROTECT_RECURSION_P(entry);
}

return result;
} else {
php_error_docref(NULL, E_WARNING, "Argument #%d must be string or array of strings, %s given", pos, zend_zval_value_name(entry));
return;
/* A user error handler may have thrown. */
return EG(exception) ? FAILURE : SUCCESS;
}

return SUCCESS;
}
/* }}} */

Expand Down Expand Up @@ -2630,7 +2655,9 @@ PHP_FUNCTION(compact)
}

for (i = 0; i < num_args; i++) {
php_compact_var(symbol_table, return_value, &args[i], i + 1);
if (UNEXPECTED(php_compact_var(symbol_table, return_value, &args[i], i + 1) == FAILURE)) {
RETURN_THROWS();
}
}
}
/* }}} */
Expand Down Expand Up @@ -4048,6 +4075,13 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ *
zend_ulong num_key;
int ret;

#ifdef ZEND_CHECK_STACK_LIMIT
if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
zend_call_stack_size_error();
return 0;
}
#endif

ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
src_zval = src_entry;
ZVAL_DEREF(src_zval);
Expand Down
27 changes: 27 additions & 0 deletions ext/standard/tests/array/gh23111.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
GH-23111 (Stack overflow in array_walk_recursive with deeply nested arrays)
--SKIPIF--
<?php
if (ini_get('zend.max_allowed_stack_size') === false) {
die('skip No stack limit support');
}
if (getenv('SKIP_ASAN')) {
die('skip ASAN needs different stack limit setting due to more stack space usage');
}
?>
--INI--
zend.max_allowed_stack_size=256K
--FILE--
<?php
$a = [];
for ($i = 0; $i < 30000; $i++) {
$a = [$a];
}
try {
array_walk_recursive($a, function ($v) {});
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}
?>
--EXPECTF--
Error: Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
27 changes: 27 additions & 0 deletions ext/standard/tests/array/gh23113.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
GH-23113 (Stack overflow in array_replace_recursive with deeply nested arrays)
--SKIPIF--
<?php
if (ini_get('zend.max_allowed_stack_size') === false) {
die('skip No stack limit support');
}
if (getenv('SKIP_ASAN')) {
die('skip ASAN needs different stack limit setting due to more stack space usage');
}
?>
--INI--
zend.max_allowed_stack_size=256K
--FILE--
<?php
$a = [];
for ($i = 0; $i < 30000; $i++) {
$a = ['k' => $a];
}
try {
array_replace_recursive($a, $a);
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}
?>
--EXPECTF--
Error: Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
41 changes: 41 additions & 0 deletions ext/standard/tests/array/gh23115.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
--TEST--
GH-23115 (Stack overflow in compact with deeply nested arrays)
--SKIPIF--
<?php
if (ini_get('zend.max_allowed_stack_size') === false) {
die('skip No stack limit support');
}
if (getenv('SKIP_ASAN')) {
die('skip ASAN needs different stack limit setting due to more stack space usage');
}
?>
--INI--
zend.max_allowed_stack_size=256K
--FILE--
<?php
/* Two elements per nesting level: the sibling must not be visited once the
* stack limit error has been thrown, so only one Error is thrown. */
$names = [];
for ($i = 0; $i < 30000; $i++) {
$names = [$names, []];
}

try {
compact($names);
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
var_dump($e->getPrevious());
}

try {
compact($names, $names);
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
var_dump($e->getPrevious());
}
?>
--EXPECTF--
Error: Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
NULL
Error: Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
NULL
4 changes: 2 additions & 2 deletions ext/zip/tests/oo_addglob_leak.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ if(!defined("GLOB_BRACE")) die ('skip requires GLOB_BRACE');
$dirname = __DIR__ . '/';
include $dirname . 'utils.inc';

$dirname = __DIR__ . '/__tmp_oo_addglob2/';
$dirname = __DIR__ . '/__tmp_oo_addglob_leak/';
$file = $dirname . 'test.zip';

@mkdir($dirname);
Expand All @@ -38,7 +38,7 @@ var_dump($zip->addGlob($dirname . 'bar.*', GLOB_BRACE, $options));
<?php
$dirname = __DIR__ . '/';
include $dirname . 'utils.inc';
rmdir_rf(__DIR__ . '/__tmp_oo_addglob2/');
rmdir_rf(__DIR__ . '/__tmp_oo_addglob_leak/');
?>
--EXPECTF--
array(1) {
Expand Down
Loading