7.6. vfork(2)は使わない

Unix ライクなシステムで新しいプロセスを簡単にかつ移植性を持たせて作成する には、fork(2) システムコールを使用します。 BSD は vfork(2)と呼ばれるシステムコールを導入して、手法の最適化を図りました。 vfork(2)は fork(2)とは異なり、execve(2V)を呼び出すか、exit するまでは、 子プロセスが親プロセスのメモリや制御スレッドを借りています。 親プロセスは子プロセスがそのリソースを使っている間、サスペンドします。 古い BSD では fork(2)がメモリを実際にコピーするのに対して、vfork(2)では そうしない点が基本です。 Linux ではこの問題はまったく発生しません。なぜなら、Linux は内部ではコピー・ オン・ライト方式を使っていて、変更があった時にだけページをコピーします (実際は、他にもまだコピーしなければいけないテーブルがいくつか存在します。 大部分の動作環境では、このオーバーヘッドはそれほど重くはありません)。 にもかかわらず、vfork(2)を使ったプログラムがいくつか存在するので、最近に なって Linux で BSD の vfork(2) 方式を実装しました(それまでは、vfork(2) は fork(2)のエイリアスでした)。

vfork(2) にはかなり問題があります。 移植性の点からすると、vfork(2)は、親プロセスに干渉しないようにするのに、 実のところかなりトリッキーところがあります。特に高レベルな言語においてその点 が顕著です。 「干渉しない」ようにするには、実際に生成されるマシンコードに反映する必要 があります。また、コンパイラは表に出ない一時的な生成物や予想外の干渉を 起こすコードを構成してしまうケースが多くあります。 結論として、vfork(2)を使っているプログラムは、コードが変わったり、コンパイラ のバージョンが変わったりするだけでたいてい機能しなくなります。

Linux システム上の安全が必要なプログラムにとって、これはさらに状況を悪くします。 なぜなら、Linux(少なくとも バージョン 2.2 の 2.2.17 まで)は vfork()の実装 に競合状態が起こる脆弱性があるからです。 Linux で特権プロセスがユーザのコマンドを実行するのに vfork(2) と execve(2) をペアで使っていると、競合状態が発生します。子プロセスが既にユーザの uid で動作しているが、execve(2)はしていないケースです。 ユーザが SIGSTOP を含むシグナルをそのプロセスに送れるかもしれません。 vfork(2)方式では、特権を持った親プロセスも子プロセスと同様にブロックされます。 結果的に、特権を持っていないプロセスが、特権を持ったプロセスを中断させられ ます。つまりこれは、特権を持ったプロセスのサービスに対するサービス拒否攻撃に なります。 少なくとも FreeBSD と OpenBSD では、このケースに対処するコードが入っています。 この問題に対する脆弱性は、知っている限りありません。 Solar Designer 氏に感謝しています。彼は 2000 年 10 月 7 日に security-audit メーリングリストで Linux において、この問題に言及し、証拠を示してくれました。

vfork(2)について、結論ははっきりしています。 プログラムでは vfork(2)を使わない、です。 こうするのは難しくないはずです。vfork(2)を主に使用するのは、vfork 方式を必要と している古いプログラムをサポートするためだからです。