13

fseekとftellを使用してファイルのサイズを決定する方法を示す投稿を読みました。

FILE *fp;
long file_size;
char *buffer;

fp = fopen("foo.bin", "r");
if (NULL == fp) {
 /* Handle Error */
}

if (fseek(fp, 0 , SEEK_END) != 0) {
  /* Handle Error */
}

file_size = ftell(fp);
buffer = (char*)malloc(file_size);
if (NULL == buffer){
  /* handle error */
}

この手法を使用しようとしていましたが、潜在的な脆弱性を説明するこのリンクに遭遇しました。

リンクでは、代わりにfstatを使用することを推奨しています。誰かがこれについてコメントできますか?

4

5 に答える 5

16

このリンクは、CERTからの多くの無意味なCコーディングアドバイスの1つです。それらの正当化は、C標準が実装に許可する自由に基づいていますが、POSIXでは許可されていないfstatため、代替手段として使用するすべての場合に関係ありません。

POSIXには以下が必要です。

  1. "b"修飾子はfopen効果がありません。つまり、テキストモードとバイナリモードは同じように動作します。これは、テキストファイルでUBを呼び出すことについての彼らの懸念が無意味であることを意味します。

  2. そのファイルには、書き込み操作と切り捨て操作によって設定されたバイト解像度サイズがあります。これは、ファイルの最後にある乱数のnullバイトに関する懸念が無意味であることを意味します。

悲しいことに、彼らが出版するこのようなナンセンスのすべてで、どのCERT出版物を真剣に受け止めるかを知るのは難しいです。それらの多くは深刻なので、これは残念です。

于 2011-05-11T00:29:59.280 に答える
7

あなたの目標がファイルのサイズを見つけることであるならば、間違いなくあなたはfstat()またはその友人を使うべきです。これは、はるかに直接的で表現力豊かな方法です。より回りくどいfseek / ftellの方法ではなく、文字通り、ファイルの統計情報を通知するようにシステムに要求しています。

ボーナスのヒント:ファイルが利用可能かどうかだけを知りたい場合は、ファイルをaccess()開いたり、統計したりするのではなく、を使用してください。これは、多くのプログラマーが気付いていないさらに単純な操作です。

于 2011-05-11T00:18:14.683 に答える
4

使用しない理由fstatは、fstatPOSIXですがfopen、C標準の一部であるためですftellfseek

C標準を実装しているがPOSIXを実装していないシステムがあるかもしれません。そのようなシステムfstatでは、まったく機能しません。

于 2011-05-11T00:36:34.140 に答える
4

私は、通常、コードのメインストリームでfseek/ftellコードを直接使用するべきではないという彼らの基本的な結論に同意する傾向がありますが、おそらくどちらも使用すべきではありませんfstat。ファイルのサイズが必要な場合、ほとんどのコードでは、のような明確で直接的な名前の何かを使用する必要がありますfilesize

さて、利用可能な場合、および(たとえば) Windows(通常は利用できない最も明白なプラットフォーム)を使用して実装する方がおそらく良いでしょう。fstatFindFirstFilefstat

話の反対側は、fseekバイナリファイルに関する制限の多く(ほとんど?)が実際にはCP / Mに由来し、ファイルのサイズをどこにも明示的に保存していなかったことです。テキストファイルの終わりは、control-Zによって通知されました。ただし、バイナリファイルの場合、実際に知っているのは、ファイルの保存に使用されたセクターだけです。最後のセクターでは、多くの場合(常にではありませんが)ゼロで埋められた未使用のデータがありました。残念ながら、有意であったゼロ、および/または有意ではなかった非ゼロ値が存在する可能性があります。

C規格全体が承認される直前に作成されていた場合(たとえば、1988年に開始され、1989年に終了した場合)、CP/Mを完全に無視していた可能性があります。しかし、良くも悪くも、CP / Mがまだ十分に広く使用されていて無視できないとき、彼らは1982年かそこらのようなものでC標準の作業を開始しました。CP / Mがなくなるまでに、多くの決定がすでに行われており、誰もがそれらを再検討したいとは思わなかった。

しかし、今日のほとんどの人にとって、意味はありません。ほとんどのコードは、大規模な作業なしではCP/Mに移植されません。これは、対処すべき比較的小さな問題の1つです。最新のプログラムをコードとデータの両方でわずか48K(またはそれくらい)のメモリで実行することは、はるかに深刻な問題です(大容量記憶装置用に最大メガバイト程度を持っていることは別の深刻な問題です)。

ただし、CERTには1つの良い点があります。ファイルのサイズを見つけて、そのスペースを割り当ててから、ファイルの内容がそこに収まると想定するべきではないということです。fseek / ftellは最新のシステムで正しいサイズを提供しますが、そのデータは実際にデータを読み取るまでに古くなる可能性があるため、とにかくバッファーをオーバーランする可能性があります。

于 2011-05-11T01:15:36.227 に答える
2

C規格によると、§7.21.3

のように、ファイル位置インジケータをファイルの終わりに設定するとfseek(file, 0, SEEK_END)、バイナリストリーム(末尾のヌル文字の可能性があるため)または初期シフト状態で確実に終了しない状態依存のエンコーディングを持つストリームの動作が定義されません。

法定書のような人は、ファイルサイズを次のように計算することでこのUBを回避できると考えるかもしれません。

fseek(file, -1, SEEK_END);
size = ftell(file) + 1;

しかし、C規格はこれも述べています:

バイナリストリームは、whence値がSEEK_ENDのfseek呼び出しを意味のある形でサポートする必要はありません。

結果として、fseek/SEEK_ENDに関してこれを修正するためにできることは何もありません。それでも、OS固有のAPI呼び出しではなく、fseek/ftellを使用したいと思います。

于 2012-11-07T11:59:19.120 に答える