From 6641cd159ead068bfa3327668771b8292c87474e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 11 Nov 2023 14:15:23 +0100 Subject: [PATCH 01/25] Fix GH-12635: Test bug69398.phpt fails with ICU 74.1 ICU 74.1 contains new locale data that breaks the test. Split the test based on the version number to resolve the issue. Closes GH-12653. --- NEWS | 3 +++ ext/intl/tests/bug69398-icu74.1.phpt | 19 +++++++++++++++++++ ext/intl/tests/bug69398.phpt | 5 ++++- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 ext/intl/tests/bug69398-icu74.1.phpt diff --git a/NEWS b/NEWS index 5139f2c4f02..2dda841f657 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.1.27 +- Intl: + . Fixed bug GH-12635 (Test bug69398.phpt fails with ICU 74.1). (nielsdos) + - PCRE: . Fixed bug GH-12628 (The gh11374 test fails on Alpinelinux). (nielsdos) diff --git a/ext/intl/tests/bug69398-icu74.1.phpt b/ext/intl/tests/bug69398-icu74.1.phpt new file mode 100644 index 00000000000..cf8ce39d1e2 --- /dev/null +++ b/ext/intl/tests/bug69398-icu74.1.phpt @@ -0,0 +1,19 @@ +--TEST-- +IntlDateFormatter::formatObject(): returns wrong value when time style is NONE. +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +setTime($millitimestamp); +echo IntlDateFormatter::formatObject($date, array(IntlDateFormatter::SHORT, IntlDateFormatter::NONE), 'vi_VN'), "\n"; +echo IntlDateFormatter::formatObject ($date, array(IntlDateFormatter::SHORT, IntlDateFormatter::NONE), 'ko_KR'), "\n"; +?> +--EXPECT-- +4/4/15 +15. 4. 4. diff --git a/ext/intl/tests/bug69398.phpt b/ext/intl/tests/bug69398.phpt index 02c4b7daeff..075dd5f4bd0 100644 --- a/ext/intl/tests/bug69398.phpt +++ b/ext/intl/tests/bug69398.phpt @@ -3,7 +3,10 @@ IntlDateFormatter::formatObject(): returns wrong value when time style is NONE. --EXTENSIONS-- intl --SKIPIF-- -= 51.1.2'); ?> += 0) die('skip for ICU >= 74.1'); +?> --FILE-- Date: Wed, 8 Nov 2023 20:06:18 +0100 Subject: [PATCH 02/25] Fix GH-12621: browscap segmentation fault when configured in the vhost The temporary HashTable has a destructor that releases the string held by the entry's value. However, browscap_intern_str(_ci) only incremented the refcount for the reference created by the return value. As the HashTable is only used during parsing, we don't need to manage the reference count of the value anyway, so get rid of the destructor. This is triggerable in two cases: - When using php_admin_value to set the ini at the activation stage - When running out of space for the opcache-interned strings Closes GH-12634. --- NEWS | 2 ++ ext/standard/browscap.c | 10 ++++---- sapi/fpm/tests/gh12621.phpt | 46 +++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 sapi/fpm/tests/gh12621.phpt diff --git a/NEWS b/NEWS index 2dda841f657..73fa39d7967 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,8 @@ PHP NEWS - Standard: . Fix memory leak in syslog device handling. (danog) + . Fixed bug GH-12621 (browscap segmentation fault when configured in the + vhost). (nielsdos) - SQLite3: . Fixed bug GH-12633 (sqlite3_defensive.phpt fails with sqlite 3.44.0). diff --git a/ext/standard/browscap.c b/ext/standard/browscap.c index 53504098fb8..22268191512 100644 --- a/ext/standard/browscap.c +++ b/ext/standard/browscap.c @@ -228,7 +228,7 @@ static zend_string *browscap_intern_str( } else { interned = zend_string_copy(str); if (persistent) { - interned = zend_new_interned_string(str); + interned = zend_new_interned_string(interned); } zend_hash_add_new_ptr(&ctx->str_interned, interned, interned); } @@ -397,10 +397,6 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callb } /* }}} */ -static void str_interned_dtor(zval *zv) { - zend_string_release(Z_STR_P(zv)); -} - static int browscap_read_file(char *filename, browser_data *browdata, int persistent) /* {{{ */ { zend_file_handle fh; @@ -430,7 +426,9 @@ static int browscap_read_file(char *filename, browser_data *browdata, int persis ctx.bdata = browdata; ctx.current_entry = NULL; ctx.current_section_name = NULL; - zend_hash_init(&ctx.str_interned, 8, NULL, str_interned_dtor, persistent); + /* No dtor because we don't inc the refcount for the reference stored within the hash table's entry value + * as the hash table is only temporary anyway. */ + zend_hash_init(&ctx.str_interned, 8, NULL, NULL, persistent); zend_parse_ini_file(&fh, persistent, ZEND_INI_SCANNER_RAW, (zend_ini_parser_cb_t) php_browscap_parser_cb, &ctx); diff --git a/sapi/fpm/tests/gh12621.phpt b/sapi/fpm/tests/gh12621.phpt new file mode 100644 index 00000000000..0ee64fbe415 --- /dev/null +++ b/sapi/fpm/tests/gh12621.phpt @@ -0,0 +1,46 @@ +--TEST-- +GH-12621 (browscap segmentation fault when configured with php_admin_value) +--SKIPIF-- + +--FILE-- +browser_name_pattern; +var_dump(\$cv); +EOT; + +$tester = new FPM\Tester($cfg, $code); +$tester->start(); +$tester->expectLogStartNotices(); +echo $tester + ->request() + ->getBody(); +$tester->terminate(); +$tester->close(); + +?> +--EXPECT-- +string(14) "*Konqueror/2.*" +--CLEAN-- + From db8c91ae9fc871018b032dbd9695ae617e15a7de Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 10 Nov 2023 12:42:17 +0100 Subject: [PATCH 03/25] Fix undeclared variable in stat tests Closes GH-12645 --- ext/standard/tests/file/file.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/file/file.inc b/ext/standard/tests/file/file.inc index d4ad02a363b..0901069717a 100644 --- a/ext/standard/tests/file/file.inc +++ b/ext/standard/tests/file/file.inc @@ -590,9 +590,9 @@ $all_stat_keys = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, "rdev", "size", "atime", "mtime", "ctime", "blksize", "blocks"); -$stat_time_diff_keys = array(8, 'atime'); - function compare_stats($stat1, $stat2, $fields, $op = "==", $flag = false ) { + $stat_time_diff_keys = array(8, 'atime'); + // dump the stat if requested if ( $flag == true ) { var_dump($stat1); From f7f9401cc842824bccc3c79873d6e9f9dfe92997 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 9 Nov 2023 20:44:30 +0100 Subject: [PATCH 04/25] Disable -fsanitize=function on Clang 17 Closes GH-12642 --- configure.ac | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/configure.ac b/configure.ac index 0c5304b9e48..91cb9e2fe4c 100644 --- a/configure.ac +++ b/configure.ac @@ -1534,6 +1534,23 @@ if test "$PHP_UNDEFINED_SANITIZER" = "yes"; then AX_CHECK_COMPILE_FLAG([-fsanitize=undefined], [ CFLAGS="$CFLAGS -fsanitize=undefined" CXXFLAGS="$CXXFLAGS -fsanitize=undefined" + + dnl Clang 17 adds stricter function pointer compatibility checks where pointer args cannot be + dnl cast to void*. In that case, set -fno-sanitize=function. + OLD_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fno-sanitize-recover=undefined" + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +void foo(char *string) {} +int main(void) { + void (*f)(void *) = (void (*)(void *))foo; + f("foo"); +} + ]])],,[ubsan_needs_no_function=yes],) + CFLAGS="$OLD_CFLAGS" + if test "$ubsan_needs_no_function" = yes; then + CFLAGS="$CFLAGS -fno-sanitize=function" + CXXFLAGS="$CFLAGS -fno-sanitize=function" + fi ], [AC_MSG_ERROR([UndefinedBehaviorSanitizer is not available])]) fi From fe34dd1b494a43b33451ceb5e449148d1e62ac84 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sun, 12 Nov 2023 14:06:02 +0100 Subject: [PATCH 05/25] Fix astat imperciseness excemption in test --- ext/standard/tests/file/file.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/tests/file/file.inc b/ext/standard/tests/file/file.inc index 0901069717a..c53c217e970 100644 --- a/ext/standard/tests/file/file.inc +++ b/ext/standard/tests/file/file.inc @@ -608,7 +608,7 @@ function compare_stats($stat1, $stat2, $fields, $op = "==", $flag = false ) { { case "==": if ( $stat1[ $fields[$index] ] != $stat2[ $fields[$index] ] ) { - if ( ! in_array( $index, $stat_time_diff_keys ) ) { + if ( ! in_array( $fields[$index], $stat_time_diff_keys ) ) { $result = false; echo "Error: stat1 do not match with stat2 at key value: $fields[$index]\n"; } elseif (abs($stat1[ $fields[$index] ] - $stat2[ $fields[$index] ]) > 1) { From c376f9943fd1146a8468b81c206e39cef07a9257 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 12 Nov 2023 18:43:35 +0100 Subject: [PATCH 06/25] Fix GH-12655: proc_open() does not take into account references in the descriptor array Closes GH-12658. --- NEWS | 2 ++ ext/standard/proc_open.c | 1 + .../tests/general_functions/gh12655.phpt | 22 +++++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 ext/standard/tests/general_functions/gh12655.phpt diff --git a/NEWS b/NEWS index 73fa39d7967..33d604bc457 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,8 @@ PHP NEWS . Fix memory leak in syslog device handling. (danog) . Fixed bug GH-12621 (browscap segmentation fault when configured in the vhost). (nielsdos) + . Fixed bug GH-12655 (proc_open() does not take into account references + in the descriptor array). (nielsdos) - SQLite3: . Fixed bug GH-12633 (sqlite3_defensive.phpt fails with sqlite 3.44.0). diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index a57e66bd979..3f8eaafd6d2 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -1096,6 +1096,7 @@ PHP_FUNCTION(proc_open) descriptors[ndesc].index = (int)nindex; + ZVAL_DEREF(descitem); if (Z_TYPE_P(descitem) == IS_RESOURCE) { if (set_proc_descriptor_from_resource(descitem, &descriptors[ndesc], ndesc) == FAILURE) { goto exit_fail; diff --git a/ext/standard/tests/general_functions/gh12655.phpt b/ext/standard/tests/general_functions/gh12655.phpt new file mode 100644 index 00000000000..c0235ee6ae6 --- /dev/null +++ b/ext/standard/tests/general_functions/gh12655.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-12655 (proc_open(): Argument #2 ($descriptor_spec) must only contain arrays and streams [Descriptor item must be either an array or a File-Handle]) +--FILE-- + [ "pipe", "r" ], // stdin is a pipe that the child will read from + 1 => [ "pipe", "w" ], // stdout is a pipe that the child will write to + 2 => [ "pipe", "w" ], // stderr is a file to write to +]; + +foreach ( $descriptor_spec as $fd => &$d ) +{ + // don't do anything, just the fact that we used "&$d" will sink the ship! +} + +$proc = proc_open(PHP_BINARY, $descriptor_spec, $pipes); +echo $proc === false ? "FAILED\n" : "SUCCEEDED\n"; + +?> +--EXPECT-- +SUCCEEDED From 28c312c9948cecf10670a0be245ee21d25e3417f Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 14 Nov 2023 18:36:07 +0100 Subject: [PATCH 07/25] [skip ci] Further increase allowable atime deviation MSAN is slow in particular, leading to potentially higher deviations. --- ext/standard/tests/file/file.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/tests/file/file.inc b/ext/standard/tests/file/file.inc index c53c217e970..c972784f9da 100644 --- a/ext/standard/tests/file/file.inc +++ b/ext/standard/tests/file/file.inc @@ -611,7 +611,7 @@ function compare_stats($stat1, $stat2, $fields, $op = "==", $flag = false ) { if ( ! in_array( $fields[$index], $stat_time_diff_keys ) ) { $result = false; echo "Error: stat1 do not match with stat2 at key value: $fields[$index]\n"; - } elseif (abs($stat1[ $fields[$index] ] - $stat2[ $fields[$index] ]) > 1) { + } elseif (abs($stat1[ $fields[$index] ] - $stat2[ $fields[$index] ]) > 2) { $result = false; echo "Error: stat1 differs too much from stat2 at key value: $fields[$index]\n"; } From 9bdd0f0de95d0db93b46af89042f3c9b4dfef972 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 9 Nov 2023 11:53:55 +0100 Subject: [PATCH 08/25] Automatically mark tests as flaky Marking all of these tests as flaky is annoying, so attempt to recognize them automatically. Closes GH-12638 --- run-tests.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/run-tests.php b/run-tests.php index 404fd3e457a..ad69de76b9a 100755 --- a/run-tests.php +++ b/run-tests.php @@ -2873,10 +2873,30 @@ SH; return $restype[0] . 'ED'; } +function is_flaky(TestFile $test): bool +{ + if ($test->hasSection('FLAKY')) { + return true; + } + if (!$test->hasSection('FILE')) { + return false; + } + $file = $test->getSection('FILE'); + $flaky_functions = [ + 'disk_free_space', + 'hrtime', + 'microtime', + 'sleep', + 'usleep', + ]; + $regex = '(\b(' . implode('|', $flaky_functions) . ')\()i'; + return preg_match($regex, $file) === 1; +} + function error_may_be_retried(TestFile $test, string $output): bool { return preg_match('((timed out)|(connection refused)|(404: page not found)|(address already in use)|(mailbox already exists))i', $output) === 1 - || $test->hasSection('FLAKY'); + || is_flaky($test); } /** From df2af7ff6583aad069f24d647e08d226c8ce6dcb Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 15 Nov 2023 20:15:44 +0100 Subject: [PATCH 09/25] Fix GH-12675: MEMORY_LEAK in phpdbg_prompt.c Have to use file_put_contents() instead of --FILE-- because we have to actually load it using the exec command, *and* have to make multiple files, and note that we can only load files relative from the current directory, so we can't rely on files being in the sapi/phpdbg/tests folder. Closes GH-12680. --- NEWS | 3 +++ sapi/phpdbg/phpdbg_prompt.c | 2 ++ sapi/phpdbg/tests/gh12675.phpt | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 sapi/phpdbg/tests/gh12675.phpt diff --git a/NEWS b/NEWS index 33d604bc457..f8a9a54289f 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,9 @@ PHP NEWS - PCRE: . Fixed bug GH-12628 (The gh11374 test fails on Alpinelinux). (nielsdos) +- PHPDBG: + . Fixed bug GH-12675 (MEMORY_LEAK in phpdbg_prompt.c). (nielsdos) + - Standard: . Fix memory leak in syslog device handling. (danog) . Fixed bug GH-12621 (browscap segmentation fault when configured in the diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index 4c50653ce66..8ca6d83c78c 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -408,6 +408,7 @@ PHPDBG_COMMAND(exec) /* {{{ */ if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) { if (PHPDBG_G(in_execution)) { if (phpdbg_ask_user_permission("Do you really want to stop execution to set a new execution context?") == FAILURE) { + free(res); return FAILURE; } } @@ -441,6 +442,7 @@ PHPDBG_COMMAND(exec) /* {{{ */ phpdbg_compile(); } else { + free(res); phpdbg_notice("Execution context not changed"); } } else { diff --git a/sapi/phpdbg/tests/gh12675.phpt b/sapi/phpdbg/tests/gh12675.phpt new file mode 100644 index 00000000000..167e68595df --- /dev/null +++ b/sapi/phpdbg/tests/gh12675.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-12675 (MEMORY_LEAK in phpdbg_prompt.c) +--INI-- +opcache.enable=0 +--PHPDBG-- +ev file_put_contents("gh12675_1.tmp", " 24 +prompt> 16 +prompt> [Cannot stat nonexistent.php, ensure the file exists] +prompt> [Set execution context: %sgh12675_1.tmp] +[Successful compilation of %sgh12675_1.tmp] +prompt> [Execution context not changed] +prompt> [Breakpoint #0 added at %sgh12675_1.tmp:2] +prompt> hi +[Breakpoint #0 at %sgh12675_1.tmp:2, hits: 1] +>00002: echo 2; +prompt> Do you really want to stop execution to set a new execution context? (type y or n): prompt> +--CLEAN-- + From 4f5ba054ba98d4c80cb98cfb40f44be3df9243f3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 15 Nov 2023 20:27:02 +0100 Subject: [PATCH 10/25] Use __DIR__-relative path in tests Otherwise we can't run them from another directory, they'll fail instead. --- ext/exif/tests/bug78793.phpt | 2 +- ext/soap/tests/bug75306.phpt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/exif/tests/bug78793.phpt b/ext/exif/tests/bug78793.phpt index 93d728ff6d1..babbe927045 100644 --- a/ext/exif/tests/bug78793.phpt +++ b/ext/exif/tests/bug78793.phpt @@ -4,7 +4,7 @@ Bug #78793: Use-after-free in exif parsing under memory sanitizer exif --FILE-- WSDL_CACHE_NONE); // Need a warm-up for globals for ($i = 0; $i < 10; $i++) { - $client = new SoapClient("ext/soap/tests/test.wsdl", $options); + $client = new SoapClient(__DIR__ . "/test.wsdl", $options); } $usage = memory_get_usage(); for ($i = 0; $i < 10; $i++) { - $client = new SoapClient("ext/soap/tests/test.wsdl", $options); + $client = new SoapClient(__DIR__ . "/test.wsdl", $options); } $usage_delta = memory_get_usage() - $usage; var_dump($usage_delta); From e1e140f2f2db45f6ecc8bbe72b9408b61d763e04 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 16 Nov 2023 14:13:38 +0100 Subject: [PATCH 11/25] Fix file test race condition 005_variation2.phpt creates files with special names, and filesize_variation5.phpt checks for filesize of inexistent files with special names. Create the files in a separate directory to avoid these tests clashing. Closes GH-12692 --- ext/standard/tests/file/005_variation2.phpt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/standard/tests/file/005_variation2.phpt b/ext/standard/tests/file/005_variation2.phpt index 55d1d666609..270a6cfbdd8 100644 --- a/ext/standard/tests/file/005_variation2.phpt +++ b/ext/standard/tests/file/005_variation2.phpt @@ -28,6 +28,11 @@ function stat_fn( $filename ) { echo "*** Testing fileattime(), filemtime(), filectime() & touch() : usage variations ***\n"; echo "\n*** testing touch ***\n"; + +$dir = __DIR__ . '/005_variation2'; +mkdir($dir); +chdir($dir); + $b = touch(false); $c = touch(''); $d = touch(' '); @@ -47,6 +52,7 @@ stat_fn('|'); var_dump(unlink(' ')); var_dump(unlink('|')); +rmdir($dir); echo "Done"; ?> From 231263749655591b6e91b693c372ec903c99bd4b Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 16 Nov 2023 14:35:37 +0100 Subject: [PATCH 12/25] Retry tests on deadlock Closes GH-12693 --- run-tests.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/run-tests.php b/run-tests.php index ad69de76b9a..39621a39aa6 100755 --- a/run-tests.php +++ b/run-tests.php @@ -2893,9 +2893,23 @@ function is_flaky(TestFile $test): bool return preg_match($regex, $file) === 1; } +function is_flaky_output(string $output): bool +{ + $messages = [ + '404: page not found', + 'address already in use', + 'connection refused', + 'deadlock', + 'mailbox already exists', + 'timed out', + ]; + $regex = '(\b(' . implode('|', $messages) . ')\b)i'; + return preg_match($regex, $output) === 1; +} + function error_may_be_retried(TestFile $test, string $output): bool { - return preg_match('((timed out)|(connection refused)|(404: page not found)|(address already in use)|(mailbox already exists))i', $output) === 1 + return is_flaky_output($output) || is_flaky($test); } From 05ba4615241f247e4acda3cda693be605647e02e Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 16 Nov 2023 16:28:12 +0100 Subject: [PATCH 13/25] [skip ci] Fix more test tmp file conflicts --- ext/standard/tests/file/is_readable_basic.phpt | 2 +- ext/standard/tests/file/is_readable_error.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/file/is_readable_basic.phpt b/ext/standard/tests/file/is_readable_basic.phpt index 7f1b85501a3..38d075439a3 100644 --- a/ext/standard/tests/file/is_readable_basic.phpt +++ b/ext/standard/tests/file/is_readable_basic.phpt @@ -15,7 +15,7 @@ require __DIR__.'/file.inc'; echo "*** Testing is_readable(): basic functionality ***\n"; // create a file -$filename = __DIR__."/is_readable.tmp"; +$filename = __DIR__."/is_readable_basic.tmp"; create_file($filename); $counter = 1; diff --git a/ext/standard/tests/file/is_readable_error.phpt b/ext/standard/tests/file/is_readable_error.phpt index 8df7ff92d52..c013f7fbb70 100644 --- a/ext/standard/tests/file/is_readable_error.phpt +++ b/ext/standard/tests/file/is_readable_error.phpt @@ -3,7 +3,7 @@ Test is_readable() function: error conditions --FILE-- From e41cbd2174c0331dd3214febbac99c0c0d7190ed Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 17 Nov 2023 10:54:23 +0000 Subject: [PATCH 14/25] Skip slow tests on Travis Closes GH-12697 --- travis/test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/travis/test.sh b/travis/test.sh index 13f5c8bd7b5..51e02dbab0c 100755 --- a/travis/test.sh +++ b/travis/test.sh @@ -4,6 +4,7 @@ set -ex # ARM64 CI reports nproc=32, which is excessive. if [ -z "$ARM64" ]; then export JOBS=$(nproc); else export JOBS=16; fi +export SKIP_SLOW_TESTS=1 export SKIP_IO_CAPTURE_TESTS=1 ./sapi/cli/php run-tests.php -P \ -g "FAIL,XFAIL,BORK,WARN,LEAK,SKIP" --offline --show-diff --show-slow 1000 \ From 0b754fc48c691e655cdaa954015a94f9fead8c6f Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 1 Mar 2023 02:19:29 +0100 Subject: [PATCH 15/25] Temporarily disable failing zlib tests on travis (#10738) --- ext/zlib/tests/gzcompress_basic1.phpt | 2 ++ ext/zlib/tests/gzdeflate_basic1.phpt | 2 ++ ext/zlib/tests/gzencode_basic1.phpt | 2 ++ 3 files changed, 6 insertions(+) diff --git a/ext/zlib/tests/gzcompress_basic1.phpt b/ext/zlib/tests/gzcompress_basic1.phpt index ec5f554153d..b5ad22341ed 100644 --- a/ext/zlib/tests/gzcompress_basic1.phpt +++ b/ext/zlib/tests/gzcompress_basic1.phpt @@ -2,6 +2,8 @@ Test gzcompress() function : basic functionality --EXTENSIONS-- zlib +--SKIPIF-- + --FILE-- --FILE-- --FILE-- Date: Thu, 16 Nov 2023 20:42:05 +0000 Subject: [PATCH 16/25] Fix bug #79945: Stream wrappers in imagecreatefrompng causes segfault Closes GH-12696 --- NEWS | 10 +++++++--- ext/gd/tests/bug79945.phpt | 22 ++++++++++++++++++++++ main/php_streams.h | 3 +++ main/streams/streams.c | 9 +++++++-- 4 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 ext/gd/tests/bug79945.phpt diff --git a/NEWS b/NEWS index f8a9a54289f..8c2485d1a65 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,10 @@ PHP NEWS - PHPDBG: . Fixed bug GH-12675 (MEMORY_LEAK in phpdbg_prompt.c). (nielsdos) +- SQLite3: + . Fixed bug GH-12633 (sqlite3_defensive.phpt fails with sqlite 3.44.0). + (SakiTakamachi) + - Standard: . Fix memory leak in syslog device handling. (danog) . Fixed bug GH-12621 (browscap segmentation fault when configured in the @@ -18,9 +22,9 @@ PHP NEWS . Fixed bug GH-12655 (proc_open() does not take into account references in the descriptor array). (nielsdos) -- SQLite3: - . Fixed bug GH-12633 (sqlite3_defensive.phpt fails with sqlite 3.44.0). - (SakiTakamachi) +- Streams: + . Fixed bug #79945 (Stream wrappers in imagecreatefrompng causes segfault). + (Jakub Zelenka) - Zip: . Fixed bug GH-12661 (Inconsistency in ZipArchive::addGlob remove_path Option diff --git a/ext/gd/tests/bug79945.phpt b/ext/gd/tests/bug79945.phpt new file mode 100644 index 00000000000..b985ddd48be --- /dev/null +++ b/ext/gd/tests/bug79945.phpt @@ -0,0 +1,22 @@ +--TEST-- +Bug #79945 (using php wrappers in imagecreatefrompng causes segmentation fault) +--EXTENSIONS-- +gd +--SKIPIF-- + +--FILE-- + +--CLEAN-- +--EXPECTF-- + +Warning: imagecreatefrompng(): "php://filter/read=convert.base64-encode/resource=%s" is not a valid PNG file in %s on line %d diff --git a/main/php_streams.h b/main/php_streams.h index ca8d9c03474..c9a17f22dab 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -206,6 +206,9 @@ struct _php_stream { * PHP_STREAM_FCLOSE_XXX as appropriate */ uint8_t fclose_stdiocast:2; + /* whether stdio cast flushing is in progress */ + int8_t fclose_stdiocast_flush_in_progress:1; + char mode[16]; /* "rwb" etc. ala stdio */ uint32_t flags; /* PHP_STREAM_FLAG_XXX */ diff --git a/main/streams/streams.c b/main/streams/streams.c index 9359ed2fccc..634c0d58348 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1291,8 +1291,13 @@ PHPAPI zend_off_t _php_stream_tell(php_stream *stream) PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) { if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) { - /* flush to commit data written to the fopencookie FILE* */ - fflush(stream->stdiocast); + /* flush can call seek internally so we need to prevent an infinite loop */ + if (!stream->fclose_stdiocast_flush_in_progress) { + stream->fclose_stdiocast_flush_in_progress = 1; + /* flush to commit data written to the fopencookie FILE* */ + fflush(stream->stdiocast); + stream->fclose_stdiocast_flush_in_progress = 0; + } } /* handle the case where we are in the buffer */ From e43ffb5023432091b38391d4034de0c8c213bf2d Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 17 Nov 2023 13:33:51 +0000 Subject: [PATCH 17/25] Fix stream fclose_stdiocast_flush_in_progress type --- main/php_streams.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/php_streams.h b/main/php_streams.h index c9a17f22dab..adf1f812736 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -207,7 +207,7 @@ struct _php_stream { uint8_t fclose_stdiocast:2; /* whether stdio cast flushing is in progress */ - int8_t fclose_stdiocast_flush_in_progress:1; + uint8_t fclose_stdiocast_flush_in_progress:1; char mode[16]; /* "rwb" etc. ala stdio */ From 6a76e5d0a2dcf46b4ab74cc3ffcbfeb860c4fdb3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 17 Nov 2023 19:45:40 +0100 Subject: [PATCH 18/25] Fix GH-12702: libxml2 2.12.0 issue building from src Fixes GH-12702. Co-authored-by: nono303 --- NEWS | 3 +++ ext/dom/document.c | 1 + ext/libxml/php_libxml.h | 1 + 3 files changed, 5 insertions(+) diff --git a/NEWS b/NEWS index 8c2485d1a65..60696c704fe 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,9 @@ PHP NEWS - Intl: . Fixed bug GH-12635 (Test bug69398.phpt fails with ICU 74.1). (nielsdos) +- LibXML: + . Fixed bug GH-12702 (libxml2 2.12.0 issue building from src). (nono303) + - PCRE: . Fixed bug GH-12628 (The gh11374 test fails on Alpinelinux). (nielsdos) diff --git a/ext/dom/document.c b/ext/dom/document.c index 59f00897a69..8312d6c5939 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -23,6 +23,7 @@ #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" #include +#include #ifdef LIBXML_SCHEMAS_ENABLED #include #include diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index c0775a07f5d..a1011f0b178 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -35,6 +35,7 @@ extern zend_module_entry libxml_module_entry; #include "zend_smart_str.h" #include +#include #define LIBXML_SAVE_NOEMPTYTAG 1<<2 From 243fa9c1431e03c23f6d681a89d35621c2e3a38f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 15 Nov 2023 21:15:02 +0100 Subject: [PATCH 19/25] Fix GH-12616: DOM: Removing XMLNS namespace node results in invalid default: prefix The namespace data is freed and set to NULL, but there remain references to the namespace declaration nodes. This (rightfully) confuses libxml2 because its invariants are broken. We also have to remove all remaining references from the subtree. This fixes the data corruption bug. Closes GH-12681. --- NEWS | 4 + ext/dom/element.c | 86 ++++++++++++++++++-- ext/dom/tests/gh12616_1.phpt | 36 +++++++++ ext/dom/tests/gh12616_2.phpt | 39 +++++++++ ext/dom/tests/gh12616_3.phpt | 152 +++++++++++++++++++++++++++++++++++ 5 files changed, 309 insertions(+), 8 deletions(-) create mode 100644 ext/dom/tests/gh12616_1.phpt create mode 100644 ext/dom/tests/gh12616_2.phpt create mode 100644 ext/dom/tests/gh12616_3.phpt diff --git a/NEWS b/NEWS index 60696c704fe..32d4375d545 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.1.27 +- DOM: + . Fixed bug GH-12616 (DOM: Removing XMLNS namespace node results in invalid + default: prefix). (nielsdos) + - Intl: . Fixed bug GH-12635 (Test bug69398.phpt fails with ICU 74.1). (nielsdos) diff --git a/ext/dom/element.c b/ext/dom/element.c index c630bec2b50..f5733c5c48b 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -724,6 +724,83 @@ PHP_METHOD(DOMElement, setAttributeNS) } /* }}} end dom_element_set_attribute_ns */ +static void dom_remove_eliminated_ns_single_element(xmlNodePtr node, xmlNsPtr eliminatedNs) +{ + ZEND_ASSERT(node->type == XML_ELEMENT_NODE); + if (node->ns == eliminatedNs) { + node->ns = NULL; + } + + for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) { + if (attr->ns == eliminatedNs) { + attr->ns = NULL; + } + } +} + +static void dom_remove_eliminated_ns(xmlNodePtr node, xmlNsPtr eliminatedNs) +{ + dom_remove_eliminated_ns_single_element(node, eliminatedNs); + + xmlNodePtr base = node; + node = node->children; + while (node != NULL) { + ZEND_ASSERT(node != base); + + if (node->type == XML_ELEMENT_NODE) { + dom_remove_eliminated_ns_single_element(node, eliminatedNs); + + if (node->children) { + node = node->children; + continue; + } + } + + if (node->next) { + node = node->next; + } else { + /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */ + do { + node = node->parent; + if (node == base) { + return; + } + } while (node->next == NULL); + node = node->next; + } + } +} + +static void dom_eliminate_ns(xmlNodePtr nodep, xmlNsPtr nsptr) +{ + if (nsptr->href != NULL) { + xmlFree((char *) nsptr->href); + nsptr->href = NULL; + } + if (nsptr->prefix != NULL) { + xmlFree((char *) nsptr->prefix); + nsptr->prefix = NULL; + } + + /* Remove it from the list and move it to the old ns list */ + xmlNsPtr current_ns = nodep->nsDef; + if (current_ns == nsptr) { + nodep->nsDef = nsptr->next; + } else { + do { + if (current_ns->next == nsptr) { + current_ns->next = nsptr->next; + break; + } + current_ns = current_ns->next; + } while (current_ns != NULL); + } + nsptr->next = NULL; + dom_set_old_ns(nodep->doc, nsptr); + + dom_remove_eliminated_ns(nodep, nsptr); +} + /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElRemAtNS Since: DOM Level 2 */ @@ -754,14 +831,7 @@ PHP_METHOD(DOMElement, removeAttributeNS) nsptr = dom_get_nsdecl(nodep, (xmlChar *)name); if (nsptr != NULL) { if (xmlStrEqual((xmlChar *)uri, nsptr->href)) { - if (nsptr->href != NULL) { - xmlFree((char *) nsptr->href); - nsptr->href = NULL; - } - if (nsptr->prefix != NULL) { - xmlFree((char *) nsptr->prefix); - nsptr->prefix = NULL; - } + dom_eliminate_ns(nodep, nsptr); } else { RETURN_NULL(); } diff --git a/ext/dom/tests/gh12616_1.phpt b/ext/dom/tests/gh12616_1.phpt new file mode 100644 index 00000000000..408d871aee6 --- /dev/null +++ b/ext/dom/tests/gh12616_1.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix) +--EXTENSIONS-- +dom +--FILE-- +loadXML( + << + CHILDREN + + XML +); + +$doc->documentElement->removeAttributeNS('http://symfony.com/schema/dic/services', ''); +echo $doc->saveXML(); + +$new = new DOMDocument(); +$new->append( + $new->importNode($doc->documentElement, true) +); + +echo $new->saveXML(); + +?> +--EXPECT-- + + + CHILDREN + + + + CHILDREN + diff --git a/ext/dom/tests/gh12616_2.phpt b/ext/dom/tests/gh12616_2.phpt new file mode 100644 index 00000000000..57138e4c45b --- /dev/null +++ b/ext/dom/tests/gh12616_2.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix) +--EXTENSIONS-- +dom +--FILE-- +loadXML( + << + + + + + XML +); + +$doc->documentElement->removeAttributeNS('http://symfony.com/schema/dic/services', 'symfony'); +$xpath = new DOMXPath($doc); +$xpath->registerNamespace('test', 'urn:test'); + +echo $doc->saveXML(); + +$result = $xpath->query('//container/services/test:service[@id="hello"]'); +var_dump($result); + +?> +--EXPECT-- + + + + + + +object(DOMNodeList)#4 (1) { + ["length"]=> + int(1) +} diff --git a/ext/dom/tests/gh12616_3.phpt b/ext/dom/tests/gh12616_3.phpt new file mode 100644 index 00000000000..871a5e4607a --- /dev/null +++ b/ext/dom/tests/gh12616_3.phpt @@ -0,0 +1,152 @@ +--TEST-- +GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix) +--EXTENSIONS-- +dom +--FILE-- +loadXML( + << + + + + + + + + + + XML +); + +$doc->documentElement->firstElementChild->removeAttributeNS('http://symfony.com/schema/dic/services', 'x'); +echo $doc->saveXML(); + +$xpath = new DOMXPath($doc); + +echo "--- Namespaces of child1 ---\n"; + +foreach ($xpath->query("/container/child1/namespace::*") as $ns) { + var_dump($ns); +} + +echo "--- Namespaces of child1/foo (both nodes) ---\n"; + +foreach ($xpath->query("/container/child1/foo/namespace::*") as $ns) { + var_dump($ns); +} + +echo "--- Namespaces of child2 ---\n"; + +foreach ($xpath->query("/container/child2/namespace::*") as $ns) { + var_dump($ns); +} + +?> +--EXPECT-- + + + + + + + + + + + +--- Namespaces of child1 --- +object(DOMNameSpaceNode)#4 (8) { + ["nodeName"]=> + string(9) "xmlns:xml" + ["nodeValue"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["nodeType"]=> + int(18) + ["prefix"]=> + string(3) "xml" + ["localName"]=> + string(3) "xml" + ["namespaceURI"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" +} +--- Namespaces of child1/foo (both nodes) --- +object(DOMNameSpaceNode)#5 (8) { + ["nodeName"]=> + string(9) "xmlns:xml" + ["nodeValue"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["nodeType"]=> + int(18) + ["prefix"]=> + string(3) "xml" + ["localName"]=> + string(3) "xml" + ["namespaceURI"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" +} +object(DOMNameSpaceNode)#8 (8) { + ["nodeName"]=> + string(9) "xmlns:xml" + ["nodeValue"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["nodeType"]=> + int(18) + ["prefix"]=> + string(3) "xml" + ["localName"]=> + string(3) "xml" + ["namespaceURI"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" +} +--- Namespaces of child2 --- +object(DOMNameSpaceNode)#9 (8) { + ["nodeName"]=> + string(9) "xmlns:xml" + ["nodeValue"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["nodeType"]=> + int(18) + ["prefix"]=> + string(3) "xml" + ["localName"]=> + string(3) "xml" + ["namespaceURI"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" +} +object(DOMNameSpaceNode)#5 (8) { + ["nodeName"]=> + string(7) "xmlns:x" + ["nodeValue"]=> + string(38) "http://symfony.com/schema/dic/services" + ["nodeType"]=> + int(18) + ["prefix"]=> + string(1) "x" + ["localName"]=> + string(1) "x" + ["namespaceURI"]=> + string(38) "http://symfony.com/schema/dic/services" + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" +} From 2b4a47ccec5e2feea8f04b2d5607d57a5d4a4520 Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Wed, 22 Nov 2023 20:25:29 -0600 Subject: [PATCH 20/25] Merge changes to CertificateGenerator.inc from PHP-8.2 This pulls only the changes made to CertificateGenerator.inc in the PHP-8.2 branch from commit 505e8d2a04b258d9982e8994e14f5e75be5e1cf8. Co-authored-by: Jakub Zelenka --- ext/openssl/tests/CertificateGenerator.inc | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/ext/openssl/tests/CertificateGenerator.inc b/ext/openssl/tests/CertificateGenerator.inc index 4783353a470..c36718cd548 100644 --- a/ext/openssl/tests/CertificateGenerator.inc +++ b/ext/openssl/tests/CertificateGenerator.inc @@ -85,8 +85,8 @@ class CertificateGenerator openssl_x509_export_to_file($this->ca, $file); } - public function saveNewCertAsFileWithKey( - $commonNameForCert, $file, $keyLength = null, $subjectAltName = null + public function saveNewCertAndKey( + $commonNameForCert, $certFile, $keyFile, $keyLength = null, $subjectAltName = null ) { $dn = [ 'countryName' => 'BY', @@ -117,7 +117,7 @@ $subjectAltNameConfig basicConstraints = CA:FALSE $subjectAltNameConfig CONFIG; - $configFile = $file . '.cnf'; + $configFile = $certFile . '.cnf'; file_put_contents($configFile, $configCode); try { @@ -146,12 +146,24 @@ CONFIG; $keyText = ''; openssl_pkey_export($this->lastKey, $keyText, null, $config); - file_put_contents($file, $certText . PHP_EOL . $keyText); + if ($certFile === $keyFile) { + file_put_contents($certFile, $certText . PHP_EOL . $keyText); + } else { + file_put_contents($certFile, $certText); + file_put_contents($keyFile, $keyText); + } } finally { unlink($configFile); } } + + public function saveNewCertAsFileWithKey( + $commonNameForCert, $file, $keyLength = null, $subjectAltName = null + ) { + $this->saveNewCertAndKey($commonNameForCert, $file, $file, $keyLength, $subjectAltName); + } + public function getCertDigest($algo) { return openssl_x509_fingerprint($this->lastCert, $algo); From 55e0748487f99e8bb60b8c4fd4f6e9c3857c8cf3 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Mon, 23 Oct 2023 13:51:03 +0100 Subject: [PATCH 21/25] Fix #50713: openssl_pkcs7_verify() may ignore untrusted CAs Closes GH-12499 --- NEWS | 4 ++ ext/openssl/openssl.c | 2 +- ext/openssl/tests/CertificateGenerator.inc | 78 +++++++++++----------- ext/openssl/tests/bug50713.phpt | 40 +++++++++++ 4 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 ext/openssl/tests/bug50713.phpt diff --git a/NEWS b/NEWS index 32d4375d545..d79d0e4fe22 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,10 @@ PHP NEWS - LibXML: . Fixed bug GH-12702 (libxml2 2.12.0 issue building from src). (nono303) +- OpenSSL: + . Fixed bug #50713 (openssl_pkcs7_verify() may ignore untrusted CAs). + (Jakub Zelenka) + - PCRE: . Fixed bug GH-12628 (The gh11374 test fails on Alpinelinux). (nielsdos) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 31baa2d0e02..a6a05fe03db 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -5382,7 +5382,7 @@ PHP_FUNCTION(openssl_pkcs7_verify) signersfilename, signersfilename_len, 3, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY)); if (certout) { int i; - signers = PKCS7_get0_signers(p7, NULL, (int)flags); + signers = PKCS7_get0_signers(p7, others, (int)flags); if (signers != NULL) { for (i = 0; i < sk_X509_num(signers); i++) { diff --git a/ext/openssl/tests/CertificateGenerator.inc b/ext/openssl/tests/CertificateGenerator.inc index c36718cd548..12764c8b63d 100644 --- a/ext/openssl/tests/CertificateGenerator.inc +++ b/ext/openssl/tests/CertificateGenerator.inc @@ -85,8 +85,8 @@ class CertificateGenerator openssl_x509_export_to_file($this->ca, $file); } - public function saveNewCertAndKey( - $commonNameForCert, $certFile, $keyFile, $keyLength = null, $subjectAltName = null + private function generateCertAndKey( + $commonNameForCert, $file, $keyLength = null, $subjectAltName = null ) { $dn = [ 'countryName' => 'BY', @@ -117,51 +117,53 @@ $subjectAltNameConfig basicConstraints = CA:FALSE $subjectAltNameConfig CONFIG; - $configFile = $certFile . '.cnf'; + $configFile = $file . '.cnf'; file_put_contents($configFile, $configCode); - try { - $config = [ - 'config' => $configFile, - 'req_extensions' => 'v3_req', - 'x509_extensions' => 'usr_cert', - ]; + $config = [ + 'config' => $configFile, + 'req_extensions' => 'v3_req', + 'x509_extensions' => 'usr_cert', + ]; - $this->lastKey = self::generateKey($keyLength); - $csr = openssl_csr_new($dn, $this->lastKey, $config); - $this->lastCert = openssl_csr_sign( - $csr, - $this->ca, - $this->caKey, - /* days */ 2, - $config, - ); - if (!$this->lastCert) { - throw new Exception('Failed to create certificate'); - } + $this->lastKey = self::generateKey($keyLength); + $csr = openssl_csr_new($dn, $this->lastKey, $config); + $this->lastCert = openssl_csr_sign( + $csr, + $this->ca, + $this->caKey, + /* days */ 2, + $config, + ); - $certText = ''; - openssl_x509_export($this->lastCert, $certText); - - $keyText = ''; - openssl_pkey_export($this->lastKey, $keyText, null, $config); - - if ($certFile === $keyFile) { - file_put_contents($certFile, $certText . PHP_EOL . $keyText); - } else { - file_put_contents($certFile, $certText); - file_put_contents($keyFile, $keyText); - } - } finally { - unlink($configFile); - } + return $config; } - public function saveNewCertAsFileWithKey( $commonNameForCert, $file, $keyLength = null, $subjectAltName = null ) { - $this->saveNewCertAndKey($commonNameForCert, $file, $file, $keyLength, $subjectAltName); + $config = $this->generateCertAndKey($commonNameForCert, $file, $keyLength, $subjectAltName); + + $certText = ''; + openssl_x509_export($this->lastCert, $certText); + + $keyText = ''; + openssl_pkey_export($this->lastKey, $keyText, null, $config); + + file_put_contents($file, $certText . PHP_EOL . $keyText); + + unlink($config['config']); + } + + public function saveNewCertAndKey( + $commonNameForCert, $certFile, $keyFile, $keyLength = null, $subjectAltName = null + ) { + $config = $this->generateCertAndKey($commonNameForCert, $certFile, $keyLength, $subjectAltName); + + openssl_x509_export_to_file($this->lastCert, $certFile); + openssl_pkey_export_to_file($this->lastKey, $keyFile, null, $config); + + unlink($config['config']); } public function getCertDigest($algo) diff --git a/ext/openssl/tests/bug50713.phpt b/ext/openssl/tests/bug50713.phpt new file mode 100644 index 00000000000..95eff2e75f9 --- /dev/null +++ b/ext/openssl/tests/bug50713.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #50713 (openssl_pkcs7_verify() may ignore untrusted CAs) +--EXTENSIONS-- +openssl +--FILE-- +saveCaCert($cacertFile); +$certificateGenerator->saveNewCertAndKey('bug50713', $certFile, $keyFile, 1024); + +var_dump(openssl_pkcs7_sign($inFile, $outFile, 'file://' . $certFile, 'file://' . $keyFile, [], PKCS7_NOCERTS)); +var_dump(openssl_pkcs7_verify($outFile, 0, $signersFile, [$cacertFile], $certFile)); +var_dump(strlen(file_get_contents($signersFile)) > 0); +?> +--CLEAN-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) From 1fdcfa4ebe842531f2b403d45a92012c83125ba1 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 20 Nov 2023 11:56:06 +0100 Subject: [PATCH 22/25] Fix use-after-free of name in var-var with malicious error handler Fixes oss-fuzz #54325 Closes GH-12732 --- NEWS | 4 ++++ Zend/tests/oss_fuzz_54325.phpt | 19 +++++++++++++++++++ Zend/zend_vm_def.h | 7 +++++++ Zend/zend_vm_execute.h | 21 +++++++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 Zend/tests/oss_fuzz_54325.phpt diff --git a/NEWS b/NEWS index d79d0e4fe22..92b6027c4fd 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.1.27 +- Core: + . Fixed oss-fuzz #54325 (Use-after-free of name in var-var with malicious + error handler). (ilutov) + - DOM: . Fixed bug GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix). (nielsdos) diff --git a/Zend/tests/oss_fuzz_54325.phpt b/Zend/tests/oss_fuzz_54325.phpt new file mode 100644 index 00000000000..d998acf1ffe --- /dev/null +++ b/Zend/tests/oss_fuzz_54325.phpt @@ -0,0 +1,19 @@ +--TEST-- +oss-fuzz #54325: Fix use-after-free of name in var-var with malicious error handler +--FILE-- + +--EXPECT-- +string(23) "Undefined variable $oof" +object(stdClass)#2 (0) { +} diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 90704993bb2..fd5b7242ba6 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1748,6 +1748,10 @@ ZEND_VM_C_LABEL(fetch_this): } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if (OP1_TYPE == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -1755,6 +1759,9 @@ ZEND_VM_C_LABEL(fetch_this): } else { retval = &EG(uninitialized_zval); } + if (OP1_TYPE == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 530fd7d3e11..bc098148541 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -9755,6 +9755,10 @@ fetch_this: } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if (IS_CONST == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -9762,6 +9766,9 @@ fetch_this: } else { retval = &EG(uninitialized_zval); } + if (IS_CONST == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) { @@ -17560,6 +17567,10 @@ fetch_this: } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -17567,6 +17578,9 @@ fetch_this: } else { retval = &EG(uninitialized_zval); } + if ((IS_TMP_VAR|IS_VAR) == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) { @@ -47008,6 +47022,10 @@ fetch_this: } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if (IS_CV == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -47015,6 +47033,9 @@ fetch_this: } else { retval = &EG(uninitialized_zval); } + if (IS_CV == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) { From daa38dd63e6837ec7e3ecdecf7e7be7b13628f16 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 20 Nov 2023 12:37:32 +0100 Subject: [PATCH 23/25] Fix in-place modification of filename in php_message_handler_for_zend php_strip_url_passwd modifies url in-place. We cannot assume from php_message_handler_for_zend that data is a temporary, modifiable string. Fixes oss-fuzz #64209 Closes GH-12733 --- NEWS | 2 ++ Zend/tests/oss_fuzz_64209.phpt | 13 +++++++++++++ main/main.c | 21 +++++++++++++++------ 3 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 Zend/tests/oss_fuzz_64209.phpt diff --git a/NEWS b/NEWS index 92b6027c4fd..b8d860bd3d9 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ PHP NEWS - Core: . Fixed oss-fuzz #54325 (Use-after-free of name in var-var with malicious error handler). (ilutov) + . Fixed oss-fuzz #64209 (In-place modification of filename in + php_message_handler_for_zend). (ilutov) - DOM: . Fixed bug GH-12616 (DOM: Removing XMLNS namespace node results in invalid diff --git a/Zend/tests/oss_fuzz_64209.phpt b/Zend/tests/oss_fuzz_64209.phpt new file mode 100644 index 00000000000..599ae258e5b --- /dev/null +++ b/Zend/tests/oss_fuzz_64209.phpt @@ -0,0 +1,13 @@ +--TEST-- +oss-fuzz #64209: Fix in-place modification of filename in php_message_handler_for_zend +--FILE-- + +--EXPECTF-- +Warning: require(://@): Failed to open stream: No such file or directory in %s on line %d + +Fatal error: Uncaught Error: Failed opening required '://@' (include_path='%s') in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/main/main.c b/main/main.c index 4868d2df400..9c62aa914eb 100644 --- a/main/main.c +++ b/main/main.c @@ -1585,15 +1585,24 @@ static void php_free_request_globals(void) static ZEND_COLD void php_message_handler_for_zend(zend_long message, const void *data) { switch (message) { - case ZMSG_FAILED_INCLUDE_FOPEN: - php_error_docref("function.include", E_WARNING, "Failed opening '%s' for inclusion (include_path='%s')", php_strip_url_passwd((char *) data), STR_PRINT(PG(include_path))); + case ZMSG_FAILED_INCLUDE_FOPEN: { + char *tmp = estrdup((char *) data); + php_error_docref("function.include", E_WARNING, "Failed opening '%s' for inclusion (include_path='%s')", php_strip_url_passwd(tmp), STR_PRINT(PG(include_path))); + efree(tmp); break; - case ZMSG_FAILED_REQUIRE_FOPEN: - zend_throw_error(NULL, "Failed opening required '%s' (include_path='%s')", php_strip_url_passwd((char *) data), STR_PRINT(PG(include_path))); + } + case ZMSG_FAILED_REQUIRE_FOPEN: { + char *tmp = estrdup((char *) data); + zend_throw_error(NULL, "Failed opening required '%s' (include_path='%s')", php_strip_url_passwd(tmp), STR_PRINT(PG(include_path))); + efree(tmp); break; - case ZMSG_FAILED_HIGHLIGHT_FOPEN: - php_error_docref(NULL, E_WARNING, "Failed opening '%s' for highlighting", php_strip_url_passwd((char *) data)); + } + case ZMSG_FAILED_HIGHLIGHT_FOPEN: { + char *tmp = estrdup((char *) data); + php_error_docref(NULL, E_WARNING, "Failed opening '%s' for highlighting", php_strip_url_passwd(tmp)); + efree(tmp); break; + } case ZMSG_MEMORY_LEAK_DETECTED: case ZMSG_MEMORY_LEAK_REPEATED: #if ZEND_DEBUG From fafa34d9cdfe5bd5f4a8ba58b63ae40434468d85 Mon Sep 17 00:00:00 2001 From: Muhammad Moinur Rahman Date: Sat, 18 Nov 2023 15:07:06 +0100 Subject: [PATCH 24/25] Add host_cpu type for FreeBSD In FreeBSD world x86_64 host type is identified as amd64 so add proper checks for FreeBSD amd64 hosts. Close GH-12736 --- ext/opcache/config.m4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4 index 2a83fa24559..0b923206282 100644 --- a/ext/opcache/config.m4 +++ b/ext/opcache/config.m4 @@ -29,7 +29,7 @@ if test "$PHP_OPCACHE" != "no"; then if test "$PHP_OPCACHE_JIT" = "yes"; then case $host_cpu in - i[[34567]]86*|x86*|aarch64) + i[[34567]]86*|x86*|aarch64|amd64) ;; *) AC_MSG_WARN([JIT not supported by host architecture]) @@ -48,7 +48,8 @@ if test "$PHP_OPCACHE" != "no"; then DASM_FLAGS="-D X64APPLE=1 -D X64=1" DASM_ARCH="x86" ;; - x86_64*) + *x86_64*|amd64-*-freebsd*) + IR_TARGET=IR_TARGET_X64 DASM_FLAGS="-D X64=1" DASM_ARCH="x86" ;; From 87107f86884d9e717a4f2caee84c0f540abddc1a Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 22 Nov 2023 13:19:10 +0300 Subject: [PATCH 25/25] Fixed GH-12748: Function JIT emits "could not convert to int" warning at the same time as invalid offset Error --- ext/opcache/jit/zend_jit_helpers.c | 3 ++- ext/opcache/tests/jit/gh12748.phpt | 35 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 ext/opcache/tests/jit/gh12748.phpt diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index fc52986ea19..de40449e409 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -1135,7 +1135,8 @@ try_string_offset: goto try_string_offset; default: zend_jit_illegal_string_offset(dim); - break; + ZVAL_NULL(result); + return; } offset = zval_get_long_func(dim, /* is_strict */ false); diff --git a/ext/opcache/tests/jit/gh12748.phpt b/ext/opcache/tests/jit/gh12748.phpt new file mode 100644 index 00000000000..d7580fdb9ab --- /dev/null +++ b/ext/opcache/tests/jit/gh12748.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-12748: Function JIT emits "could not convert to int" warning at the same time as invalid offset Error +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--FILE-- +getMessage(), "\n"; +} +try { + echo "empty():\n"; + var_dump(empty($container[new stdClass()])); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +try { + echo "Coalesce():\n"; + var_dump($container[new stdClass()] ?? 'default'); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +isset(): +bool(false) +empty(): +bool(true) +Coalesce(): +Cannot access offset of type stdClass on string