質問

DJBの本を読んでいた 「Qmail 1.0 の 10 年後のセキュリティについての考え」 そして彼は、ファイル記述子を移動するための次の関数をリストしました。

int fd_move(to,from)
int to;
int from;
{
  if (to == from) return 0;
  if (fd_copy(to,from) == -1) return -1;
  close(from);
  return 0;
}

このコードは close の戻り値をチェックしていないことに気づき、close(2) のマニュアルページを読んだところ、 できる で失敗する EINTR, この場合、適切な動作は、同じ引数を使用して close を再度呼び出すことであると思われます。

このコードは、C と UNIX の両方で私よりもはるかに豊富な経験を持つ人によって書かれており、さらに qmail では 10 年以上変更されていないため、このコードを正しくするために私が見逃しているニュアンスがあるに違いないと思います。誰かそのニュアンスを説明してもらえますか?

役に立ちましたか?

解決

それはfd_copyまたはdup2関数であるように、ファイル記述子がdup'dされる

は、あなたが同じことを(カーネル内すなわち同じstruct file)を参照する複数のファイル記述子になってしまいます。そのうちの一つを閉じると、単にその参照カウントをデクリメントします。それはの最後のの近くにありますしない限り、操作が基本となるオブジェクト上で実行されません。結果として、そのようなEINTREIO等の条件は不可能である。

他のヒント

答えは 2 つあります。

  1. 彼は、一般的なコードを省略することについて主張しようとしていたのですが、そのような例では、簡潔さと明確さのためにエラー チェックが省略されていることがよくあります。
  2. close(2) は EINTER を返す可能性がありますが、実際にはそうなりますか? もしそうなら、合理的に何をしますか?一度再試行しますか?成功するまで再試行しますか?EIO を取得したらどうなりますか?これはほぼあらゆる意味を意味する可能性があるため、ログに記録して次に進む以外に合理的な手段はありません。EIO の後に再試行すると EBADF が発生する可能性がありますが、その後はどうなるでしょうか?記述子が閉じていると仮定して次に進みますか?

すべてのシステムコールは EINTR を返す可能性があり、特に、遅い人間を待機している read(2) のようなブロックを返すものはそうです。これはより可能性の高いシナリオであり、優れた「端末から入力を取得する」ルーチンは実際にこれをチェックします。これは、ログ ファイルの書き込み時であっても、write(2) が失敗する可能性があることも意味します。ロガーが生成したエラーをログに記録しようとしますか、それとも諦めたほうがよいでしょうか?

は別の可能性は、コールがシグナルによって中断されないことを確実にするために何かをしている彼の機能は、専用のアプリケーション(または1つの一部)で使用されていることです。あなたが信号で重要な何かをするつもりはないしている場合は、それらに応答する必要はありません、それはむしろラップよりもすべてのアウト、EINTR再試行内のすべての単一のブロッキングシステムコールにそれらをマスクする意味があります。あなたが終了することにより、それを扱う場合はSIGPIPE SIGKILLように頻繁にどのような場合には、正しいユーザースペースアプリに配信されることはありませんSIGSEGVと同様の致命的なエラーと一緒に、あなたを殺すでしょう、もちろんものを除きます。

とにかく、彼が話してすべてがセキュリティである場合には、その後、恐らく彼はcloseを再試行する必要はありません。近くにはEIOで失敗した場合は、その後、彼はそれを再試行することはできません、それは永続的なエラーになります。したがって、それはcloseが成功したことを彼のプログラムの正しさのために必要ではありません。これはよく、それはcloseがEINTRに再試行されることを彼のプログラムの正しさのために必要ではないことのいずれかであってもよい。

は通常、あなたはあなたのプログラムが成功するために最善の努力をしたい、その手段がEINTRに再試行します。しかし、これは、セキュリティとは別の問題です。それははむしろ永久的な理由のためのよりEINTRを、失敗したを起こる、ではないことを、あなたのプログラムが設計されている場合ので、何らかの理由で失敗するいくつかの機能は、セキュリティ上の欠陥ではないこと、そして特に事実欠陥。彼は彼が再試行にの必要性のない理由をいくつかの理由を証明しているので、そうしても、気にしない場合、私はすべて驚いでもないようにDJBは、かなり独断されることが知られています彼のプログラムは、多分それは現在、(明示的に決定的な瞬間に、ユーザーによってkillで無害な信号を送られているように)失敗した特定の状況でハンドルをフラッシュすることに成功できるようになります。

編集:それはEINTRに再試行すると、潜在的に自身が一定の条件の下でのセキュリティ上の欠陥かもしれないことを私に起こります。これは、コードのそのセクションに新しい動作が導入されています。それはそれはcloseする一つの試みを行ってから戻ってくる前に信号洪水、に応じて、無限にループすることができます。私は、これは何の問題(すべての後に、close自体は、それが返されますどのようにすぐに保証しません)qmailの原因となることを確実に知りません。しかし、一つの試みの後にあきらめている場合は、それはもっともらしく賢いかもしれ分析するために、コードをより簡単にありません。かではない。

あなたはその再試行防止信号が偽の故障の原因にDoSの脆弱性を、と思うかもしれません。しかし、再試行は、信号洪水が無限ストールが発生し、別の(より困難)のDoSの脆弱性を、可能にします。絶対的なセキュリティの質問、彼はqmailのとのdjbdnsを書いたときDJBがに興味があったの一種であるバイナリ「このアプリは、投与することができますか?」、という点では、それは違いはありません。何かが一度発生する可能性があれば、通常その手段は、それが何度も発生する可能性があります。

のみ壊れUnix系OSは今まであなたが明示的にそれを求めずEINTRを返します。 signal()のためのまともなセマンティクスは、再起動可能なシステムコール(「BSDスタイル」)を可能にします。 SysVのセマンティクス(信号を中断する)を持つシステム上でプログラムを構築する場合、それが存在しない場合、あなたは常にあなたがsignal()の面で自分自身を定義することができbsd_signal()への呼び出し、とsigaction()への呼び出しを置き換える必要があります。

それはあなたのシグナルハンドラをインストールしていない限り、いいえのシステムは、信号の受信時にEINTRを返すことを指摘、さらに価値があります。デフォルトのアクションは、その場に残されている場合、信号が無いアクションに設定されている場合、システムコールが中断されるために、または、それは不可能です。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top