ext/mbstring: mb_regex_substitute() reads past the replacement string's end on a truncated trailing multibyte lead byte
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 74/100
Línea de trabajo
Lee ext/mbstring/php_mbregex.c e inspecciona el bucle principal de mb_regex_substitute() junto con las llamadas cercanas con límite. Empieza ejecutando el reproductor proporcionado de mb_ereg_replace() y después revisa los tests pertinentes de mbstring para comprobar la cobertura. El trabajo está hecho cuando el byte multibyte final truncado se copia solo hasta el final de la cadena de reemplazo y el reproductor devuelve el resultado esperado de 2 bytes.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- C
- Estrellas
- 41.7k
- Forks
- 8.5k
- Merge medio
- 2 d 10 h
- PR fusionados (30 d)
- 158
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de php/php-src
-
Windows CLI: get_ps_title() passes bytes to GetConsoleTitleW() instead of characters, overflowing the 260-wchar_t bufferPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 79/100
Los mantenedores suelen responder en 1 día
-
zend_set_local_var() overwrites existing compiled variable (CV) zval without calling zval_ptr_dtor()Posiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. AbiertoBug Status: Needs Triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
master: part of the mbstring bailout fix got lost in a mergePosiblemente ocupada @lazerg la tomó hace 7 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Bug Status: Needs Triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 64/100
Los mantenedores suelen responder en 1 día
-
quoted_printable_encode() produces lines longer than 76 characters for bytes 0xF5 to 0xFFPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día
Todos los issues de php/php-src
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
libsdl-org/SDL#16607 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
PowerShell/Win32-OpenSSH#2482 · 1 reacción ·
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
OpenMathLib/OpenBLAS#6192 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
Los mantenedores suelen responder en 2 días