A multibyte lead byte at the end of a multipart field name made the
scan advance by the full character width past the terminating NUL,
reading out of bounds. Advance byte by byte and stop at the
terminator in both advance sites, as php_mb_rfc1867_substring_conf()
already does.
Closes GH-24498
- Replace bailout on OOM with UnwindExit
- Properly reset various globals that may be accessed during startup
Fix GHSA-w8hw-w9fx-j79w (CVE-2026-109618)
Fix GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
Co-authored-by: Ilija Tovilo <ilija.tovilo@me.com>
Co-authored-by: Jakub Zelenka <bukka@php.net>
Co-authored-by: Nora Dossche <7771979+ndossche@users.noreply.github.com>
Co-authored-by: Nicolas Grekas <nicolas.grekas@gmail.com>
The UTF-8 encoder turned surrogate codepoints into 3-byte sequences like
"\xED\xA0\x80", which are not valid UTF-8. Such codepoints come from
UCS-2, UCS-4 or HTML-ENTITIES input, or from mb_decode_numericentity().
Since mbstring marks the UTF-8 strings it returns as valid UTF-8,
functions that trust that flag, like mb_check_encoding(), mb_scrub() or
preg_match(), accepted them too.
Surrogates are now replaced by the substitute character, as codepoints
above U+10FFFF already are. The internal mode used by mb_strpos() and
similar functions still encodes them, so that different surrogates don't
match each other.
offset_to_pointer_utf8() walks the haystack using the UTF-8 mblen table.
For a truncated multibyte sequence, the lead byte's table entry can exceed
the remaining bytes and leave the search start pointer past the end.
Passing that pointer to zend_memnstr() triggers an assertion in debug
builds and an out-of-bounds read in release builds, producing bogus
offsets or a crash for sufficiently long haystacks.
Clamp the pointer to the end of the string, as mb_str_split() already does
for the same table walk. Add regression coverage for forward and reverse
searches, negative offsets, and offsets beyond the haystack.
Fixes GH-23106.
Closes#23107
When a \k<name> backref in an mb_ereg_replace() replacement string lacks
its closing delimiter, the error path computed p = name_end + 1 past eos
and copied the trailing NUL terminator into the output; the multibyte
sibling path (p += clen after \k) could overshoot eos and read heap bytes
past the string. Both paths are now clamped to eos so verbatim fallback
never copies beyond the parsed name.
Closes GH-23499
mb_find_strpos() converts the haystack and needle to UTF-8 and does the length and offset math on the converted strings.
The negative-offset branch computed the needle length from the raw needle instead of needle_u8.
mb_fast_strlen_utf8() reads those bytes as UTF-8, so the count is wrong whenever they do not decode one to one.
A byte in 0x80-0xBF (ISO-8859-1) undercounts, and a wider encoding such as an ASCII needle in UTF-16 overcounts, so the reverse-search window is shifted.
Use needle_u8, the converted needle the rest of the branch already searches with.
This avoids a crash in cases where the list of candidate encodings is so huge
that alloca would fail. Such crashes have been observed when the list of
encodings was larger than around 208,000 entries.
The aim of this PR is twofold:
- Reduce the number of highly similar TMP|VAR handlers
- Avoid ZVAL_DEREF in most of these cases
This is achieved by guaranteeing that all zend_compile_expr() calls, as well as
all other compile calls with BP_VAR_{R,IS}, will result in a TMP variable. This
implies that the result will not contain an IS_INDIRECT or IS_REFERENCE value,
which was mostly already the case, with two exceptions:
- Calls to return-by-reference functions. Because return-by-reference functions
are quite rare, this is solved by delegating the DEREF to the RETURN_BY_REF
handler, which will examine the stack to check whether the caller expects a
VAR or TMP to understand whether the DEREF is needed. Internal functions will
also need to adjust by calling the zend_return_unwrap_ref() function.
- By-reference assignments, including both $a = &$b, as well as $a = [&$b]. When
the result of these expressions is used in a BP_VAR_R context, the reference
is unwrapped via a ZEND_QM_ASSIGN opcode beforehand. This is exceptionally
rare.
Closes GH-20628
Thanks to the GitHub user vi3tL0u1s (Viet Hoang Luu) for reporting this issue.
The MacJapanese legacy text encoding has a very unusual property; it is possible for a string
to encode more codepoints than it has bytes. In some corner cases, this resulted in a situation
where the implementation code for mb_substr() would allocate a buffer of size -1. As you can
probably imagine, that doesn't end well.
Fixes GH-20832.