Commit f6ca007e52c for php
commit f6ca007e52c400467b82e7c6bc13c5588d850416
Author: Jorg Sowa <jorg.sowa@gmail.com>
Date: Sat Nov 14 17:03:14 2026 +0100
Forbid \C in patterns enabling UTF mode with (*UTF) (GH-25452)
\C was only forbidden with the u modifier, so the GH-21134 fix did not
apply to patterns starting with (*UTF) or (*UTF8).
Closes GH-25452
diff --git a/NEWS b/NEWS
index 7b2fd52737b..a5fa9c4ead9 100644
--- a/NEWS
+++ b/NEWS
@@ -82,6 +82,10 @@ PHP NEWS
. Fixed stack buffer overflow with long connection user names, passwords,
charsets, or roles. (Ilia Alshanetsky)
+- PCRE:
+ . Fixed \C being allowed in patterns enabling UTF mode with (*UTF) instead
+ of the u modifier. (Jorg Sowa)
+
- SNMP:
. Fixed use-after-free of the context name set by SNMP::setSecurity().
(lacatoire)
diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c
index 5146260b323..d5cec133878 100644
--- a/ext/pcre/php_pcre.c
+++ b/ext/pcre/php_pcre.c
@@ -591,6 +591,8 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo
uint32_t coptions = 0;
#endif
uint32_t eoptions = PHP_PCRE_DEFAULT_EXTRA_COPTIONS;
+ uint32_t all_options;
+ uint32_t has_backslash_c;
PCRE2_UCHAR error[128];
PCRE2_SIZE erroffset;
int errnumber;
@@ -785,6 +787,22 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo
/* Compile pattern and display a warning if compilation failed. */
re = pcre2_compile((PCRE2_SPTR)pattern, pattern_len, coptions, &errnumber, &erroffset, cctx);
+ /* In-pattern verbs such as (*UTF) can enable PCRE2_UTF, in which \C is unsafe. */
+ if (re != NULL && !(coptions & PCRE2_NEVER_BACKSLASH_C)
+ && pcre2_pattern_info(re, PCRE2_INFO_HASBACKSLASHC, &has_backslash_c) >= 0
+ && has_backslash_c
+ && pcre2_pattern_info(re, PCRE2_INFO_ALLOPTIONS, &all_options) >= 0
+ && (all_options & PCRE2_UTF)) {
+ pcre2_code_free(re);
+ if (key != regex) {
+ zend_string_release_ex(key, 0);
+ }
+ php_error_docref(NULL, E_WARNING, "Compilation failed: using \\C is incompatible with UTF-8 mode");
+ pcre_handle_exec_error(PCRE2_ERROR_INTERNAL);
+ efree(pattern);
+ return NULL;
+ }
+
if (re == NULL) {
if (key != regex) {
zend_string_release_ex(key, 0);
diff --git a/ext/pcre/tests/preg_inline_utf_backslash_c.phpt b/ext/pcre/tests/preg_inline_utf_backslash_c.phpt
new file mode 100644
index 00000000000..bbfcc7f5357
--- /dev/null
+++ b/ext/pcre/tests/preg_inline_utf_backslash_c.phpt
@@ -0,0 +1,41 @@
+--TEST--
+In-pattern (*UTF) forbids \C like the /u modifier
+--FILE--
+<?php
+$patterns = [
+ '/(*UTF)\C/',
+ '/(*UTF8)a\C/',
+ '/(*LIMIT_MATCH=100)(*UTF)a\C/',
+ '/\C/u',
+];
+foreach ($patterns as $pattern) {
+ var_dump(preg_match($pattern, "a\u{E4}"));
+}
+
+// GH-21134 reproducer using (*UTF) instead of /u
+var_dump(preg_match_all("/(*UTF)(.*)\\C/", "\u{E0}", $m), $m);
+
+// \C is still allowed outside UTF mode, and an escaped backslash is not \C
+var_dump(preg_match('/a\C/', "a\u{E4}", $m), bin2hex($m[0]));
+var_dump(preg_match('/(*UTF)\\\\C/', 'x\C'));
+?>
+--EXPECTF--
+
+Warning: preg_match(): Compilation failed: using \C is incompatible with UTF-8 mode in %s on line %d
+bool(false)
+
+Warning: preg_match(): Compilation failed: using \C is incompatible with UTF-8 mode in %s on line %d
+bool(false)
+
+Warning: preg_match(): Compilation failed: using \C is incompatible with UTF-8 mode in %s on line %d
+bool(false)
+
+Warning: preg_match(): Compilation failed: using \C is incompatible with the 'u' modifier at offset 2 in %s on line %d
+bool(false)
+
+Warning: preg_match_all(): Compilation failed: using \C is incompatible with UTF-8 mode in %s on line %d
+bool(false)
+NULL
+int(1)
+string(4) "61c3"
+int(1)