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)