7

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

int fd_move(to,from)
int に;
int から;
{
  if (to == from) return 0;
  if (fd_copy(to,from) == -1) return -1;
  閉じる (から);
  0 を返します。
}

このコードは close の戻り値をチェックしないことに気がついたので、close(2) のマニュアル ページを読みましEINTR。同じ議論で。

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

4

4 に答える 4

2

私は2つの答えを持っています:

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

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

于 2010-04-03T06:30:50.803 に答える
1

fd_copyor関数のように、ファイル記述子が複製されると、複数のファイル記述子が同じもの (つまり、カーネル内dup2の同じもの) を参照することになります。struct fileそれらの 1 つを閉じると、その参照カウントが単純に減少します。最後のクローズでない限り、基になるオブジェクトに対して操作は実行されません。そのため、EINTRやなどの条件EIOはありえません。

于 2010-04-03T17:11:12.207 に答える
0

もう 1 つの可能性は、呼び出しがシグナルによって中断されないようにするために何らかの処理を行ったアプリケーション (またはアプリケーションの一部) でのみ、この関数が使用されていることです。シグナルで重要なことを何もしないのであれば、それらに応答する必要はありません。ブロックしているすべてのシステム コールを EINTR 再試行でラップするのではなく、シグナルをすべてマスクする方が理にかなっている可能性があります。もちろん、あなたを殺すものを除いて、終了して処理する場合は SIGKILL と頻繁に SIGPIPE が発生し、SIGSEGV や同様の致命的なエラーが発生し、いずれにしても正しいユーザー空間アプリに配信されることはありません。

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

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

編集: EINTR を再試行すること自体が、特定の条件下でセキュリティ上の欠陥になる可能性があることに気付きました。これにより、コードのそのセクションに新しい動作が導入されます。以前は 1 回試行しcloseてから戻るのに対し、シグナル フラッドに応答して無期限にループすることができます。これが qmail に何らかの問題を引き起こすかどうかはわかりません (結局のところ、qmailclose自体はそれがいつ戻るかを保証しません)。しかし、1 回の試行であきらめた方がコードの分析が容易になるのであれば、それは賢明な選択かもしれません。か否か。

再試行することで、信号が偽の障害を引き起こす DoS の欠陥を防ぐことができると考えるかもしれません。しかし、再試行すると、別の (より困難な) DoS の欠陥が許されます。この脆弱性では、シグナル フラッドが無期限の停止を引き起こします。DJB が qmail と djbdns を書いたときに興味を持っていた絶対的なセキュリティの質問である「このアプリは DoSed できますか?」というバイナリに関しては、違いはありません。何かが 1 回発生する可能性がある場合、通常、それは何度も発生する可能性があることを意味します。

于 2010-04-03T08:53:16.777 に答える
0

EINTR明示的に要求しない限り、壊れた unice だけが返されます。signal()再起動可能なシステム コールを有効にするための適切なセマンティクス(「BSD スタイル」)。sysv セマンティクス (シグナルの割り込み) を使用してシステム上でプログラムを構築する場合は、常に へsignal()の呼び出しを への呼び出しに置き換える必要があります。これは、存在しない場合にbsd_signal()自分で定義できます。sigaction()

さらに、シグナル ハンドラをインストールしていない限り、シグナルの受信時にシステムが応答しないことに注意してください。EINTRデフォルトのアクションがそのまま残っている場合、またはシグナルがアクションなしに設定されている場合、システム コールを中断することはできません。

于 2010-07-26T12:40:03.217 に答える