Commit 70603b9465f for php.net
commit 70603b9465f52a33bc5bb51ed4580a9f5ed56b34
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Mon Aug 17 06:55:19 2026 -0400
Stop grapheme_strrev from using UBRK_DONE as a byte index (#23323)
ubrk_previous() returns UBRK_DONE after the first boundary. The loop
condition ran before that assignment, so the body treated -1 as an
offset and wrote into the zend_string NUL. Break when the iterator is
done, and NUL-terminate the result of zend_string_alloc.
diff --git a/NEWS b/NEWS
index a0c4359f4a0..6550a7a9641 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,10 @@ PHP NEWS
left busy for the next fetch, and rows delivered from a result another
statement took over. (KentarouTakeda)
+- Intl:
+ . Fixed grapheme_strrev() treating UBRK_DONE as a byte index and leaving
+ the result without a terminating NUL. (iliaal)
+
- Phar:
. Fixed Phar archives being automatically detected when ".phar" only occurs
in a directory name or is not a filename extension in an included file's
diff --git a/ext/intl/grapheme/grapheme_string.cpp b/ext/intl/grapheme/grapheme_string.cpp
index 5e614be6ae7..a1daae84db9 100644
--- a/ext/intl/grapheme/grapheme_string.cpp
+++ b/ext/intl/grapheme/grapheme_string.cpp
@@ -1175,6 +1175,9 @@ U_CFUNC PHP_FUNCTION(grapheme_strrev)
current = ZSTR_LEN(string);
for (end = pstr; pos != UBRK_DONE; ) {
pos = ubrk_previous(bi);
+ if (pos == UBRK_DONE) {
+ break;
+ }
end_len = current - pos;
for (int32_t j = 0; j < end_len; j++) {
*p++ = *(pstr + pos + j);
@@ -1182,6 +1185,7 @@ U_CFUNC PHP_FUNCTION(grapheme_strrev)
current = pos;
}
ubrk_end:
+ ZSTR_VAL(ret)[ZSTR_LEN(ret)] = '\0';
RETVAL_NEW_STR(ret);
ubrk_close(bi);
close:
diff --git a/ext/intl/tests/grapheme_strrev_ubrk_done.phpt b/ext/intl/tests/grapheme_strrev_ubrk_done.phpt
new file mode 100644
index 00000000000..6f70f7cd1f7
--- /dev/null
+++ b/ext/intl/tests/grapheme_strrev_ubrk_done.phpt
@@ -0,0 +1,25 @@
+--TEST--
+grapheme_strrev() stops at UBRK_DONE instead of using it as a byte index
+--EXTENSIONS--
+intl
+--FILE--
+<?php
+
+$cases = [
+ 'abc',
+ 'a',
+ '土下座',
+ "null\x00byte",
+];
+
+foreach ($cases as $s) {
+ $rev = grapheme_strrev($s);
+ echo strlen($s), ' ', strlen($rev), ' ', bin2hex($rev), "\n";
+}
+
+?>
+--EXPECT--
+3 3 636261
+1 1 61
+9 9 e5baa7e4b88be59c9f
+9 9 65747962006c6c756e