You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ext/mbstring: mb_regex_substitute() reads past the replacement string's end on a truncated trailing multibyte lead byte #24769
509e27bae1 (GH-23499, fixed August 2026) corrected two places in mb_regex_substitute() (ext/mbstring/php_mbregex.c, the replacement-string interpolation behind mb_ereg_replace()/mb_eregi_replace()) where php_mb_mbchar_bytes()'s claimed character width was used to advance a pointer without checking it against the buffer's actual remaining length -- both were clamped to MIN(p + clen, eos). f6852befa3 (GH-24498, fixed October 2026) separately fixed the same bug class in a different file, php_mb_rfc1867_getword().
Neither fix touched the firstphp_mb_mbchar_bytes() call in mb_regex_substitute()'s main loop -- the check that runs on every character, before any backslash/backref handling, to decide whether it's an ordinary character or the start of a backreference:
while (p<eos) {
...
clen= (int) php_mb_mbchar_bytes(p, enc);
if (clen!=1||p==eos||p[0] !='\\') {
/* skip anything that's not an ascii backslash */smart_str_appendl(pbuf, p, clen);
p+=clen;
continue;
}
If the character at p is not a backslash, and its lead byte (looked up in the current mbregex encoding's mblen_table) claims a width of 2-4 bytes but fewer than clen - 1 bytes actually remain before eos, smart_str_appendl(pbuf, p, clen) copies clen bytes starting at p regardless -- reading past the replacement string's real content. This is the identical shape as the two already-fixed call sites a few lines later in the same function, which use MIN(p + clen, eos) instead.
Minimal reproducer:
<?phpmb_regex_encoding('UTF-8');
$replacement = "x" . "\xF0"; // ends in a UTF-8 4-byte lead byte with 0 continuation bytes presentvar_dump(mb_ereg_replace('a', $replacement, 'a'));
Resulted in this output:
string(5) "x..."
(5 bytes: the input is only 2 bytes, but php_mb_mbchar_bytes() claims the trailing \xF0 byte is a 4-byte character, so 4 bytes get copied starting from it -- 3 of them past the replacement string's actual end.)
But I expected this output instead: a 2-byte result ("x\xF0"), i.e. the same MIN(p + clen, eos)-style clamping already applied to the other two call sites in this function.
This was repeatable across 20 differently-sized/content inputs ending the same way -- always exactly claimed_width - 1 extra bytes beyond the logical input length. In shorter variants the extra bytes were non-zero, unpredictable content rather than a fixed pattern, consistent with genuine adjacent heap memory being copied into the return value.
Suggested fix: apply the same clamp already used nearby in the same function:
Hi @lazerg appreciate the fast turnaround on this issue. Quick side note: I have also raised two security vulnerabilities. Could you help point someone in the right direction to review them?
Activity
kunwaratulhax0r commentedon Oct 27, 2026
Hi @lazerg appreciate the fast turnaround on this issue. Quick side note: I have also raised two security vulnerabilities. Could you help point someone in the right direction to review them?
lazerg commentedon Oct 27, 2026
Security reports go through the private form at https://github.com/php/php-src/security/advisories/new, or to security@php.net if the form doesn't work for you. See
SECURITY.md. If you filed yours as regular issues, send them there instead, with a short reproducer.kunwaratulhax0r commentedon Oct 27, 2026
Yeah already reported via private form https://github.com/php/php-src/security/advisories/new. Will wait for 2 more days to get a confirmation. Thanks for the quick response. Much appreciated.
iluuu1994 commentedon Oct 28, 2026
Please don't report potential security issues publicly...
Edit: Oh, I see Ilia already did that.
kunwaratulhax0r commentedon Oct 28, 2026
Hi @iluuu1994 I believe above reported issue is general issue not a potential security issue. If you think so I can move it to privately.