14

dlopen を使用して動的ライブラリ (Linux、i386、.so) をロードする C++ で書かれたプログラムがあります。その後ライブラリ ファイルを変更すると、プログラムがクラッシュする傾向があります。おそらくファイルは単にメモリにマップされているため、これは理解できます。

私の質問は、ファイルのコピーを自分で作成してそれを dlopen する以外に、その後の変更に対して安全な共有オブジェクトをロードする方法、またはロードした共有オブジェクトへの変更から回復する方法はありますか? ?

明確化:問題は、「プログラムをクラッシュさせずに新しいライブラリをインストールするにはどうすればよいか」ではなく、「私が管理していない誰かがライブラリをコピーしている場合、それを防ぐことができるか?」です。

4

5 に答える 5

19

新しいライブラリをインストールする前にライブラリを使用している場合rm、システムは i ノードを割り当て、ファイルを開き、プログラムを実行したままにしていると思います。(そして、プログラムが最終的に終了すると、ほとんど隠されているが、まだそこにあるファイル リソースが解放されます。)

更新:わかりました、明確化後。MAP_COPY動的リンカは、フラグが利用可能な場合は に渡すことで、実際にはこの問題を完全に「解決」しmmap(2)ます。ただし、MAP_COPYLinux には存在せず、今後予定されている機能ではありません。2番目に良いのはMAP_DENYWRITE、ローダーが使用していると私が信じているものであり、Linux APIにあり、Linuxが使用していたものです。領域がマップされている間は、書き込みでエラーが発生します。それでもrmとreplaceを許可する必要があります。ここでの問題は、ファイルへの読み取りアクセス権を持つ人なら誰でもファイルをマップして書き込みをブロックできることです。これにより、ローカルの DoS ホールが開かれます。(考えてみてください/etc/utmp。実行許可ビットを使用してこれを修正するという提案があります。)

これは気に入らないでしょうが、MAP_DENYWRITE機能を復元する簡単なカーネル パッチがあります。Linux にはまだ機能がありmmap(2)ます。アーキテクチャごとに複製されたコードでパッチを適用する必要があります.ia32の場合、ファイルはarch/x86/ia32/sys_ia32.c.

asmlinkage long sys32_mmap2(unsigned long addr, unsigned long len,
                            unsigned long prot, unsigned long flags,
                            unsigned long fd, unsigned long pgoff)
{
        struct mm_struct *mm = current->mm;
        unsigned long error;
        struct file *file = NULL;

        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); // fix this line to not clear MAP_DENYWRITE

資格情報を持つ悪意のあるローカル ユーザーがいない限り、これは問題ありません。これはリモート DoS ではなく、単なるローカル DoS です。

于 2009-10-15T17:11:31.147 に答える
7

ライブラリの新しいバージョンをインストールする場合、正しい手順は、同じディレクトリに新しいファイルを作成し、古いファイルの名前を変更することです。開いている間は古いファイルが残り、引き続き使用されます。

RPM などのパッケージ マネージャーはこれを自動的に行うため、実行中に共有ライブラリと実行可能ファイルを更新できますが、古いバージョンは実行され続けます。

新しいバージョンを取得する必要がある場合は、プロセスを再起動するか、ライブラリをリロードします (プロセスを再起動した方がよいように思えます)。プログラムはそれ自体を実行できます。init でもこれを行うことができます。

于 2009-10-15T20:11:51.463 に答える
4

ファイルの書き込み権限を持っている場合、誰かがライブラリを上書きするのを防ぐことはできません。

メモリはライブラリ ファイルをマップするためdlopen、ファイルに対するすべての変更は、それを開いているすべてのプロセスで表示されます。

このdlopen関数は、共有ライブラリを使用する最もメモリ効率の良い方法であるため、メモリ マッピングを使用します。プライベート コピーはメモリを浪費します。

他の人が言ったように、Unix で共有ライブラリを置き換える適切な方法は、ライブラリを新しいコピーで上書きするのではなく、リンク解除または名前変更を使用することです。コマンドはこれinstallを適切に行います。

于 2009-11-10T00:00:21.117 に答える
1

これは興味深い質問です。私はLinuxでこのような穴を見つけるのが嫌いで、それらを修正する方法を見つけるのが大好きです。

私の提案は、Linux上の一時ファイルに関するこの質問に対する@PaulTomblinの回答に触発されています。ここでの他の回答のいくつかは、このメカニズムの存在を示唆していますが、要求されたようにクライアントアプリケーションからそれを悪用する方法については説明していません。

私はこれをテストしていないので、どれだけうまくいくかわかりません。また、一時ファイルが作成されてからリンクが解除されるまでの短い期間に関連する競合状態に関連する、セキュリティ上の軽微な懸念がある場合があります。また、私が提案しているライブラリのコピーを作成する可能性については、すでにお気づきのことと思います。私のひねりは、ライブラリを実際に開いたままにしておく時間に関係なく、一時的なコピーがファイルシステムのエントリとして一瞬だけ存在することです。

ライブラリをロードする場合は、次の手順に従います。

  1. ファイルを一時的な場所にコピーします。おそらくmkstemp()で始まります。
  2. dlopen()を使用してライブラリの一時コピーをロードします
  3. unlink()一時ファイル
  4. 通常どおり続行します。dlclose()を実行すると、ファイルのリソースが自動的に削除されます。

実際にファイルをコピーしなくても、「ファイルをコピーする」手順を実行するための本当に簡単な方法があればいいのにと思います。ハードリンクが思い浮かびますが、これらの目的にはうまくいかないと思います。Linuxにlink()と同じくらい使いやすいコピーオンライトメカニズムがあれば理想的ですが、私はそのような機能を知りません。

編集:@Zan Lynxの回答は、ダイナミックライブラリのカスタムコピーを作成することは、それらが複数のプロセスに複製される場合、無駄になる可能性があることを指摘しています。したがって、私の提案は、それが慎重に適用された場合にのみ意味があります-踏みつけられるリスクのあるライブラリ(おそらく/libまたは/usr / libにファイルを含まないすべてのライブラリの小さなサブセット)にのみ適用されます。

于 2009-11-09T23:41:10.767 に答える
1

ライブラリがメモリのどこにマップされているかがmprotectわかれば、それを書き込み可能にして、各ページに単純な書き込みを実行できる可能性があります (たとえば、各ページの最初のバイトを読み込んで書き戻すなど)。これにより、すべてのページのプライベート コピーが取得されます。

「mprotect」が機能しない場合 (機能しない可能性があります。元のファイルが読み取り専用で開かれている可能性があります)、リージョンを別の場所にコピーし、(を使用してmmap) リージョンをプライベートな書き込み可能なリージョンに再マップしてから、コピーします。リージョンバック。

OSに「この読み取り専用領域をコピーオンライト領域に変換する」機能があればいいのにと思います。そのようなものは存在しないと思いますが。

これらのシナリオのいずれにおいても、依然として脆弱なウィンドウがあります。dlopen が初期化子を呼び出している間、または remap 呼び出しが発生する前に、誰かがライブラリを変更できます。@DigitalRoss が説明するように動的リンカーを修正できない限り、本当に安全ではありません。

とにかく、あなたの下からあなたのライブラリを編集しているのは誰ですか? その人を見つけて、フライパンで頭を殴ってください。

于 2009-11-08T22:05:34.943 に答える