Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

ext/mbstring: mb_regex_substitute() reads past the replacement string's end on a truncated trailing multibyte lead byte

オープン 初心者向け
#24,769 コメント 3 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@lazerg がすでに取り組んでいます。

2026年10月27日 から。

  • #24772 @lazerg による — オープン

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
74/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
c
領域
backend, security

調査の方向性

ext/mbstring/php_mbregex.cを読み、mb_regex_substitute()のメインループと近くにある長さを制限した呼び出し箇所を調べてください。まず、提供されたmb_ereg_replace()の再現コードを実行し、次に関連するmbstringのテストでカバレッジを確認してください。切り詰められた末尾のマルチバイトバイトが置換文字列の末尾までだけコピーされ、再現コードが期待される2バイトの結果を返せば完了です。

索引モデルが issue の本文から書いたものです。

説明

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.

主要言語
C
スター
40.7k
フォーク
8.3k
平均マージ
2日 17時間
マージ済み PR(30日)
159

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

php/php-src のほかの issue

php/php-src の issue をすべて見る

似ている issue

C の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。