
2025/12/14 7:58
Linux Sandboxes and Fil-C
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
メモリ安全性とサンドボックスはプログラムの異なる部分を保護するため、両方が強力なセキュリティに必要です。純粋な Java プログラムはメモリ安全であってもファイルシステムの syscalls を通じて任意のファイルを書き込むことができるし、逆にすべての能力を取り消したアセンブリプログラムでもメモリバグがある場合がありますが、カーネルが特権 syscalls を殺すためサンドボックスから逃げられません。サンドボックスは意図的に許容範囲を広く設計しているため、攻撃者は残されたメモリ安全性のバグを利用してブローカー・プロセスへ到達することができるので、両方の防御を組み合わせるとより強固な保護が得られます。
本書では、C/C++ 用に設計され、システムコールまで安全性を保証し、init や udevd などの低レベルコンポーネントで使用できるメモリ安全ランタイム「Fil‑C」への OpenSSH の seccomp ベース Linux サンドボックス移植方法について説明します。OpenSSH は既に chroot を採用し、
sshd ユーザー/グループとして特権なしで実行し、setrlimit を使用し、非許可 syscalls を SECCOMP_RET_KILL_PROCESS で殺す seccomp‑BPF フィルタを適用しています。Fil‑C はその runtime 内で自動的にこれらの syscalls を許可することで簡素化します。背景スレッドは存続させつつスレッド生成を防ぐため、Fil‑C は API void zlock_runtime_threads(void) を追加し、必要なスレッドを事前確保してシャットダウンを無効にします。
OpenSSH の seccomp フィルタは強化されています。失敗時の挙動が
SECCOMP_RET_KILL から SECCOMP_RET_KILL_PROCESS に変更され、mmap 許可リストに新たに MAP_NORESERVE フラグが追加され、sched_yield が許可されています。サンドボックスは二つの prctl コール(PR_SET_NO_NEW_PRIVS と PR_SET_SECCOMP)で構築され、エラー検出も行われます。Fil‑C のランタイムは filc_runtime_threads_handshake で全スレッドとハンドシェイクし、各スレッドが no_new_privs ビットと seccomp フィルタを持つことを保証します。複数のユーザー スレッドが検出された場合、安全エラーが発生します。
メモリ安全性とサンドボックスを組み合わせることで、OpenSSH はより厳格な隔離を実現し、メモリバグによる権限昇格リスクを低減します。このアプローチは他のセキュリティクリティカルプロジェクトにも採用を促す可能性があります。
本文
メモリ安全性とサンドボックス化
メモリ安全性とサンドボックスは別々の概念です。
互いに独立しており、メモリ安全性があるからといって必ずしもサンドボックス化されているわけではなく、逆もまた同様です。
-
メモリ安全だがサンドボックス化されていない 例:純粋な Java プログラムで、ユーザー入力に応じて任意のファイルを読み書きできるもの。OS はユーザーがアクセス可能なファイルなら何でも上書き許可します。プログラム自体はメモリ安全でも危険です。
サンドボックスもなく、Java ランタイムにコード実行バグがあれば、攻撃者は任意のファイルを上書きできるでしょう。 -
サンドボックス化されているがメモリ安全でない 例:OS に対して「計算だけ許可」するよう要求するアセンブリプログラム。カーネルはファイル関連システムコールを試みるとプロセスを終了させます。多くのメモリ安全性バグ(アセンブリではよくある)があっても、ファイルを書き換えることはできません。
実際にはサンドボックスに意図的な穴が存在します。サンドボックス化されたプロセスは権限付き仲介プロセスとメッセージングできます。攻撃者はメモリ安全性バグを利用して悪意あるメッセージを送信し、仲介プロセスに乗っ取る可能性があります。
最良の防御は サンドボックス化+メモリ安全性 の両方です。
この文書では Fil‑C のメモリ安全機能と OpenSSH の seccomp ベース Linux サンドボックスを組み合わせる方法を説明します。
背景
Fil‑C は C/C++ のメモリ安全実装です。多くの安全言語とは異なり、コードが Linux システムコールに到達する直前まで安全性を保証し、
init や udevd など低レベルコンポーネントでも十分に動作します。OpenSSH を含む多くのプログラムは Fil‑C 上で実行され、seccomp‑BPF サンドボックスを利用しています。
OpenSSH は非特権 sshd‑session プロセスに対して以下でサンドボックスを構築します。
- chroot – ファイルシステムビューを制限。
- 非特権ユーザー/グループ –
で実行し、追加の権限は付与しない。sshd - setrlimit – ファイルオープン数・プロセス生成・書き込みを制限。
- seccomp‑BPF – 非特権プロセスに許可されたシステムコールだけを列挙し、他は
を送る。SIGSYS
Chromium と Mozilla は seccomp‑BPF による Linux サンドボックスの優れたノートを公開しています。
Fil‑C は chroot やユーザー/グループ変更を簡単に扱えるようにしており、これらは Fil‑C で trivially 許可されます。
setrlimit と seccomp‑BPF は Fil‑C ランタイムがスレッドを生成しメモリを確保・同期するため、特別な配慮が必要です。
Fil‑C ランタイムでのスレッド作成防止
Fil‑C ランタイムは GC 用にバックグラウンドスレッドを起動し、アイドル時に停止します。
プログラムが再び活性化すると、そのスレッドは自動的に再開されます。
新しいスレッドの生成は OpenSSH の
setrlimit で「新プロセス禁止」ルール(スレッドは軽量プロセス)を破ることになり、clone3 などが許可されていないためです。この回帰を防ぐために
<stdfil.h> に次の API を追加しました。
void zlock_runtime_threads(void);
zlock_runtime_threads() はランタイムが必要とする全スレッドを即座に作成し、停止させないようにします。OpenSSH では
ssh_sandbox_child() 内で setrlimit や seccomp コールの前に呼び出されます。
OpenSSH サンドボックスへの調整
zlock_runtime_threads() が以降のスレッド生成をブロックするため、既存の setrlimit はそのままで問題ありません。seccomp フィルタには次の変更を加えました。
| 変更点 | 理由 |
|---|---|
を採用( ではなく) | サンドボックス違反時に Fil‑C バックグラウンドスレッドを終了させるため。 |
を mmap の許可リストへ追加 | Fil‑C アロケータが必要とするオプションで、セキュリティ上の懸念はない。 |
を許可 | 意味的にノーオペレーションであり、Fil‑C がロックに使用している。 |
他に変更は不要です。必須の
futex 系システムコールは既に許可済みです。
Fil‑C での prctl
実装
prctlOpenSSH は seccomp フィルタを二つの
prctl 呼び出しで設置します。
/* PR_SET_NO_NEW_PRIVS */ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { debug("%s: prctl(PR_SET_NO_NEW_PRVSS): %s", __func__, strerror(errno)); nnp_failed = 1; } /* PR_SET_SECCOMP */ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &preauth_program) == -1) debug("%s: prctl(PR_SET_SECCOMP): %s", __func__, strerror(errno)); else if (nnp_failed) fatal("%s: SECCOMP_MODE_FILTER で設定に失敗しました。", __func__);
これらのシステムコールは呼び出しスレッドだけに影響します。特別な処理を行わないと、Fil‑C のバックグラウンドスレッドは
no_new_privs やフィルタを持たず、メモリ安全性バグでサンドボックスを回避できてしまいます。
対策として Fil‑C ランタイムは独自のラッパーを実装しました。
/* 各ランタイムスレッドでコールバックを呼ぶ。 */ PAS_API void filc_runtime_threads_handshake( void (*callback)(void* arg), void* arg);
このコールバックが各スレッドに対して要求された
prctl を実行し、すべてのスレッドで no_new_privs が設定され、seccomp フィルタが適用される ことを保証します。
プログラムが複数ユーザー・スレッドを持つ場合、これら二つの
prctl は Fil‑C の安全性エラーを引き起こします。
結論
堅牢なセキュリティにはメモリ安全性とサンドボックス化を併用することが不可欠です。
本ガイドでは、OpenSSH の seccomp サンドボックスを Fil‑C へ移植しつつ、元の保護レベルと Fil‑C のメモリ安全保証を維持する方法を示しました。