11

親プロセスが大量のメモリを使用しているため、カーネル オーバーコミット ポリシーの構成によってforkは失敗する場合がerrnoあります。ENOMEM子プロセスは、execls のような低メモリ消費プログラムのみである可能性があります。

問題を明確にするために、/proc/sys/vm/overcommit_memory が 2 に設定されている場合、(仮想) メモリの割り当ては に制限されSWAP + MEMORY * ration(default to 50%)ます。プロセスが fork するとき、COW のおかげで仮想メモリはコピーされません。ただし、カーネルは仮想メモリ空​​間を割り当てる必要があります。類推として、fork は物理メモリを割り当てない malloc(仮想メモリ空​​間サイズ) に似ており、共有メモリに書き込むと仮想メモリのコピーが発生し、物理メモリが割り当てられます。overcommit_memory が 2 に設定されている場合、仮想メモリ領域の割り当てが原因で fork が失敗することがあります。

fork以下の条件で、親プロセスの仮想メモリ空​​間を継承しないプロセスは可能ですか?

  1. exec子プロセスが後で呼び出す場合fork

  2. 子プロセスが呼び出さexecれず、親プロセスからのグローバル変数または静的変数を使用しない場合。たとえば、子プロセスはログを記録してから終了します。

4

5 に答える 5

8

Basile Starynkevitchが答えたように、それは不可能です。

ただし、これには、Linux 固有の動作やメモリ オーバーコミット制御に依存しない、非常に単純で一般的なソリューションが使用されています。

大規模な親プロセスに UNIX ドメイン ソケットを作成させ、できるだけ早くスレーブ プロセスを fork させ、スレーブ内の他のすべての記述子を閉じます ( STDIN_FILENOSTDOUT_FILENO、およびSTDERR_FILENOを再度開きます/dev/null)。ストリームソケットも機能しますが、単純さと保証のためにデータグラムソケットを好みます。

まれに、スレーブ プロセスに別の専用の小さなヘルパー プログラムを実行させると便利な場合があります。ほとんどの場合、これは必要なく、セキュリティ設計がはるかに簡単になります。(Linux では、Unix ドメイン ソケットを使用してデータを渡すときにSCM_CREDENTIALS/proc/PID/exe補助メッセージを含め、その中のプロセス ID を使用して、ピアが疑似ファイルを使用している ID/実行可能ファイルを確認できます。)

いずれにせよ、スレーブ プロセスはソケットからの読み取りをブロックします。相手側がソケットを閉じると、読み取り/受信は 0 を返し、スレーブ プロセスは終了します。

スレーブ プロセスが受信する各データグラムには、実行するコマンドが記述されています。(データグラムを使用すると、エスケープなどを行わずに、NUL 文字で区切られた C 文字列を使用できます。通常、Unix ストリーム ソケットを使用するには、何らかの方法で「コマンド」を区切る必要があります。これは、コマンド コンポーネント文字列の区切り文字をエスケープすることを意味します。)

スレーブ プロセスは 1 つ以上のパイプを作成し、子プロセスをフォークします。この子プロセスは元の Unix ソケットを閉じ、標準ストリームをそれぞれのパイプの端で置き換え (もう一方の端を閉じる)、必要なコマンドを実行します。個人的には、実行の成功を検出するために Linux で追加の close-on-exec ソケットを使用することを好みます。エラーの場合、errno コードがソケットに書き込まれるため、スレーブの親も確実に障害と正確な理由を検出できます。成功した場合、slave-parent は不要なパイプ エンドを閉じ、元のプロセスに成功について応答し、もう一方のパイプ エンドをSCM_RIGHTS補助データとして返します。メッセージを送信した後、パイプの残りの部分を閉じて、新しいメッセージを待ちます。

元のプロセス側では、上記のプロセスはシーケンシャルです。一度に 1 つのスレッドのみが外部プロセスの実行を開始できます。(ミューテックスを使用してアクセスをシリアル化するだけです。) 複数を同時に実行できます。シリアル化されるのは、スレーブ ヘルパーへの要求とスレーブ ヘルパーからの応答だけです。

それが問題である場合 (一般的なケースではありません)、たとえば、各メッセージの前に ID 番号 (親プロセスによって割り当てられ、単調に増加) を付けることで、接続を多重化できます。その場合、おそらく親側で専用スレッドを使用してスレーブとの通信を管理します。これは、複数のスレッドが同時に同じソケットから読み取ることはできず、決定論的な結果を期待できないためです。

スキームのさらなる改善には、実行されるプロセスに専用のプロセス グループを使用する、(スレーブ プロセスに制限を設定することによって) それらに制限を設定する、特権スレーブを使用して専用のユーザーおよびグループとしてコマンドを実行する、などがあります。

特権スレーブの場合は、親に別のヘルパー プロセスを実行させるのが最も便利です。Linux では、双方がSCM_CREDENTIALSUnix ドメイン ソケットを介して補助メッセージを使用して、ピアの ID (PID、および ID を使用して実行可能ファイル) を検証できるため、堅牢なセキュリティを簡単に実装できます。(ただし/proc/PID/exe、悪意のあるプログラムによってメッセージが送信され、適切なプログラムをすばやく実行するが、すぐに終了させるコマンドライン引数を使用してメッセージを送信する攻撃をキャッチするために、複数回チェックする必要があることに注意してください。正しい実行可能ファイルが要求を行った一方で、記述子のコピー (つまり、通信チャネル全体) が悪意のあるユーザーによって制御されていました)。

要約すると、元の問題は解決できますが、提起された質問への答えはノーです。権限 (ユーザー アカウント) や機能 (Linux) の変更など、実行がセキュリティに敏感な場合は、設計を慎重に行う必要があります。考慮されますが、通常の場合、実装は非常に簡単です。

必要に応じて詳しく教えていただければ幸いです。

于 2015-07-31T00:29:41.397 に答える