ext/mbstring: mb_regex_substitute() reads past the replacement string's end on a truncated trailing multibyte lead byte
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 2/5
- Tempo estimado
- 1-3 horas
- Facilidade para iniciantes
- 74/100
Direção de pesquisa
Leia ext/mbstring/php_mbregex.c e inspecione o loop principal em mb_regex_substitute() junto com os pontos de chamada com limite nas proximidades. Comece executando o reproducer fornecido para mb_ereg_replace() e, em seguida, verifique a cobertura nos testes relevantes de mbstring. O trabalho estará concluído quando o byte multibyte final truncado for copiado somente até o fim da string de substituição e o reproducer retornar o resultado esperado de 2 bytes.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
Description
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 first php_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:
<?php
mb_regex_encoding('UTF-8');
$replacement = "x" . "\xF0"; // ends in a UTF-8 4-byte lead byte with 0 continuation bytes present
var_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:
clen = (int) php_mb_mbchar_bytes(p, enc);
if (clen != 1 || p == eos || p[0] != '\\') {
size_t avail = MIN((size_t) clen, (size_t) (eos - p));
smart_str_appendl(pbuf, p, avail);
p += avail;
continue;
}
Confirmed present, unmodified, on PHP-8.3, PHP-8.4, and PHP-8.5, and master.
PHP Version
PHP 8.7.0-dev (cli) (built: Oct 26 2026 09:57:01) (NTS DEBUG)
Copyright © The PHP Group and Contributors
Zend Engine v4.7.0-dev, Copyright © Zend by Perforce
with Zend OPcache v8.7.0-dev, Copyright ©, by Zend by Perforce
Also confirmed present on the PHP-8.3, PHP-8.4, and PHP-8.5 branches via direct content check.
Operating System
Ubuntu 24.04 (Docker), but this is pure string-handling logic, not OS-specific.
- Linguagem predominante
- C
- Estrelas
- 41.7k
- Forks
- 8.5k
- Merge médio
- 2d 10h
- PRs com merge (30d)
- 158
Preparar o ambiente
- Sem Dockerfile nem arquivo Docker Compose
- Sem modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de php/php-src
-
fileinfo: built-in magic database php_magic_database is not aligned, breaks on strict-alignment CPUs (ARMv5, 32-bit)Talvez já em andamento @lazerg assumiu hoje. AbertaBug Extension: fileinfo Status: Verified
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 73/100
Mantenedores costumam responder em até 1 dia
-
Windows CLI: get_ps_title() passes bytes to GetConsoleTitleW() instead of characters, overflowing the 260-wchar_t bufferTalvez já em andamento Um pull request vinculado a esta issue está aberto ou já foi mesclado. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 79/100
Mantenedores costumam responder em até 1 dia
-
master: part of the mbstring bailout fix got lost in a mergeTalvez já em andamento @lazerg assumiu há 7 dias. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
Mantenedores costumam responder em até 1 dia
-
Bug Status: Needs Triage
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 64/100
Mantenedores costumam responder em até 1 dia
-
quoted_printable_encode() produces lines longer than 76 characters for bytes 0xF5 to 0xFFTalvez já em andamento Um pull request vinculado a esta issue está aberto ou já foi mesclado. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
Mantenedores costumam responder em até 1 dia
Todas as issues de php/php-src
Issues semelhantes
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
InukaAsith/TwinDesk#84 ·
Mantenedores costumam responder em até 2 dias
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
CanadaHonk/porffor#426 ·
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 65/100
FujiNetWIFI/fujinet-firmware#2221 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 77/100
raysan5/raylib.com#132 ·
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100