
この記事は技術者向けです。一般の方は、影響と対策を平易にまとめた「php2shell の解説記事」をご覧ください。
- 2件とも、修正は1つのコミット「Avoid state corruption via bailout」にまとまっています。中身は「メモリ不足(OOM)のときに longjmp で処理を抜けるのをやめる」ことと、「リクエストの境目でグローバル状態をリセットし直す」ことの2つです。
- CVE-2026-109618 は、初期化の途中で OOM が起きると、後片付けが中途半端なデータを読んでしまう問題です。mod_php はリクエストの開始処理に失敗しても後片付けを必ず呼ぶため、アプリのコードが動く前に成立します。
- CVE-2026-109619 は、解放済みのメモリを指すグローバル状態が残り、同じワーカーの次のリクエストが通常の初期化より前にそれを使う問題です。確認された経路は、PHP 8.5 の致命的エラーのバックトレースと、Session Upload Progress です。
- Apache・PHP-FPM の SAPI のソースは変わっていません。SAPI ごとの差は、開始処理に失敗したときの振る舞いと、リクエストの前に INI を当てる経路の有無から生まれています。
この記事では、攻撃の再現手順や攻撃コードには触れません。PHP 公式の GitHub セキュリティアドバイザリ(GHSA)と、php-src の修正コミットを読み、修正の意図と、防御側が判断するための材料に絞って書きます。推測を含む箇所は、そのつど「推測」と書いています。
概要(CVE・CVSS・影響範囲・クレジット)
PHP 公式は 2026年10月21日、2件を修正した PHP 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 を公開しました。GHSA の公開時刻は 13:34 UTC(日本時間 22:34)です[php.net・GitHub]。
| 項目 | CVE-2026-109618 | CVE-2026-109619 |
|---|---|---|
| GHSA | GHSA-w8hw-w9fx-j79w | GHSA-6wmf-4rv5-p3rp |
| 題名 | RCE via OOM corruption | RCE via stale global state |
| 深刻度 | Critical。CVSS v4.0 で 9.2。ベクターは2件とも CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N | |
| CWE | 2件とも同じ。CWE-416(解放後の使用)、CWE-755、CWE-824、CWE-1325、CWE-1419 | |
| 影響バージョン(GHSA の記載) | 8.2.35 未満、8.3.36 未満、8.4.27 未満、8.5.12 未満。GHSA に載っているのはサポート中のブランチだけで、8.1 以前の範囲は書かれていない | |
| 修正版 | 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12。7.4・8.0・8.1 は、ソースのみの最小限のバックポート | |
| 確認された影響 | Web ワーカーの権限での、認証なしの RCE。mod_php で完全な RCE を確認 | 認証なしのヒープの解放後使用、メモリの漏えい、RCE。セッション経路の RCE は mod_php と FPM で確認 |
| 報告者(GitHub アカウント) | lcfr-hax、sagitzadik-wiz、tomash-areion、amyb-oai | |
| 修正の開発者 | arnaud-lb、iluuu1994、bukka、ndossche(コミットの共同作者には Nicolas Grekas も入っている) | |
CVSS の AC:H(攻撃の難しさが高い)と AT:P(攻撃に前提条件がある)は、GHSA の記載と照らすと、ヒープの配置への依存や「同じワーカーにリクエストが届く必要がある」といった条件を反映したものと読めます(推測。GHSA はベクターの各値の理由を書いていません)。GHSA は「ワーカーが多いと1回あたりの成功率は下がるが、条件はなくならない」としています。
発見の経緯については、Wiz が自社ブログで、8月19日に最初の脆弱性を見つけ、8月24日に PHP のセキュリティチームへ報告し、9月21日に PHP 側が受領して修正の調整が始まったと説明しています。調査には Wiz Atlas、Gemini 4 Argon、GPT‑5.6 Sol を使ったとしています。Cloudflare は、脆弱性を開示したのは Calif.io、Wiz、OpenAI、Quarkslab だと書いています[Wiz・Cloudflare]。
Wiz のブログには、upload progress まわりの解放後使用やメモリの漏えいを CVE-2026-109618 側に、バックトレースや OPcache の問題を CVE-2026-109619 側に並べているように読める表があります。一方、GHSA では Session Upload Progress もバックトレースも CVE-2026-109619 の「確認された経路」として書かれています。この記事では GHSA の記載に従います。
修正コミットの全体像
サポート中のブランチの修正は、PHP-8.2 ブランチの1コミットです。これが 8.3 → 8.4 → 8.5 へ順にマージされています。
| ブランチ | コミット | 内容 |
|---|---|---|
| PHP-8.2 | 3584a98 | Avoid state corruption via bailout(作成日 2026-10-18)。続く a34a205 で NEWS に 8.2.35 の項目を追加 |
| PHP-8.3 | c1a5f46 | PHP-8.2 からのマージ(2026-10-21 14:12 UTC) |
| PHP-8.4 | 8a94bb3 | PHP-8.3 からのマージ(同 14:27 UTC) |
| PHP-8.5 | c7273b9 | PHP-8.4 からのマージ(同 14:30 UTC) |
コミットメッセージは次の2行で、そのあとに2つの GHSA が並んでいます。
- Replace bailout on OOM with UnwindExit
- Properly reset various globals that may be accessed during startup
PHP-8.2 では 389 ファイル、10,759 行の追加と 988 行の削除です。追加の多くはテスト(oom_*.phpt など)です。本体で変わったのは、Zend エンジン(Zend/zend_alloc.c のメモリ管理、zend_execute_API.c、zend_hash.c、VM など)、main/(main.c、SAPI.c、rfc1867.c、php_variables.c、ストリーム)、そして ext/session、ext/standard、ext/opcache、ext/pcre、ext/json、ext/mbstring など多数の拡張です。
一方、sapi/apache2handler と sapi/fpm のソースは変わっていません(FPM はテスト1件のみ)。sapi/ 以下で変わったソースは fuzzer と phpdbg だけです。SAPI ごとの影響の違いは、SAPI を直したのではなく、エンジン側の直し方で吸収されています。

