web-dev-qa-db-ja.com

ファイル記述子を強制的に閉じる最も安全な方法

時々、ファイルシステムをアンマウントしたり、ループデバイスを切り離したりする必要がありますが、それはbusyです。これは、ファイル記述子が開いているため、おそらくsmbサーバープロセスが原因です。

強制的にアンマウントするには、問題のあるプロセスを強制終了します(または_kill -SIGTERM_を試します)が、smb接続を閉じます(開いているファイルの一部を閉じる必要がない場合でも)。 。

プロセスに特定のファイル記述子を強制的に強制するハックな方法について説明します heregdbを使用してclose(fd)を呼び出します。しかし、これは危険なようです。閉じた記述子がリサイクルされるとどうなりますか?プロセスは、完全に異なるファイルを参照するようになったことに気づかずに、古い格納された記述子を使用する場合があります。

私はアイデアを持っていますが、どのような欠陥があるのか​​わかりません:gdbを使用し、_/dev/null_で_O_WRONLY_を開きます(編集:_O_PATH_として提案されたコメントより良い代替手段)、次に_dup2_を使用して問題のファイル記述子を閉じ、その記述子を_/dev/null_に再利用します。この方法では、ファイル記述子への読み取りまたは書き込みは失敗します。

このような:

_Sudo gdb -p 234532
(gdb) set $dummy_fd = open("/dev/null", 0x200000) // O_PATH
(gdb) p dup2($dummy_fd, offending_fd)
(gdb) p close($dummy_fd)
(gdb) detach
(gdb) quit
_

何がうまくいかないのでしょうか?

6

gdbを使用してプロセスをいじるのはほとんど安全ではありませんが、緊急事態が発生し、プロセスを開いたままにし、関係するすべてのリスクとコードを理解する必要がある場合に必要になることがあります。

ほとんどの場合、私は単にプロセスを終了しますが、場合によっては異なり、関連するシステムとプロセスを所有する環境、プロセスの実行状況、「強制終了してもよい」または「いいえ、まず最初に連絡してください」など。これらの詳細は、ほこりが落ち着いたら、検視会議で解決する必要がある場合があります。移行が計画されている場合は、問題のあるファイル記述子を開いているプロセスがあるかどうかを事前に確認して、緊急でない設定(移行の時間帯にのみ実行されるcronジョブまたはその他のスケジュールされたタスク)で処理できるようにすることをお勧めします。昼間のみチェックした場合、簡単に見逃される可能性があります)。

書き込み専用vs読み取りvs読み書き

すべてのファイル記述子が書き込み専用ではないため、ファイル記述子O_WRONLYを再度開くという考えには問題があります。 John ViegaとMatt Messierは、「Secure Programming Cookbook for C and C++」の本でより微妙なアプローチをとり、標準入力や標準エラーとは異なる方法で標準入力を処理します(p。25、「ファイル記述子の安全な管理」)。

static int open_devnull(int fd) {
  FILE *f = 0;

  if (!fd) f = freopen(_PATH_DEVNULL, "rb", stdin);
  else if (fd == 1) f = freopen(_PATH_DEVNULL, "wb", stdout);
  else if (fd == 2) f = freopen(_PATH_DEVNULL, "wb", stderr);
  return (f && fileno(f) == fd);
}

gdbの場合、記述子(またはFILE *ハンドル)が読み取り専用か、読み取り専用か書き込み専用か、および/dev/nullで適切な置換が開かれているかどうかを確認する必要があります。そうでない場合、書き込み専用になった一度読み取り専用のハンドルは、プロセスがそこから読み取ろうとすると、不要なエラーを引き起こします。

何が問題でしょうか?

ファイル記述子(およびおそらくFILE *ハンドルも)が背後でいじられるときにプロセスがどのように正確に動作するかはプロセスに依存し、その記述子が現在使用されていない「悪夢モード」に使用されない場合の「大した問題」から変化しますフラッシュされていないデータ、ファイルが適切に閉じられていなかったことを示すインジケータ、またはその他の予期しない問題が原因で、どこかで破損したファイル。

FILE *ハンドルの場合、ハンドルを閉じる前にfflush(3)呼び出しを追加すると役立つ場合や、ダブルバッファリングやその他の問題が発生する場合があります。これは、ソースコードが何を期待しているのかを正確に知らなくても、gdbでランダムな呼び出しを行う際のいくつかの危険の1つです。ソフトウェアには、fd記述子または処理する必要があるFILE *ハンドルに加えて、さらに複雑なレイヤーが組み込まれている場合があります。コードにパッチを当てるモンキーは、簡単にモンキーレンチになる可能性があります。

概要

標準の終了シグナルをプロセスに送信すると、システムが正常にシャットダウンするときと同じように、リソースを適切に閉じる機会が与えられます。 gdbを使用してプロセスをいじると、適切に処理が完了せず、状況がさらに悪化する可能性があります。

1
thrig

o_WRONLYで/ dev/nullを開き、次にdup2で問題のファイル記述子を閉じて、その記述子を/ dev/nullに再利用します。この方法では、ファイル記述子への読み取りまたは書き込みは失敗します。

記述子を/dev/nullに複製すると、書き込みはfailではなくsucceed、読み取りは成功し、0(eof)を返します。

これは必要な場合とそうでない場合があります。

Linuxでは、フラグ= 3(O_WRONLY|O_RDWR aka O_NOACCESS)でファイルを開くこともできます。これにより、読み取りまたは書き込みがEBADFで失敗します。

このファイルはioctlでのみ使用できます-これは、他の回答やコメントでは触れられていない危険をもたらします。ファイル記述子で実行される操作は読み取りと書き込みだけではありません。 (lseekまたはftruncateはどうですか?).

更新:

ドキュメントに記載されていないO_WRONLY|O_RDWRO_PATH = 010000000 / 0x200000よりも良いものを見つけました。 open(2)マンページによると:

O_PATH (since Linux 2.6.39)
     Obtain a file descriptor that can be used for two  purposes:  to
     indicate a location in the filesystem tree and to perform opera-
     tions that act purely at the file descriptor  level.   The  file
     itself  is not opened, and other file operations (e.g., read(2),
     write(2), fchmod(2), fchown(2), fgetxattr(2), mmap(2)) fail with
     the error EBADF.

    The  following operations can be performed on the resulting file
     descriptor:

    *  close(2); fchdir(2) (since Linux 3.5); fstat(2) (since  Linux
        3.6).

    *  Duplicating  the  file  descriptor (dup(2), fcntl(2) F_DUPFD,
        etc.).
2
pizdelect