diff --git a/UPGRADING b/UPGRADING index 77d8463afe990..9a1b37d98f7f4 100644 --- a/UPGRADING +++ b/UPGRADING @@ -19,6 +19,11 @@ PHP 8.6 UPGRADE NOTES 1. Backward Incompatible Changes ======================================== +- PCRE: + . preg_grep() now returns false instead of a partial array when a PCRE + execution error occurs (e.g. malformed UTF-8 input with the /u modifier). + This is consistent with other preg_* functions. + - Phar: . Invalid values now throw in Phar::mungServer() instead of being silently ignored. @@ -100,6 +105,10 @@ PHP 8.6 UPGRADE NOTES . Output of openssl_x509_parse() contains criticalExtensions listing all critical certificate extensions. +- PCNTL: + . pcntl_alarm() now throws a ValueError if the seconds argument is + lower than zero or greater than platform's UINT_MAX. + - Phar: . Phar::mungServer() now supports reference values. diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 53286723e1980..0ef87fffa287e 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -212,7 +212,7 @@ PHP_RINIT_FUNCTION(pcntl) PCNTL_G(last_error) = 0; PCNTL_G(num_signals) = NSIG; #ifdef SIGRTMAX - /* At least FreeBSD reports an incorrecrt NSIG that does not include realtime signals. + /* At least FreeBSD reports an incorrect NSIG that does not include realtime signals. * As SIGRTMAX may be a dynamic value, adjust the value in INIT. */ if (NSIG < SIGRTMAX + 1) { PCNTL_G(num_signals) = SIGRTMAX + 1; @@ -314,6 +314,11 @@ PHP_FUNCTION(pcntl_alarm) Z_PARAM_LONG(seconds); ZEND_PARSE_PARAMETERS_END(); + if (seconds < 0 || seconds > UINT_MAX) { + zend_argument_value_error(1, "must be between 0 and %u", UINT_MAX); + RETURN_THROWS(); + } + RETURN_LONG((zend_long) alarm(seconds)); } /* }}} */ @@ -870,17 +875,8 @@ PHP_FUNCTION(pcntl_signal_get_handler) Z_PARAM_LONG(signo) ZEND_PARSE_PARAMETERS_END(); - // note: max signal on mac is SIGUSR2 (31), no real time signals. - int sigmax = NSIG - 1; -#if defined(SIGRTMAX) - // oddily enough, NSIG on freebsd reports only 32 whereas SIGRTMIN starts at 65. - if (sigmax < SIGRTMAX) { - sigmax = SIGRTMAX; - } -#endif - - if (signo < 1 || signo > sigmax) { - zend_argument_value_error(1, "must be between 1 and %d", sigmax); + if (signo < 1 || signo >= PCNTL_G(num_signals)) { + zend_argument_value_error(1, "must be between 1 and %d", PCNTL_G(num_signals) - 1); RETURN_THROWS(); } @@ -1153,7 +1149,7 @@ static void pcntl_siginfo_to_zval(int signo, siginfo_t *siginfo, zval *user_sigi case SIGFPE: case SIGSEGV: case SIGBUS: - add_assoc_double_ex(user_siginfo, "addr", sizeof("addr")-1, (zend_long)siginfo->si_addr); + add_assoc_long_ex(user_siginfo, "addr", sizeof("addr")-1, (zend_long)siginfo->si_addr); break; #if defined(SIGPOLL) && !defined(__CYGWIN__) case SIGPOLL: diff --git a/ext/pcntl/tests/pcntl_alarm_invalid_value.phpt b/ext/pcntl/tests/pcntl_alarm_invalid_value.phpt new file mode 100644 index 0000000000000..59e74662f6f7c --- /dev/null +++ b/ext/pcntl/tests/pcntl_alarm_invalid_value.phpt @@ -0,0 +1,35 @@ +--TEST-- +pcntl_alarm() rejects invalid values +--EXTENSIONS-- +pcntl +--SKIPIF-- + +--FILE-- +getMessage() . \PHP_EOL; +} + +try { + pcntl_alarm(PHP_INT_MIN); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} + +try { + pcntl_alarm(PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} + +var_dump(pcntl_alarm(0)); + +?> +--EXPECTF-- +pcntl_alarm(): Argument #1 ($seconds) must be between 0 and %d +pcntl_alarm(): Argument #1 ($seconds) must be between 0 and %d +pcntl_alarm(): Argument #1 ($seconds) must be between 0 and %d +int(0) diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index 4efe0542b7638..7b07c8240be04 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -831,6 +831,7 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo if (key != regex) { zend_string_release_ex(key, 0); } + pcre2_code_free(new_entry.re); php_error_docref(NULL, E_WARNING, "Internal pcre2_pattern_info() error %d", rc); pcre_handle_exec_error(PCRE2_ERROR_INTERNAL); return NULL; @@ -841,6 +842,7 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo if (key != regex) { zend_string_release_ex(key, 0); } + pcre2_code_free(new_entry.re); php_error_docref(NULL, E_WARNING, "Internal pcre_pattern_info() error %d", rc); pcre_handle_exec_error(PCRE2_ERROR_INTERNAL); return NULL; @@ -1286,7 +1288,18 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str, if (subpats != NULL) { /* Try to get the list of substrings and display a warning if failed. */ if (UNEXPECTED(offsets[1] < offsets[0])) { - if (match_sets) efree(match_sets); + if (match_sets) { + for (i = 0; i < num_subpats; i++) { + zend_array_destroy(match_sets[i]); + } + efree(match_sets); + } + if (marks) { + zend_array_destroy(marks); + } + if (match_data != mdata) { + pcre2_match_data_free(match_data); + } php_error_docref(NULL, E_WARNING, "Get subpatterns list failed"); RETURN_FALSE; } @@ -2987,6 +3000,11 @@ PHPAPI void php_pcre_grep_impl(pcre_cache_entry *pce, zval *input, zval *return if (match_data != mdata) { pcre2_match_data_free(match_data); } + + if (PCRE_G(error_code) != PHP_PCRE_NO_ERROR) { + zend_array_destroy(Z_ARR_P(return_value)); + RETURN_FALSE; + } } /* }}} */ diff --git a/ext/pcre/tests/preg_grep_error_utf8.phpt b/ext/pcre/tests/preg_grep_error_utf8.phpt new file mode 100644 index 0000000000000..efdd7632ab711 --- /dev/null +++ b/ext/pcre/tests/preg_grep_error_utf8.phpt @@ -0,0 +1,44 @@ +--TEST-- +preg_grep() returns false on match execution error (e.g. malformed UTF-8) +--FILE-- + +--EXPECTF-- +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +array(2) { + [0]=> + string(3) "foo" + [1]=> + string(3) "bar" +} +bool(true) diff --git a/ext/pcre/tests/preg_match_all_negative_length_match.phpt b/ext/pcre/tests/preg_match_all_negative_length_match.phpt new file mode 100644 index 0000000000000..b18007cd32562 --- /dev/null +++ b/ext/pcre/tests/preg_match_all_negative_length_match.phpt @@ -0,0 +1,10 @@ +--TEST-- +preg_match_all() resource cleanup when \K in lookahead causes negative-length match +--FILE-- + +--EXPECTF-- +Warning: preg_match_all(): Compilation failed: \K is not allowed in lookarounds (but see PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK) at offset %d in %s +bool(false) diff --git a/ext/pcre/tests/preg_match_negative_length_match.phpt b/ext/pcre/tests/preg_match_negative_length_match.phpt new file mode 100644 index 0000000000000..221ea4fb9e54c --- /dev/null +++ b/ext/pcre/tests/preg_match_negative_length_match.phpt @@ -0,0 +1,10 @@ +--TEST-- +preg_match() resource cleanup when \K in lookahead causes negative-length match +--FILE-- + +--EXPECTF-- +Warning: preg_match(): Compilation failed: \K is not allowed in lookarounds (but see PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK) at offset %d in %s +bool(false) diff --git a/run-tests.php b/run-tests.php index 634beffa533ca..68d2cb69256da 100755 --- a/run-tests.php +++ b/run-tests.php @@ -859,7 +859,7 @@ function write_information(array $user_tests, $phpdbg): void $exts = get_loaded_extensions(); $ext_dir = ini_get('extension_dir'); foreach (scandir($ext_dir) as $file) { - if (preg_match('/^(?:php_)?([_a-zA-Z0-9]+)\.(?:so|dll)$/', $file, $matches)) { + if (preg_match('/^(?:php_)?([_a-zA-Z0-9]+)\.(?:' . PHP_SHLIB_SUFFIX . ')$/', $file, $matches)) { if (!extension_loaded($matches[1])) { $exts[] = $matches[1]; }