CVE-2026-31431、通称 Copy Fail は、Linux カーネルの暗号処理インターフェイス algif_aead に存在したローカル権限昇格脆弱性である[1][2][3]。ローカル権限昇格とは、攻撃者が最初から対象システム上で何らかの低権限コードを実行できる場合に、その権限をより高い権限、典型的には root 権限へ引き上げる種類の脆弱性を指す。したがって、この問題は単体でインターネット越しに即座に root を取る脆弱性ではない。しかし、Web アプリケーション侵害、SSH アカウント漏洩、CI/CD ジョブ、コンテナ内実行、共有サーバー上の一般ユーザー権限と組み合わさると、低権限の入口をシステム全体の侵害へ変える増幅器になり得る。
本稿では、この脆弱性を単なるニュースや対処手順としてではなく、技術構造、運用リスク、数理モデル、哲学的含意まで含めて整理する。まず、AF_ALG、algif_aead、AEAD、splice()、ページキャッシュ、scatterlist、setuid root バイナリといった基本用語を確認する。そのうえで、なぜ「小さな 4 byte 書き込み」が root 権限昇格へ接続し得るのか、なぜ暫定対策として algif_aead の無効化が意味を持つのか、なぜディストリビューションごとに対応差が出るのかを順に説明する。
脆弱性識別と修正方針の根拠は、NVD、Debian Security Tracker、Ubuntu Security Notice、Ubuntu CVE ページ、Linux Kernel documentation、man-pages などの一次情報に置く。NVD と Debian Security Tracker に記録された修正の中核は crypto: algif_aead – Revert to operating out-of-place であり、2017 年に導入された in-place 処理の複雑性を退け、associated data を直接コピーする out-of-place 処理へ戻す点にある[4][5][6][7][8]。
この問題の本質は、単に 4 byte の小さな書き込みができるという点にはない。Linux のページキャッシュ、AF_ALG、AEAD、splice()、scatterlist、setuid root バイナリという複数の正当な仕組みが組み合わさり、本来は書いてはいけない場所に書けてしまう構造が成立した点にある[9][10]。言い換えれば、ファイルシステム文脈では読み取り専用であるはずの page が、暗号処理文脈では出力先として扱われ、意味境界が保存されなかったことが問題である。各ディストリビューションの対応、たとえば Ubuntu が kmod 更新によって algif_aead モジュールのロード無効化を暫定策として配布し、最終的には修正済みカーネルへ移行させた経緯は、後続章で詳しく扱う[11][12]。
0. この脆弱性を読むための基本用語
CVE-2026-31431 を理解するには、最初に Linux カーネルの内部で使われるいくつかの用語を整理する必要がある。この脆弱性は、単に暗号処理のバグでも、単にファイル書き込みのバグでもない。カーネル内の暗号 API、ゼロコピー I/O、ページキャッシュ、setuid root バイナリ、メモリ上の実行内容が結合した結果、本来書いてはいけない場所に小さな書き込みが届いてしまう問題である。したがって、最初にそれぞれの部品が何であり、通常はどのような役割を持つのかを確認する。
| 用語 | 意味 | この脆弱性での役割 |
|---|---|---|
| Linux カーネル | プロセス、メモリ、ファイル、ネットワーク、デバイス、権限を管理する OS の中核である。 | 今回の脆弱性はアプリケーションではなく、Linux カーネル内部のデータ受け渡しと暗号処理の境界で発生する。 |
| AF_ALG | ユーザー空間のプログラムから Linux カーネル内の暗号機能を socket 形式で使うためのインターフェイスである。 | 攻撃者がカーネル内の暗号処理へデータを流し込む入口になる。 |
| algif_aead | AF_ALG の中で AEAD、すなわち認証付き暗号を扱うためのカーネル側部品である。 | 入力と出力の扱いに問題があり、読み取り専用であるべきページキャッシュが出力先へ混入する経路を作る。 |
| AEAD | Authenticated Encryption with Associated Data の略で、暗号化と改ざん検出を同時に扱う暗号方式である。 | 暗号方式そのものが破られたわけではなく、AEAD 処理に渡される入出力領域の扱いが問題になる。 |
| authencesn | Linux カーネルの暗号テンプレートの一種で、認証処理と暗号処理を組み合わせるための部品である。 | 内部処理で発生する小さな書き込みが、誤ってページキャッシュへ届く経路に関係する。 |
| splice() | ユーザー空間のバッファーを経由せず、file descriptor 間でデータをカーネル内で受け渡す Linux のシステムコールである。 | ファイルのページキャッシュをコピーせずに暗号処理経路へ渡すため、読み取り専用ファイル由来のページが処理対象に入る。 |
| file descriptor | プロセスが開いているファイル、pipe、socket などを識別するための番号である。 | splice() は file descriptor 同士をつなぎ、ファイル、pipe、AF_ALG socket の間でデータを流す。 |
| ページキャッシュ | Linux がディスク上のファイル内容を高速に再利用するため、メモリ上に保持するキャッシュである。 | ディスク上のファイルは変わらないまま、メモリ上のファイル内容だけが汚染されるため、通常のファイル改ざん監視では見逃されやすい。 |
| setuid root バイナリ | 一般ユーザーが実行しても、ファイル所有者である root の権限で動く特殊な実行ファイルである。 | ページキャッシュ上の setuid root バイナリが汚染されると、改変された命令が root 権限で実行され得る。 |
| in-place 処理 | 入力領域と出力領域を同じ場所として扱い、コピーを減らす最適化である。 | 入力として渡された読み取り専用ページが、出力先としても扱われる危険な構造を生む。 |
| scatterlist | 連続していない複数のメモリ領域を、ひとまとまりのデータ列として扱うためのカーネル内部構造である。 | source scatterlist と output scatterlist の境界が崩れることで、ページキャッシュが書き込み経路へ混入する。 |
| ローカル権限昇格 | すでに対象システム上で一般ユーザー権限を持つ攻撃者が、root などの高権限へ昇格する攻撃である。 | この脆弱性はインターネット越しに単独で侵入するものではなく、侵入後に root へ上がるための第 2 段階として危険である。 |
この表から分かるように、Copy Fail は単一機能の単純な失敗ではない。AF_ALG はカーネル暗号 API への入口であり、algif_aead は AEAD 処理を扱う部品であり、splice() はコピーを避けて file descriptor 間でデータを渡す仕組みであり、ページキャッシュはファイル内容のメモリ上コピーであり、setuid root バイナリは一般ユーザーから呼び出されても root 権限で動く実行ファイルである。これらはそれぞれ単独では正当な機能だが、結合したときに「読み取り専用であるべきページキャッシュが、暗号処理の出力先として扱われる」という境界破壊が起きる。
1. Copy Fail の要点
Copy Fail は、Linux カーネル内部で「読むだけのデータ」と「書き込んでよい出力先」の区別が崩れたことにより、本来は一般ユーザーが更新できないはずのファイル内容を、ページキャッシュ上で一時的に汚染できる脆弱性である。問題の中心は、AF_ALG socket 経由で使われる algif_aead の AEAD 処理と、ユーザー空間を経由せず file descriptor 間でデータを渡す splice() の結合にある。splice() は読み取り可能ファイルのページキャッシュをコピーせずにカーネル内処理経路へ渡し、algif_aead の in-place 処理は入力領域と出力領域を重ねる。この 2 つが重なると、読み取り専用であるべきページキャッシュが、暗号処理の出力先として扱われる経路が成立する。
技術的には、2017 年に導入された in-place optimization 後の構造で、req->src と req->dst が重なり、source scatterlist の tag page が output scatterlist へ chain される。この状態で splice() によりファイル由来の page cache が流れ込むと、その page cache が暗号処理の出力経路へ混入する。結果として、非特権ユーザーが読み取り可能なファイルのページキャッシュへ、制御された小さな書き込みを行える状態になる。
| 観点 | 内容 |
|---|---|
| CVE 番号 | CVE-2026-31431。 |
| 通称 | Copy Fail。 |
| 脆弱性の種類 | Linux カーネルのローカル権限昇格。 |
| 中心部品 | AF_ALG、algif_aead、authencesn、splice()、ページキャッシュ、setuid root バイナリ。 |
| 攻撃前提 | 対象システム上で非特権ユーザー権限のコードを実行できること。 |
| 主な影響 | setuid root バイナリのページキャッシュ汚染を通じた root 権限取得、共有環境やコンテナ環境での境界突破。 |
| 本対策 | 修正済みカーネルへ更新し、再起動する。 |
| 暫定対策 | algif_aead モジュールのロード無効化、または seccomp 等による AF_ALG socket の制限。 |
この脆弱性を理解するうえで重要なのは、「小さな書き込みだから軽い」と考えてはならない点である。書き込み量が小さくても、書き込み先が root 権限で実行される setuid バイナリのページキャッシュであれば、実行時の命令列を変えることができる。ディスク上のファイルが変わらなくても、実行時に参照されるメモリ上の内容が変われば、CPU はその変更後の命令を実行する。したがって Copy Fail の本質は、ファイル改ざんではなく、実行時意味の汚染である。
2. splice() はコマンドではなくシステムコールである
splice() は、一般ユーザーがターミナルで実行するコマンドではない。/usr/bin/splice のような実行ファイルを削除すれば対策になる、という性質のものではない。splice() は Linux カーネルが提供するシステムコールであり、man-pages では、カーネルアドレス空間とユーザーアドレス空間の間でコピーせず、2 つの file descriptor 間でデータを移動する機構として定義されている[13]。
通常のファイルコピーでは、ディスク上のファイルからページキャッシュへ入り、さらにユーザー空間バッファーへコピーされ、そこから再びカーネル側へコピーされる。splice() はこの往復コピーを避け、pipe を中継しながらカーネル内で参照を受け渡す。これは性能上は有効だが、読み取り専用のファイル由来 page が別のカーネル処理へ参照として渡るため、参照先の意味属性を壊さない実装が必須になる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #define _GNU_SOURCE #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { int in_fd = open("input.txt", O_RDONLY); int pipe_fd[2]; if (in_fd < 0) { perror("open"); return EXIT_FAILURE; } if (pipe(pipe_fd) < 0) { perror("pipe"); close(in_fd); return EXIT_FAILURE; } ssize_t moved = splice(in_fd, NULL, pipe_fd[1], NULL, 4096, 0); if (moved < 0) { perror("splice file to pipe"); close(pipe_fd[0]); close(pipe_fd[1]); close(in_fd); return EXIT_FAILURE; } if (splice(pipe_fd[0], NULL, STDOUT_FILENO, NULL, moved, 0) < 0) { perror("splice pipe to stdout"); } close(pipe_fd[0]); close(pipe_fd[1]); close(in_fd); return EXIT_SUCCESS; } |
このコードは攻撃コードではない。splice() が通常のプログラムから呼び出される OS 機能であることを示すだけである。pipe と splice() の実装解説からも、この機構の目的がユーザー空間とカーネル空間のコピー削減にあることが確認できる[14]。したがって、対策はコマンド削除ではなく、脆弱なカーネル経路の修正、または必要に応じたシステムコールや socket family の制限になる。
3. AF_ALG はカーネル暗号 API への入口である
AF_ALG は、Linux カーネル内の暗号 API をユーザー空間から利用するための socket family である。ここでいう socket は、TCP や UDP のようなネットワーク通信だけを意味しない。Linux では、データを流し込み、カーネル側で処理し、結果を受け取るための汎用的な入出力口として socket が使われることがある。AF_ALG はその暗号処理版であり、ユーザー空間のプログラムがカーネル内の暗号アルゴリズムを選び、鍵を設定し、入力データを渡し、処理結果を受け取るための入口である。
Linux Kernel documentation によれば、Kernel Crypto API の userspace interface は in-place cipher operation を許容し、send / write の入力バッファーと read / recv の出力バッファーが同一であり得る[15]。これは性能上は有利である。別の出力バッファーを用意せず、入力と同じ場所を処理結果で上書きできるからである。しかし、この自由度は同時に危険でもある。入力として渡された領域が、本当に書き換えてよい作業用メモリなのか。それとも、読み取り専用ファイルに由来するページキャッシュなのか。この区別が崩れると、暗号処理は単なる計算ではなく、本来書いてはいけない場所を更新する経路になり得る。
libkcapi は、この AF_ALG インターフェイスをユーザー空間ライブラリとして扱うための実装であり、Linux カーネルが export する kernel crypto API をアプリケーションから利用可能にする[16]。ユーザー空間からカーネル暗号 API を使う性能上の意義は大きい。暗号処理をカーネル側の実装へ集約でき、ハードウェア支援や既存のカーネル暗号基盤を利用しやすくなるためである。ただし、その意義は、入力と出力の所有権、参照先、書き込み可能性が正しく管理される場合に限って成立する[17]。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <linux/if_alg.h> #include <sys/socket.h> #include <string.h> #include <unistd.h> int main(void) { int tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0); struct sockaddr_alg sa; memset(&sa, 0, sizeof(sa)); sa.salg_family = AF_ALG; strcpy((char *)sa.salg_type, "skcipher"); strcpy((char *)sa.salg_name, "cbc(aes)"); bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa)); unsigned char key[16] = "examplekey12345"; setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key, sizeof(key)); int opfd = accept(tfmfd, NULL, 0); /* * Real code sends plaintext, IV, and operation metadata, * then reads ciphertext from opfd. */ close(opfd); close(tfmfd); return 0; } |
このコードは、AF_ALG が通常のプログラムから socket として開かれることを示す最小例である。攻撃コードではない。socket(AF_ALG, SOCK_SEQPACKET, 0) によってカーネル暗号 API への変換 socket を作り、sockaddr_alg で利用する暗号種別とアルゴリズム名を指定し、bind() でその設定を結び付ける。setsockopt() は鍵を設定し、accept() は実際の暗号処理用 socket を作る。現実の処理では、ここに平文、初期化ベクトル、処理種別、追加データなどが送られ、結果として暗号文または復号結果が返る。
AF_ALG 自体は悪ではない。問題は、AF_ALG の AEAD 経路に、splice() によってファイル由来のページキャッシュ参照が入り、さらに algif_aead の in-place 処理が source と destination の境界を曖昧にした点である。通常の SSH、TLS、OpenSSL、GnuTLS がすべて algif_aead を直接使うわけではないため、algif_aead 無効化の副作用は多くの環境で限定的と考えられる。しかし、AF_ALG や libkcapi を直接使う独自アプリケーション、カーネル暗号 API に依存した高速暗号処理、組み込み用途、特殊な VPN や IPsec 関連処理、暗号アクセラレーター連携では影響確認が必要である。したがって暫定対策として algif_aead を無効化する場合は、単に「暗号機能を切る」と理解するのではなく、「AF_ALG 経由で AEAD を使う特定の経路を止める」と理解する必要がある。
4. algif_aead と authencesn の役割
algif_aead は、AF_ALG 経由で AEAD、すなわち Authenticated Encryption with Associated Data を扱うカーネル側インターフェイスである。AEAD は、暗号化と改ざん検知を一体化した方式であり、暗号化対象データだけでなく、暗号化しない associated data も認証対象に含める。authencesn は、そのような認証付き暗号処理で使われるテンプレートの一種である。
| 部品 | 通常の役割 | Copy Fail での問題 |
|---|---|---|
| AF_ALG | ユーザー空間からカーネル暗号 API を使う入口。 | splice() で流されたファイル由来 page が暗号処理経路へ入る。 |
| algif_aead | AEAD 暗号処理を AF_ALG 経由で扱うインターフェイス。 | in-place 最適化により source と destination の境界が壊れる。 |
| authencesn | 認証付き暗号処理のテンプレート。 | 内部処理で発生する小さな書き込みが誤った page へ届く。 |
| scatterlist | 連続していない複数のメモリ領域を 1 つのデータ列として扱う。 | source 側の page が destination 側へ chain される。 |
| associated data | 暗号化しないが認証対象に含めるデータ。 | AD のコピーや配置の扱いが修正説明に現れる。 |
ここで重要なのは、HMAC、SHA-256、AES-CBC そのものが破られたわけではないことだ。壊れているのは暗号数学ではなく、暗号処理に渡されたデータの配置、参照、入力と出力の境界である。暗号 API は、データを安全に変換するための機構であるはずだが、そのデータ経路が別の層のページキャッシュを出力先として扱ってしまった。
5. ページキャッシュは実行時の実体になり得る
ページキャッシュは、Linux がファイルシステムとやり取りする主要な仕組みである。Linux Kernel documentation では、page cache はユーザーとカーネルの残りの部分がファイルシステムと相互作用する主要な経路であり、通常の read、write、mmap は page cache を通ると定義されている[18]。
したがって、ページキャッシュは単なる高速化の副産物ではない。ファイルを実行するとき、ディスク上の byte 列だけでなく、メモリ上に保持された page が実行時内容として扱われる。Copy Fail の危険性は、ディスク上の /usr/bin/su を改ざんしなくても、ページキャッシュ上の内容を一時的に汚染し、実行時に root 権限で読ませられる点にある。
ページキャッシュ汚染の重大性は、ディスク上のファイル改ざんと異なり、変更が通常のオンディスク checksum 比較に現れにくい点にもある。汚染された page が dirty page として扱われず、writeback machinery によってディスクへ flush されないなら、AIDE、Tripwire、OSSEC のようなオンディスク整合性検査だけでは実行時改変を捕捉できない[10]。これは、フォレンジック上も重要である。ディスクは変わっていないのに、実行時の挙動だけが変わるからである。
| 観点 | ディスク改ざん | ページキャッシュ汚染 |
|---|---|---|
| 永続性 | 再起動後も残る。 | 通常は再起動で消える。 |
| 検知 | ファイルハッシュで検出しやすい。 | ディスクだけを見る検査では見えにくい。 |
| 実行時影響 | ある。 | ある。 |
| フォレンジック | 比較的追いやすい。 | メモリ上の一時状態として消えやすい。 |
| Copy Fail との関係 | 主経路ではない。 | 主経路である。 |
6. setuid root バイナリは権限昇格の増幅器になり得る
setuid は、実行したユーザーではなく、ファイル所有者の権限でプログラムを動かす仕組みである。setuid(2) の仕様上、set-user-ID プログラムは実効ユーザー ID を通じて必要な特権処理を実行できる[19]。代表例は /usr/bin/su や passwd である。これらは、一般ユーザーが直接 root 権限を持たなくても、認証、パスワード変更、ユーザー切り替えなどの限定された高権限処理を安全に実行するために存在する。
したがって、setuid そのものが悪いわけではない。setuid は Unix 系 OS が長く利用してきた正当な権限委譲の仕組みであり、一般ユーザーと管理者権限の間に必要な橋をかけるための機構である。問題は、setuid root バイナリが root 権限で実行されるという性質を持つため、別の層で実行時内容を汚染できる脆弱性があると、その脆弱性の影響を root 権限まで増幅してしまう点にある。
通常、setuid root バイナリは一般ユーザーから書き換えられない。ディスク上の /usr/bin/su や passwd は root が所有し、一般ユーザーには書き込み権限がない。しかし、Copy Fail の問題はディスク上のファイルそのものを書き換えることではない。ファイル内容がメモリ上に読み込まれたページキャッシュに対して、別のカーネル経路から小さな書き込みが届く点にある。ディスク上の権限チェックをすり抜けて、実行時に参照される命令列だけが一時的に変わるため、setuid root バイナリは攻撃の増幅器になり得る。
| 段階 | 処理 | 意味 |
|---|---|---|
| 1 | 一般ユーザーが /usr/bin/su を読み取り可能ファイルとして開く。 | 通常の権限では書き込みできないが、読み取り可能な対象として page cache に載せる入口になる。 |
| 2 | /usr/bin/su の内容がページキャッシュに載る。 | ディスク上のファイル内容がメモリ上の page として保持され、以後の I/O で参照される。 |
| 3 | splice() でその page 参照が AF_ALG 経路へ渡る。 | ユーザー空間バッファーにコピーせず、カーネル内の参照として暗号処理経路へ接続される。 |
| 4 | algif_aead / authencesn の処理で出力先境界が崩れる。 | 入力由来の page が、暗号処理の出力先として扱われ、読み取り専用という意味属性が保存されなくなる。 |
| 5 | ページキャッシュ上の /usr/bin/su に制御された 4 byte が届く。 | ディスク上の /usr/bin/su を直接書き換えずに、実行時に参照されるメモリ上の内容が汚染される。 |
| 6 | 4 byte 書き込みを繰り返して短い命令列を構成する。 | 単発では小さな書き込みでも、値・位置・反復を制御できれば payload を組み立てられる。 |
| 7 | su を実行する。 | 通常の setuid root バイナリとして、ページキャッシュ上の実行時内容が CPU に読み込まれる。 |
| 8 | setuid により、汚染された命令列が root 権限で実行される。 | 一般ユーザーによる小さな page cache 汚染が、root 権限実行へ増幅される。 |
この流れで重要なのは、setuid が脆弱なのではなく、setuid が高権限実行の正当な経路であるため、そこへ別経路の controlled write primitive が接続すると影響が一気に拡大するという点である。controlled write primitive とは、攻撃者が書き込み先と書き込み内容を一定程度制御できる最小能力である。通常の一時バッファーに 4 byte 書けるだけなら影響は限定的だが、root 権限で実行される setuid バイナリのページキャッシュに 4 byte 書けるなら、同じ小さな書き込みでも意味がまったく変わる。
したがって、この章で扱うべき本質は、setuid の廃止論ではない。setuid は必要な仕組みである。問題は、低権限の書き込み能力が、ページキャッシュ、実行ファイル、setuid、root 権限という複数の層を経由して、高権限の実行能力へ変換される構造である。この変換が成立すると、単なるメモリ汚染はローカル権限昇格へ変わる。
7. PoC の構造をコードレベルで読む
PoC とは Proof of Concept の略であり、ある脆弱性が理論上の可能性にとどまらず、実際に成立し得ることを示す検証用コードを指す。ただし、本稿では公開 PoC の完全な再掲は行わない。目的は攻撃手順を再利用可能な形で提示することではなく、CVE-2026-31431 がどのような構造で成立するのかを、コードの流れに近い形で理解することである。したがって、以下に示すコード断片は実行可能な exploit ではなく、各段階で何が起きているかを示すための抽象化された疑似コードである。
この章で注目するべき点は、個々の API 呼び出しそのものではない。重要なのは、どの段階で「読み取り専用ファイルのページキャッシュ」が「暗号処理の出力先」として扱われるようになり、どの段階で小さな書き込みが高権限実行へ接続されるかである。つまり、読むべき対象は攻撃コードではなく、意味境界が破れる順序である。
| 段階 | 処理の意味 | 境界破壊との関係 |
|---|---|---|
| 段階 1 | AF_ALG / authencesn の処理経路を作る。 | ユーザー空間からカーネル暗号 API へデータを渡す入口ができる。 |
| 段階 2 | 制御された 4 byte 書き込みを作る。 | 単なるデータ処理ではなく、書き込み先と書き込み値をある程度制御できる primitive になる。 |
| 段階 3 | splice() で対象ファイルの page 参照を渡す。 | ユーザー空間バッファーではなく、ページキャッシュ由来の参照が暗号処理経路へ入る。 |
| 段階 4 | 4 byte 書き込みを反復する。 | 小さな書き込みが、短い命令列を構成できる程度の変更へ増幅される。 |
| 段階 5 | setuid root バイナリを実行する。 | 汚染されたページキャッシュ上の命令列が root 権限で実行される。 |
段階 1: AF_ALG / authencesn の operation socket を作る
最初の段階では、ユーザー空間のプログラムが AF_ALG socket を開き、Linux カーネル内の暗号 API を利用するための処理経路を作る。ここで指定されるのが AEAD 系の処理であり、問題の経路では authencesn と呼ばれる暗号テンプレートが関係する。この段階だけを見れば、正当なカーネル暗号 API の利用であり、まだ脆弱性そのものは現れていない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /* * Conceptual AF_ALG setup. * This is not a complete program. */ tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0) bind(tfmfd, { type: "aead", name: "authencesn(hmac(sha256),cbc(aes))" }) setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key, key_length) opfd = accept(tfmfd, NULL, 0) |
この疑似コードが示しているのは、AF_ALG が通常のプログラムから socket として開かれ、暗号処理の種類と鍵を設定し、実際の operation socket を作る、という流れである。ここで重要なのは、AF_ALG 自体が危険な機能なのではなく、後続の splice() と algif_aead の in-place 処理が結合したときに、入力と出力の境界が破れる点である。
段階 2: 4 byte 書き込み primitive を作る
次に、攻撃に使える最小能力として controlled write primitive が成立する。primitive とは、攻撃全体を構成する最小単位の能力を指す。この場合は、「読み取り可能ファイルのページキャッシュ上の狙った位置へ、制御された 4 byte を到達させる能力」である。これは、通常の write() で対象ファイルへ書くこととはまったく異なる。通常の write() なら、/usr/bin/su のような root 所有ファイルには権限で拒否される。しかし Copy Fail では、ファイルへの書き込みではなく、暗号処理の出力経路へ混入した page への書き込みとして実行される。
1 2 3 4 5 6 | def overwrite_four_bytes(target_file_fd, target_offset, four_bytes): # Prepare the AF_ALG operation context. # Place the controlled 4-byte value in the operation layout. # Route the target file page into the kernel crypto path. # Trigger the vulnerable path that writes into the selected page. pass |
この疑似コードは、実際の exploit を示しているのではない。示しているのは、攻撃構造の抽象形である。通常の権限モデルでは、非特権ユーザーが setuid root バイナリへ write() することはできない。しかし、ページキャッシュが暗号処理の出力先として誤って扱われると、ファイル権限ではなくカーネル内部のデータ経路として書き込みが発生する。ここに、許可モデルと実行モデルの不一致がある。
段階 3: splice() で page 参照を渡す
splice() は、ユーザー空間のバッファーを経由せず、file descriptor 間でデータを移動するシステムコールである。このため、対象ファイルの内容は単なる byte 列としてコピーされるのではなく、ページキャッシュ由来の参照としてカーネル内の処理経路に渡される。Copy Fail で問題になるのは、この page 参照が AF_ALG / algif_aead の処理経路へ入り、さらに出力側として扱われ得る点である。
1 2 3 4 5 6 7 8 9 10 | /* * Conceptual data routing. * The point is page reference movement, not a normal userspace copy. */ pipefd = pipe() splice(target_file_fd, target_offset, pipefd.write_end, NULL, page_length, 0) splice(pipefd.read_end, NULL, af_alg_operation_fd, NULL, page_length, 0) |
この 2 段階の流れにより、ユーザー空間バッファーにコピーされた安全な作業用データではなく、対象ファイルのページキャッシュ参照がカーネル内の暗号処理経路へ入る。ゼロコピー最適化は性能上有効だが、コピーしないということは、元の page の意味属性も正しく持ち運ばなければならないということである。Copy Fail では、この意味属性の継承が破れた。
段階 4: 4 byte を繰り返して payload にする
4 byte という単位は、人間の文章として見ると小さい。しかし、実行ファイルの機械語命令として見ると、分岐、ジャンプ、呼び出し先、戻り値、短い命令列を変えるには十分な大きさである。さらに、同じ 4 byte 書き込みを複数回使えるなら、小さな変更を積み重ねて短い payload を構成できる。
1 2 3 4 5 6 7 | payload = build_small_payload() offset = 0 while offset < len(payload): chunk = payload[offset:offset + 4] overwrite_four_bytes(target_file_fd, target_offset + offset, chunk) offset += 4 |
ここで重要なのは、4 byte 書き込みの単発の大きさではない。危険性を決めるのは、書き込み値を制御できること、書き込み先の offset をある程度制御できること、同じ操作を反復できること、そして書き込み先が root 権限で実行される setuid バイナリのページキャッシュであることだ。この 4 条件がそろうと、小さな primitive は高権限実行へ接続される。
段階 5: setuid root バイナリを実行する
最後に、汚染されたページキャッシュを使って setuid root バイナリが実行される。通常であれば、/usr/bin/su は認証処理を行い、条件を満たした場合だけユーザー切り替えを行う。しかし、実行時に参照されるページキャッシュ上の命令列が短い payload に置き換わっていれば、CPU はその変更後の命令を実行する。setuid root バイナリである以上、その命令列は root 権限で動く。
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* * Final conceptual step. * The binary on disk is not necessarily modified. * The cached executable content is what matters at runtime. */ execute("/usr/bin/su") /* * If the executable page cache has been polluted, * the CPU may execute the modified instruction sequence * with the effective privileges of the setuid root binary. */ |
この段階で、ページキャッシュ汚染は単なるメモリ破壊ではなく、ローカル権限昇格へ変わる。ディスク上のファイルが変更されていなくても、実行時に参照されるメモリ上の内容が変わっていれば、CPU はその内容を命令として解釈する。したがって Copy Fail の核心は、ファイル改ざんではなく、実行時内容の汚染であり、さらにそれが setuid root という正当な高権限実行経路へ接続される点にある。
8. 4 byte が重大化する条件
Copy Fail を理解するときに、最も誤解されやすい点が「4 byte しか書けないなら大したことはないのではないか」という直感である。この直感は、文章や画像の感覚で byte 数を見ているために生じる。しかし、実行ファイルの世界では、4 byte は小さな単位ではない。CPU が実行する機械語命令は、数 byte 程度の短い列で意味を持つ。1 つの条件分岐、1 つのジャンプ、1 つの即値、1 つの関数呼び出し先、1 つの比較結果が、数 byte の変更で別の意味に変わることがある。
たとえば、文章で考えれば、長い契約書全体を書き換えなくても、「拒否」を「承認」に変えるだけで意味は反転する。プログラムでも同じである。巨大な実行ファイル全体を書き換える必要はない。権限確認の結果、条件分岐、ジャンプ先、実行開始位置、短い命令列の一部を変えられれば、プログラム全体の振る舞いが変わる。したがって、問題は「4 byte という量」ではなく、「その 4 byte がどこに入り、どの権限で実行されるか」である。
この点を単純化して、認証処理を例に考える。実際の /usr/bin/su の処理はもっと複雑だが、構造だけを取り出せば、プログラムには次のような分岐がある。
1 2 3 4 | if password_is_correct: continue_as_root() else: reject_and_exit() |
攻撃者がプログラム全体を書き換える必要はない。もし実行時の命令列の一部を変え、失敗時の分岐を成功時の経路へ寄せることができれば、認証処理全体の意味が崩れる。もちろん、現実の exploit はこのような単純な Python 風の分岐を書き換えるわけではない。CPU が読む機械語命令列を対象にする。しかし、本質は同じである。重要な分岐点の数 byte が変われば、プログラムの制御フローは変わる。
もう 1 つ重要なのは、4 byte 書き込みが単発で終わるとは限らないことである。Copy Fail のように、制御された小さな書き込みを複数回繰り返せる場合、4 byte は積み木の 1 個になる。1 個の積み木は小さいが、狙った位置に繰り返し置けるなら、短い構造物を作れる。同じように、4 byte 書き込みを offset を変えながら繰り返せるなら、短い payload、すなわち攻撃者が意図した小さな命令列を構成できる。
| 段階 | 書き込み位置 | 書き込む内容 | 意味 |
|---|---|---|---|
| write 1 | target_offset + 0 | payload の 1 個目の 4 byte。 | payload の先頭部分を配置する。 |
| write 2 | target_offset + 4 | payload の 2 個目の 4 byte。 | 先頭に続く部分を配置する。 |
| write 3 | target_offset + 8 | payload の 3 個目の 4 byte。 | さらに後続部分を配置する。 |
| write 4 | target_offset + 12 | payload の 4 個目の 4 byte。 | payload を 4 byte 単位で連結していく。 |
この表で示したいのは、4 byte write が 1 回だけなら影響は限定される可能性があるが、書き込み位置を 4 byte ずつずらしながら反復できるなら、連続した byte 列を構成できるという点である。したがって、危険性は 1 回あたりの byte 数ではなく、値の制御、位置の制御、反復可能性、実行経路への接続によって決まる。
このように見ると、4 byte は「小さいから安全」ではない。むしろ、書き込み値を選べること、書き込み位置を調整できること、同じ操作を繰り返せること、書き込み先が実行コードであること、さらにその実行コードが root 権限で動くことがそろうと、4 byte は十分に危険である。
| 条件 | 意味 | なぜ重大化するか | Copy Fail での位置づけ |
|---|---|---|---|
| 制御性 | 攻撃者が書き込む値を選べる。 | 単なるランダムな破壊ではなく、意図した命令や値を置けるため。 | controlled 4-byte write として説明される。 |
| 位置制御 | 攻撃者が書き込む位置を調整できる。 | 無関係な場所ではなく、分岐、命令、payload 配置先など意味のある場所を狙えるため。 | 対象ファイル内 offset を利用してページキャッシュ上の位置へ接続する。 |
| 反復性 | 同じ primitive を複数回使える。 | 4 byte を積み重ね、より長い命令列や payload を構成できるため。 | payload を 4 byte ずつ配置する構造が成立する。 |
| 実行接続 | 書き込み先が実行時に CPU に読まれる。 | 単なるデータ破壊ではなく、CPU が変更後の byte 列を命令として実行するため。 | setuid root バイナリのページキャッシュが対象になる。 |
| 権限接続 | 変更後の命令列が高権限で実行される。 | 同じ命令でも一般ユーザー権限では限定的だが、root 権限ならシステム全体を操作できるため。 | setuid により、汚染された実行内容が root 権限で動き得る。 |
| 検知困難性 | ディスク上の改ざんとして残りにくい。 | ファイルハッシュやパッケージ整合性検査では異常が見えにくいため。 | ディスク上の /usr/bin/su ではなく、ページキャッシュ上の内容が汚染される。 |
この条件を満たさない 4 byte 書き込みは、必ずしも重大ではない。たとえば、自分の一時ファイルや自分のプロセスの作業用バッファーに 4 byte 書けても、それだけで root 権限は得られない。問題は、4 byte が「意味のある場所」に届くことである。書き込み先が実行コードであり、そのコードが setuid によって root 権限で動き、しかも書き込み値と位置を攻撃者が調整できるなら、4 byte は権限構造全体を変える入口になる。
ここで、ページキャッシュ汚染という性質も効いてくる。ディスク上の /usr/bin/su が改ざんされていれば、ファイルハッシュ、パッケージ検証、更新時刻、監査ログなどで異常に気づける可能性がある。しかし Copy Fail で問題になるのは、ディスク上の原本ではなく、メモリ上のページキャッシュである。つまり、原本の契約書は変わっていないが、実際に読み上げられる手元のコピーだけが差し替わる。これにより、通常のファイル改ざん検知では見逃されやすくなる。
したがって、Copy Fail における 4 byte 書き込みの重大性は、次のように整理できる。
| 要素 | 意味 | 重大化する理由 |
|---|---|---|
| 小さな書き込み | 単発では 4 byte 程度の限定的な更新である。 | 量は小さくても、書き込み先が重要領域なら影響は小さくない。 |
| 書き込み値の制御 | 攻撃者が書き込む byte 列を選べる。 | 偶発的な破壊ではなく、意図した命令列や値を構成できる。 |
| 書き込み位置の制御 | 攻撃者が対象ファイル内の offset を調整できる。 | 重要な命令位置や制御に関わる位置へ狙って書ける。 |
| 反復可能性 | 同じ primitive を複数回使える。 | 小さな書き込みを積み重ね、より大きな payload を構成できる。 |
| 実行コードへの接続 | 書き込み先が実行時に CPU に命令として読まれる。 | 単なるデータ破壊ではなく、実行される処理の変更になる。 |
| root 権限への接続 | 汚染された命令列が setuid root バイナリとして実行される。 | 一般ユーザー権限の操作が root 権限の実行へ増幅される。 |
| ディスク改ざんを伴わない検知困難性 | ディスク上のファイルではなくページキャッシュ上の内容が汚染される。 | ファイル整合性チェックだけでは異常を見落とす可能性がある。 |
| 帰結 | 上記の条件が結合する。 | 小さな書き込みが、ローカル権限昇格へ接続する。 |
この式が示すように、セキュリティ上の危険は、操作の物理量ではなく、その操作が接続する権限構造によって決まる。4 byte は小さい。しかし、その 4 byte が root 権限で実行される命令列に入り、しかも反復的に配置できるなら、それはシステム全体の制御へ接続する。Copy Fail が重大なのは、小さな書き込みが大きな権限構造へ接続してしまうからである。
9. 暫定対策として algif_aead を無効化する意味
カーネル更新と再起動をすぐに実施できない場合、暫定対策として algif_aead を無効化する方法がある。Ubuntu は、修正済みカーネルが利用可能になるまでの緩和として、kmod パッケージで algif_aead モジュールのロードを無効化した[11]。これは、CVE-2026-31431 の問題経路に含まれる algif_aead を使わせないことで、脆弱な処理経路そのものに到達しにくくする対策である。
ここで注意すべきなのは、algif_aead 無効化は「Linux の暗号機能をすべて止める」対策ではないという点である。algif_aead は、AF_ALG 経由で AEAD、すなわち認証付き暗号を扱うためのカーネル側インターフェイスである。したがって、この暫定対策が止めるのは、主にユーザー空間から AF_ALG socket を通じて algif_aead の AEAD 処理を使う経路である。通常の OpenSSL、GnuTLS、SSH、HTTPS がすべてこの経路を直接使うわけではないため、多くの一般的なサーバーでは副作用は限定的と考えられる。しかし、AF_ALG や libkcapi を明示的に使うアプリケーションでは処理が失敗する可能性がある。
手動で同種の緩和を行う場合は、modprobe の設定で algif_aead のロードを失敗させる。modprobe は、Linux カーネルモジュールを読み込むための仕組みである。以下の設定では、algif_aead を読み込もうとしたときに実際のモジュールロードではなく /bin/false を実行させる。/bin/false は常に失敗するだけのプログラムなので、結果として algif_aead のロードは失敗する。
1 2 | echo "install algif_aead /bin/false" | sudo tee /etc/modprobe.d/disable-algif_aead.conf sudo rmmod algif_aead 2>/dev/null || true |
1 行目は、今後 algif_aead が自動または手動でロードされそうになったときに、そのロードを失敗させる設定を追加する。設定ファイル名は任意だが、/etc/modprobe.d/ 配下に .conf ファイルとして配置する必要がある。2 行目は、すでに algif_aead がロード済みの場合に、それを現在のカーネルから外そうとする処理である。rmmod はロード済みカーネルモジュールを取り外すコマンドであり、2>/dev/null はエラーメッセージを捨てるための指定である。最後の || true は、すでにロードされていない場合や取り外しに失敗した場合でも、この暫定処理全体を異常終了扱いにしないために付けている。
ただし、この方法には限界がある。第一に、algif_aead がモジュールではなくカーネル本体に built-in されている場合、rmmod では外せない。built-in とは、取り外し可能な部品ではなく、カーネル本体に組み込まれている状態を意味する[20]。第二に、すでに修正済みカーネルへ更新済みであれば、同じ無効化が必ずしも必要とは限らない。第三に、この対策はあくまで脆弱な経路を止める緩和策であり、根本修正ではない。最終対策は、修正済みカーネルへ更新し、そのカーネルで再起動することである。
現在 algif_aead がロードされているかどうかは、/proc/modules を見れば確認できる。/proc/modules は、現在ロードされているカーネルモジュールの一覧を示す仮想ファイルである。次の確認コマンドは、algif_aead が現在ロードされていれば loaded、ロードされていなければ not loaded と表示する。
1 2 3 4 5 | if grep -qE '^algif_aead ' /proc/modules; then echo "algif_aead is loaded" else echo "algif_aead is not loaded" fi |
この確認結果は、「現時点で algif_aead がロードされているか」を示すだけである。not loaded と表示されても、将来何らかのプログラムが AF_ALG 経由で AEAD 処理を使おうとしたときに自動ロードされる可能性は残る。そのため、単に現在の状態を見るだけではなく、modprobe 設定やディストリビューション提供の kmod 更新により、今後のロードも抑止することが暫定対策として意味を持つ[21]。
暫定対策を施した場合の影響範囲は、システムが AF_ALG / algif_aead をどの程度利用しているかによって変わる。一般的な Web サーバーや SSH サーバーでは、多くの場合、TLS や SSH の暗号処理は OpenSSL、GnuTLS、libcrypto などのユーザー空間ライブラリで処理されるため、algif_aead 無効化の直接影響は小さい。一方で、AF_ALG を明示的に使う独自アプリケーション、libkcapi を使うプログラム、カーネル暗号 API へ処理を寄せた高速暗号処理、特殊な VPN、IPsec、組み込み機器、HPC や研究計算基盤の一部では、AEAD 処理が失敗したり、性能が低下したり、別経路へ fallback したりする可能性がある。
| 利用形態 | 無効化の影響 | 判断 |
|---|---|---|
| 一般的な SSH / HTTPS サーバー | 多くの場合、直接影響は小さい。SSH や TLS が常に algif_aead を直接使うわけではないためである。 | カーネル更新までの暫定緩和として現実的である。 |
| 一般的なデスクトップ環境 | 通常利用では影響が見えない可能性が高い。ただし、特殊な暗号処理アプリを使っている場合は確認が必要である。 | 公開サーバーより優先度は下がるが、更新または緩和は行うべきである。 |
| AF_ALG を直接使う独自アプリケーション | AF_ALG 経由の AEAD 処理が失敗する可能性がある。 | 無効化前に利用有無を確認し、テスト環境で動作確認する必要がある。 |
| libkcapi 利用アプリケーション | libkcapi 経由で kernel crypto API を使っている場合、構成次第で影響する。 | 依存関係、設定、ログ、実行時エラーを確認する。 |
| VPN / IPsec / 暗号アクセラレーター連携 | 構成によってはカーネル暗号 API への依存があり、接続失敗や性能低下が起きる可能性がある。 | 本番適用前に検証し、可能なら修正済みカーネル更新を優先する。 |
| コンテナ基盤 | ホストカーネルを共有するため、脆弱性緩和として有効になり得る。一方で、コンテナ内ワークロードが AF_ALG を必要とする場合は影響し得る。 | algif_aead 無効化に加え、seccomp による AF_ALG socket 制限も検討する。 |
| CI/CD ランナー | 外部由来コードが実行される環境では、ローカル権限昇格の踏み台になりやすい。無効化の副作用より緩和の優先度が高い場合が多い。 | 早期に緩和し、可能なタイミングで修正済みカーネルへ移行する。 |
| HPC / 研究計算基盤 | 複数ユーザーが同一カーネルを共有するため、脆弱性リスクが高い。一方で、特殊な計算ワークロードがカーネル暗号 API を使う可能性もある。 | 利用状況を確認しつつ、計画的なカーネル更新を優先する。 |
暫定対策の判断で重要なのは、リスクと影響を分けて考えることである。リスクとは、algif_aead を有効にしたままにすることで、Copy Fail の攻撃経路が残ることである。影響とは、algif_aead を無効化した結果、正当なアプリケーションの一部が失敗する可能性である。公開サーバー、CI/CD ランナー、コンテナホスト、共用 Linux 環境では、攻撃者が非特権コードを実行できる状況が比較的起きやすいため、リスクが高い。これらの環境では、多少の互換性確認を要しても、暫定緩和を優先する合理性がある。
一方で、AF_ALG や libkcapi を利用することが分かっている特殊環境では、無効化による副作用を事前に確認する必要がある。その場合でも、何もしないという判断は危険である。選択肢は、修正済みカーネルへ早期に更新する、メンテナンス時間を確保して再起動する、一時的に algif_aead を無効化して影響を監視する、seccomp などで AF_ALG socket の利用を制限する、という形になる。暫定対策は本対策の代替ではないが、カーネル更新までの時間を安全に稼ぐための現実的な手段である。
9.1 暫定対策によって止まる経路
algif_aead の無効化は、脆弱な kernel を完全に修復する対策ではなく、問題のある AEAD 用 AF_ALG 経路を使わせないための遮断である。したがって、効果は明確である。AF_ALG 経由で algif_aead を使う処理は失敗し、splice() と algif_aead が組み合わさる攻撃経路は成立しにくくなる。一方で、カーネル全体が修正されたわけではないため、kernel 更新と再起動を不要にするものではない。
| 暫定対策 | 止めるもの | 止めないもの | 位置づけ |
|---|---|---|---|
| algif_aead のロード無効化 | AF_ALG 経由の AEAD algif_aead 利用を止める。 | 脆弱な kernel 実装そのものは置き換えない。 | 修正済み kernel までの緩和策である。 |
| 既存 algif_aead モジュールの rmmod | すでにロード済みの algif_aead を外す。 | built-in の場合や使用中の場合は外せない可能性がある。 | 状態確認と組み合わせて使う。 |
| seccomp による AF_ALG 制限 | 特定プロセスやコンテナから暗号 socket 作成を抑止する。 | ホスト全体の kernel 修正にはならない。 | CI やコンテナで有効な多層防御である。 |
| 修正済み kernel への更新と再起動 | 脆弱な実装を修正済み実装へ置き換える。 | 不要機能や過剰権限の運用リスクまでは自動的に消さない。 | 本対策である。 |
9.2 暫定対策で影響を受ける可能性がある処理
algif_aead を無効化した場合に影響を受けるのは、主としてユーザー空間から AF_ALG を通じて kernel crypto API の AEAD 処理を直接使うプログラムである。通常の HTTPS 通信、SSH、一般的な OpenSSL 利用、ブラウザー通信がすべて algif_aead に依存しているわけではない。多くの一般的なサーバーでは副作用は限定的である可能性が高いが、独自アプリケーション、組み込み用途、暗号アクセラレーション、libkcapi 利用、特殊な VPN や IPsec 周辺では確認が必要になる。
| 対象 | 想定される影響 | 確認方法 | 判断 |
|---|---|---|---|
| 通常の Web サーバー | 多くの場合は直接影響しない。 | サービス再起動後に HTTPS、アプリケーションログ、TLS 終端の異常を確認する。 | 暫定対策を入れやすい。 |
| 通常の SSH サーバー | 多くの場合は直接影響しない。 | 別セッションを残したまま新規 SSH 接続を確認する。 | 影響は限定的と考えやすい。 |
| AF_ALG を直接使う独自プログラム | AEAD の socket 作成や bind が失敗する可能性がある。 | socket(AF_ALG) と algif_aead 利用箇所をソースコード、strace、ログで確認する。 | 事前検証が必要である。 |
| libkcapi 利用プログラム | AEAD 系の処理が失敗する可能性がある。 | 依存パッケージ、リンク状況、実行時エラーを確認する。 | 利用している場合は検証環境で試す。 |
| コンテナ基盤 | コンテナ内で AF_ALG を使う workload が失敗する可能性がある。 | seccomp profile、capability、workload ログを確認する。 | 防御効果と互換性を比較して判断する。 |
| HPC / 研究計算基盤 | 多数ユーザーが kernel crypto API を使う可能性を完全には排除できない。 | ジョブ失敗ログ、module 利用、暗号ライブラリ利用を確認する。 | 緩和より計画的 kernel 更新を優先する。 |
| 組み込み機器や特殊用途 | 暗号処理を kernel crypto API に強く依存している場合に影響が出る。 | ベンダー資料、設定、実機ログ、暗号処理テストを確認する。 | 一律適用ではなく影響確認が必要である。 |
9.3 暫定対策を入れる際の運用手順
暫定対策は、コマンドを実行して終わりではない。目的は、修正済みカーネルへ更新して再起動するまでの間、CVE-2026-31431 の攻撃経路に含まれる algif_aead を使わせないことである。そのため、実施前には現在の状態を確認し、実施後にはロード抑止が効いているかを確認し、さらに必要なサービスに副作用が出ていないかを見る必要がある。特にリモートサーバーでは、作業中の SSH 接続を切らずに残し、別の端末で再ログインできることを確認してから進めるべきである。
運用上は、次の順序で扱うのが安全である。
| 段階 | 目的 | 確認内容 |
|---|---|---|
| 手順 1 | 現状確認。 | algif_aead が現在ロードされているか、対象ホストが Debian / Ubuntu のどの系列か、修正済みカーネルへ更新可能かを確認する。 |
| 手順 2 | 暫定対策の適用。 | modprobe 設定で algif_aead の今後のロードを失敗させ、すでにロード済みなら取り外しを試みる。 |
| 手順 3 | 適用確認。 | algif_aead が現在ロードされていないこと、および再ロードが抑止されることを確認する。 |
| 手順 4 | 副作用確認。 | systemd の failed unit、警告ログ、主要サービスの動作を確認する。 |
| 手順 5 | ロールバック準備。 | 必要なサービスが壊れた場合に、追加した modprobe 設定を削除して戻せるようにする。 |
| 手順 6 | 本対策への移行。 | 修正済みカーネルへ更新し、再起動後に起動中カーネルが修正版であることを確認する。 |
まず、現在 algif_aead がロードされているかを確認する。ここで loaded と表示された場合、現在のカーネル上で algif_aead が利用可能になっている。not loaded と表示された場合でも、将来の自動ロードまでは防げていないため、暫定対策としては modprobe 設定によるロード抑止が必要になる。
1 2 3 4 5 6 | # Check whether algif_aead is currently loaded. if grep -qE '^algif_aead ' /proc/modules; then echo "algif_aead loaded" else echo "algif_aead not loaded" fi |
次に、algif_aead の今後のロードを失敗させる設定を追加する。これは、algif_aead をロードしようとしたときに、実際のモジュールロードではなく /bin/false を実行させる設定である。/bin/false は必ず失敗するため、結果として algif_aead はロードされない。
1 2 | # Disable future autoloading as a temporary mitigation. echo "install algif_aead /bin/false" | sudo tee /etc/modprobe.d/disable-algif_aead.conf |
すでに algif_aead がロード済みの場合は、現在のカーネルから取り外しを試みる。ロードされていない場合や、依存関係などにより取り外せない場合もあるため、ここでは失敗しても処理全体を止めない形にする。ただし、取り外せなかった場合は、その理由を確認する必要がある。built-in、すなわちカーネル本体に組み込まれている場合は、rmmod では取り外せない。
1 2 | # Try to unload the module if it is already loaded. sudo rmmod algif_aead 2>/dev/null || true |
暫定対策を適用した後は、現在ロードされていないことを再確認する。この確認は、現在のロード状態を見るためのものである。ロード抑止設定そのものは /etc/modprobe.d/disable-algif_aead.conf の内容で確認する。
1 2 3 4 5 6 7 8 9 | # Confirm the current module state. if grep -qE '^algif_aead ' /proc/modules; then echo "WARNING: algif_aead is still loaded" else echo "OK: algif_aead is not loaded" fi # Confirm the mitigation configuration. cat /etc/modprobe.d/disable-algif_aead.conf |
さらに、ロード抑止が効いているかを確認する。次のコマンドは algif_aead のロードを試みるため、本番環境では実施タイミングに注意する。ただし、暫定対策が正しく入っていれば modprobe は失敗し、algif_aead はロードされない。
1 2 3 4 5 6 | # Confirm that loading is blocked. if sudo modprobe algif_aead 2>/dev/null; then echo "WARNING: algif_aead can still be loaded" else echo "OK: algif_aead loading is blocked" fi |
次に、副作用を確認する。algif_aead 無効化により、一般的な SSH や HTTPS が直ちに壊れる可能性は高くないが、AF_ALG や libkcapi を直接使うアプリケーション、特殊な VPN、IPsec、暗号アクセラレーター連携、独自の高速暗号処理では影響が出る可能性がある。そのため、systemd の failed unit、警告ログ、主要サービスの状態を確認する。
1 2 3 4 5 6 7 8 9 10 | # Check failed systemd units. systemctl --failed # Check recent warnings and errors. journalctl -p warning -n 100 # Check important services explicitly. systemctl status ssh --no-pager systemctl status apache2 --no-pager systemctl status nginx --no-pager |
上記の service 名は環境に合わせて調整する。Apache を使っていなければ apache2 の確認は不要であり、Nginx を使っていなければ nginx の確認も不要である。重要なのは、暫定対策を入れた直後に、実際に使っているサービスの失敗や警告が増えていないかを確認することである。
もし必要なサービスが壊れた場合は、追加した modprobe 設定を削除してロールバックする。ただし、ロールバックは脆弱性経路を再び開く可能性があるため、単に戻して放置するのではなく、修正済みカーネルへの更新と再起動を優先する必要がある。
1 2 3 4 5 | # Roll back the temporary mitigation if a required service breaks. sudo rm -f /etc/modprobe.d/disable-algif_aead.conf # Try to load algif_aead again only if it is required by the environment. sudo modprobe algif_aead 2>/dev/null || true |
暫定対策の評価軸は、脆弱性経路の遮断効果と業務影響の比較である。公開サーバー、共有サーバー、CI/CD ランナー、コンテナホストのように、非特権ユーザー権限でコードが実行される入口が複数ある環境では、短期的には互換性リスクより権限昇格リスクが重くなりやすい。一方、特定の暗号処理 workload が AF_ALG / algif_aead に依存する環境では、緩和策を入れる前に検証環境で影響を確認し、可能なら短いメンテナンス時間で修正済みカーネルへ更新して再起動するほうが合理的である。
最後に、本対策へ移行する。暫定対策は、修正済みカーネルへ更新できない時間を埋めるための手段であり、恒久対策ではない。カーネル更新後は、起動中のカーネルが更新後のものになっているかを確認する。パッケージだけ更新しても、再起動前で古いカーネルが動き続けている場合、脆弱性は残る。
1 2 3 4 | # Update packages and reboot into the fixed kernel. sudo apt update sudo apt full-upgrade sudo reboot |
1 2 | # Confirm the running kernel after reboot. uname -r |
10. splice() を消せばよいという発想が誤りである理由
Copy Fail の説明では splice() が何度も出てくるため、直感的には「では splice() を消せばよいのではないか」と考えたくなる。しかし、この発想は問題の捉え方として粗すぎる。splice() は一般ユーザーがターミナルで実行するコマンドではなく、Linux カーネルが提供するシステムコールである。/usr/bin/splice のような実行ファイルを削除すればよい、という種類のものではない。また、splice() は正常な用途を持つ性能最適化機能であり、ファイル、pipe、socket の間でデータを効率よく受け渡すために使われる。したがって、問題は splice() 単体にあるのではなく、splice() によって page 参照が渡され、その参照が algif_aead の in-place 処理で出力先として扱われた組み合わせにある。
この違いを見誤ると、対策が過剰か、無効か、あるいは副作用の大きいものになる。セキュリティ対策では、「危険に見える機能を丸ごと消す」ことよりも、「どの経路が危険で、どこを止めれば十分か」を見極める必要がある。Copy Fail では、危険な経路は splice() 全体ではなく、読み取り可能ファイルのページキャッシュ参照が AF_ALG / algif_aead の AEAD 処理へ入り、in-place 処理によって出力先境界が崩れる部分である。
| 対策 | 何を止めるか | 止めないもの | 問題点・副作用 | 評価 |
|---|---|---|---|---|
| 修正済みカーネルへ更新して再起動 | algif_aead の脆弱な実装そのものを修正する。 | splice()、AF_ALG、ページキャッシュ、setuid などの正常機能は維持する。 | 再起動が必要であり、サーバーではメンテナンス時間の調整が必要になる。 | 本対策である。 |
| algif_aead のロード無効化 | AF_ALG 経由で algif_aead の AEAD 処理を使う経路を止める。 | splice() 全体、通常のファイル I/O、通常の OpenSSL / GnuTLS 利用、setuid の仕組みは止めない。 | AF_ALG / libkcapi を使う独自アプリケーション、特殊な暗号処理、VPN、IPsec、暗号アクセラレーター連携では影響が出る可能性がある。 | カーネル更新までの暫定対策として現実的である。 |
| AF_ALG socket の制限 | ユーザー空間からカーネル暗号 API へアクセスする経路を制限する。 | splice() 自体、通常のファイル I/O、ユーザー空間暗号ライブラリは止めない。 | seccomp や LSM の設定が必要であり、AF_ALG を正当に使う workload には影響する。 | コンテナ、CI/CD、非信頼コード実行環境で有効な多層防御である。 |
| splice() 全体の禁止 | splice() を使う全経路を止める。 | AF_ALG や algif_aead の存在そのものは止めない。 | 正常なゼロコピー I/O、プロキシ、ファイル転送、サーバー処理、バックアップ、パイプ処理などに副作用が出る可能性が高い。問題経路に比べて止める範囲が広すぎる。 | 通常は粗すぎる対策である。 |
| コマンド削除 | 何も本質的には止めない。 | splice() はシステムコールなので、プログラムからの呼び出しは残る。 | /usr/bin/splice のようなコマンドがあるわけではなく、削除という発想自体が対象を誤認している。 | 無効である。 |
| setuid の一括無効化 | setuid root バイナリを介した権限昇格経路の一部を止める。 | ページキャッシュ汚染や algif_aead の脆弱性そのものは止めない。 | passwd、su、sudo など、正当な管理機能やユーザー操作が壊れる可能性が高い。setuid は必要な権限委譲機構であり、無差別に消す対象ではない。 | 一般的な暫定対策としては不適切である。 |
この表から分かるように、対策には「本対策」「暫定対策」「多層防御」「過剰対策」「無効な対策」がある。修正済みカーネルへの更新と再起動は、脆弱な実装そのものを直すため本対策である。algif_aead の無効化は、問題経路を使わせないための暫定対策である。AF_ALG socket の制限は、コンテナや CI/CD のように非信頼コードが動く環境で、攻撃面を減らす多層防御である。一方、splice() 全体の禁止や setuid の一括無効化は、正常機能まで広く壊す可能性が高い。コマンド削除は、そもそも splice() がコマンドではないため対策にならない。
より丁寧に言えば、splice() は「危険な機能」ではなく、「意味境界を保ったまま使う必要がある低レイヤー機能」である。ゼロコピー I/O は、ユーザー空間とカーネル空間のコピーを減らし、性能を上げる。その一方で、データをコピーせず page 参照として渡すため、渡された page が読み取り専用なのか、書き込み可能なのか、ファイル由来なのか、一時バッファーなのかという意味属性を失ってはならない。Copy Fail では、この意味属性が algif_aead の in-place 処理側で壊れた。
コンテナ環境では、この問題がさらに重要になる。コンテナはプロセス空間、ファイルシステム名前空間、ネットワーク名前空間などを分離するが、通常はホストカーネルを共有する。したがって、コンテナ内の非特権プロセスであっても、ホストカーネルの脆弱なシステムコールやカーネル API に到達できる場合がある。page cache、setuid root バイナリ、AF_ALG、splice() の組み合わせがコンテナ境界をまたぐ場合、問題は単なるコンテナ内の局所権限昇格ではなく、ホスト側の境界破壊へ発展し得る[10]。
そのため、コンテナや CI/CD では、単に「ホストを更新する」だけでなく、非信頼コードから到達できるカーネル機能を減らすことが重要になる。seccomp で不要な socket family やシステムコールを制限する、特権コンテナを避ける、ホスト側の setuid バイナリをコンテナから不要に見せない、CI ジョブを使い捨て VM や隔離された runner で動かす、といった対策は、Copy Fail に限らず、ローカル権限昇格系の脆弱性に対して有効である[22][23][24]。
結論として、splice() を消すという発想は、原因の粒度を誤っている。Copy Fail の本質は、splice()、AF_ALG、algif_aead、ページキャッシュ、setuid root バイナリが結合したときに、読み取り専用であるべき page が書き込み可能な出力先として再解釈されたことである。したがって、対策も機能単体の雑な削除ではなく、危険な結合経路を断ち、最終的には脆弱な実装を修正済みカーネルで置き換えることに置くべきである。
11. ディストリビューションごとに対応差が出る理由
Linux カーネルの脆弱性を評価するときは、まず「Linux カーネルそのもの」と「各ディストリビューションで実際に配布される kernel パッケージ」を分けて考える必要がある。Linux カーネルは upstream で開発されるが、利用者が Debian、Ubuntu、Red Hat 系、SUSE 系、Arch 系などで実際に使う kernel は、各ディストリビューションがそれぞれの方針で選び、パッケージ化し、必要な修正を取り込み、場合によっては backport して配布したものである。したがって、同じ CVE であっても、全ディストリビューションで同じ時点に、同じ形で、同じ緩和策が提供されるとは限らない。
ここで重要なのは、ディストリビューション名だけで安全性を判断できないという点である。実際の影響は、採用している kernel 系列、修正 commit の取り込み状況、対象リリースのサポート状態、kernel module の構成、暫定緩和の有無、更新後に再起動済みかどうかによって決まる。たとえば、パッケージとして修正済み kernel が配布されていても、対象ホストがまだ再起動しておらず古い kernel で動いていれば、実行中のシステムは修正済みとは言えない。逆に、version 文字列だけでは古く見えても、ディストリビューション側が修正を backport していれば、すでに対策済みである場合もある。
本稿で Debian と Ubuntu を中心に扱うのは、両者がサーバー、デスクトップ、VPS、クラウド、個人運用環境で広く使われており、影響判断の実務上の関心が高いためである。また、Ubuntu は Debian 系のディストリビューションでありながら、セキュリティ通知、kernel 提供、kmod による暫定緩和の出し方が Debian と同一ではない。この 2 つを比較すると、同じ Linux カーネル脆弱性でも、ディストリビューションごとに対応の見え方と運用判断が変わる理由を理解しやすい。
Debian は Debian Security Tracker で CVE-2026-31431 を追跡し、linux パッケージの修正状況として管理する[8]。これは、対象リリースごとに linux パッケージへ修正が入っているかを確認する方式である。Debian では、stable、oldstable、testing、unstable などで kernel パッケージの系列や修正取り込みの時点が異なるため、単に Debian という名前だけで影響有無を判断するのではなく、対象リリース、インストール済み kernel パッケージ、起動中 kernel を確認する必要がある。
Ubuntu は Ubuntu CVE ページと USN において、修正済み kernel の提供状況に加え、kmod による algif_aead ロード無効化という暫定緩和を明示している[11][12]。これは、修正済み kernel へ移行するまでの間、問題経路に含まれる algif_aead をロードできないようにすることで、攻撃経路を一時的に塞ぐ対応である。つまり Ubuntu の対応は、kernel 修正だけでなく、再起動までの時間差や更新展開の遅れを埋めるための緩和策をパッケージとして配布している点に特徴がある。
| 観点 | Debian | Ubuntu | 差が生じる理由 |
|---|---|---|---|
| 主な情報源 | Debian Security Tracker。 | Ubuntu CVE ページ、Ubuntu Security Notice。 | 各ディストリビューションが独自のセキュリティ情報公開基盤を持つため。 |
| 本体修正 | linux パッケージの修正状況として管理する。 | linux / linux-generic / cloud kernel など、Ubuntu の kernel パッケージ系列ごとに管理する。 | 採用 kernel、パッケージ名、提供系列がディストリビューションごとに異なるため。 |
| 暫定緩和 | 基本的には tracker と kernel 更新状況を見て、必要なら運用側で判断する。 | kmod による algif_aead ロード無効化を暫定緩和として明示する。 | 修正済み kernel が全環境で即時に起動されるとは限らないため、ディストリビューション側が追加緩和を配る場合がある。 |
| 実行中 kernel | 更新済みでも再起動前なら古い kernel が動いている可能性がある。 | 同様に、更新済みでも再起動前なら古い kernel が動いている可能性がある。 | kernel 脆弱性では、インストール済み package と起動中 kernel が一致しない時間差が生じるため。 |
| 運用判断 | 対象リリース、linux パッケージ、起動中 kernel、必要な暫定緩和を確認する。 | USN、Ubuntu CVE ページ、kernel package、kmod 緩和、起動中 kernel を確認する。 | ディストリビューション名ではなく、実際の稼働状態で判断する必要があるため。 |
この違いは、どちらが単純に安全で、どちらが危険であるという話ではない。ディストリビューションごとに、修正の取り込み方、利用者への通知方法、暫定緩和の配布方法、対象リリースの管理方法が異なるという話である。したがって、運用者が見るべきなのは、ディストリビューション名そのものではなく、対象リリースの advisory、修正済み package、起動中 kernel、再起動状態、暫定緩和の有無である。
ここで重要なのは、「Ubuntu の最新版は影響なし」「Debian は影響あり」といった単純な言い方を避けることである。ある時点で Ubuntu の新しい系列が影響を受けないように見える場合でも、それは Ubuntu という名前の効果ではなく、採用 kernel に脆弱な実装が含まれていない、修正 commit がすでに取り込まれている、または暫定緩和が適用されている、という具体的な理由による。同様に、Debian で影響がある場合も、Debian だから危険なのではなく、対象リリースの kernel パッケージに脆弱な実装が残っている、または更新後に再起動されていない、という構造で判断する必要がある。
他のディストリビューションでも考え方は同じである。Red Hat 系、SUSE 系、Arch 系、Gentoo、Alpine、Amazon Linux、Oracle Linux、Rocky Linux、AlmaLinux などでも、確認すべき対象はディストリビューション名そのものではない。確認すべきなのは、起動中の kernel、該当 CVE に対するセキュリティアドバイザリ、修正済み kernel パッケージの有無、backport の有無、algif_aead が module か built-in か、暫定緩和が配布されているか、そして再起動済みかである。Red Hat の CVE 情報も、この問題を Linux kernel の algif_aead cryptographic algorithm interface に incorrect in-place operation が導入された事例として整理している[25][26][27][28][29]。
| 確認項目 | 見るべき理由 | 典型的な確認先 |
|---|---|---|
| 起動中の kernel | パッケージを更新していても、再起動前なら古い kernel が動き続けるため。 | uname -r、パッケージ管理コマンド。 |
| 対象リリース | 同じディストリビューションでも stable、oldstable、LTS、interim release などで修正状況が異なるため。 | /etc/os-release、各ディストリビューションの CVE ページ。 |
| 修正済み kernel パッケージ | 本対策は脆弱な kernel 実装を修正済み実装へ置き換えることだからである。 | Debian Security Tracker、Ubuntu USN、Red Hat CVE、SUSE advisory など。 |
| backport の有無 | kernel の見かけ上のバージョンが古くても、修正だけが取り込まれている場合があるため。 | ディストリビューションの changelog、security tracker、advisory。 |
| algif_aead の扱い | module ならロード無効化で暫定緩和できるが、built-in なら同じ方法では外せないため。 | /proc/modules、modinfo、kernel config。 |
| 暫定緩和の有無 | 修正済み kernel へ移るまでの間、攻撃経路を一時的に塞いでいる可能性があるため。 | kmod 設定、/etc/modprobe.d/、ディストリビューションのセキュリティ通知。 |
| 再起動済みか | kernel 脆弱性は、修正済みパッケージをインストールしただけでは解消せず、修正済み kernel で起動する必要があるため。 | uname -r、reboot 履歴、稼働中 kernel とインストール済み kernel の比較。 |
ディストリビューションごとの対応差とは、脆弱性の物理的性質が変わるという意味ではない。CVE-2026-31431 の根は Linux カーネルの algif_aead 周辺にある。しかし、実際に利用者へ届く対策は、各ディストリビューションの kernel パッケージ、backport 方針、セキュリティ通知、暫定緩和、再起動運用を通じて提供される。このため、利用者が見るべきなのは「どのディストリビューションなら安全か」というラベルではなく、「自分の起動中 kernel は修正済みか」「暫定緩和は入っているか」「更新後に再起動したか」という具体的な状態である。
12. リスクと影響範囲
Copy Fail は、単体ではインターネット越しにいきなり root 権限を取得する脆弱性ではない。攻撃者は、まず対象システム上で何らかのコードを実行できる必要がある。この意味では、分類上はローカル権限昇格である。しかし、ここで「ローカルだから低リスク」と考えるのは誤りである。現実の攻撃では、初期侵入と権限昇格は分かれていることが多い。最初は Web アプリケーションの脆弱性、漏洩した SSH アカウント、悪性依存パッケージ、CI/CD のビルドスクリプト、コンテナ内の RCE などで低権限の実行入口を得る。その後、ローカル権限昇格を使って root へ到達する。Copy Fail は、この第 2 段階で使われ得る脆弱性である。
したがって、リスク評価では「外部から直接叩けるか」だけを見てはいけない。見るべきなのは、対象ホスト上で誰のコードが動くかである[2][10][3]。非信頼コード、外部由来コード、複数ユーザーのコード、コンテナ内プロセス、CI/CD ジョブ、Web サーバーユーザーのコードが動く環境では、ローカル権限昇格の価値は大きくなる。逆に、完全に停止している VM イメージや、ネットワークに接続せず外部コードも実行しない単独環境では、直ちに悪用される可能性は低い。ただし、その環境を起動してネットワークや共有フォルダーへ接続した瞬間、評価は変わる。
Copy Fail のリスクは、次の 4 段階で考えると整理しやすい。
| 段階 | 問い | Copy Fail での意味 |
|---|---|---|
| 第 1 段階: 脆弱性の存在 | 対象 kernel に脆弱な algif_aead 経路が残っているか。 | 修正済み kernel で起動していなければ、脆弱性が残る可能性がある。 |
| 第 2 段階: 到達可能性 | 攻撃者または非信頼コードが対象ホスト上で実行できるか。 | SSH、Web RCE、CI/CD、コンテナ、共有ユーザー環境では到達可能性が高くなる。 |
| 第 3 段階: 権限増幅 | 低権限の実行が root 権限へ接続するか。 | setuid root バイナリのページキャッシュ汚染により、root 権限実行へ接続し得る。 |
| 第 4 段階: 被害拡大 | root 取得後に何が可能になるか。 | 認証情報の窃取、永続化、ログ改ざん、他ユーザー侵害、コンテナホスト侵害、横展開が可能になる。 |
この整理から分かるように、Copy Fail の危険性は「リモートかローカルか」という単純な分類では決まらない。たとえば、公開 Web サーバーで Web アプリケーションに RCE がある場合、攻撃者は最初に www-data などの低権限ユーザーとしてコードを実行する。その時点では root ではない。しかし、そこから Copy Fail を使って root へ昇格できるなら、Web アプリケーションの侵害はサーバー全体の侵害へ拡大する。CI/CD ランナーでも同じである。外部 PR や依存パッケージのビルド処理が runner 上で実行されるなら、ローカル権限昇格は runner ホストの root 取得へ接続し得る。
影響範囲は、環境ごとに異なる。以下の表では、単に「危険」「安全」と分けるのではなく、初期侵入の入口、Copy Fail が担う役割、成功時の影響、運用上の優先度を分けて整理する。
| 環境 | 初期侵入・実行入口 | Copy Fail が担う役割 | 成功時の影響 | 優先度 |
|---|---|---|---|---|
| 停止中の古い VM イメージ | 停止中であればコードは実行されない。 | 停止中は悪用経路にならない。 | 起動しなければ直ちに影響しない。ただし、起動後に外部接続すると評価が変わる。 | 起動前に隔離方針を決め、起動後ただちに更新する。 |
| 個人デスクトップ | 悪性アプリ、ブラウザー経由の攻撃、開発ツール、依存パッケージ。 | ユーザー権限で動いた悪性コードが root へ昇格する経路になり得る。 | 端末全体の制御、認証情報の窃取、永続化、他環境への横展開。 | 通常のセキュリティ更新を速やかに適用する。 |
| 公開 Web サーバー | CMS、プラグイン、Web アプリケーション RCE、アップロード処理の不備。 | www-data などの低権限ユーザーから root へ昇格する第 2 段階になる。 | サーバー全体の侵害、設定改ざん、秘密鍵や DB 認証情報の窃取、ログ改ざん。 | 高。公開面を持つため、kernel 更新と再起動を優先する。 |
| SSH 一般ユーザーありのサーバー | 正規ユーザー、漏洩鍵、弱いパスワード、踏み台アカウント。 | 一般ユーザー権限を root 権限へ増幅する。 | ホスト全体の制御、他ユーザー領域へのアクセス、永続化、横展開。 | 高。複数ユーザーが存在するなら優先度を上げる。 |
| 共有サーバー | 正規ユーザーが最初から存在し、ローカルコード実行が前提になる。 | 通常ユーザーの操作をホスト全体の root 侵害へ変える。 | 他ユーザーのデータ侵害、共有基盤全体の改ざん、監査困難化。 | 非常に高。ローカル権限昇格との相性が最も悪い環境の一つである。 |
| CI/CD ランナー | ビルドスクリプト、外部 PR、依存パッケージ、テストコード、生成スクリプト。 | runner ユーザーで実行されたコードをホスト root へ接続する。 | CI ホスト侵害、シークレット窃取、成果物改ざん、サプライチェーン攻撃。 | 非常に高。外部由来コードを扱うなら特に優先する。 |
| コンテナホスト | コンテナ内 RCE、悪性イメージ、過剰権限コンテナ、共有 volume。 | コンテナ内の低権限実行を、共有 kernel 経由でホスト側侵害へ接続し得る。 | コンテナ境界突破、ホスト侵害、他コンテナや secrets へのアクセス。 | 非常に高。ホスト kernel を共有している点が本質的なリスクである。 |
| HPC / 研究計算基盤 | 多数ユーザーのジョブ投入、共有ファイルシステム、研究コード、外部ソフトウェア。 | 一般ユーザーの計算ジョブを計算ノード root 侵害へ変える。 | 計算ノード侵害、共有ストレージへの影響、他ユーザーの研究データ漏洩、基盤全体の信用低下。 | 非常に高。多数ユーザーが正当にローカルコードを実行するため、優先度が高い。 |
ここで特に注意すべきなのは、ローカル権限昇格は「入口」ではなく「増幅器」として働く点である。外部から直接 root を取れないから安全なのではない。攻撃者が別の入口から低権限を得たとき、その低権限を root へ引き上げる装置になる。攻撃の現実では、この組み合わせが重要である。初期侵入、権限昇格、永続化、横展開は別々の脆弱性や設定不備を組み合わせて行われる。Copy Fail は、そのうち権限昇格段階に位置づく。
リスク評価では、次の問いを順番に確認するとよい。
| 確認項目 | 見るべき内容 | 高リスクになる条件 |
|---|---|---|
| 起動中 kernel | 修正済み kernel で実際に起動しているか。 | パッケージ更新済みでも再起動しておらず、古い kernel が動いている。 |
| algif_aead の状態 | algif_aead がロード可能か、既にロードされているか、無効化されているか。 | 脆弱な経路が module として利用可能なまま残っている。 |
| ローカル実行入口 | 誰のコードがそのホスト上で動くか。 | 非信頼コード、複数ユーザー、CI、コンテナ、Web RCE の可能性がある。 |
| 権限分離 | seccomp、AppArmor、SELinux、namespaces、capabilities が効いているか。 | コンテナやジョブが広い system call surface と capability を持つ。 |
| 更新容易性 | kernel 更新と再起動をどれだけ早く実施できるか。 | 再起動できない、検証環境がない、メンテナンス手順が未整備である。 |
| 侵害時の局所化 | root 取得後にどこまで被害が広がるか。 | 共有 secrets、広い sudo 権限、共有 mount、横展開しやすい構成がある。 |
したがって、運用上の評価では、インターネット公開ポートの有無だけを基準にしてはいけない。公開ポートが少なくても、SSH 一般ユーザーが多いサーバー、CI/CD ランナー、コンテナホスト、HPC のような共有実行環境ではリスクが高い。一方、公開 Web サーバーでは、Web アプリケーション側の RCE と組み合わさることでリスクが高まる。評価軸は、「外から直接 root を取れるか」ではなく、「攻撃者または非信頼コードが対象ホスト上で低権限実行へ到達できるか」「そこから root へ昇格された場合に何が失われるか」である。
13. 古い Linux VM イメージの扱い
検証用の古い Linux VM イメージは、停止している限り直ちに攻撃されるわけではない。停止中の VM は CPU 上で実行されておらず、kernel も動いていないため、CVE-2026-31431 のようなローカル権限昇格がその場で発火することはない。しかし、問題は起動した瞬間に始まる。古い VM は、過去の kernel、古いパッケージ、古い認証設定、古い SSH 設定、古い証明書、古い依存関係、古いユーザーアカウントを抱えたまま、現代のネットワーク、共有フォルダー、CI/CD、パッケージリポジトリ、ホスト OS と接続される可能性がある。したがって、古い VM イメージは単なる保管物ではなく、起動時に危険化し得る実行環境として扱うべきである。
古い VM のリスクは、停止中、起動直後、更新中、更新後で異なる。停止中は実行されないため直接の悪用はない。起動直後は、脆弱な kernel がそのまま動くため危険である。更新中は、ネットワークに接続する必要が出る場合があり、その間に外部と接触する。更新後は、修正済み kernel で再起動して初めて安全側へ移る。つまり、パッケージ更新コマンドを実行した時点ではまだ不十分であり、再起動して起動中 kernel が更新済みであることを確認する必要がある。
| 状態 | リスク | 注意点 | 推奨対応 |
|---|---|---|---|
| 停止中 | 直ちに悪用される可能性は低い。 | ただし、イメージ内には古い kernel や未修正パッケージが残っている。 | 起動前にネットワーク設定、共有フォルダー、snapshot 方針を決める。 |
| 起動直後 | 脆弱な kernel が動いている可能性がある。 | この時点で外部ネットワークや共有環境へ接続するとリスクが上がる。 | 隔離ネットワークで起動し、不要な外部接続を避ける。 |
| 更新中 | リポジトリ接続など最小限のネットワークが必要になる場合がある。 | 更新中も古い kernel で動いているため、再起動までは脆弱性が残る。 | 必要な宛先だけ許可し、更新後すぐ再起動する。 |
| 更新後・再起動前 | パッケージは更新済みでも、起動中 kernel は古いままの可能性がある。 | kernel 脆弱性では、この状態を安全と見なしてはいけない。 | 必ず再起動する。 |
| 更新後・再起動後 | 修正済み kernel で起動していればリスクは大きく下がる。 | 起動中 kernel、algif_aead 状態、不要サービスを確認する。 | 確認後に通常ネットワークや共有環境へ接続する。 |
古い VM を扱うときに避けるべきなのは、「とりあえず普通に起動してから考える」ことである。古い VM は、起動した時点で古い世界の状態を現在の環境へ持ち込む。特に、共有フォルダー、クリップボード共有、ホストオンリー以外のネットワーク、ブリッジ接続、CI 連携、SSH 公開、NFS / SMB mount、Docker socket 共有のような接続は、VM 内の脆弱性をホストや周辺環境へ接続する経路になり得る。検証目的なら、まず隔離して起動し、更新し、再起動し、状態確認してから接続範囲を広げるべきである。
| 扱い | 推奨 | 理由 |
|---|---|---|
| 調査目的で起動する | NAT なし、ホストオンリー、または完全隔離ネットワークで起動する。 | 脆弱な kernel のまま外部ネットワークへ接続しないため。 |
| パッケージ更新する | 必要なリポジトリへの最小限の通信だけ許可し、更新後に必ず再起動する。 | kernel 更新は再起動して初めて起動中 kernel に反映されるため。 |
| 古い状態を保存したい | 起動前 snapshot と更新後 snapshot を分けて保存する。 | 脆弱状態を保存しつつ、通常利用は更新後状態へ移すため。 |
| 他者のコードを実行する | 脆弱 kernel 上では避ける。 | 非信頼コードがローカル権限昇格へ接続する可能性があるため。 |
| CI や共有開発に接続する | 修正済み kernel で再起動し、状態確認するまで接続しない。 | CI や共有開発環境では外部由来コードや共有情報へ接続しやすいため。 |
| 共有フォルダーを有効にする | 更新前は原則無効にする。 | VM 内の侵害がホスト側ファイルへ影響する経路を減らすため。 |
| ブリッジネットワークを使う | 更新前は避ける。 | VM が LAN 上の通常ホストとして露出し、意図しない接続を受ける可能性があるため。 |
実務上は、古い Linux VM イメージを次のような手順で扱うのが安全である。第一に、起動前に snapshot を取る。第二に、ネットワークを隔離する。第三に、起動後、まず現在の kernel とディストリビューションを確認する。第四に、必要な範囲だけネットワークを開いて security update を適用する。第五に、更新後に再起動する。第六に、起動中 kernel が更新済みであることを確認する。第七に、algif_aead の状態や不要サービスを確認する。第八に、必要なら更新後 snapshot を取る。ここまで終えてから、通常の検証、共有フォルダー、CI 接続、外部コード実行へ進む。
| 手順 | 作業 | 目的 |
|---|---|---|
| 1 | 起動前 snapshot を取る。 | 古い状態を保全し、失敗時に戻せるようにする。 |
| 2 | ネットワークを隔離する。 | 脆弱な状態のまま外部と接続しないようにする。 |
| 3 | 起動後に kernel、OS、更新状態を確認する。 | 脆弱性評価の前提を把握する。 |
| 4 | 必要な通信だけ許可して security update を適用する。 | 攻撃面を増やさず、修正済みパッケージを取得する。 |
| 5 | 更新後に再起動する。 | 修正済み kernel を実際に起動する。 |
| 6 | 起動中 kernel が更新済みであることを確認する。 | 更新済みパッケージと実行中 kernel の不一致を避ける。 |
| 7 | 不要サービス、共有フォルダー、algif_aead、AF_ALG 到達性を確認する。 | 残存する攻撃面を減らす。 |
| 8 | 更新後 snapshot を取る。 | 以後の検証用に安全側へ寄せた基準状態を保存する。 |
古い VM イメージは、検証や再現性のために価値がある。しかし、価値があるからといって、そのまま現在の運用環境へ接続してよいわけではない。古い VM は、過去の脆弱性、過去の設定、過去の依存関係を含む時間のカプセルである。安全に扱うには、停止中は保管物、起動直後は脆弱な実行環境、更新後再起動までは暫定状態、再起動後確認済みで初めて検証環境、というように状態を分けて考える必要がある。
14. 確認ワンライナーと限界
CVE-2026-31431 の影響有無を確認するとき、「この 1 行を実行すれば安全か危険かが完全に分かる」というコマンドを期待したくなる。しかし、kernel 脆弱性ではその期待は危険である。理由は、ディストリビューションごとに修正の取り込み方が異なるからである。あるディストリビューションでは upstream kernel のバージョンを大きく上げず、古い kernel 系列に修正 commit だけを backport することがある。その場合、kernel version 文字列だけを見ると古く見えても、実際には修正済みである可能性がある。逆に、パッケージを更新済みでも、再起動していなければ古い kernel が動き続けている。
したがって、ここで示すワンライナーは「安全性を証明するコマンド」ではない。目的は、調査の入口として、起動中の kernel、algif_aead のロード状態、更新候補、kmod の導入状態をまとめて見ることである。この結果をもとに、Debian Security Tracker、Ubuntu Security Notice、各ディストリビューションの CVE 情報と照合する必要がある。
まず、Debian / Ubuntu 系で、現在の状態をざっと確認するなら次のようにする。
1 2 3 4 5 6 7 8 | uname -r if grep -qE '^algif_aead ' /proc/modules; then echo "algif_aead loaded" else echo "algif_aead not loaded" fi apt list --upgradable 2>/dev/null | grep -E '^(linux-image|linux-modules|kmod)/' || true dpkg -l kmod | awk 'NR==1 || /^ii/ {print}' |
このコマンド群は、4 つの情報を出す。1 つ目の uname -r は、現在起動中の kernel version を表示する。2 つ目の grep は、algif_aead が現在ロードされているかを確認する。3 つ目の apt list は、linux-image、linux-modules、kmod など、今回の対策に関係し得る更新候補が残っているかを見る。4 つ目の dpkg -l kmod は、kmod パッケージがどの状態で入っているかを見る。
| 確認項目 | コマンド | 分かること | 分からないこと |
|---|---|---|---|
| 起動中 kernel | uname -r | いま実際に動いている kernel の version。 | その kernel に CVE-2026-31431 の修正が backport 済みかどうか。 |
| algif_aead の現在のロード状態 | grep -qE ‘^algif_aead ‘ /proc/modules | 現時点で algif_aead がロード済みかどうか。 | 将来自動ロードされるかどうか、built-in かどうか、修正済みかどうか。 |
| 更新候補 | apt list –upgradable | kernel や kmod の未適用更新が残っているか。 | 更新済みパッケージで再起動済みかどうか。 |
| kmod の状態 | dpkg -l kmod | kmod パッケージがインストールされているか。 | Ubuntu の暫定緩和が必ず有効かどうか。 |
さらに、algif_aead のロード抑止設定が入っているかを確認するなら、/etc/modprobe.d/ 配下を確認する。これは、Ubuntu の kmod 緩和や手動対策により、algif_aead のロードを失敗させる設定が入っているかを見るためである。
1 | grep -R --line-number 'algif_aead' /etc/modprobe.d/ 2>/dev/null || true |
この結果に install algif_aead /bin/false のような行があれば、algif_aead のロードを失敗させる設定が入っている可能性が高い。ただし、設定が存在しても、すでに built-in された algif_aead には効かない。また、設定がないからといって直ちに脆弱とは限らない。修正済み kernel で起動していれば、暫定緩和が不要な場合もある。
Red Hat 系で同じ発想の確認を行うなら、apt や dpkg ではなく rpm / dnf を見る。確認対象は同じであり、起動中 kernel、更新候補、algif_aead のロード状態である。
1 2 3 4 5 6 7 8 | uname -r if grep -qE '^algif_aead ' /proc/modules; then echo "algif_aead loaded" else echo "algif_aead not loaded" fi dnf check-update 'kernel*' 'kmod*' || true rpm -q kernel kmod |
このように、確認コマンドはディストリビューションごとに異なる。しかし、見るべき観点は共通している。重要なのは、コマンドの出力をそのまま安全判定にしないことである。kernel version、更新候補、モジュール状態、ディストリビューションの advisory を合わせて見る必要がある。
最終判断は、次の順序で行うのがよい。
| 判断段階 | 見るもの | 判断内容 |
|---|---|---|
| 1 | ディストリビューションの CVE / security advisory。 | 自分のリリースが影響対象か、修正済みパッケージが提供されているかを確認する。 |
| 2 | インストール済み kernel package。 | 修正済みパッケージを導入済みかを確認する。 |
| 3 | uname -r。 | 実際に起動している kernel が更新後のものかを確認する。 |
| 4 | /proc/modules と /etc/modprobe.d/。 | 暫定緩和として algif_aead のロード状態やロード抑止を確認する。 |
| 5 | サービス状態とログ。 | 更新または暫定緩和による副作用が出ていないかを確認する。 |
結論として、確認ワンライナーは「調査を始めるための入口」であり、「安全証明」ではない。CVE-2026-31431 のような kernel 脆弱性では、パッケージ更新、backport、暫定緩和、起動中 kernel、再起動状態が絡む。したがって、最終的には各ディストリビューションの security advisory と、自分の環境で実際に動いている kernel の状態を照合しなければならない。
15. 技術的本質は意味境界の破綻である
Copy Fail の技術的本質は、「書いてはいけない場所に書けてしまう」ことである。しかし、この表現だけではまだ表面的である。さらに深く見ると、問題は「同じメモリ上の page が、文脈によって異なる意味を持ち、その意味のうち最も重要な制約が途中で失われた」ことにある。ファイルシステムの文脈では、その page は読み取り専用ファイルに由来するページキャッシュである。splice() の文脈では、その page はコピーを避けて受け渡される参照である。AF_ALG / algif_aead の文脈では、その page は暗号処理に渡された入力の一部である。ところが、内部処理の構造によって、その page が出力先の一部として扱われると、読み取り専用であるという意味が失われる。
ここでいう「意味」とは、文学的な意味ではない。OS の実装上の意味である。ある page がどのファイルに由来するのか、誰がその page を書いてよいのか、その page は入力なのか出力なのか、実行時に CPU が命令として読むのか、root 権限で実行されるのか、といった属性である。これらの属性は、CPU の byte 列そのものには書かれていない。OS のデータ構造、ページテーブル、ファイル権限、カーネル API の契約、呼び出し規約、実行時の権限モデルによって維持されている。
Copy Fail では、この属性の連鎖が破れた。/usr/bin/su のページキャッシュは、一般ユーザーにとっては読み取り対象であって書き込み対象ではない。ところが、splice() によって page 参照としてカーネル内の別経路へ渡され、algif_aead の in-place 処理で source と destination の境界が崩れると、その page が output scatterlist 側へ混入する。output scatterlist は、暗号処理の結果を書き込むための領域である。つまり、ファイルシステム文脈では「書いてはいけない page」だったものが、暗号処理文脈では「書き込み先」として再解釈されたのである。
| 文脈 | 同じ page の意味 | 期待される扱い | 破綻した場合に起きること |
|---|---|---|---|
| ファイルシステム | 読み取り専用ファイル由来のページキャッシュ。 | 一般ユーザーからは書き込み不可である。 | ディスク上の権限モデルを迂回して、実行時内容が汚染される。 |
| splice() | コピーせず渡されるデータ参照。 | 参照共有しても、元の page の更新権限は変えない。 | コピーしないことにより、元の page が別処理の出力先へ混入し得る。 |
| AF_ALG / algif_aead | 暗号処理に渡された入力。 | 入力と出力の境界を守る。 | 読み取り専用入力が、書き込み可能な出力先として扱われる。 |
| authencesn | 内部処理で使われる tag / AD 周辺の領域。 | 出力として書く場所を、入力由来の保護された page と混同しない。 | 制御された小さな書き込みが、ページキャッシュへ到達する。 |
| setuid 実行 | root 権限で実行される命令列。 | 攻撃者由来の変更を含まない。 | 汚染された命令列が root 権限で実行される。 |
この表で重要なのは、同じ page が層ごとに別の意味を持つことである。物理的には同じメモリ領域でも、ファイルシステムから見ればファイル内容のキャッシュであり、splice() から見ればコピーを避けて渡す参照であり、暗号処理から見れば処理対象データであり、setuid 実行から見れば root 権限で実行される命令列である。安全な OS では、これらの意味が重なっても、最も強い制約が失われてはならない。読み取り専用であるという制約、一般ユーザーが書けないという制約、root 実行される命令列に攻撃者由来の変更を入れないという制約は、文脈が変わっても保存される必要がある。
しかし、CPU 自体はこの意味を理解しない。CPU にとっては、あるアドレスから byte を読み、別のアドレスへ byte を書き、命令ポインターが指す byte 列を命令として実行するだけである。CPU は、その byte 列が /usr/bin/su に由来するのか、一般ユーザーが書いてはいけないのか、setuid により root 権限で実行されるのかを、意味として理解しているわけではない。それらを理解し、区別し、守っているのは OS の抽象層である。
したがって、脆弱性とは、単に「CPU が間違えた」ことではない。CPU は与えられた命令を実行しただけである。間違えたのは、抽象層の意味を保存する設計である。ファイルシステムの権限モデルでは禁止されていた更新が、暗号処理の入出力モデルでは禁止されたまま扱われなかった。言い換えれば、許可モデルと実行モデルがずれた。人間が設計した意味上は「書いてはいけない場所」だったが、実行経路上は「書き込み先」として到達可能になったのである。
この構造は、Copy Fail に限らない。OS、データベース、ブラウザー、仮想化基盤、コンテナ、ファイルシステム、ネットワークスタックでは、同じデータが複数の文脈を移動する。ある文脈ではユーザー入力、別の文脈では SQL、別の文脈では HTML、別の文脈では JavaScript、別の文脈ではファイルパスになる。ある文脈では読み取り専用の blob、別の文脈では展開後の実行コードになる。多くの脆弱性は、この文脈変換の途中で意味境界が壊れることで発生する。
たとえば、SQL インジェクションでは、単なる文字列として扱うべき入力が、SQL 文の一部として再解釈される。クロスサイトスクリプティングでは、表示用のテキストとして扱うべき文字列が、ブラウザー上で JavaScript として再解釈される。パストラバーサルでは、単なるファイル名として扱うべき入力が、上位ディレクトリーを指すパス操作として再解釈される。Copy Fail では、読み取り専用ファイル由来の page が、暗号処理の出力先として再解釈された。対象は異なるが、構造は同じである。
| 脆弱性の型 | 本来の意味 | 誤った再解釈 | 境界破綻 |
|---|---|---|---|
| SQL インジェクション | ユーザー入力文字列。 | SQL 構文。 | データと命令の境界が壊れる。 |
| クロスサイトスクリプティング | 表示用テキスト。 | ブラウザーで実行される JavaScript。 | テキストと実行コードの境界が壊れる。 |
| パストラバーサル | 許可されたファイル名。 | 任意のディレクトリーを指すパス。 | 名前とアクセス範囲の境界が壊れる。 |
| Copy Fail | 読み取り専用ファイル由来のページキャッシュ。 | 暗号処理の出力先。 | 読み取り対象と書き込み対象の境界が壊れる。 |
この意味で、Copy Fail は低レイヤー版の意味境界破綻である。Web アプリケーションでは文字列の意味が壊れる。OS カーネルでは page の意味が壊れる。どちらも、ある文脈では安全だった対象が、別の文脈で危険なものとして再解釈される点で共通している。セキュリティとは、単に危険な入力を拒否することではない。データが複数の層を移動しても、そのデータに付随する制約、権限、所有者、用途、実行可能性が失われないようにすることである。
したがって、Copy Fail の技術的本質は、CVE-2026-31431 という個別番号に閉じない。これは、計算機が byte 列しか直接扱わない一方で、人間と OS がその byte 列に多層の意味を与えていることから生じる、構造的なリスクである。書いてはいけない場所に書けてしまうとは、物理的なメモリ更新の問題であると同時に、意味境界の保存に失敗したという問題なのである。
16. 数理モデル 1: 許可モデルと実行モデルの不一致
ここからは、ここまで述べてきた構造を数理モデルとして整理する。ただし、数式を導入する目的は、問題を難しく見せることではない。むしろ逆である。Copy Fail の本質である「書いてはいけない場所に書けてしまう」という構造を、曖昧な比喩ではなく、どこで矛盾が起きたのか分かる形にするためである。
まず区別すべきなのは、「許可されていること」と「実際に起きること」である。安全なシステムでは、この 2 つは一致していなければならない。つまり、許可されていない書き込みは、実行経路上でも起きてはならない。ところが脆弱性では、この対応が破れる。設計上は許可されていないはずの更新が、別のカーネル経路を通ることで実際には起きてしまう。
この区別を表すために、まず 4 つの要素を置く。
| 記号 | 読み方 | 意味 | Copy Fail での例 |
|---|---|---|---|
| \(a\) | actor | 操作を行う主体。 | 非特権ユーザー。 |
| \(w\) | write operation | 書き込み操作。 | ページキャッシュへ 4 byte 書く操作。 |
| \(x\) | target object | 操作対象となる領域。 | /usr/bin/su 由来のページキャッシュ。 |
| \(k\) | context | その対象をどの文脈で見ているか。 | ファイルシステム文脈、暗号処理文脈、実行文脈。 |
ここで重要なのは、同じ対象 \(x\) でも、文脈 \(k\) によって意味が変わることである。/usr/bin/su 由来のページキャッシュは、ファイルシステム文脈では「一般ユーザーが書けないファイル内容」である。しかし、暗号処理文脈で誤って出力先として扱われると、「暗号処理が結果を書き込む領域」のように振る舞ってしまう。これが文脈の違いである。
次に、ある文脈 \(k\) において、主体 \(a\) が対象 \(x\) に書き込むことが許可されるかを \(\operatorname{Allow}\) で表す。
\operatorname{Allow}(a,w,x,k)\in\{0,1\}
\]
この式は、「文脈 \(k\) において、主体 \(a\) が、対象 \(x\) に、書き込み操作 \(w\) を行ってよいか」を表す。値が \(1\) なら許可、値が \(0\) なら不許可である。ここでは複雑な確率値ではなく、まず単純に「許可されるか、されないか」の二値として扱う。
| 式の部分 | 意味 | 具体例 |
|---|---|---|
| \(\operatorname{Allow}\) | 許可判定を表す関数。 | OS の権限モデル、ファイル権限、ページ属性などをまとめた抽象表現。 |
| \(a\) | 誰が操作するか。 | 非特権ユーザー \(u\)。 |
| \(w\) | どの操作をするか。 | 書き込み。 |
| \(x\) | どこへ操作するか。 | setuid root バイナリ由来のページキャッシュ \(p_{\mathrm{suid}}\)。 |
| \(k\) | どの文脈で判断するか。 | ファイルシステム文脈 \(k_{\mathrm{fs}}\)。 |
| \(\{0,1\}\) | 結果が 0 または 1 であること。 | 0 は不許可、1 は許可。 |
通常、非特権ユーザー \(u\) が、setuid root バイナリ由来のページキャッシュ \(p_{\mathrm{suid}}\) に、ファイルシステム文脈 \(k_{\mathrm{fs}}\) で書き込むことは許されない。たとえば /usr/bin/su は root が所有する実行ファイルであり、一般ユーザーが通常の write() で書き換えることはできない。この状態を次の式で表す。
\operatorname{Allow}(u,w,p_{\mathrm{suid}},k_{\mathrm{fs}})=0
\]
この式の意味は単純である。「非特権ユーザー \(u\) が、ファイルシステム文脈で、setuid root バイナリ由来のページキャッシュ \(p_{\mathrm{suid}}\) に書くことは許可されていない」ということである。ここまでは、通常の Unix 権限モデルと一致している。一般ユーザーが /usr/bin/su を勝手に書き換えられないからこそ、setuid root バイナリは権限委譲の仕組みとして成立する。
しかし、脆弱性では「許可されていない」ことと「実際に起きない」ことがずれる。そこで、次に実際に書き込みが発生するかを \(\operatorname{Write}\) で表す。
\operatorname{Write}(a,x,k)\in\{0,1\}
\]
\(\operatorname{Write}(a,x,k)\) は、「主体 \(a\) に由来する処理によって、文脈 \(k\) で対象 \(x\) への書き込みが実際に発生するか」を表す。安全なシステムなら、\(\operatorname{Allow}=0\) である書き込みについては、\(\operatorname{Write}=0\) でなければならない。つまり、許可されていない書き込みは、実際にも起きてはいけない。
ところが Copy Fail では、暗号処理文脈 \(k_{\mathrm{crypto}}\) において、実際にページキャッシュへの書き込みが発生し得る。
\operatorname{Write}(u,p_{\mathrm{suid}},k_{\mathrm{crypto}})=1
\]
この式は、「非特権ユーザー \(u\) に由来する処理によって、暗号処理文脈 \(k_{\mathrm{crypto}}\) では、setuid root バイナリ由来のページキャッシュ \(p_{\mathrm{suid}}\) への書き込みが実際に発生する」ことを表す。ここで重要なのは、ファイルシステムに対して通常の write() をしているのではない点である。通常の write() なら権限で拒否される。しかし、splice() によって page 参照が AF_ALG / algif_aead の経路へ入り、そこで出力先として扱われるため、ファイルシステムの許可判定とは別の経路で書き込みが起きる。
つまり、Copy Fail では次の 2 つが同時に成立する。
| 観点 | 式 | 意味 |
|---|---|---|
| 許可モデル | \(\operatorname{Allow}(u,w,p_{\mathrm{suid}},k_{\mathrm{fs}})=0\) | 一般ユーザーが setuid root バイナリ由来の page に書くことは許可されていない。 |
| 実行モデル | \(\operatorname{Write}(u,p_{\mathrm{suid}},k_{\mathrm{crypto}})=1\) | 別文脈の暗号処理経路では、その page への書き込みが実際に起きる。 |
この 2 つが同時に成立することが、脆弱性の中核である。そこで、脆弱性 \(V\) を次の条件として定義できる。
V=\left[\operatorname{Allow}(u,w,p_{\mathrm{suid}},k_{\mathrm{fs}})=0\right]\land\left[\operatorname{Write}(u,p_{\mathrm{suid}},k_{\mathrm{crypto}})=1\right]
\]
この式は、「ファイルシステム文脈では許可されていない書き込みが、暗号処理文脈では実際に発生するなら、脆弱性 \(V\) が成立する」という意味である。左側の角括弧は「本来は禁止されている」という条件を表し、右側の角括弧は「にもかかわらず実際には起きる」という条件を表す。中央の \(\land\) は「かつ」を意味する。つまり、禁止と実行が同時に成立する矛盾を表している。
このモデルから分かるのは、Copy Fail が単なる permission bit の問題ではないということである。ファイル権限だけを見れば、一般ユーザーは /usr/bin/su に書けない。これは正しい。しかし、ファイル権限は、ファイルシステム文脈における許可モデルである。問題は、その page が splice() によって別のカーネル経路へ渡され、暗号処理文脈で出力先として扱われたときにも、その禁止が保存されるかである。
安全なシステムであるためには、次の含意が成り立つ必要がある。
\operatorname{Allow}(a,w,x,k)=0 \Rightarrow \operatorname{Write}(a,x,k’)=0
\]
これは、「ある文脈 \(k\) で禁止されている書き込みは、別の文脈 \(k’\) を通っても実際に起きてはならない」という意味である。もちろん、現実の OS では文脈ごとに正当な変換があるため、この式は単純化である。しかし、保護された page が別文脈へ渡る場合には、少なくとも禁止属性が失われてはならない。Copy Fail は、この含意が破れた事例である。
したがって、数理モデルとして見た Copy Fail の本質は、次のように言える。
| 観点 | 内容 | 意味 |
|---|---|---|
| 許可モデル | 一般ユーザーは setuid root バイナリ由来の page に書けない。 | ファイルシステム文脈では、非特権ユーザーによる protected page への書き込みは許可されていない。 |
| 実行モデル | algif_aead の暗号処理経路では、その page に書き込みが届く。 | 別文脈のカーネル内部処理では、同じ page が出力先として扱われ、実際の書き込みが発生する。 |
| 破綻 | 「書けないはず」と「実際には書ける」が同時に成立する。 | 許可モデルと実行モデルが一致せず、禁止された更新が別経路から実行可能になる。 |
この整理により、問題の位置が明確になる。Copy Fail で壊れたのは、単一のファイル権限ではない。壊れたのは、文脈をまたいでも禁止された更新を禁止されたまま保つという、OS 全体の意味保存である。ファイルシステム、splice()、AF_ALG、algif_aead、ページキャッシュ、setuid 実行という複数の層を移動するうちに、「この page は一般ユーザーが書いてはいけない」という制約が、暗号処理の出力先という別文脈で失われたのである。
17. 数理モデル 2: 意味保存の破れ
前章では、「許可されていない書き込み」と「実際に起きる書き込み」がずれることを数式で表した。本章では、さらに一段深く、なぜそのずれが起きたのかを整理する。鍵になるのは、同じ page が文脈によって異なる意味を持つという点である。
ここでいう「意味」とは、抽象的な比喩ではない。OS がその対象に与えている実装上の属性である。たとえば、ある page が読み取り専用ファイルに由来するのか、一時的な作業バッファーなのか、暗号処理の入力なのか、出力なのか、実行時に命令として読まれるのか、root 権限で実行されるのか、といった性質である。CPU から見れば同じ byte 列でも、OS から見ればその byte 列には多層の意味が重なっている。
この意味を数理的に表すため、対象 \(x\) の文脈 \(k\) における意味を \(\mu_k(x)\) と置く。
\mu_k(x)
\]
この記号は、「対象 \(x\) が、文脈 \(k\) では何として扱われるか」を表す。たとえば、同じ page \(p\) でも、ファイルシステム文脈、splice() 文脈、暗号処理文脈、実行文脈では扱われ方が異なる。
| 記号 | 意味 | Copy Fail での例 |
|---|---|---|
| \(x\) | 対象となる領域。 | メモリ上の page。 |
| \(p\) | 具体的な page。 | /usr/bin/su 由来のページキャッシュ。 |
| \(k\) | 文脈。 | ファイルシステム、splice()、暗号処理、setuid 実行。 |
| \(\mu_k(x)\) | 文脈 \(k\) における対象 \(x\) の意味。 | 読み取り専用ファイル由来、出力先、実行命令列など。 |
まず、ページキャッシュ \(p\) をファイルシステム文脈で見る。/usr/bin/su のような setuid root バイナリに由来する page は、一般ユーザーにとって読み取り可能であっても、書き込み可能ではない。つまり、この page の意味は「読み取り専用ファイル由来のキャッシュ」である。
\mu_{\mathrm{fs}}(p)=\mathrm{ReadOnlyFileCache}
\]
この式は、「ファイルシステム文脈 \(\mathrm{fs}\) において、page \(p\) は ReadOnlyFileCache として扱われる」という意味である。ここで重要なのは、読み取り可能と書き込み可能は違うという点である。一般ユーザーは /usr/bin/su を読み取れるかもしれない。しかし、読み取れることは、書き換えられることを意味しない。ファイルシステム文脈では、この page には読み取り専用という意味が付いている。
次に、同じ page \(p\) が暗号処理文脈に入った場合を考える。安全な実装であれば、入力として渡された page は入力であり、出力先として書き換えてよい領域と混同してはならない。しかし Copy Fail では、algif_aead の in-place 処理と scatterlist の扱いにより、この page が書き込み可能な出力先として扱われる経路が成立する。
\mu_{\mathrm{crypto}}(p)=\mathrm{WritableOutput}
\]
この式は、「暗号処理文脈 \(\mathrm{crypto}\) において、同じ page \(p\) が WritableOutput として扱われる」ことを表す。ここに意味の衝突がある。ファイルシステム文脈では読み取り専用だった同じ page が、暗号処理文脈では出力先、すなわち書き込み対象として扱われている。
| 同じ page | 文脈 | 意味 | 書き込み可否 |
|---|---|---|---|
| \(p\) | \(k_{\mathrm{fs}}\) | 読み取り専用ファイル由来のページキャッシュ。 | 一般ユーザーは書けない。 |
| \(p\) | \(k_{\mathrm{crypto}}\) | 暗号処理の出力先。 | 処理結果が書き込まれる。 |
この 2 つが同時に成立すると、OS の意味モデルが破綻する。物理的には同じ page なのに、ある文脈では「書いてはいけないもの」、別の文脈では「書く場所」として扱われるためである。安全なシステムでは、文脈が変わっても、読み取り専用という制約は失われてはならない。
この「文脈が変わる」ことを、変換 \(T\) として表す。ここでは、page \(p\) がファイルシステム文脈から splice() や AF_ALG を経由して暗号処理文脈へ渡ることを、抽象的に \(T(p)\) と書く。
T(p)
\]
\(T(p)\) は、新しい別物の page を作るという意味ではない。ここでは、「page \(p\) が別の処理文脈に渡された状態」を表している。たとえば、splice() によって、ファイル由来の page 参照が pipe を経由して AF_ALG の operation socket へ渡る。この移動全体を、文脈変換 \(T\) として見ている。
安全なシステムでは、読み取り専用属性はこの変換を通じても保存されなければならない。つまり、もとの page \(p\) が読み取り専用なら、別文脈に渡された \(T(p)\) も、書き込み可能な出力先として扱われてはならない。
\mathrm{readonly}(p)\Rightarrow\mathrm{readonly}(T(p))
\]
この式は、「page \(p\) が readonly であるなら、文脈変換後の \(T(p)\) も readonly として扱われなければならない」という意味である。中央の \(\Rightarrow\) は「ならば」を表す。つまり、読み取り専用という制約は、ファイルシステムから暗号処理へ移ったからといって消えてはならない。
しかし Copy Fail では、この含意が破れる。
\mathrm{readonly}(p)\nRightarrow\mathrm{readonly}(T(p))
\]
この式は、「もとの page \(p\) は readonly であるにもかかわらず、変換後の \(T(p)\) が readonly として扱われるとは限らない」ことを表す。より具体的には、読み取り専用ファイル由来の page が、暗号処理の output scatterlist に混入し、書き込み先として扱われる。これが意味保存の破れである。
この破れを、少し一般化して書くと次のようになる。
\exists p:\ \mu_{\mathrm{fs}}(p)=\mathrm{ReadOnlyFileCache}\ \land\ \mu_{\mathrm{crypto}}(T(p))=\mathrm{WritableOutput}
\]
この式は、「ある page \(p\) が存在して、その page はファイルシステム文脈では ReadOnlyFileCache であるにもかかわらず、暗号処理文脈では WritableOutput として扱われる」という意味である。記号 \(\exists p\) は「そのような page \(p\) が存在する」を表す。中央の \(\land\) は「かつ」である。したがって、この式は、同じ対象に対して両立してはならない 2 つの意味が同時に成立していることを表している。
この数理モデルから、Copy Fail の本質は次のように整理できる。
| 段階 | 意味 |
|---|---|
| ファイルシステム文脈 | page \(p\) は読み取り専用ファイル由来のキャッシュである。 |
| 文脈変換 | splice() と AF_ALG により、page \(p\) の参照が暗号処理へ渡る。 |
| 暗号処理文脈 | page \(p\) が出力先として扱われる。 |
| 破綻 | 読み取り専用という意味が、文脈変換後に保存されていない。 |
この式が示すのは、計算機安全性とは byte 列そのものの問題ではなく、byte 列に重ねられた意味が文脈をまたいで保存されるかどうかの問題だということである。byte 列には、それだけを見ても「これは読み取り専用」「これは root 権限で実行される」「これは暗号処理の入力」「これは出力先」とは書かれていない。これらの意味は、OS の抽象層が管理している。だからこそ、抽象層をまたぐ変換では、その意味を失わないことが重要になる。
Copy Fail は、意味保存が破れた事例である。同じ page が、ファイルシステムでは読み取り専用、暗号処理では出力先、setuid 実行では root 権限の命令列という複数の意味を持った。そして、その中で最も保護されるべき「一般ユーザーが書いてはいけない」という意味が、暗号処理経路で失われた。これが、数理モデルとして見た CVE-2026-31431 の中核である。
18. 数理モデル 3: リスク関数
前章までは、Copy Fail の構造を「許可モデルと実行モデルの不一致」および「意味保存の破れ」として整理した。本章では、その構造が実際の運用リスクとしてどの程度危険になるのかを数理モデルで表す。ここで扱うのは、「脆弱性が存在するかどうか」だけではない。同じ脆弱性でも、外部から誰もコードを実行できない単独端末と、複数ユーザーがログインする共有サーバーと、外部コードを動かす CI/CD ランナーと、コンテナを多数動かすホストでは、リスクの大きさが異なる。したがって、リスクは CVE 番号だけで決まるのではなく、脆弱性、露出、攻撃容易性、影響度、防御有効性の組み合わせとして評価する必要がある。
この関係を、単純化して次のように置く。
R_t=V_t\cdot X_t\cdot A_t\cdot I_t\cdot(1-D_t)
\]
このモデルでは、各変数を原則として \(0\) から \(1\) の範囲に正規化された評価値として扱う。\(0\) はその要素がほぼ存在しない状態、\(1\) はその要素が最大に近い状態を表す。たとえば、脆弱な kernel が起動していなければ \(V_t\) は \(0\) に近く、脆弱な kernel が起動中で algif_aead 経路へ到達できるなら \(V_t\) は \(1\) に近くなる。この正規化を置くことで、積の形は「どれか 1 つの条件が欠ければ総合リスクは下がり、すべてがそろうほどリスクが増幅する」という関係を表せる。
この式は、時点 \(t\) における脆弱性リスク \(R_t\) を表す。各項は、単独でリスクを決めるものではない。脆弱性が存在しても、攻撃者が到達できなければリスクは下がる。攻撃者が到達できても、悪用が極めて困難ならリスクは下がる。悪用が容易でも、成功時の影響が小さければリスクは限定される。逆に、脆弱性が存在し、攻撃者が到達でき、悪用しやすく、成功時に root 権限へ到達し、防御や検知が弱いなら、リスクは大きくなる。
| 記号 | 読み方 | 意味 | 値が大きい場合 | 値が小さい場合 |
|---|---|---|---|---|
| \(R_t\) | risk | 時点 \(t\) における総合リスク。 | 実運用上、早急な対策が必要である。 | 影響は限定的、または緩和済みである。 |
| \(V_t\) | vulnerability | 脆弱性そのものが対象環境に存在する度合い。 | 脆弱な kernel / algif_aead 経路が残っている。 | 修正済み kernel で起動済み、または影響しない構成である。 |
| \(X_t\) | exposure | 攻撃者がその脆弱性へ到達できる露出度。 | ローカル実行、SSH、Web RCE、CI、コンテナ foothold がある。 | 非信頼コードを実行する入口がほとんどない。 |
| \(A_t\) | attackability | 悪用のしやすさ。 | 公開情報や PoC があり、条件がそろえば再現しやすい。 | 環境依存が強く、悪用に高度な条件が必要である。 |
| \(I_t\) | impact | 成功時の影響度。 | root 権限取得、ホスト侵害、コンテナ境界突破につながる。 | 限定されたユーザー権限内の影響にとどまる。 |
| \(D_t\) | defense | 防御、検知、緩和の有効性。 | 修正済み kernel、algif_aead 無効化、seccomp、監視が効いている。 | 未更新、未再起動、暫定緩和なし、検知困難である。 |
この式で \((1-D_t)\) としているのは、防御が強いほどリスクが下がることを表すためである。たとえば \(D_t=0\) なら、防御がまったく効いていないため \((1-D_t)=1\) となり、リスクはそのまま残る。逆に \(D_t=1\) なら、防御が完全に効いている理想状態なので \((1-D_t)=0\) となり、このモデル上の残存リスクは 0 に近づく。現実には完全な \(D_t=1\) は簡単ではないが、修正済み kernel で再起動し、不要な経路を制限し、ログ監視やコンテナ制限を組み合わせることで、\(D_t\) を高めることはできる。
Copy Fail に当てはめると、まず \(V_t\) は「脆弱な kernel / algif_aead 経路が対象環境に存在するか」を表す。修正済み kernel へ更新し、その kernel で再起動していれば \(V_t\) は低くなる。パッケージを更新していても再起動しておらず、古い kernel が動いているなら \(V_t\) は残る。algif_aead がモジュールとして存在し、ロード可能であり、脆弱な処理経路が残っているなら、\(V_t\) は高い。
次に \(X_t\) は露出度である。Copy Fail はローカル権限昇格であり、通常は攻撃者が対象システム上で何らかのコードを実行できる必要がある。このため、「ネットワーク越しに単独で即 root になる脆弱性」とは異なる。しかし、ローカル権限昇格だから低リスクだと決めるのは誤りである。現代の運用環境では、ローカル実行権限の入口は多数ある。SSH の一般ユーザー、Web アプリケーションの RCE、CI/CD ジョブ、コンテナ内プロセス、共有サーバーの一般ユーザー、研究計算基盤のジョブ実行環境などがある。これらの入口があるほど、\(X_t\) は高くなる。
| 環境 | \(X_t\) の評価 | 理由 |
|---|---|---|
| 単独利用のデスクトップ | 比較的低い。 | 非信頼ユーザーが常時コードを実行する前提ではないため。ただし、マルウェア感染時には問題になる。 |
| 一般的な公開 Web サーバー | 中程度から高い。 | Web アプリケーションの脆弱性、アップロード処理、スクリプト実行、侵入後の権限昇格に接続し得るため。 |
| SSH で複数ユーザーがログインするサーバー | 高い。 | 非特権ユーザーが正当にローカルコードを実行できるため。 |
| CI/CD ランナー | 高い。 | 外部由来のビルドスクリプト、テストコード、依存パッケージが実行される可能性があるため。 |
| コンテナホスト | 高い。 | コンテナ内プロセスがホスト kernel の攻撃面に到達し得るため。 |
| HPC / 研究計算基盤 | 高い。 | 多数ユーザーが同一基盤上でジョブを実行し、ローカル権限昇格の入口が多いため。 |
\(A_t\) は攻撃容易性である。ここでは、脆弱性が理論上存在するだけでなく、実際に exploit へ落とし込めるかが問題になる。Copy Fail では、制御された 4 byte 書き込み、ページキャッシュ汚染、setuid root バイナリへの接続という構造が問題になる。環境差、対象バイナリ、kernel の構成、ディストリビューションの修正状況に左右される部分はあるが、単なるクラッシュではなく root 権限取得へ接続し得る構造が示されているため、攻撃容易性は軽視できない。
\(I_t\) は成功時の影響度である。Copy Fail の場合、成功時の影響は大きい。ローカル権限昇格により root 権限を取得されれば、ファイル改ざん、機密情報の読み取り、認証情報の窃取、永続化、ログ改ざん、横展開、コンテナホスト侵害などが可能になる。単なる一般ユーザー権限の侵害と root 権限の侵害では、被害の質が異なる。root はシステム全体の制御権限であり、攻撃者がそこへ到達すると、以後の検知や復旧も難しくなる。
\(D_t\) は防御、検知、緩和の有効性である。修正済み kernel への更新と再起動は \(D_t\) を大きく上げる。algif_aead の無効化は、脆弱な経路へ到達しにくくするため、暫定的に \(D_t\) を上げる。seccomp による AF_ALG socket の制限、コンテナの権限削減、特権コンテナの禁止、CI/CD runner の隔離も \(D_t\) を上げる。一方で、未更新、未再起動、ロード可能な algif_aead、非信頼コード実行環境、監視不足は \(D_t\) を下げる。
| 対策 | \(D_t\) への効果 | 限界 |
|---|---|---|
| 修正済み kernel へ更新して再起動 | 脆弱な実装そのものを置き換えるため、最も効果が高い。 | 再起動前は古い kernel が動き続ける。メンテナンス調整が必要である。 |
| algif_aead のロード無効化 | 問題経路に含まれる algif_aead を使わせないため、暫定緩和になる。 | built-in の場合は外せない。AF_ALG / libkcapi 利用アプリに副作用があり得る。 |
| AF_ALG socket の制限 | 非信頼コードから kernel crypto API へ到達しにくくする。 | 正当な AF_ALG 利用も制限し得る。seccomp などの設計が必要である。 |
| コンテナ権限の削減 | コンテナ内コードからホスト kernel への危険な到達を減らす。 | ホスト kernel が脆弱なままなら、本対策にはならない。 |
| 監視とログ確認 | 異常な挙動や副作用に気づきやすくする。 | ページキャッシュ汚染はディスク改ざんとして残りにくく、検知には限界がある。 |
このモデルから、「ローカル脆弱性だから低リスク」という評価が不十分であることが分かる。確かに、Copy Fail は単独でインターネット越しに侵入する脆弱性ではない。しかし、攻撃者が別の経路で一般ユーザー権限やコンテナ内実行権限を得た場合、Copy Fail は root 権限へ上がるための第 2 段階になる。このとき \(X_t\) は高くなり、成功時には root 権限取得に至るため \(I_t\) も高い。さらに、ページキャッシュ汚染はディスク上のファイル改ざんとして残りにくいため、検知困難性によって \(D_t\) は下がりやすい。
具体的には、次のような複合リスクがある。
| 入口 | Copy Fail との結合 | 結果 |
|---|---|---|
| Web アプリケーションの RCE | Web サーバーユーザー権限でローカル exploit を実行する。 | www-data などから root へ昇格し、サーバー全体を侵害する。 |
| SSH 一般ユーザー | 正規ユーザーまたは漏洩アカウントが exploit を実行する。 | 一般ユーザー権限から root へ昇格する。 |
| CI/CD ジョブ | ビルドスクリプトやテストコードとしてローカル exploit を実行する。 | runner ホストの侵害、秘密情報の窃取、横展開につながる。 |
| コンテナ内 foothold | コンテナ内プロセスからホスト kernel の脆弱経路へ到達する。 | コンテナ境界を越え、ホスト側の制御へ発展する可能性がある。 |
| 共有計算基盤 | 一般ユーザーのジョブとして exploit を実行する。 | 共用ホスト全体、他ユーザーのデータ、計算基盤管理権限が危険になる。 |
したがって、Copy Fail のリスク評価では、脆弱性単体だけでなく、自分の環境で \(X_t\)、\(I_t\)、\(D_t\) がどうなるかを確認する必要がある。個人の閉じた検証 VM では \(X_t\) が低く、実運用リスクは限定的かもしれない。一方、公開サーバー、CI/CD、コンテナホスト、共有 Linux 環境では \(X_t\) が高く、成功時の \(I_t\) も大きい。さらに、未更新で暫定緩和もないなら \(D_t\) が低くなるため、総合リスク \(R_t\) は大きくなる。
この章の結論は、次のようにまとめられる。
R_t
=
V_t
\cdot X_t
\cdot A_t
\cdot I_t
\cdot (1-D_t)
\]
| 要素 | 意味 |
|---|---|
| \(V_t\) | 脆弱性が存在するか。 |
| \(X_t\) | 攻撃者が到達できるか。 |
| \(A_t\) | 悪用しやすいか。 |
| \(I_t\) | 成功時の影響が大きいか。 |
| \(1-D_t\) | 防御・検知・緩和が不十分か。 |
Copy Fail はローカル権限昇格である。しかし、現代の運用環境では、ローカル実行権限はしばしば別の入口から得られる。したがって、「ローカルだから低リスク」ではなく、「自分の環境でローカル実行権限に到達する入口がどれだけあるか」「成功時に root へ到達する影響をどう評価するか」「修正済み kernel と暫定緩和が入っているか」を見る必要がある。数理モデルは、その判断を分解するための道具である。
19. 数理モデル 4: 人間の点検能力の限界
ここまで見ると、自然に次の問いが出てくる。なぜこのような問題を事前にすべて見つけられないのか。Linux カーネルのような重要な基盤であれば、事前に完全点検しておけばよいのではないか。理想としてはその通りである。しかし、現実には完全点検はほぼ不可能である。理由は、人間の注意力や技術力が不足しているからだけではない。より根本的には、計算機システムの組み合わせ空間が、人間が現実に確認できる範囲をはるかに超えるからである。
Copy Fail は、その典型例である。問題は、algif_aead だけを読めば直ちに見えるわけではない。splice() だけを読んでも見えない。ページキャッシュだけを読んでも見えない。setuid root バイナリだけを読んでも見えない。危険性は、AF_ALG、algif_aead、authencesn、splice()、scatterlist、ページキャッシュ、setuid root バイナリ、in-place optimization が結合したところに現れる。つまり、部品単体の点検ではなく、部品同士の相互作用を点検しなければならない。
この「組み合わせの爆発」を、まず単純なモデルで表す。システムに \(n\) 個の部品があり、それぞれの部品 \(i\) が \(m_i\) 個の状態を持つとする。このとき、全体として考え得る状態空間 \(\Omega\) の大きさは、次のように表せる。
|\Omega|=\prod_{i=1}^{n}m_i
\]
この式は、「全体の状態数は、各部品の状態数を掛け合わせたものになる」という意味である。たとえば、部品が 3 個あり、それぞれ 10 通りの状態を持つなら、全体は \(10 \times 10 \times 10 = 1000\) 通りになる。部品が 10 個なら \(10^{10}\) 通りになる。部品が 100 個なら、もはや人間が個別に眺められる量ではない。
| 記号 | 意味 | Copy Fail での読み替え |
|---|---|---|
| \(\Omega\) | システム全体の状態空間。 | Linux カーネルと周辺機能が取り得る組み合わせ全体。 |
| \(|\Omega|\) | 状態空間の大きさ。 | 点検すべき組み合わせの総量。 |
| \(n\) | 部品数。 | AF_ALG、algif_aead、splice()、ページキャッシュ、setuid、コンテナ、LSM など。 |
| \(m_i\) | 部品 \(i\) が取り得る状態数。 | 有効 / 無効、module / built-in、入力 / 出力、read-only / writable、権限状態、実行文脈など。 |
| \(\prod\) | 積を取る記号。 | 部品ごとの状態が組み合わさって、全体の確認範囲が増えることを表す。 |
この式が重要なのは、部品数が少し増えるだけで、全体の組み合わせが急激に増えることを示している点である。たとえば、AF_ALG の有無、algif_aead の有無、splice() の利用、対象が通常ファイルか setuid バイナリか、page が読み取り専用か、in-place 処理か out-of-place 処理か、コンテナ内から到達できるか、seccomp で制限されているか、という条件を並べるだけでも、組み合わせは一気に増える。しかも現実のカーネルには、これよりはるかに多くの条件がある。
一方、人間が現実に観測・点検できる範囲を \(\Omega_{\mathrm{human}}\) と置く。これは、人間のレビュー、テスト、設計確認、運用確認、脆弱性診断、再現試験などによって実際に調べられた範囲である。理想的には \(\Omega_{\mathrm{human}} = \Omega\) であってほしい。しかし、現実には次が成り立つ。
|\Omega_{\mathrm{human}}|\ll|\Omega|
\]
\(\ll\) は「はるかに小さい」という意味である。つまり、人間が実際に見られる範囲は、システム全体の可能な組み合わせに比べて圧倒的に小さい。これは怠慢の問題ではなく、構造的な問題である。カーネル開発者、ディストリビューションのメンテナー、セキュリティ研究者、運用者がどれほど努力しても、すべての機能、すべてのコンパイルオプション、すべてのディストリビューション、すべてのハードウェア、すべての実行経路、すべての攻撃者の組み合わせを完全に検査することはできない。
ここで、観測済みの空間を \(\Omega_{\mathrm{observed}}\) とする。これは、テスト、レビュー、fuzzing、形式検証、実運用、障害報告、CVE、PoC、監査によって、何らかの形で確認された領域である。すると、未知の脆弱性空間 \(U_t\) は、全体空間と観測済み空間の差分として表せる。
U_t=|\Omega|-|\Omega_{\mathrm{observed}}|
\]
この式は、「まだ観測されていない組み合わせが、未知のリスク空間として残る」ことを表している。\(U_t\) が大きいほど、未知の不具合や未知の脆弱性が潜んでいる可能性が高い。逆に、レビュー、テスト、fuzzing、形式検証、実運用での観測が進むほど、\(|\Omega_{\mathrm{observed}}|\) は増え、\(U_t\) は小さくなる。しかし、現実には \(|\Omega|\) も同時に増え続ける。新機能、性能最適化、互換性維持、新しいデバイス、新しいプロトコル、新しい仮想化基盤、新しいクラウド構成が追加されるためである。
| 増加要因 | \(|\Omega|\) を増やす理由 | Copy Fail との関係 |
|---|---|---|
| 新機能 | 新しい API や処理経路が増える。 | AF_ALG のように、ユーザー空間からカーネル機能を使う入口が増える。 |
| 性能最適化 | コピー削減、in-place 処理、参照共有が増える。 | splice() や in-place optimization が、意味境界を複雑にする。 |
| 互換性維持 | 古い仕様と新しい仕様が共存する。 | 想定外の古い経路や特殊構成が残る。 |
| モジュール構成 | module / built-in / 未ロード / 自動ロードの差が生じる。 | algif_aead の暫定緩和が効く環境と効かない環境が分かれる。 |
| コンテナと仮想化 | 同じ kernel を複数の隔離環境が共有する。 | コンテナ内の非特権コードがホスト kernel の攻撃面に触れる。 |
| CI/CD と自動実行 | 外部由来コードを自動的に実行する機会が増える。 | ローカル権限昇格の入口である \(X_t\) が増える。 |
したがって、未知の脆弱性空間 \(U_t\) はゼロになりにくい。人間が点検を進めて \(|\Omega_{\mathrm{observed}}|\) を増やしても、同時に機能追加や運用複雑化によって \(|\Omega|\) も増えるからである。この構造を考えると、計算機の安全性を「すべてを事前に確認すれば保証できる」と考えるのは現実的ではない。必要なのは、完全点検ではなく、未知領域が残ることを前提にした安全設計である。
この点を、もう少し運用に近い形で書くと、次のようになる。
\frac{dU_t}{dt}=\frac{d|\Omega|}{dt}-\frac{d|\Omega_{\mathrm{observed}}|}{dt}
\]
この式は、未知領域 \(U_t\) の増減を表している。左辺は、未知領域が時間とともにどれだけ増えるかである。右辺の第 1 項 \(\frac{d|\Omega|}{dt}\) は、システム全体の複雑さが増える速度である。右辺の第 2 項 \(\frac{d|\Omega_{\mathrm{observed}}|}{dt}\) は、人間やツールが観測済み領域を増やす速度である。もしシステムが複雑化する速度のほうが、観測や検証が進む速度より大きければ、未知領域は増える。
| 式の部分 | 意味 | 実務上の対応 |
|---|---|---|
| \(\frac{d|\Omega|}{dt}\) | 複雑化の速度。 | 不要機能を減らす、設計を単純化する、攻撃面を削る。 |
| \(\frac{d|\Omega_{\mathrm{observed}}|}{dt}\) | 観測・検証の速度。 | テスト、fuzzing、レビュー、形式検証、監視、脆弱性情報追跡を強化する。 |
| \(\frac{dU_t}{dt}\) | 未知領域の増減。 | 未知リスクが拡大しているか、縮小しているかを示す。 |
このモデルから、セキュリティ対策の基本方針が見えてくる。第一に、\(|\Omega|\) を無制限に増やさないことである。不要な kernel module を有効にしない、不要な socket family を使わせない、特権コンテナを避ける、setuid バイナリを最小限にする、不要なサービスを止める、CI/CD runner を分離する、といった対策は、攻撃面を減らし、状態空間そのものを小さくする。これは「防御を強くする」以前に、「危険な組み合わせを減らす」対策である。
第二に、\(|\Omega_{\mathrm{observed}}|\) を増やすことである。コードレビュー、静的解析、動的解析、fuzzing、syzkaller のようなカーネル fuzzing、形式検証、回帰テスト、セキュリティ監査、運用ログ監視、脆弱性情報の継続追跡は、未知領域を観測済み領域へ変換する作業である[30][31]。Copy Fail のような問題も、人間の目視だけで全経路を事前に見つけるのは難しい。したがって、機械的探索、異常系テスト、境界条件の自動探索が重要になる。
第三に、未知領域が残る前提で、破局を局所化することである。完全に脆弱性をゼロにできないなら、脆弱性が見つかったときに被害が広がらない構造を作る必要がある。権限分離、sandbox、seccomp、namespace、LSM、コンテナ分離、VM 分離、最小権限、読み取り専用 mount、immutable infrastructure、迅速な rollback は、このためにある。つまり、未知の脆弱性が存在しても、直ちに root 権限やホスト全体の侵害へ接続しないようにする。
| 方針 | 数理モデル上の意味 | 実務上の例 |
|---|---|---|
| 攻撃面削減 | \(|\Omega|\) を小さくする。 | 不要 module の無効化、不要サービス停止、AF_ALG 制限、特権コンテナ禁止。 |
| 重点検査 | 危険な部分から \(|\Omega_{\mathrm{observed}}|\) を増やす。 | setuid、kernel API、zero-copy、in-place 処理、認証処理を重点的に見る。 |
| fuzzing | 人間が思いつかない状態を機械的に探索する。 | 異常入力、システムコールの組み合わせ、境界条件、race 条件を試す。 |
| 形式検証 | 特定の性質が破れないことを数学的に検査する。 | メモリ安全性、権限不変条件、型による所有権管理。 |
| 監視 | 未知領域で起きた異常を観測済みへ変換する。 | ログ監視、EDR、auditd、異常な権限昇格やプロセス生成の検知。 |
| 迅速な patching | 観測された脆弱性を残存リスクから除去する。 | security advisory 追跡、kernel 更新、再起動計画、rollback 準備。 |
この観点から見ると、計算機安全性は一度達成すれば終わる状態ではない。むしろ、複雑化するシステムと、それを観測・修正する人間やツールとの継続的な競争である。新しい機能が入れば \(|\Omega|\) が増える。新しいテストや fuzzing が入れば \(|\Omega_{\mathrm{observed}}|\) が増える。新しい exploit が出れば、それまで未知だった領域が危険として観測される。新しい patch が入れば、その危険領域が縮小される。この繰り返しである。
Copy Fail は、この構造をよく示している。2017 年の in-place optimization は、性能上は合理的な変更だった。しかし、その変更が splice()、ページキャッシュ、AF_ALG、setuid root バイナリと組み合わさったとき、未知の危険領域 \(U_t\) が生じた。その後、研究者の解析や PoC によって、その領域は観測済みとなり、CVE として識別され、ディストリビューションの security tracker や advisory に反映され、kernel 修正や algif_aead 無効化という対策へ接続された。これは、未知領域が観測され、更新される過程である。
したがって、「人間が計算機の安全性を担保するなら、それは永遠に終わらない戦いなのではないか」という問いに対する答えは、かなりの程度で「そうである」と言える。ただし、それは絶望を意味しない。完全点検が不可能であることと、安全性を高められないことは違う。人間は、全探索ではなく、構造化された防御、観測の自動化、攻撃面削減、形式的制約、迅速な更新、運用設計によって、未知領域の影響を小さくし続けることができる。
この章の結論は、次のように整理できる。
| 観点 | 整理 |
|---|---|
| 完全点検 | 現実には困難である。理由は、状態空間 \(\Omega\) が組み合わせ爆発するためである。 |
| 現実的な安全性 | \(\Omega\) を小さくし、\(\Omega_{\mathrm{observed}}\) を増やし、\(U_t\) を監視し、発見後に迅速に縮小する。さらに、未知の脆弱性があっても破局しないように局所化する。 |
| Copy Fail の教訓 | 単体機能の正しさだけでは足りない。機能同士の結合で生まれる未知領域を前提に、防御を設計する必要がある。 |
この意味で、計算機安全性は、有限な人間が無限に近い組み合わせ空間へ向き合う営みである。人間は全体を完全には見られない。しかし、どこが危険になりやすいかを抽象化し、危険な結合を減らし、観測技術を発達させ、発見後に素早く更新することはできる。安全性とは、完全無欠な静止状態ではなく、未知領域を前提にした継続的な更新過程なのである。
20. 構造振動モデルとの接続
前章では、計算機安全性を「完全点検できない組み合わせ空間」として整理した。そこでは、システム全体の状態空間 \(\Omega\) が増え、人間や検査ツールが観測できる範囲 \(\Omega_{\mathrm{observed}}\) がそれに追いつかないため、未知の脆弱性空間 \(U_t\) が残り続けると述べた。しかし、この説明だけではまだ静的である。実際の計算機システムは、ある時点で完成して止まるものではない。性能改善、機能追加、互換性維持、脆弱性発見、パッチ適用、再起動、暫定緩和、運用変更によって、常に構造が更新される。
この「構造が更新され続ける」という点で、CVE-2026-31431 は構造振動モデルと接続できる。構造振動モデルは、対象を固定された完成物ではなく、複雑化、観測、歪み、削減、再安定化の過程として見る枠組みである。既存の論考では、哲学的概念を構造の揺れとして数理モデル化し、複雑化圧、削減、歪み、観測、更新を用いて整理している[32]。また、抽象哲学を運用可能なモデルへ変換する論考では、哲学を構造更新を扱う設計言語として捉え、観測、構造抽出、損失、バイアスを実運用へ接続している[33]。
ここで構造振動モデルを持ち出す理由は、CVE-2026-31431 を単なる「バグ発見と修正」の出来事としてではなく、計算機システムがどのように複雑化し、どこで歪みを蓄積し、どのような観測によって問題が露出し、どのような修正で再安定化されるかを見るためである。つまり、構造振動モデルは本稿の技術説明を置き換えるものではない。技術説明で明らかにした AF_ALG、algif_aead、splice()、ページキャッシュ、setuid root バイナリの相互作用を、より大きな「構造更新の過程」として捉え直すための補助モデルである。
まず、計算機システムの現在の構造状態を \(S_t\) と置く。これは、時点 \(t\) における Linux カーネル、カーネルモジュール、システムコール、ファイルシステム、権限モデル、運用設定、コンテナ設定、更新状態をまとめた構造である。CVE-2026-31431 の場合、\(S_t\) には AF_ALG、algif_aead、authencesn、splice()、ページキャッシュ、setuid root バイナリ、in-place optimization が含まれる。
次に、構造を複雑にする圧力を \(G_t\) と置く。これは、性能改善、ゼロコピー、in-place 処理、互換性維持、機能追加、ハードウェア支援、コンテナ対応、クラウド運用などである。これらは悪ではない。むしろ、現代の OS には必要な要求である。しかし、要求が増えるほど構造は複雑になり、部品同士の相互作用も増える。
一方、構造を整理し、危険な組み合わせを減らす力を \(R_t\) と置く。これは、パッチ、設計単純化、不要機能の無効化、seccomp、権限分離、fuzzing、形式検証、回帰テスト、運用標準化などである。さらに、構造内に蓄積する不整合、見落とし、過小評価、互換性優先、再起動忌避、不要機能放置を \(D_t\) と置く。ここでいう \(D_t\) は、前章の防御有効性 \(D_t\) とは意味が異なるため、混同を避けるなら歪み項を \(H_t\) と表すほうがよい。
S_{t+1}=S_t+G_t-R_t-H_t
\]
この式は、時点 \(t\) の構造 \(S_t\) が、複雑化圧 \(G_t\) によって拡張され、整理・削減 \(R_t\) によって抑制され、歪み \(H_t\) によって不安定化しながら、次の時点の構造 \(S_{t+1}\) へ更新されることを表す。ここで重要なのは、構造が一方向に良くなるわけでも、一方向に悪くなるわけでもないという点である。性能最適化は利点を生むが、同時に新しい相互作用を生む。パッチは問題を修正するが、新しい互換性問題を生むこともある。安全性は静的状態ではなく、更新過程の中で保たれる。
| 項 | 一般的意味 | CVE-2026-31431 での意味 | 注意点 |
|---|---|---|---|
| \(S_t\) | 現在の構造状態。 | Linux カーネル、AF_ALG、algif_aead、authencesn、splice()、ページキャッシュ、setuid root バイナリを含む実装状態。 | 単一のコード片ではなく、複数部品の結合状態として見る。 |
| \(G_t\) | 複雑化圧。 | in-place optimization、ゼロコピー、性能要求、互換性要求、カーネル暗号 API の userspace interface。 | 悪ではないが、相互作用空間を増やす。 |
| \(R_t\) | 整理・削減。 | 修正済み kernel、algif_aead 無効化、AF_ALG 制限、seccomp、不要機能の抑制。 | 構造を単純化し、危険な結合経路を減らす。 |
| \(H_t\) | 歪み。 | ローカル脆弱性の過小評価、再起動忌避、互換性優先、不要 module の放置、意味境界の見落とし。 | 観測されるまで潜在し、発見後に CVE として可視化される。 |
CVE-2026-31431 をこの式で読むと、2017 年の in-place optimization は \(G_t\) に含まれる。これは性能上の合理性を持つ変更だった。しかし、その変更が splice()、ページキャッシュ、AF_ALG、setuid root バイナリと結合したとき、意味境界の歪み \(H_t\) が生じた。具体的には、ファイルシステム文脈では読み取り専用である page が、暗号処理文脈では出力先として扱われるという歪みである。この歪みは直ちに社会的なリスクとして見えるわけではない。研究者の解析、PoC、CVE 登録、ディストリビューションの advisory によって初めて観測される。
この観測入力を \(O_t\) として追加すると、構造更新はさらに明確になる。\(O_t\) は、fuzzing、AI 支援スキャン、研究者の解析、PoC、CVE、運用者の検証、ログ、クラッシュ報告、セキュリティ advisory などである。観測がなければ、歪みは潜在したまま残る。観測が入ることで、未知だった構造的不整合が可視化され、修正、緩和、運用判断へ接続される。
S_{t+1}=S_t+G_t+O_t-R_t-H_t
\]
この式では、観測入力 \(O_t\) を構造更新の要素として明示している。観測は、単に外から眺める行為ではない。セキュリティにおいて観測は、構造を変える。PoC が公開されれば、リスク評価は変わる。CVE が採番されれば、ディストリビューションの tracker や advisory に載る。advisory が出れば、運用者は kernel 更新、再起動、暫定緩和を行う。つまり、観測は \(S_t\) を次の \(S_{t+1}\) へ更新する入力である。
| 観測入力 \(O_t\) | 構造更新への影響 | CVE-2026-31431 での例 |
|---|---|---|
| 研究者の解析 | 潜在していた不整合を説明可能な形にする。 | AF_ALG、algif_aead、splice()、page cache の結合構造が明らかになる。 |
| PoC | 理論上の可能性を、再現可能な危険として示す。 | controlled 4-byte write が root 権限取得へ接続し得ることを示す。 |
| CVE | 問題を共通識別子で追跡可能にする。 | CVE-2026-31431 として、ディストリビューションや運用者が同じ問題を参照できる。 |
| ディストリビューション advisory | 利用者が自分の環境で更新判断できるようにする。 | Debian Security Tracker、Ubuntu CVE ページ、USN などで修正状況が示される。 |
| 運用者の検証 | 抽象的な脆弱性を、自分の環境のリスクへ変換する。 | 起動中 kernel、algif_aead ロード状態、kmod 緩和、再起動状態を確認する。 |
さらに、構造の複雑さ \(C_t\) を考えると、構造振動モデルとしての性質がより明確になる。複雑さは単調に増えるだけではない。機能追加や最適化で増え、整理や削減で下がり、観測によって不安定さが可視化され、パッチによって再安定化する。この揺れを、単純化して次のように表せる。構造振動モデルを数理モデルとして定義する論考でも、構造は静的な完成物ではなく、観測と更新によって安定性を変える過程として扱われる[34]。
C_{t+1}-2C_t+C_{t-1}=aG_t-bR_t-c(C_t-C^*)+\epsilon_t
\]
この式は、構造の複雑さ \(C_t\) が時間的に振動することを表す。左辺の \(C_{t+1}-2C_t+C_{t-1}\) は、複雑さの変化の加速度のようなものを表す。右辺の \(aG_t\) は複雑化圧が複雑さを押し上げる効果である。\(bR_t\) は整理・削減が複雑さを下げる効果である。\(c(C_t-C^*)\) は、望ましい複雑さ \(C^*\) から外れたときに戻そうとする安定化項である。最後の \(\epsilon_t\) は、予期しない外乱、未知の相互作用、運用上の例外、まだ観測されていない脆弱性を表す。
| 項 | 意味 | Copy Fail での読み方 |
|---|---|---|
| \(C_t\) | 時点 \(t\) の構造複雑性。 | カーネル機能、最適化、モジュール、権限境界、運用設定が作る複雑さ。 |
| \(aG_t\) | 複雑化圧による増加。 | in-place optimization、ゼロコピー、AF_ALG、性能要求が構造を複雑にする。 |
| \(-bR_t\) | 整理・削減による低下。 | パッチ、algif_aead 無効化、AF_ALG 制限、seccomp、不要 module の抑制。 |
| \(-c(C_t-C^*)\) | 望ましい複雑さへ戻す安定化項。 | 過剰な複雑化を設計・運用・ポリシーで抑える力。 |
| \(\epsilon_t\) | 未知の外乱。 | 未発見の脆弱性、想定外の機能結合、PoC による突然の可視化。 |
このモデルで見ると、Copy Fail は突然無から発生したのではない。構造内には、性能最適化、ゼロコピー、in-place 処理、ページキャッシュ、setuid root バイナリという要素がすでに存在していた。その組み合わせの中に、意味境界の不整合が潜在していた。観測入力 \(O_t\) によってその不整合が可視化され、CVE として識別され、修正済み kernel、algif_aead 無効化、seccomp 制限、運用確認という \(R_t\) が投入された。これにより、構造は再安定化へ向かう。
したがって、構造振動モデルとの接続は、CVE-2026-31431 を哲学的に飾るためではない。むしろ、技術的脆弱性を「複雑化した構造が、観測によって歪みを露出し、修正によって再安定化する過程」として読むためである。セキュリティは、完成済みの安全状態ではない。複雑化する構造、未知の歪み、観測、修正、再起動、緩和、再設計が繰り返される更新過程である。この意味で、Copy Fail は、計算機安全性が構造振動として現れる具体例である。
21. 哲学者との接続一覧
ここまで、CVE-2026-31431 を技術的に見てきた。AF_ALG、algif_aead、splice()、ページキャッシュ、setuid root バイナリという複数の仕組みが結合し、本来書けないはずの領域へ書き込みが到達する構造を確認した。さらに、数理モデルでは、許可モデルと実行モデルの不一致、意味保存の破れ、リスク関数、人間の点検能力の限界として整理した。ここから哲学へ接続する理由は、話題を抽象的に飾るためではない。問題の本質が、単なるプログラムミスではなく、原因、本質、認識限界、分散知識、責任、技術と人間の関係にまで広がるからである。
哲学とは、ここでは「古い思想家の名前を並べること」ではない。複雑な問題に対して、何を原因と見なすのか、どこまで人間は知り得るのか、矛盾はどのように構造変化を生むのか、技術を扱う人間にはどのような責任があるのかを考えるための道具である。CVE-2026-31431 は Linux カーネルの具体的な脆弱性である。しかし、その奥には、計算機システムが複雑化し、人間の理解を超え、観測と修正によって安全性を保つしかないという、より一般的な問題がある。
以下では、まず、哲学者がどのような問いを扱ったのかを簡潔に示し、そのうえで CVE-2026-31431 とどう接続できるかを整理する。
| 哲学者・思想家 | どのような人物か | 思想の核 | CVE-2026-31431 への接続 | 数理モデル上の対応 |
|---|---|---|---|---|
| アリストテレス | 古代ギリシアの哲学者で、物事を理解するには原因、本質、目的、成立条件を見なければならないと考えた人物である。 | 原因、本質、成立条件。 | Copy Fail の本質は、4 byte という量ではない。その 4 byte が、ページキャッシュ、setuid root バイナリ、root 権限実行へ接続する成立条件にある。 | \(\operatorname{Ess}(S_t,O_t)\) |
| デカルト | 近代哲学の出発点に位置づけられる哲学者で、複雑な問題を小さく分解し、順序立てて考える方法を重視した人物である。 | 分解、順序、再構成。 | AF_ALG、splice()、ページキャッシュ、setuid を個別に分解して理解し、その後で再統合時の相互作用を見る必要がある。 | \(D_{\mathrm{cmp}}(S_t)\) |
| ヘーゲル | ドイツ観念論の哲学者で、矛盾や対立を単なる失敗ではなく、構造が変化する契機として捉えた人物である。 | 矛盾、運動、止揚。 | 読み取り専用 page と暗号処理の出力先が同じ page に重なる矛盾が露出し、out-of-place 修正や緩和策へ向かう。 | \(\operatorname{Auf}(S_t,O_t)\) |
| ダーウィン | 生物進化論を体系化した自然科学者で、環境による選択圧と適応の考え方を示した人物である。 | 選択圧、淘汰、適応。 | 性能最適化や複雑な機能は、攻撃者、研究者、fuzzing、運用現場という選択圧にさらされ、危険な構造は修正・淘汰される。 | \(\operatorname{Select}(S_t,O_t)\) |
| ハイエク | 社会全体の知識は一箇所に集約できず、分散した個人や組織の知識として存在すると考えた経済学者・思想家である。 | 分散知識。 | Linux カーネル全体の安全性は、中央設計者 1 人が完全に把握できるものではない。開発者、研究者、ディストリビューション、運用者の分散観測が必要になる。 | \(\sum_i O_t^{(i)}\) |
| サイモン | 人間は完全合理的にすべてを探索できず、限られた情報と能力の中で判断するという限定合理性を示した研究者である。 | 限定合理性。 | 人間はカーネルの全組み合わせを探索できないため、完全保証ではなく、重点検査、fuzzing、patching、運用緩和で現実的に対応するしかない。 | \(|\Omega_{\mathrm{human}}|\ll|\Omega|\) |
| カーネマン | 人間の判断が直感やバイアスに左右されることを実証的に示した心理学者・行動経済学者である。 | ヒューリスティックとバイアス。 | 「4 byte だから軽い」「ローカル脆弱性だから後回し」「ディスクは改ざんされていないから安全」という直感が、リスク判断を歪める。 | \(\operatorname{RiskBias}_t\) |
| ポパー / パース | ポパーは反証可能性を重視した科学哲学者であり、パースは探究と可謬主義を重視したプラグマティズムの思想家である。 | 可謬主義、反証、探究。 | 安全性は一度証明された永遠の状態ではない。まだ反証されていない仮説であり、新しい PoC や観測によって更新される。 | \(F_t\) |
| ハイデガー | 存在や技術のあり方を問い、技術が世界をどのように見せ、扱わせるかを考えた哲学者である。 | 技術による世界の開示。 | CPU にとっては単なるアドレスへの byte 書き込みでも、人間の運用世界では root 権限で実行される命令列の改変である。同じ byte 列が文脈により異なる意味を持つ。 | \(\mu_k(x)\) |
| ヨナス | 現代技術が未来世代や社会全体へ影響を及ぼすため、人間には新しい責任倫理が必要だと考えた哲学者である。 | 技術時代の責任。 | カーネルや基盤ソフトウェアの安全性は、開発者だけの局所問題ではない。クラウド、サーバー、研究基盤、利用者全体へ波及する社会的責任である。 | \(\mathbb{E}[L_{t+n}]\) |
アリストテレスは、物事を理解するには「それが何であるか」だけでなく、「何によってそうなるのか」「どの条件で成立するのか」を見る必要があると考えた。これを Copy Fail に当てはめると、単に「4 byte 書ける」という事実だけでは本質に届かない。問題は、その 4 byte がどの条件のもとでページキャッシュへ届き、setuid root バイナリへ接続し、root 権限で実行されるかである。つまり、本質は量ではなく成立条件にある[35]。
デカルト的方法は、複雑な問題を小さな要素に分け、単純なものから順序立てて考え、最後に全体を再構成する姿勢である。CVE-2026-31431 を理解するには、この方法が有効である。AF_ALG とは何か、algif_aead とは何か、splice() とは何か、ページキャッシュとは何か、setuid root バイナリとは何かを分解して理解し、その後で、それらが結合したときに何が起きるかを見なければならない。個別部品だけを見ても、結合リスクは見えない[36]。
ヘーゲルの観点から見ると、Copy Fail は矛盾の露出として理解できる。同じ page が、ファイルシステム文脈では読み取り専用であり、暗号処理文脈では出力先として扱われる。この 2 つの意味は同時に成立してはならない。ところが、脆弱な経路ではそれが同時に成立する。この矛盾が PoC や CVE によって観測可能になり、修正済み kernel、algif_aead 無効化、運用緩和という構造更新へ進む。矛盾は単なる失敗ではなく、構造が変わる契機になる[37]。
ダーウィン的に見れば、計算機システムも選択圧にさらされている。性能最適化、ゼロコピー、in-place 処理は、速度や効率という圧力から生まれる。一方で、攻撃者、研究者、fuzzing、運用障害、CVE 公開は、安全性という別の選択圧をかける。危険な実装は、攻撃圧と検証圧によって露出し、修正され、場合によっては無効化される。この意味で、ソフトウェア構造も環境圧にさらされながら変化する[38]。
ハイエクの分散知識の観点では、Linux カーネルの安全性は中央集権的に完全把握できるものではない。カーネル開発者、ディストリビューションメンテナー、セキュリティ研究者、クラウド運用者、一般ユーザー、CI/CD 管理者が、それぞれ異なる断片的知識を持つ。脆弱性情報、PoC、tracker、advisory、パッチ、運用確認は、この分散した知識を接続する仕組みである。安全性は、単一の天才が全体を理解することで成立するのではなく、分散観測と更新のネットワークによって維持される[39]。
サイモンの限定合理性は、人間が全組み合わせを完全に探索できないという前章の数理モデルと直結する。人間は \(|\Omega|\) 全体を見られない。見られるのは \(|\Omega_{\mathrm{human}}|\) という限られた範囲である。したがって、現実の安全性は、完全な最適解ではなく、満足化、優先順位付け、重点検査、fuzzing、形式検証、迅速な patching によって成立する。これは妥協ではなく、有限な認知能力を持つ人間が複雑な計算機を扱うための現実的な方法である[40]。
カーネマンの観点では、脆弱性評価には直感の罠がある。「4 byte しか書けないから小さい」「ローカル権限昇格だから緊急度は低い」「ディスク上のファイルは変わっていないから大丈夫」という判断は、人間のヒューリスティックに基づく。しかし、実際には 4 byte が root 権限で実行される命令列に届けば重大であり、ローカル権限昇格は Web RCE、CI/CD、コンテナ foothold と結合すれば重大であり、ページキャッシュ汚染はディスク改ざんとして残らないからこそ検知しにくい。直感は、リスクの構造を過小評価することがある[41]。
ポパーとパースの観点では、安全性は「完全に証明された永遠の真理」ではなく、継続的な反証と探究の過程である。ある時点で安全に見える実装も、まだ破られていないだけかもしれない。PoC は、理論上の安全仮説に対する反証として機能する。CVE は、その反証を社会的に共有する識別子である。パッチは、反証を受けた構造の更新である。したがって、安全性とは固定された状態ではなく、可謬性を前提に更新され続ける仮説である[42]。
ハイデガーの技術論に接続すると、Copy Fail は「同じ byte 列がどのように世界に現れるか」の問題でもある。CPU にとっては、あるアドレスに byte を書くだけである。しかし OS の世界では、それは読み取り専用ファイル由来のページキャッシュであり、setuid 実行の文脈では root 権限で実行される命令列であり、運用者の世界ではサーバー全体の安全性を揺るがす改変である。技術は単なる道具ではなく、対象を特定の意味で現れさせる枠組みである。Copy Fail は、その意味の層が破れた事例である[43]。
ヨナスの責任倫理から見ると、カーネル脆弱性は開発者や管理者だけの局所問題ではない。Linux カーネルは、クラウド、スマートフォン、サーバー、研究基盤、金融システム、個人端末、CI/CD、コンテナ基盤に広く使われる。基盤ソフトウェアの小さな意味境界の破れは、社会全体の情報基盤へ波及し得る。したがって、修正済み kernel の提供、advisory の公開、運用者の更新、再起動、暫定緩和は、単なる技術作業ではなく、技術時代における責任の実践である[44]。
以上を踏まえると、哲学との接続は、CVE-2026-31431 を大げさに語るための装飾ではない。むしろ、技術的事実をより深い層で整理するための補助線である。アリストテレスは成立条件を問う。デカルトは分解と再構成を促す。ヘーゲルは矛盾から構造変化を見る。ダーウィンは選択圧としての検証と攻撃を考えさせる。ハイエクは分散知識の必要性を示す。サイモンは完全探索の不可能性を示す。カーネマンは直感的過小評価を警告する。ポパーとパースは安全性を可謬的な探究として捉える。ハイデガーは byte 列に重なる意味の層を問う。ヨナスは基盤技術を扱う責任を問う。Copy Fail は、これらの問いが現代の Linux カーネル上で具体化した事例なのである。
22. 哲学的に見た Copy Fail の核心
前章では、複数の哲学者や思想家の観点を一覧として整理した。ただし、そのまま名前を並べるだけでは、CVE-2026-31431 の理解にはつながらない。本章では、そこから本稿に必要な論点だけを取り出す。目的は、Copy Fail を哲学用語で飾ることではない。技術的に確認してきた「小さな書き込みが、文脈をまたいで高権限実行へ接続する」という構造を、原因、本質、認識限界、責任の問題として再整理することである。
まず、アリストテレス的な見方から入る。アリストテレスは、物事を理解するには、単に材料を見るだけでは足りず、それがどのような形を取り、何によって作用し、何の結果へ向かうのかを見る必要があると考えた。この考えをそのまま専門用語として持ち込む必要はない。Copy Fail に必要な形に言い換えれば、「4 byte という量だけを見ても本質は分からない。どこに書かれ、何として解釈され、どの権限で実行されるかを見なければならない」ということである。
4 byte は単なる素材である。自分の一時ファイルに 4 byte 書けるだけなら、重大な問題ではない。しかし、ページキャッシュ、scatterlist、AF_ALG、algif_aead、setuid root バイナリという構造の中に入ると、同じ 4 byte は別の意味を持つ。CPU に読まれる命令列になり、root 権限で実行される経路へ接続し、システム全体の制御に届く可能性を持つ。つまり、問題の本質は 4 byte という大きさではなく、その 4 byte が成立条件の中でどの位置を占めるかにある。
次に、デカルト的な見方である。デカルト的方法の要点は、複雑な問題を小さく分解し、単純なものから順序立てて考え、最後に全体を再構成することである。CVE-2026-31431 でも、この方法は有効である。AF_ALG とは何か、algif_aead とは何か、splice() とは何か、ページキャッシュとは何か、setuid root バイナリとは何かを分けて理解しなければ、全体像は見えない。
しかし、この脆弱性が示すのは、分解だけでは足りないという点でもある。AF_ALG は、それ自体としてはカーネル暗号 API への入口である。splice() は、それ自体としてはゼロコピー I/O のための正常なシステムコールである。ページキャッシュは、ファイル I/O を高速化するための正常な仕組みである。setuid は、限定された高権限処理を実行するための正当な権限委譲機構である。個別に見れば、どれも必要な仕組みである。しかし、これらを再構成したとき、読み取り専用 page が暗号処理の出力先として扱われる経路が生まれる。したがって、現代のセキュリティレビューは、分解だけでなく、再結合時の相互作用を検査しなければならない。
| 見方 | 単体で見た意味 | 結合後に生じる問題 |
|---|---|---|
| AF_ALG | ユーザー空間からカーネル暗号 API を使う入口。 | splice() で渡された page 参照を含む暗号処理経路になる。 |
| splice() | ユーザー空間コピーを避ける正常なシステムコール。 | 読み取り専用ファイル由来の page 参照を別処理へ渡す。 |
| ページキャッシュ | ファイル内容をメモリ上に保持する高速化機構。 | ディスク上のファイルを変えずに、実行時内容だけが汚染される。 |
| setuid | 必要な特権処理を限定的に実行する正当な仕組み。 | 汚染された命令列が root 権限で実行される増幅器になる。 |
ヘーゲル的な見方では、矛盾が構造変化の契機になる。ここでも、専門用語を前提にする必要はない。Copy Fail では、同じ page に 2 つの両立しない意味が重なった。ファイルシステム文脈では、その page は読み取り専用である。暗号処理文脈では、その page が出力先として扱われる。この 2 つは同時に成立してはならない。にもかかわらず、脆弱な経路では同時に成立した。これが矛盾である。
CVE は、この矛盾が社会的に可視化された名前である。PoC は、その矛盾が実際に成立することを示す観測である。修正済み kernel は、その矛盾を解消する構造更新である。algif_aead の無効化は、修正済み kernel へ移るまで矛盾した経路を使わせない暫定緩和である。このように見ると、脆弱性対応とは、単に悪いコードを直す作業ではなく、構造内の矛盾を観測し、名前を与え、対策として再構成する過程である。
ダーウィン的な見方は、さらに別の層を示す。ここでは生物進化そのものを論じるのではなく、選択圧という考え方を借りる。OS の実装には、性能圧、互換性圧、保守性圧、安全性圧がかかっている。in-place optimization や splice() は、性能圧のもとでは合理的である。コピーを減らし、処理を速くし、効率を上げる。しかし、攻撃者、研究者、fuzzing、PoC、CVE、ディストリビューション運用は、安全性圧をかける。性能上は有利でも、意味境界を壊す実装は、この安全性圧によって発見され、修正され、淘汰される。
ハイエク的な見方では、知識の分散が問題になる。Linux カーネルの全体を、単一の人間が完全に理解することはできない。AF_ALG に詳しい開発者、splice() に詳しい開発者、ページキャッシュに詳しい開発者、ディストリビューションのメンテナー、セキュリティ研究者、クラウド運用者、一般の管理者は、それぞれ異なる断片的知識を持つ。CVE、advisory、tracker、PoC、パッチ、運用記事は、それらの分散知識を接続する仕組みである。安全性は、中央の完全知ではなく、分散した観測と更新の連鎖によって支えられる。
サイモン的な見方では、人間の限定合理性が中心になる。人間は、すべての機能、すべての状態、すべての組み合わせを完全探索できない。前章の数理モデルで述べたように、\(|\Omega_{\mathrm{human}}|\ll|\Omega|\) が現実である。したがって、現実の安全性は、完全保証ではなく、優先順位付け、重点検査、fuzzing、形式検証、攻撃面削減、迅速な patching によって成り立つ。これは理想からの敗北ではない。有限の認知能力を持つ人間が巨大な計算機システムを扱うための合理的な方法である。
| 哲学的観点 | Copy Fail で見える問題 | 実務上の示唆 |
|---|---|---|
| 原因と成立条件 | 4 byte そのものではなく、root 実行へ接続する条件が本質である。 | 単発の現象ではなく、成立条件の連鎖を見る。 |
| 分解と再構成 | 単体では正常な機能が、結合時に危険になる。 | 部品レビューだけでなく、相互作用レビューを行う。 |
| 矛盾と構造更新 | 読み取り専用 page と書き込み出力先が同じ page に重なる。 | 矛盾を観測し、CVE、パッチ、緩和策として再構成する。 |
| 選択圧 | 性能最適化が安全性圧にさらされる。 | 性能改善には、攻撃面の増加評価を併せる。 |
| 分散知識 | 単一主体が全体を把握できない。 | 研究者、メンテナー、運用者、ユーザーの観測を接続する。 |
| 限定合理性 | 全組み合わせ探索は不可能である。 | 完全保証ではなく、重点検査、fuzzing、patching、攻撃面削減を組み合わせる。 |
カーネマン的な見方では、人間のリスク判断の歪みが問題になる。人間は「小さい」「ローカル」「一時的」「ディスクに残らない」といった表面特徴に引っ張られやすい。4 byte だから小さい、ローカル脆弱性だから緊急ではない、ディスク上のファイルが変わらないから大丈夫、と判断したくなる。しかし、4 byte が root 権限で実行される命令列に届くなら重大である。ローカル権限昇格は、Web RCE、CI/CD、コンテナ foothold と結合すれば重大である。ディスクに残らないページキャッシュ汚染は、むしろ検知しにくい。直感的な軽視が、実際のリスクを見誤らせる。
ポパーやパースの観点では、安全性は永遠に証明された状態ではなく、反証と探究の過程である。ある実装が安全に見えるのは、まだ破られていないという意味でしかない場合がある。PoC は、暗黙の安全仮説に対する反証である。CVE は、その反証を共有可能な形にする識別子である。パッチは、反証を受けて仮説を更新する作業である。したがって、安全性とは、完全な確定ではなく、可謬性を前提にした継続的更新である。
ハイデガー的な見方では、同じ byte 列が文脈によって異なる意味を持つ点が重要になる。CPU にとっては、あるアドレスに byte を書くだけである。しかし OS にとっては、その byte は読み取り専用ファイル由来のページキャッシュかもしれない。setuid 実行にとっては、root 権限で実行される命令列かもしれない。運用者にとっては、サーバー全体の制御を奪われる入口かもしれない。技術は、対象を単なる物理量としてではなく、特定の意味を持つものとして現れさせる。Copy Fail は、その意味の層がずれた事例である。
ヨナス的な見方では、基盤ソフトウェアを扱う責任が問題になる。Linux カーネルは個人の趣味的プログラムではなく、サーバー、クラウド、研究基盤、金融、通信、個人端末、CI/CD、コンテナ基盤に広く使われる。したがって、小さな意味境界の破れは、広い社会的影響へ波及し得る。開発者は修正を出し、ディストリビューションは advisory を出し、運用者は更新し、再起動し、暫定緩和を判断する。この連鎖全体が、技術時代の責任である。
以上をまとめると、哲学的に見た Copy Fail の核心は、次の 4 点に圧縮できる。第一に、本質は量ではなく成立条件にある。第二に、単体で正しい部品も、結合時に危険になり得る。第三に、人間は全体を完全には点検できず、分散観測と継続更新に頼らざるを得ない。第四に、基盤ソフトウェアの安全性は、単なる実装品質ではなく、社会的責任の問題でもある。
したがって、Copy Fail は単なる Linux カーネルの一脆弱性ではない。低レイヤーの byte 更新が、文脈をまたいで意味を変え、権限構造を変え、人間の認識限界と運用責任を露出させる事例である。哲学はこの問題を抽象化するための飾りではなく、技術的事実の背後にある構造を見通すための道具として機能する。
23. 抜本的な対策の方向
ここまでの議論をそのまま受けるなら、抜本的な対策は「CVE-2026-31431 だけを直すこと」では終わらない。もちろん、今回の直接対策は修正済みカーネルへ更新し、そのカーネルで再起動することである。カーネル更新がすぐにできない場合は、algif_aead の無効化や AF_ALG socket の制限によって、問題経路への到達を一時的に抑える。しかし、それは個別事象への対応である。前章までに見た本質は、より広い。問題は、同じ page が文脈をまたいで異なる意味を持ち、読み取り専用、書き込み可能、入力、出力、実行可能、root 実行といった意味属性が保存されなかったことにある。また、人間は巨大な組み合わせ空間を完全点検できない。したがって、抜本的な方向は、意味境界を壊しにくくし、危険な組み合わせを減らし、破られても局所化し、発見後にすぐ更新できる構造を作ることになる。
第一の方向は、意味属性を機械的に追跡できる形にすることである。Copy Fail では、ファイルシステム文脈では読み取り専用だった page が、暗号処理文脈では出力先として扱われた。これは、読み取り専用、書き込み可能、ユーザー由来、カーネル管理、ページキャッシュ由来、in-place 可能、実行可能といった意味が、文脈変換の中で十分に保存されなかったということである。コメントや開発者の暗黙知に頼るだけでは、この種の境界は破れやすい。理想的には、型、所有権、権限、静的解析、形式仕様、検証可能な API 契約によって、入力として受け取った page を出力先にしてよいのか、読み取り専用由来の page を書き込み対象にしていないかを、機械的に追えるようにする必要がある。
第二の方向は、ゼロコピーと in-place 最適化を、境界をまたぐ処理では慎重に扱うことである。ゼロコピーは性能上有効であり、in-place 処理もメモリ効率の点で合理的である。しかし、コピーしないということは、物理的な分離を減らすということでもある。別のバッファーへ明示的にコピーすれば、入力と出力の境界は比較的分かりやすい。一方、同じ page 参照を複数の処理経路で共有すると、その page がどの文脈で何を意味しているのかを正しく保存しなければならない。したがって、ゼロコピーや in-place 最適化を使う場合は、性能上の利益だけでなく、意味属性の保存、所有権、書き込み可否、参照元の由来を検査する設計が必要になる。
第三の方向は、不要な攻撃面を減らすことである。人間が全組み合わせを点検できないなら、そもそも組み合わせ空間 \(\Omega\) を小さくする必要がある。使っていない kernel module、不要な AF_ALG 経路、過剰な container capability、不要な setuid バイナリ、広すぎる CI 権限、不要な共有マウント、不要なホスト機能への到達経路は、残っているだけで将来の結合リスクになる[22][23][24][45][46]。今回の algif_aead 無効化も、この考え方の一部である。使っていない経路を閉じれば、未知の脆弱性が存在しても、攻撃者がそこへ到達する可能性を下げられる。
第四の方向は、破られても局所化することである。完全な無欠性を前提にした設計は、現実の計算機システムでは脆い。未知の脆弱性は残る。設定ミスも起きる。外部由来コードが実行されることもある。したがって、ある層が破られても、直ちに root 権限、ホスト全体、他ユーザー、別システムへ広がらない構造が必要である。seccomp、AppArmor、SELinux、namespaces、capabilities、IOMMU、read-only mount、noexec、nodev、nosuid、VM 隔離、CI/CD runner 分離は、この考え方の実装である。これらは脆弱性を消す仕組みではなく、脆弱性が悪用されたときの到達範囲を狭める仕組みである。
第五の方向は、更新しやすい運用を作ることである。カーネル脆弱性では、修正済みパッケージを入れるだけでは不十分であり、修正済みカーネルで再起動しなければならない。再起動できないサーバー、更新手順が曖昧なサーバー、検証環境がないサーバー、影響確認の責任者が不明なサーバーでは、脆弱性対応が遅れる。したがって、ローリング更新、冗長構成、メンテナンス時間、検証環境、再起動計画、ロールバック手順を平時から用意しておくことが、セキュリティ対策そのものになる。更新容易性は、運用上の便利機能ではなく、防御力である。
第六の方向は、観測可能性を高めることである。人間は全探索できないため、未知の異常を早く観測済み領域へ移す必要がある。audit、eBPF 監視、ログ、EDR、systemd の failed unit 監視、設定差分管理、パッケージ更新状況の可視化、起動中 kernel とインストール済み kernel の差分確認は、このためにある。ただし、Copy Fail のようなページキャッシュ汚染は、ディスク上のファイル改ざんとして残りにくい。したがって、監視だけで完全に防げるわけではない。観測可能性は、修正、緩和、攻撃面削減、局所化と組み合わせて初めて意味を持つ。
| 方向 | 前章までの問題との接続 | 目的 | 実装例 |
|---|---|---|---|
| 意味属性の型化 | 読み取り専用 page が書き込み可能な出力先として再解釈された問題への対策である。 | 読み書き、所有権、実行可能性、入力、出力を機械的に追う。 | Rust、静的解析、形式仕様、所有権モデル、API 契約の明確化。 |
| 複雑性の削減 | ゼロコピーや in-place 処理が意味境界を曖昧にした問題への対策である。 | 危険な最適化を必要範囲に閉じ、境界をまたぐ処理を単純化する。 | in-place 回避、out-of-place 化、不要な参照共有の削除、境界条件テスト。 |
| 攻撃面削減 | 人間が全組み合わせを点検できないという限界への対策である。 | 使わない経路を閉じ、攻撃者が到達できる状態空間を小さくする。 | モジュール無効化、AF_ALG 制限、seccomp、capability 削減、不要サービス停止。 |
| 局所化 | ローカル権限昇格が root 侵害やホスト侵害へ広がる問題への対策である。 | 侵害されても全体へ広げない。 | VM 隔離、コンテナ hardening、runner 分離、namespaces、AppArmor、SELinux。 |
| 更新容易性 | 修正済み kernel が出ても再起動しなければ脆弱性が残る問題への対策である。 | 修正を速く、安全に、確実に適用する。 | 再起動計画、ローリング更新、冗長構成、検証環境、ロールバック手順。 |
| 観測可能性 | 未知の脆弱性空間 \(U_t\) が残り続ける問題への対策である。 | 異常、未更新、設定差分、攻撃兆候を早く見つける。 | audit、eBPF 監視、ログ、EDR、設定差分管理、脆弱性情報追跡。 |
このように見ると、抜本的な対策は単一の銀の弾丸ではない。Rust を使えばすべて解決するわけではない。seccomp を入れればすべて安全になるわけでもない。監視を入れれば侵害が消えるわけでもない。必要なのは、意味属性を壊しにくい実装、危険な組み合わせを減らす設計、到達経路を減らす運用、破られても広がらない隔離、すぐ更新できる体制、異常を観測する仕組みを重ねることである。
CVE-2026-31431 から得られる教訓は、計算機安全性を「バグがあるかないか」だけで見てはいけないということである。低レイヤーの byte 列には、人間と OS が与えた意味が重なっている。読み取り専用、書き込み可能、入力、出力、実行可能、root 権限、ユーザー由来、カーネル管理という意味が、文脈をまたいでも保存されなければならない。抜本的な対策とは、この意味保存を設計、実装、検証、運用の全層で支えることである。
24. 計算機安全性は終わらない戦いである
ここまでの議論から導かれる結論は、計算機安全性は終わらない戦いである、ということである。ただし、この言い方は単なる悲観ではない。終わらないということは、無意味であるということではない。生命が環境変化に応じて代謝と修復を続けるように、科学が仮説と反証と更新を続けるように、制度が例外と破綻と改正を通じて更新されるように、計算機安全性もまた、完成された静止状態ではなく、観測、検証、修正、緩和、再設計、運用の継続過程として成立する。
CVE-2026-31431 は、この事実を非常に低いレイヤーで露出させた。問題は、単に Linux カーネルにバグがあったというだけではない。性能最適化として導入された in-place 処理、コピー削減のための splice()、ユーザー空間からカーネル暗号 API を使う AF_ALG、ファイル I/O を高速化するページキャッシュ、限定的な権限委譲を行う setuid root バイナリという、それぞれ単体では正当な仕組みが結合したときに、読み取り専用であるはずの page が書き込み可能な出力先として扱われた。つまり、問題は「壊れた部品」だけではなく、「正当な部品同士の結合が、想定外の意味変換を生んだ」ことにある。
このため、計算機安全性を単純な不良品検査として捉えることはできない。不良品検査であれば、悪い部品を見つけて取り除けばよい。しかし、Copy Fail のような問題では、AF_ALG も splice() もページキャッシュも setuid も、それ自体を単純に悪として捨てることはできない。どれも現代の OS において必要な役割を持つ。危険は、部品そのものよりも、部品同士の境界、参照、所有権、書き込み可否、実行権限、文脈変換の中に現れる。したがって、安全性とは、単体部品の正しさだけでなく、相互作用の正しさを維持する営みである。
この相互作用の空間は、人間の点検能力を常に超える。カーネル、ドライバー、ファイルシステム、暗号 API、仮想化、コンテナ、CI/CD、クラウド、HPC、デスクトップ、組み込み機器は、それぞれ別々の条件、設定、権限、モジュール構成、更新状態を持つ。ある環境では algif_aead が module として存在し、別の環境では built-in かもしれない。ある環境では非信頼コードが CI runner 上で実行され、別の環境では単独ユーザーの検証 VM でしかない。ある環境では seccomp が効き、別の環境では広い system call surface が残っている。この差異が、脆弱性のリスクを環境ごとに変える。
したがって、安全性は「この CVE は危険か安全か」という二分法では足りない。より正確には、どの kernel が起動しているか、修正済み package が入っているか、再起動済みか、algif_aead が使えるか、AF_ALG に到達できるか、非特権ユーザーやコンテナ内プロセスがローカルコードを実行できるか、setuid root バイナリへ接続できるか、監視とログがあるか、侵害時に局所化できるか、という複数の条件を積み上げて判断する必要がある。安全性とは、単一の真偽値ではなく、構成、運用、観測、更新状態の関数である。
R_t=V_t\cdot X_t\cdot A_t\cdot I_t\cdot(1-D_t)
\]
このリスク関数で見れば、CVE-2026-31431 の危険性は「ローカル権限昇格だから低い」とは言えない。たしかに、攻撃者が対象システム上でコードを実行できなければ、直接の悪用は難しい。しかし現代の運用では、ローカル実行権限への入口は多い。Web アプリケーションの RCE、漏洩した SSH アカウント、CI/CD のビルドスクリプト、コンテナ内 foothold、共有計算基盤の一般ユーザー、開発環境の依存パッケージ実行などがある。入口 \(X_t\) が高い環境では、ローカル権限昇格は第 2 段階の攻撃として重大化する。
さらに、成功時影響 \(I_t\) は大きい。root 権限を取られれば、認証情報の窃取、設定改ざん、ログ改ざん、永続化、横展開、他ユーザーのデータ取得、コンテナホスト侵害、バックアップや監視系の無効化が可能になる。ページキャッシュ汚染はディスク上のファイル改ざんとして残りにくいため、検知や事後解析も難しくなる。つまり、Copy Fail は「小さな書き込み」の問題ではなく、「小さな書き込みが高権限の意味空間に接続する」問題である。
この構造は、計算機が byte 列しか直接扱わないことに由来する。CPU は、その byte が読み取り専用ファイル由来なのか、root 権限で実行される命令なのか、一般ユーザーが書いてはいけないものなのかを意味として理解しない。CPU は、アドレスを読み、書き、命令として実行する。意味を与えているのは、OS、ファイルシステム、権限モデル、カーネル API、実行文脈、運用者の設計である。したがって、計算機安全性とは、byte 列そのものを守ることではなく、byte 列に重ねられた意味、権限、所有者、文脈、用途を保存することである。
しかし、その意味保存は簡単ではない。性能最適化はしばしば境界を薄くする。ゼロコピーはコピーを減らす代わりに参照共有を増やす。in-place 処理はメモリ効率を高める代わりに入力と出力の境界を曖昧にし得る。カーネル API の userspace interface は機能を広げる代わりに攻撃面を増やす。コンテナは隔離を提供するが、ホストカーネルを共有する。CI/CD は自動化を進めるが、外部由来コードを実行する入口にもなる。便利さ、性能、互換性、自動化は、安全性と常に緊張関係にある。
| 価値 | 利点 | 安全性上の緊張 | Copy Fail との接続 |
|---|---|---|---|
| 性能 | コピー削減、低遅延、高スループットを実現する。 | 参照共有や in-place 処理により、入力と出力の境界が曖昧になる。 | splice() と in-place 処理の結合が page の意味境界を壊した。 |
| 機能性 | ユーザー空間からカーネル機能を利用できる。 | 攻撃者も同じ入口に到達できる可能性がある。 | AF_ALG がカーネル暗号 API への到達経路になった。 |
| 互換性 | 多様な workload と既存アプリケーションを支える。 | 古い経路、特殊構成、想定外の組み合わせが残りやすい。 | ディストリビューションや kernel 構成ごとに対応差が生じる。 |
| 自動化 | CI/CD やコンテナにより開発と運用を高速化する。 | 非信頼コードの実行入口が増える。 | ローカル権限昇格の露出 \(X_t\) が高くなり得る。 |
| 隔離 | コンテナや namespace により環境を分ける。 | ホストカーネル共有により、kernel 脆弱性には同時に晒される。 | コンテナ内 foothold がホスト侵害へ接続し得る。 |
ここに、人間の認識限界が重なる。人間はすべての組み合わせを見られない。開発者は自分の担当領域には詳しいが、すべての subsystem の相互作用を完全には把握できない。ディストリビューションメンテナーは修正を backport できるが、全利用者の構成を知らない。運用者は自分のサーバーを管理できるが、kernel 内部の全実装を理解しているわけではない。セキュリティ研究者は未知の経路を発見できるが、全環境の影響を網羅できるわけではない。安全性は、単一主体の完全知ではなく、分散知識の連結によって維持される。
この点で、CVE、security tracker、USN、ディストリビューション advisory、kernel patch、PoC、運用確認コマンド、暫定緩和手順は、単なる情報断片ではない。それらは、分散した知識を接続するための社会的インフラである。CVE は問題に名前を与える。tracker は対象 package と修正状況を追跡する。advisory は運用者に判断材料を与える。patch は構造を更新する。暫定緩和は更新までの時間を埋める。再起動は修正済み構造を実際に有効化する。この連鎖全体が、安全性を成立させる。
AI 支援探索の進展は、この戦いをさらに加速させる。AI は、人間が見落とす組み合わせを探索し、コード断片を要約し、PoC を読み解き、ログを分類し、設定差分を抽出し、既知 CVE と環境状態を照合する助けになる。一方で、攻撃側にとっても、脆弱性理解、exploit 作成、環境適応、攻撃経路の自動化を支援する道具になり得る。したがって、AI は安全性を自動的に解決する救世主ではない。AI は観測能力を拡張するが、その観測をどう運用に接続し、どこまで信頼し、どう検証し、どの判断を人間が責任として引き受けるかが問題になる。
この構造は、過去の記事群で扱ってきた「世界を固定物ではなく更新され続ける構造として捉える」視点と接続できる。宇宙、観測、意識、クオリア、時間を、静止した実体ではなく、更新、履歴、観測、意味生成の過程として考えるなら、計算機安全性も同じ形式を持つ。安全性は固定された属性ではない。安全性は、観測され、検証され、反証され、修正され、再起動され、運用に反映されることで、その時点ごとに成立する構造状態である[47][48][49][50][51]。
ここで重要なのは、「終わらない戦い」を根性論にしないことである。終わらないから頑張り続ける、という話では足りない。終わらないなら、終わらないことを前提にした構造を作らなければならない。具体的には、攻撃面を減らす。権限を最小化する。不要な module を無効にする。非信頼コードを隔離する。CI runner を使い捨てにする。kernel 更新と再起動を計画可能にする。暫定緩和を標準手順化する。影響確認とロールバックを用意する。脆弱性情報を追跡する。観測と更新が回る運用を作る。安全性は、英雄的な単発対応ではなく、更新可能な制度として設計する必要がある。
| 終わらない理由 | 構造的原因 | 必要な対応 |
|---|---|---|
| 機能が増え続ける。 | 性能、互換性、利便性、ハードウェア対応により \(|\Omega|\) が増える。 | 不要機能を減らし、危険な組み合わせを抑える。 |
| 人間は全体を見られない。 | \(|\Omega_{\mathrm{human}}|\ll|\Omega|\) であり、全探索は不可能である。 | fuzzing、静的解析、形式検証、分散レビューを組み合わせる。 |
| 攻撃者も進化する。 | PoC、AI、公開情報、攻撃自動化により探索能力が上がる。 | 防御側も観測、検証、更新を高速化する。 |
| 運用遅延が残る。 | kernel 更新には再起動、検証、メンテナンス調整が必要である。 | 冗長化、ローリング更新、再起動計画、検証環境を用意する。 |
| 未知領域が残る。 | 未観測の相互作用 \(U_t\) はゼロにならない。 | 侵害の局所化、最小権限、監視、迅速な patching を前提にする。 |
したがって、計算機安全性は、完成された城壁ではない。城壁という比喩は、防御が一度作ればそこに固定されるものだと誤解させる。現実の安全性は、観測、検証、反証、修正、再設計、運用、責任分担の継続過程である。CVE-2026-31431 は、書いてはいけない場所に書けてしまうという素朴な事実から、計算機世界の本質を露出させた。計算機は byte を扱うが、人間はそこに意味を与える。安全性とは、その意味が文脈をまたいでも保存されるように、構造を更新し続けることである。
この意味で、終わらない戦いとは、無限の敗北ではなく、継続的な適応である。生命が環境の変化に応じて代謝を続けるように、科学が反証によって理論を更新するように、制度が失敗から規則を更新するように、計算機安全性も発見された破綻を構造更新へ変換する[52]。Copy Fail は、その更新過程の一事例である。重要なのは、脆弱性を見つけたことだけではない。そこから、意味境界、許可モデル、実行モデル、観測限界、運用責任を問い直すことである。
25. 結論
CVE-2026-31431 は、Linux カーネルの algif_aead における in-place 処理の設計不備として説明できる。AF_ALG は、ユーザー空間からカーネル暗号 API を利用するための socket family である。algif_aead は、その中で AEAD 処理を扱う経路である。splice() は、ユーザー空間を経由せず file descriptor 間でデータを受け渡すシステムコールである。ページキャッシュは、ファイル内容をメモリ上に保持する仕組みである。setuid root バイナリは、一般ユーザーから起動されても、必要な範囲で root 権限の処理を行うための正当な権限委譲機構である。
これらの仕組みは、それぞれ単体では正当である。AF_ALG はカーネル暗号機能を提供する。splice() は効率的な I/O を実現する。ページキャッシュはファイルアクセスを高速化する。setuid は passwd や su のような管理操作を成立させる。しかし、CVE-2026-31431 では、これらが特定の形で結合した結果、読み取り専用ファイル由来のページキャッシュが、暗号処理の出力先として扱われる経路が成立した。その結果、非特権ユーザーから制御された小さな書き込みがページキャッシュへ到達し、setuid root バイナリの実行時内容を汚染し、root 権限昇格へ接続し得る構造が生まれた。
ここで重要なのは、4 byte という量の小ささではない。問題は、その 4 byte がどこに書かれ、何として解釈され、どの権限で実行されるかである。自分の一時ファイルに 4 byte 書けるだけなら重大ではない。しかし、root 権限で実行される命令列に 4 byte を配置できるなら、話はまったく変わる。小さな書き込みが重大化する条件は、制御性、位置制御、反復性、高権限実行への接続、検知困難性である。Copy Fail は、この条件を満たし得るため重大である。
技術的対策は明確である。最終対策は、修正済みカーネルへ更新し、そのカーネルで再起動することである。カーネル脆弱性では、パッケージを更新しただけでは不十分である。再起動前で古い kernel が動き続けていれば、脆弱性は残る。すぐに更新または再起動できない場合は、algif_aead のロード無効化、AF_ALG socket の制限、seccomp、コンテナ権限削減などを暫定策として検討する。ただし、これらは本対策ではなく、修正済み kernel へ移行するまでのリスクを下げるための措置である。
運用上の優先度は環境によって変わる。公開サーバー、共有サーバー、CI/CD runner、コンテナホスト、HPC / 研究計算基盤のように、非特権ユーザーや非信頼コードがローカル実行へ到達しやすい環境では、優先度を高く見るべきである。単独ユーザーの閉じた検証 VM では相対的に露出は低いが、古い VM イメージを不用意にネットワークへ接続したり、未更新のまま外部コードを実行したりすれば、リスクは上がる。したがって、古い VM イメージは、起動前に隔離し、ネットワークを制限し、起動後ただちに更新し、必要なら snapshot を取り直すべきである。
しかし、本稿の中心は、技術的対策の列挙だけではない。CVE-2026-31431 は、計算機安全性が値の正しさだけでなく、意味境界の保存に依存していることを示している。ファイルシステム文脈では、/usr/bin/su 由来の page は読み取り専用であり、一般ユーザーが書いてはいけない。暗号処理文脈では、output scatterlist は書き込み先である。この 2 つの意味が同じ page に重なったとき、許可モデルと実行モデルがずれた。ある層では禁止されていた更新が、別の層では許可された出力として再解釈された。これが、意味境界の破綻である。
V=\left[\operatorname{Allow}(u,w,p_{\mathrm{suid}},k_{\mathrm{fs}})=0\right]\land\left[\operatorname{Write}(u,p_{\mathrm{suid}},k_{\mathrm{crypto}})=1\right]
\]
この式は、Copy Fail の構造を簡潔に表す。ファイルシステム文脈では、非特権ユーザー \(u\) が setuid root バイナリ由来の page \(p_{\mathrm{suid}}\) に書くことは許可されていない。しかし、暗号処理文脈では、その page への書き込みが実際に発生する。この「許可されていない」と「実際には起きる」が同時に成立する状態が、脆弱性である。
さらに、意味保存の観点では、次のように表せる。
\exists p:\ \mu_{\mathrm{fs}}(p)=\mathrm{ReadOnlyFileCache}\ \land\ \mu_{\mathrm{crypto}}(T(p))=\mathrm{WritableOutput}
\]
これは、ある page \(p\) が、ファイルシステム文脈では読み取り専用ファイル由来のキャッシュであるにもかかわらず、文脈変換 \(T\) の後、暗号処理文脈では書き込み可能な出力先として扱われることを表す。ここに、CVE-2026-31431 の本質がある。byte 列そのものではなく、その byte 列に重ねられた読み取り専用、書き込み可能、入力、出力、実行可能、root 権限という意味が、文脈をまたいで保存されなかったのである。
この教訓を一般化すると、計算機安全性は次のような動的均衡として表せる。
\operatorname{Security}(S_t)=\alpha B_t+\beta E_t+\gamma P_t+\lambda O_t-\delta M_t-\eta X_t
\]
ここで \(S_t\) は時点 \(t\) のシステム構造である。\(B_t\) は境界の強さであり、読み取り専用、書き込み可能、実行可能、ユーザー由来、カーネル管理といった境界がどれだけ保たれているかを表す。\(E_t\) は説明可能性であり、設計者、運用者、検証者がその構造をどれだけ理解し、説明できるかを表す。\(P_t\) は更新可能性であり、修正済み kernel への更新、再起動、ロールバック、暫定緩和をどれだけ迅速に実行できるかを表す。\(O_t\) は観測可能性であり、脆弱性情報、ログ、監視、設定差分、起動中 kernel の状態をどれだけ把握できるかを表す。\(M_t\) は意味不整合であり、文脈をまたいだときに意味属性が破れる度合いである。\(X_t\) は攻撃露出であり、攻撃者や非信頼コードが危険経路へ到達できる度合いである。
| 項 | 意味 | 安全性への作用 | Copy Fail での読み方 |
|---|---|---|---|
| \(B_t\) | 境界の強さ。 | 大きいほど安全性を高める。 | 読み取り専用 page が出力先にならないようにする境界。 |
| \(E_t\) | 説明可能性。 | 大きいほど設計・検証・運用判断がしやすい。 | AF_ALG、splice()、page cache、setuid の相互作用を説明できること。 |
| \(P_t\) | 更新可能性。 | 大きいほど発見後の残存リスクを下げる。 | 修正済み kernel への更新、再起動、暫定緩和を速く行えること。 |
| \(O_t\) | 観測可能性。 | 大きいほど未知の問題を早く発見しやすい。 | CVE、advisory、起動中 kernel、algif_aead 状態、ログを確認できること。 |
| \(M_t\) | 意味不整合。 | 大きいほど安全性を下げる。 | 読み取り専用 page が WritableOutput として扱われること。 |
| \(X_t\) | 攻撃露出。 | 大きいほど安全性を下げる。 | SSH、Web RCE、CI、コンテナ foothold からローカル実行へ到達できること。 |
この式は、完全安全を約束するものではない。むしろ、完全安全という発想を捨てるための式である。安全性は、境界、説明可能性、更新可能性、観測可能性によって高まり、意味不整合と攻撃露出によって下がる。したがって、取るべき方向は明確である。意味境界を強くする。設計を説明可能にする。更新と再起動を可能にする。観測可能性を高める。意味不整合を減らす。攻撃露出を削る。これらを同時に進めることが、現実的な安全性である。
この意味で、CVE-2026-31431 は単なる Linux カーネルの 1 つの CVE ではない。もちろん、まずは技術的に処理すべき脆弱性であり、修正済み kernel への更新が必要である。しかし同時に、それは、計算機が byte 列だけを扱い、人間と OS がそこに意味を重ねているという事実を露出させた事例でもある。書いてはいけない場所に書けてしまうとは、単にメモリ更新が発生したということではない。人間が設計した意味境界が、実行経路上で保存されなかったということである。
計算機安全性を人間が担保する限り、それは終わらない戦いである。だが、その戦いは無意味ではない。人間は全体を完全には見られないが、攻撃面を減らすことはできる。意味境界を明確にすることはできる。観測を増やすことはできる。修正を速く適用することはできる。破られても局所化する構造を作ることはできる。分散した知識を CVE、tracker、advisory、patch、運用手順として接続することはできる。
したがって、Copy Fail から得られる最も本質的な教訓は、計算機安全性を静的な完成物として見てはならない、ということである。安全性は、構造、意味、観測、更新、責任の継続過程である。CVE-2026-31431 は、Linux カーネルの一脆弱性であると同時に、計算機世界における意味境界の破綻、人間の認識限界、分散知識、運用責任を露出させた事例である。技術的には修正済み kernel へ更新する。本質的には、意味を保存し、観測し、更新し続ける構造を作る。これが、本稿の結論である。
参考文献
- CVE Program, CVE Record: CVE-2026-31431. https://www.cve.org/CVERecord?id=CVE-2026-31431
- IPA, Linux の脆弱性対策について(CVE-2026-31431、Copy Fail). https://www.ipa.go.jp/security/security-alert/2026/alert20260501.html
- CERT-EU, High Vulnerability in the Linux Kernel. https://cert.europa.eu/publications/security-advisories/2026-005/
- NVD, CVE-2026-31431 Detail. https://nvd.nist.gov/vuln/detail/CVE-2026-31431
- Linux kernel CVE announce, CVE-2026-31431. https://lore.kernel.org/linux-cve-announce/2026042214-CVE-2026-31431-3d65%40gregkh/
- Kernel.org git repositories, crypto: algif_aead – Revert to operating out-of-place. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5
- GitHub Advisory Database, CVE-2026-31431. https://github.com/advisories/GHSA-2274-3hgr-wxv6
- Debian Security Tracker, CVE-2026-31431. https://security-tracker.debian.org/tracker/CVE-2026-31431
- Xint, Copy Fail: 732 Bytes to Root on Every Major Linux Distribution. https://xint.io/blog/copy-fail-linux-distributions
- Microsoft Security Blog, CVE-2026-31431: Copy Fail vulnerability enables Linux root privilege escalation across cloud environments. https://www.microsoft.com/en-us/security/blog/2026/05/01/cve-2026-31431-copy-fail-vulnerability-enables-linux-root-privilege-escalation/
- Ubuntu Security Notice USN-8226-1, kmod update. https://ubuntu.com/security/notices/USN-8226-1
- Ubuntu, CVE-2026-31431. https://ubuntu.com/security/CVE-2026-31431
- Linux man-pages project, splice(2) — Linux manual page. https://man7.org/linux/man-pages/man2/splice.2.html
- Oracle Linux Blog, An In-Depth Look at Pipe and Splice implementation. https://blogs.oracle.com/linux/pipe-and-splice
- The Linux Kernel documentation, User Space Interface — Kernel Crypto API. https://docs.kernel.org/crypto/userspace-if.html
- libkcapi documentation, Linux Kernel Crypto API User Space Interface Library. https://www.chronox.de/libkcapi/html/Intro.html
- Cloudflare, The Linux Crypto API for user applications. https://blog.cloudflare.com/the-linux-crypto-api-for-user-applications/
- The Linux Kernel documentation, Page Cache. https://docs.kernel.org/mm/page_cache.html
- Linux man-pages project, setuid(2) — Linux manual page. https://man7.org/linux/man-pages/man2/setuid.2.html
- The Linux Kernel documentation, Kernel module signing facility. https://docs.kernel.org/admin-guide/module-signing.html
- Linux man-pages project, modprobe.d(5) — Linux manual page. https://man7.org/linux/man-pages/man5/modprobe.d.5.html
- Linux man-pages project, seccomp(2) — Linux manual page. https://man7.org/linux/man-pages/man2/seccomp.2.html
- Linux man-pages project, capabilities(7) — Linux manual page. https://man7.org/linux/man-pages/man7/capabilities.7.html
- Linux man-pages project, namespaces(7) — Linux manual page. https://man7.org/linux/man-pages/man7/namespaces.7.html
- Red Hat, CVE-2026-31431. https://access.redhat.com/security/cve/cve-2026-31431
- SUSE, CVE-2026-31431. https://www.suse.com/security/cve/CVE-2026-31431.html
- SUSE, Security update for the Linux Kernel. https://www.suse.com/support/update/announcement/2026/suse-su-20261674-1
- Arch Linux, CVE-2026-31431. https://security.archlinux.org/CVE-2026-31431
- Amazon Linux Security Center, CVE-2026-31431. https://explore.alas.aws.amazon.com/CVE-2026-31431.html
- NIST, Secure Software Development Framework (SSDF) Version 1.1. https://csrc.nist.gov/pubs/sp/800/218/final
- MITRE CWE, CWE-669: Incorrect Resource Transfer Between Spheres. https://cwe.mitre.org/data/definitions/669.html
- id774, 哲学はなぜ構造の揺れを数理モデルで説明できるのか(2026-04-06). https://blog.id774.net/entry/2026/04/06/4328/
- id774, 抽象哲学を運用可能なモデルへ変換する(2026-04-07). https://blog.id774.net/entry/2026/04/07/4335/
- id774, 構造振動モデルを数理モデルとして定義する(2026-04-05). https://blog.id774.net/entry/2026/04/05/4318/
- Stanford Encyclopedia of Philosophy, Aristotle on Causality. https://plato.stanford.edu/entries/aristotle-causality/
- Stanford Encyclopedia of Philosophy, Descartes’ Method. https://plato.stanford.edu/entries/descartes-method/
- Stanford Encyclopedia of Philosophy, Hegel’s Dialectics. https://plato.stanford.edu/entries/hegel-dialectics/
- Stanford Encyclopedia of Philosophy, Darwinism. https://plato.stanford.edu/entries/darwinism/
- Stanford Encyclopedia of Philosophy, Friedrich Hayek. https://plato.stanford.edu/entries/friedrich-hayek/
- Stanford Encyclopedia of Philosophy, Bounded Rationality. https://plato.stanford.edu/entries/bounded-rationality/
- Tversky, Amos and Daniel Kahneman, Judgment under Uncertainty: Heuristics and Biases. https://stanford.edu/~jlmcc/Presentations/tversky_kahneman_1974.pdf
- Stanford Encyclopedia of Philosophy, Karl Popper. https://plato.stanford.edu/entries/popper/
- Stanford Encyclopedia of Philosophy, Philosophy of Technology. https://plato.stanford.edu/entries/technology/
- Stanford Encyclopedia of Philosophy, Computing and Moral Responsibility. https://plato.stanford.edu/archives/spr2023/entries/computing-responsibility/
- The Linux Kernel documentation, Linux Security Modules. https://docs.kernel.org/userspace-api/lsm.html
- Ubuntu Security documentation, AppArmor. https://documentation.ubuntu.com/security/security-features/privilege-restriction/apparmor/
- id774, 宇宙を構造として再定義する(2026-03-27). https://blog.id774.net/entry/2026/03/27/4171/
- id774, 観測を情報更新として定式化する宇宙論(2026-03-30). https://blog.id774.net/entry/2026/03/30/4239/
- id774, 意識の定義を数理モデルで記述する(2026-04-02). https://blog.id774.net/entry/2026/04/02/4269/
- id774, クオリアを構造振動として記述する(2026-04-22). https://blog.id774.net/entry/2026/04/22/4582/
- id774, 時間はなぜ一方向に進むのか(2026-04-26). https://blog.id774.net/entry/2026/04/26/4613/
- id774, タイタニック号事故から読み解く巨大システム運用の本質(2026-05-02). https://harpuia.id774.net/entry/2026/05/02/4656/