8

私は数週間、システムに依存しない必要がある 20 年のコードを更新するという問題に対処してきました (Linux と Windows の両方で動作します)。これには、Time-of-Check、Time-of-Use (TOCTOU) の問題が含まれます。ここにスレッドを立てましたが、あまりうまくいきませんでした。しばらく反芻し、問題を深く調べた後、私の質問が少しよく理解できたと思います。もう少し上手に聞いてもいいのかな…

私が読んだことから、コードはファイルが存在するかどうかを確認し、アクセス可能であればファイルを開き、いくつかの操作を行い、最後にファイルを閉じる必要があります。これを行う最善の方法は、 へlstat()の呼び出し、 へfopen()の呼び出し、fstat()(TOCTOU を除外するための) への呼び出し、そして操作とファイルのクローズです。

ただし、相互互換性のために使用すべきではないのとほぼ同じように、システムにとらわれないプログラムでの使用を除外し、C標準で定義されているのではなく、POSIXで定義されているlstat()と信じるようになりました。これをどのように実装しますか?fstat()open()

私の最初の投稿を見ると、20 年前の開発者が C プリプロセッサを使用して相互互換性のある部分にコードを切り分けていたことがわかりますが、それを行ったとしても、何を置き換えるlstat()fstat()(彼らのWindows の対応物)。

編集: この投稿に短縮コードを追加しました。不明な点がある場合は、元の投稿に移動してください

#ifdef WIN32
    struct _stat buf;
#else
    struct stat buf;
#endif //WIN32

    FILE *fp;
    char data[2560];

    // Make sure file exists and is readable
#ifdef WIN32
    if (_access(file.c_str(), R_OK) == -1) {
#else
    if (access(file.c_str(), R_OK) == -1) {
#endif //WIN32

        char message[2560];
        sprintf(message, "File '%s' Not Found or Not Readable", file.c_str());
        throw message;
        }

    // Get the file status information
#ifdef WIN32
    if (_stat(file.c_str(), &buf) != 0) {
#else
    if (stat(file.c_str(), &buf) != 0) {
#endif //WIN32

        char message[2560];
        sprintf(message, "File '%s' No Status Available", file.c_str());
        throw message;
        }

    // Open the file for reading
    fp = fopen(file.c_str(), "r");
    if (fp == NULL) {
        char message[2560];
        sprintf(message, "File '%s' Cound Not be Opened", file.c_str());
        throw message;
    }

    // Read the file
    MvString s, ss;
    while (fgets(data, sizeof(data), fp) != (char *)0) {
        s = data;
        s.trimBoth();
        if (s.compare( 0, 5, "GROUP" ) == 0) {
            //size_t t = s.find_last_of( ":" );
            size_t t = s.find( ":" );
            if (t != string::npos) {
                ss = s.substr( t+1 ).c_str();
                ss.trimBoth();
                ss = ss.substr( 1, ss.length() - 3 ).c_str();
                group_list.push_back( ss );
            }
        }
    }
    // Close the file
    fclose(fp);
}
4

1 に答える 1

9

ファイルが存在し、開くことができるかどうかを確認する信頼できる方法は、開いてみることです。開いていれば大丈夫でした。開いていない場合は、何が問題なのかを分析するために時間を費やすことを検討できます。

このaccess()関数は、あなたが考えていることとは異なる質問を正式に行います。「実ユーザー ID または実グループ ID はファイルにアクセスできますか」と尋ねますが、プログラムは実効ユーザー ID または実効グループ ID を使用してファイルにアクセスします。プログラムが SUID または SGID を実行しておらず、SUID または SGID を実行しているプログラムから起動されていない場合 (これは通常のケースです)、違いはありません。しかし、質問は異なります。

stat()or の使用はlstat()役に立たないようです。特に、lstat()シンボリックリンクから開始するかどうかのみを通知しますが、コードはそれを気にしません。

access()と の呼び出しの両方で、stat()脆弱性の TOCTOU ウィンドウが提供されます。ファイルは、存在すると報告された後に削除されるか、存在しないと報告された後に作成される可能性があります。

単に呼び出しfopen()て、それが機能するかどうかを確認する必要があります。コードはより単純になり、TOCTOU の問題に対してより耐性があります。open()追加のコントロール ( など) をすべて使用するかどうかを検討してO_EXCLから、ファイル記述子をファイル ポインター ( fdopen()) に変換する必要がある場合があります。

これはすべて Unix 側に当てはまります。

詳細は異なりますが、Windows 側では、ファイルを開いて失敗した場合に適切に対応することをお勧めします。

どちらのシステムでも、関数 open に提供されるオプションが適切であることを確認してください。

于 2015-06-02T18:32:22.787 に答える