CVE-2026-109618:OOM で中断した後の後片付け
GHSA の説明
PHP は、メモリ不足(OOM)のエラーで longjmp() を使い、いまの処理から一気に抜けてリクエストの処理を打ち切ります(PHP の中では bailout と呼びます)。GHSA によると、リクエスト用のメモリが、初期化を終える前に一時的に参照できる状態になる箇所があります。そこでメモリ確保に失敗すると、途中で抜けたことが考慮されず、メモリが不正な状態のまま残ります。失敗した確保は、その不正なメモリと無関係でもかまいません。そのあとリクエストの終了処理が、その状態を読んだり、その値で分岐したりします。
GHSA は、PHP の上限の範囲内に見えるリクエストで OOM に届く要因として、multipart の解析について次の3点を挙げています。
- 使える Content-Length がない multipart リクエストが、最初の
post_max_sizeの確認を通り、そのまま本文を送り続けられる - multipart の1パートに付けられるヘッダーの数に制限がない。PHP 内部での表現は、送られたバイト数よりかなり大きなメモリを使う
- 空のファイル欄は
max_file_uploadsに数えられないが、ファイル情報の配列は作られる。入れ子の項目名で、配列を作る処理が増える
このほか、アプリの処理で大きなメモリ確保が起きる場合も OOM に届くとしています。
修正 1:OOM で longjmp しない
修正前の Zend/zend_alloc.c では、上限を超えると zend_mm_safe_error() が E_ERROR を出したうえで bailout していました(3584a98 で削除された行)。
- heap->overflow = 1;
- zend_try {
- zend_error_noreturn(E_ERROR, format, limit, ... size);
- } zend_catch {
- } zend_end_try();
- heap->overflow = 0;
- zend_bailout();
修正後は、memory_limit を超えても、その場では抜けません。新しい zend_mm_check_limit() が、エラーの内容を EG(delayed_oom) に記録し、VM への割り込みフラグ(EG(vm_interrupt))を立てるだけです。呼び出し元は確保に成功したものとして処理を続けます(後述の fallible な確保では NULL が返ります)。記録された OOM は、VM の安全な位置(命令と命令の間)で zend_handle_delayed_oom() が処理し、スクリプトの実行中なら UnwindExit を投げて、通常の例外と同じ経路で巻き戻します。GHSA はこれを「次の VM の安全な位置で発動する、遅延した穏やかな soft OOM」と説明しています。
ただし、それだけだとメモリを使い放題になります。そこで2つ目の上限として hard limit が入りました。初期値は memory_limit の 200%(memory_limit が 1GB を超える場合は +1GB)で、新しい INI 項目 memory_hard_limit_percent(100 以上、または -1)で変えられます。hard limit に届くと、次のメッセージを標準エラー出力に書き、_exit(137) でプロセスごと終了します。
Hard memory limit of %zu bytes exhausted (tried to allocate %zu bytes) in %s on line %d
mmap() などの下位の確保そのものに失敗した場合も、修正後は bailout せず、Out of memory を出して exit(1) します。プロセスを終わらせるので、中途半端な状態を後片付けが読むことはありません。
修正 2:multipart の解析を、OOM を前提に書き直す
OOM ですぐ抜けなくなったので、解析の処理は「OOM が記録されたら、自分で片付けて抜ける」必要があります。main/rfc1867.c では、メモリを確保したあとに ZEND_HAS_OOM() を確かめ、確保済みのバッファを解放して fileupload_done へ抜ける分岐が数十か所に入りました。本文の読み込みなど一部の確保は、失敗すると NULL を返す fallible な確保(safe_ferealloc(..., /* fallible */ true))に変わっています。GHSA が挙げた要因には、次の変更が対応していると読めます。
- Content-Length がない本文:
fill_buffer()の先頭で、読み込んだ量がpost_max_sizeを超えたら読み込みをやめるようになりました。超えた場合は解析の最後にActual POST length does not match Content-Length, and exceeds N bytesという警告を出します。 - パートのヘッダーの数:
multipart_buffer_headers()は、Content-DispositionとContent-Type以外のヘッダーを保存しなくなり、同じ名前の2つ目も捨てるようになりました。 - 入れ子の項目名:
main/php_variables.cのphp_register_variable_ex()が、入れ子の1段ごとに OOM を確かめ、超えていたら登録をやめます。
空のファイル欄と max_file_uploads の関係については、この記事を書いた時点で、対応する変更箇所を特定できていません。
+ if (SG(post_max_size) > 0 && SG(read_post_bytes) > SG(post_max_size)) {
+ return 0;
+ }
(main/rfc1867.c の fill_buffer()。3584a98)
開始処理での本文の解析が終わったところでは、main/SAPI.c の sapi_handle_post() が記録された OOM を処理します。解析の関数がバッファを片付けて戻ったあとに、エラーとして扱う順序です。拡張(ext/pcre、ext/standard/string.c、ext/json、Zend/zend_hash.c など)の多数の変更は、OOM で抜けなくなったことに合わせて、確保の失敗や ZEND_HAS_OOM() を確かめる処理を足したものと見られます(推測。全ファイルは読み切れていません)。
CVE-2026-109619:リクエストをまたいで残る状態
GHSA の説明
PHP のワーカーには、エラー、セッション、オブジェクト、拡張などのグローバル状態があり、その一部は1つのリクエストが持つメモリを指しています。本来は、リクエストのメモリを解放する前にリセットする必要があります。ところが、入力、設定、セッション、エラー処理の一部は、次のリクエストの通常の初期化より前に動きます。そこで前のリクエストがリセットし損ねた状態を読み、解放後の使用になります。
GHSA は、成立には「書き手(producer)」と「読み手(consumer)」の両方が要るとしています。書き手は、リクエストのメモリを指す値をグローバルな場所に置く処理です。致命的エラー(OOM、構文エラー、require の失敗、Maximum execution time exceeded など)でリセットの前に処理が打ち切られると、値が残ります。終了処理の後半で出た警告が、いったん消したエラー状態を書き戻すこともあります。読み手は、同じワーカーの次のリクエストで、通常の初期化より前にその値を使う処理です。確認された読み手は次の2つです。
- 初期化前の INI の警告:リクエストごとに当てる INI が、非推奨や不正な値で警告を出すとき、古いエラー状態を読む。警告が表示・記録されるかは関係ない(
error_reporting、@、display_errorsでは防げない) - Session Upload Progress:アップロードの進み具合を書き込むために、通常のセッション初期化より前にセッションの処理へ入る。
session_start()を呼ぶアプリでなくても動く
具体例として、GHSA は2つを「確認済み」としています。1つは PHP 8.5 の fatal_error_backtraces で、致命的エラーのバックトレースの状態が終了処理で必ずリセットし損ねられていたものです。mod_php での RCE はこの経路で確認されています。もう1つはセッションの upload progress で、OOM などで処理が打ち切られると、セッションやデコーダーのグローバル状態が解放済みのメモリを指したまま残ります。
修正:境目でのリセットを足す
修正は、残りうる状態を1つずつリセットする形です。主なものを挙げます(いずれも 3584a98)。
main/main.cのphp_request_shutdown():$_GETなどの元になるPG(http_globals)を解放したあとZVAL_UNDEFにする。メモリマネージャを止める前にclear_last_error()を呼ぶ。最後にPG(modules_activated) = 0にする。コメントは「php_request_startup() より前に参照されることがある」Zend/zend_execute_API.c:終了処理で、ユーザーのエラーハンドラー、記録中のエラー、EG(exception)などを空にする(コメントは「init_executor より前に zend_error_zstr_at から使われることがある」)。PHP 8.5 では、ここでEG(last_fatal_error_backtrace)もZVAL_UNDEFにしているext/standard/basic_functions.c:新しいphp_basic_early_rinit()を、リクエストのデータを処理する前(php_request_startup()の中)に呼び、serialize・unserialize の状態と URL リライターの状態を初期化するext/session/session.c:upload progress の開始時(MULTIPART_EVENT_START)に、セッションのグローバル状態を先に初期化する。セッションの初期化に失敗したら書き込まずに戻る。upload progress の処理中は、セッションを不完全クラス(__PHP_Incomplete_Class)としてだけ復元し、オートロードや__wakeup()・__unserialize()を動かさない。処理後は$_SESSIONを消すext/opcache:bailout で抜けたときに、確保しかけたクラスのキャッシュ用スロットを捨てる処理を追加
+ /* Runs before RINIT: no user code may be invoked, see php_session_unserialize_init() */
+ PS(in_rfc1867_update) = true;
+ if (php_session_initialize() == FAILURE || PS(session_status) != php_session_active) {
(ext/session/session.c の php_session_rfc1867_update())
GHSA は、修正では確認された経路以外の状態もまとめてリセットしたとしています。どの状態が実際に悪用できるかは、アプリ・拡張・状態が置かれた位置しだいだという説明です。
SAPI 別の考え方
PHP 公式は、Web のリクエストを処理するすべての PHP(PHP-FPM、mod_php、php-cgi、FrankenPHP、LiteSpeed)を対象とし、mod_php のリスクが最も高いとしています[php.net]。差が出る理由は主に2つです。1つは、開始処理(php_request_startup())に失敗したときに、後片付けを呼ぶのか、プロセスを終えるのか。もう1つは、通常の初期化より前に INI を当てる経路があるかどうかです。
| SAPI | 109618(OOM) | 109619(残留状態) |
|---|---|---|
| Apache mod_php | 開始処理に失敗しても終了処理を必ず呼ぶ。本文の自動解析で OOM を起こせば、アプリのコードより前に成立する。既定の入力・メモリ上限と通常の prefork 構成で完全な RCE を確認 | .htaccess と Apache の php_value・php_flag・php_admin_* が php_request_startup() より前に当たり、INI の読み手になる。バックトレース経路とセッション経路の両方で RCE を確認 |
| PHP-FPM | 開始処理に失敗すると子プロセスを終えるので、不正な状態は使われない。ただし開始処理が成功したあとの OOM は残る。ソースの分析からは影響あり、FPM での完全な攻撃は未実証 | nginx などが PHP_VALUE/PHP_ADMIN_VALUE を送り、それが初期化前に警告を出す場合に条件付きで影響。プール設定はワーカー起動時、.user.ini は初期化後に当たるので、読み手にならない。アプリの実行中に状態が置かれ、致命的エラーのあと upload progress が読む経路で、通常の dynamic プールでの RCE を確認(適した処理がアプリにあることが前提) |
| FrankenPHP | ソースの分析では mod_php と同じく終了処理を必ず呼ぶため、アプリに依存しない攻撃を受ける可能性が高い | 既定の設定では、初期化前の INI の読み手は確認されていない。ただし影響がないという意味ではない |
| LiteSpeed(LSAPI) | FPM と同じ作り。アプリ次第の攻撃は可能性が高いが未検証 | 環境変数で INI を早い段階に当てられるため、条件付きの候補 |
| php-cgi | GHSA に個別の記載はない。php.net は対象に含めている | |
(表の内容は、php-cgi の行を除いて GHSA の記載です)
mod_php の振る舞いは、変更されていない sapi/apache2handler/sapi_apache2.c で確かめられます。php_apache_request_ctor()(中で php_request_startup() を呼ぶ)が失敗すると zend_bailout() し、zend_end_try() を抜けたあと、無条件に php_apache_request_dtor()(php_request_shutdown())を呼びます。PHP-FPM の sapi/fpm/fpm/fpm_main.c は、php_request_startup() == FAILURE のとき php_module_shutdown() してプロセスを終えます。
php-cgi について補足します。ここからは推測です。リクエストごとにプロセスを起動する従来の CGI であれば、ワーカーが次のリクエストを処理しないので、109619 の「同じワーカーの次のリクエスト」という条件は満たしにくいと考えられます。FastCGI として常駐させ、1プロセスで複数のリクエストを処理する使い方では、FPM と同じように考えるのが安全です。どちらの使い方でも、109618 の影響は残ると考えてください。
公式の緩和策が効く理由・効かない理由
緩和策の一覧と副作用は「php.ini の設定変更や WAF で防げる?」にまとめています。ここでは、上で見た仕組みから、なぜ効くのか・どこが残るのかを整理します。どの策も、PHP 公式は「完全な対策は更新だけ」としています。

memory_limit=-1(109618)
確認された攻撃は、攻撃者が狙って起こせる「memory_limit 超過」の OOM を使います。上限をなくせば、その OOM は起きません。それでも mmap() などの下位の確保が失敗すれば、修正前の PHP は同じ longjmp() の経路に入ります。GHSA が「完全な緩和ではない」とするのはこのためです。RLIMIT_AS や RLIMIT_DATA で代用してはいけない理由も同じで、上限に届くと mmap() が失敗し、脆弱な経路を自分から呼び込むことになります。cgroup の memory.max は代わりになりえますが、確保の失敗を防ぐ保証はないとされています。
アプリから上書きされない形(FPM プールや Apache の php_admin_value)で設定するよう求めているのは、どこかで有限の値に戻されると、制御できる OOM が復活するからです。
post_max_size と前段での上限(109618)
GHSA が挙げた OOM の要因(Content-Length のない本文、ヘッダーの数、入れ子)は、どれも「宣言された大きさより多くのメモリを使わせる」ものです。前段(Web サーバーやプロキシ)で、デコード後の大きさ、項目数、パート数、ヘッダー数、入れ子の深さに上限をかければ、PHP に届く前に断れます。GHSA は、Content-Length がないリクエストにも上限がかかるようにすること、multipart を使わないアプリなら前段で断ることを求めています。修正版を当てたあとも、hard OOM(プロセスの終了)とサービス停止のリスクを下げるとしています。
session.upload_progress.enabled=0(109619)
確認されたセッション経路では、upload progress が書き手にも読み手にもなります。無効にすると、両方がなくなります。ほかの経路(バックトレースや INI の警告など)には効きません。
ini_set() や FPM の .user.ini で設定しても効かないのは、適用のタイミングの問題です。upload progress は、本文の解析中、つまりスクリプトが動く前に動きます。ini_set() はスクリプトの中で呼ぶものなので間に合いません。GHSA によると .user.ini も通常の初期化のあとに当たります。そのため php.ini、FPM プール、本文の解析より前に当たるサーバー設定で入れ、全ワーカーを再起動する必要があります。GHSA は、修正版を当てたあとも、この機能を使わないなら無効のままにしておくのが賢明だとしています。
fatal_error_backtraces=0(PHP 8.5・109619)
確認された書き手のうち、バックトレースの状態をなくします。修正では、この状態を終了処理でリセットするようになりました。その修正がない状態で、書き手そのものを止める策です。
INI の警告をなくす・pm.max_requests=1(109619)
読み手の1つは、初期化前に当たる INI の警告です。.htaccess や PHP_VALUE に、非推奨の項目(auto_detect_line_endings=1、明示した mbstring.internal_encoding など)や不正な値が残っていると、それが読み手になります。GHSA は、設定を php.ini・FPM プール・起動時のサーバー設定に寄せ、古い指定を消すよう勧めています。
性能を問わないなら pm.max_requests=1 も挙げられています。1ワーカーが1リクエストしか処理しなければ、「同じワーカーの次のリクエスト」が来ないためです。
検知:ログとプロセスで見るところ
GHSA が挙げるログの確認
GHSA(CVE-2026-109619)は、隔離した環境での監査として、起動時の設定で log_errors=On、使える error_log、error_reporting=E_ALL にし、Apache・PHP-FPM・リバースプロキシ・サービスのログで次を探すよう勧めています。
- スクリプトが動く前に繰り返し出る警告・非推奨・コアのエラー
- 拡張やサーバーの後片付けの段階で出る警告など
- 致命的エラー、構文エラー、捕捉されない例外のメッセージ
Maximum execution time exceeded- 攻撃者が操作できるルート・Content-Type・ファイル・コールバック・プラグイン設定に限って起きる失敗
同時に GHSA は、ログにエラーがないことは安全の証明にならないとしています。過去のログは非推奨などを除いて集めていたかもしれず、E_ALL でも、試したルートと状態しか分かりません。メッセージを書く前に失敗が起きることもあります。
修正前の環境で気にしたいログ
ここからは、GHSA の説明とコードからの推測を含みます。
- multipart の POST の直後に出る
Allowed memory size of N bytes exhausted:スクリプト実行前のエラーは、ファイル名がUnknown、行番号が 0 で記録されることが多い(修正後のコードでも、実行中のファイルがないときはこの扱い)。アップロードを受け付けないはずの URL でこれが続くなら、109618 を狙った試行の可能性がある - Apache の子プロセスや FPM の子プロセスの異常終了・入れ替わりの増加:ヒープの配置に依存する攻撃は、失敗するとワーカーが落ちやすいと考えられる。Wiz も、説明のつかないワーカーの入れ替わりを兆候に挙げている
- アクセスログ側:大きな、または Content-Length のない multipart の POST と、それに続く 500 の連続。Wiz は「異常な multipart リクエストのあとのメモリ上限エラーや 500 の繰り返し」と、レスポンス中の大きすぎる・形の崩れた
PHPSESSIDを挙げている
修正後の環境で出るようになるログ
Hard memory limit of N bytes exhausted ...が標準エラー出力に出て、プロセスが終了ステータス 137 で終わる- multipart で
post_max_sizeを超えて送られた場合にActual POST length does not match Content-Length, and exceeds N bytes - 7.4・8.0・8.1 の最小限のバックポートでは、OOM のたびに
Fatal error: ...を標準エラー出力に書いてプロセスを終える(後述)
これらは修正が働いた結果であり、攻撃の証拠とは限りません。ただ、普段は出ないメッセージが特定の URL に集中していれば、試行を疑う手がかりになります(推測)。
プロセスとレスポンスの観点
確認された影響は「Web ワーカーのアカウント権限での RCE」です。侵入後の痕跡として、Web サーバーや PHP のプロセスがシェルやダウンロードツールを起動していないか、Web ルートに知らないファイルが増えていないか、を確かめます。手順は「Webサイトが乗っ取られていないか確かめる方法」にまとめています。
Cloudflare は、同じ HTTP トランザクションのリクエストとレスポンスをあわせて調べる「full transaction detection」を新しく用意し、php2shell 向けの検知がこれを使う最初のマネージドルールだと説明しています。レスポンス側のルールは、条件に合うオリジンの応答を、要求元に届く前に止められます。リクエストだけでは攻撃と正規の通信を見分けきれないことがある、というのが理由です。マネージドルールセット(無料・有料)に「Anomalous Execution」1〜7 を、全プラン向けのレスポンス側ルールに1件を追加し、無料・有料の WAF の利用者は 10月21日 03:02 UTC から保護されていたとしています。攻撃者がルールの回避を試みていることも観測したとしています[Cloudflare]。
バックポートとディストリビューションの番号
7.4・8.0・8.1 向けの最小限のバックポート
PHP のセキュリティチームは、サポートが終わった 7.4・8.0・8.1 にも、ソースだけの修正ブランチを出しました。コミットメッセージは「Minimal mitigation for EOL version.」です。
| ブランチ | コミット |
|---|---|
| PHP-7.4-CVE-2026-109618-109619 | 712e60f |
| PHP-8.0-CVE-2026-109618-109619 | 82f8f31 |
| PHP-8.1-CVE-2026-109618-109619 | 1d7dec1 |
中身はサポート中のブランチと大きく違い、変更は 7.4 で 17 ファイル・約 240 行の追加です。
- OOM はプロセスを終える:soft OOM・hard limit の仕組みは入れず、
zend_mm_safe_error()がFatal error: ...を標準エラー出力に書いて_exit(1)する。OOM のたびにワーカーが1つ落ちる - Session Upload Progress を止める:
php_session_rfc1867_callback()の条件がif (!PS(rfc1867_enabled) || true)になり、INI の値に関係なく何もせず戻る。進捗バーの機能は動かなくなる - リセットの追加:
PG(http_globals)、ユーザーのエラーハンドラー、EG(exception)、SG(request_info)の各ポインタなどを NULL や UNDEF に戻す - multipart のヘッダー:
Content-Disposition/Content-Type以外を保存しない変更は共通。7.4 だけは、8.x にすでにあるmax_multipart_body_parts(既定は -1 で、max_input_vars+max_file_uploads)も追加 - 8.1 には OPcache の変更も入っている
このため、自分でビルドして使う場合は「upload progress が使えなくなる」「メモリ上限に届いたリクエストはワーカーごと落ちる」ことを前提にしてください。php.net は、8.1 以前はディストリビューションや LTS の事業者のバックポートを確かめるか、サポート中のバージョンへ移るよう求めています[php.net]。
ディストリビューションの番号
php.net によると、ディストリビューションと LTS の事業者には事前にパッチが渡されていました。番号の決め方はディストリごとに違います。
- Debian:セキュリティトラッカー(CVE-2026-109618)では、bookworm の php8.2 が
8.2.35-1~deb12u1(DLA-4848-1)、trixie の php8.4 が8.4.27-1~deb13u1(DSA-6574-1)で修正済み。上流の修正版と同じ番号を取り込んでいる。確認した時点で forky・sid は未修正の表示だった - Ubuntu:USN-8948-1 で修正。上流の 8.5.12 などには上げず、修正だけを取り込むため、パッケージの番号は上流と一致しない
- remi:php.net は、
rpms.remirepo.netには当日の更新がないとして、ディストリのパッケージかソースビルドへの切り替えを勧めている
Ubuntu・Debian での具体的な番号の確かめ方と更新の手順は「VPS・自社サーバーのPHPを修正版に更新する手順」にまとめています。更新後は、PHP-FPM か Apache を再起動しないと古いワーカーが残る点にも注意してください。
SAPI と設定を確かめたうえでの修正版の適用、緩和策の設定、更新後のログ・侵害の痕跡の確認までお手伝いします。無料相談はこちら
参考にした情報
- GitHub(php-src)のセキュリティアドバイザリ:GHSA-w8hw-w9fx-j79w(CVE-2026-109618) / GHSA-6wmf-4rv5-p3rp(CVE-2026-109619)
- php-src の修正コミット:3584a98(PHP-8.2) / c7273b9(PHP-8.5 へのマージ) / 7.4・8.0・8.1 のバックポートブランチ
- php.net「Security advisory: Critical remote code execution vulnerabilities, update now」
- Cloudflare ブログ(WAF による防御と full transaction detection)
- Wiz Research「PHP2Shell」(発見者の一社による解説。二次情報として扱った)
- Debian Security Tracker(CVE-2026-109618) / Ubuntu USN-8948-1


