web-dev-qa-db-ja.com

getline()対fgets():メモリ割り当ての制御

ファイルから行を読み取るには、getline()およびfgets() POSIX関数があります(恐ろしいgets()は無視されます)。 getline()は、必要に応じてラインバッファーを割り当てるため、fgets()よりも優先されるのは常識です。

私の質問です:それは危険ではありませんか?偶然または悪意により、誰かが_'\n'_バイトを含まない100GBのファイルを作成した場合は、それがgetline()の呼び出しで異常な量のメモリを割り当てませんか?

23
edavid

私の質問です:それは危険ではありませんか?偶然または悪意により、誰かが「\ n」バイトを含まない100GBのファイルを作成した場合はどうなりますか?これにより、getline()呼び出しで異常な量のメモリが割り当てられませんか?

はい、あなたが説明することはもっともらしいリスクです。しかしながら、

  • プログラムが行全体を一度にメモリにロードする必要がある場合、getline()がそれを試みることを許可することは、fgets()で実行する独自のコードを書くことよりも本質的に危険ではありません。そして
  • そのような脆弱性を持つプログラムがある場合、setrlimit()を使用して、予約できる(仮想)メモリの総量を制限することにより、リスクを軽減できます。これを使用して、システムの残りの部分に干渉するのに十分なメモリを正常に割り当てる代わりに、それを失敗させることができます。

全体として最良だと思うのは、そもそも全行単位(一度に)の入力を必要としないコードを記述することですが、そのようなアプローチには独自の複雑さがあります。

17
John Bollinger

はい、危険な場合があります。これが他のコンピューターでどのように機能するかわからないが、以下のコードを実行すると、コンピューターがフリーズしてハードリセットが必要になるまでになりました。

/* DANGEROUS CODE */

#include <stdio.h>

int main(void)
{
    FILE *f;
    char *s;
    size_t n = 0;

    f = fopen("/dev/zero", "r");
    getline(&s, &n, f);

    return 0;
}
11
Tom Zych

getline関数はmallocreallocを内部的に使用し、失敗した場合は-1を返すため、malloc(100000000000)を呼び出そうとした場合と結果に違いはありません。 。つまり、errnoENOMEMに設定され、getlineは-1を返します。

したがって、getlineを使用した場合でも、fgetsを使用して同じことを行おうとした場合でも、完全な行を確実に読み取るために手動でメモリ割り当てを行う場合は、同じ問題が発生します。

3
dbush

一部のコーディングガイドライン(MISRA Cなど)では、動的メモリ割り当て(getline()など)を使用できない場合があります。その理由は、たとえば、メモリリークの回避などです。

許容されるすべての行の最大サイズがわかっている場合は、fgets()の代わりにgetline()を使用してメモリ割り当てを回避し、潜在的なメモリリークポイントを1つ削除することができます。

1
SKi

実際には、長すぎる行をどのように処理するかによって異なります。

まともなサイズのバッファを使用したfgetsは一般的に機能し、「失敗」したことを検出できます-バッファの末尾に改行文字がありません。バッファがオーバーフローしているかどうかを確認するために常にstrlen()を実行することを回避することは可能ですが、それは別の問題です。

おそらくあなたの戦略は単に処理できない行をスキップすることです、あるいはおそらく行の残りはとにかく無視するコメントにすぎないでしょう。その場合、fgetsをループに入れるのは簡単です割り当てペナルティなしで行の残りを破棄します。

とにかく行全体を読みたい場合は、getlineの方が適しています。悪意のあるユーザーは、あなたが説明した悪い振る舞いを引き起こしたり、入力ファイル名として/ dev/randomなどを渡したりするために、多くのディスク容量を必要とします。

繰り返しますが、getlineが再割り当てできない場合は、回復できる方法で失敗しますが、複数行の読み取りにバッファーを再利用している場合は、まだ割り当てられており、失敗する前に大きくなる可能性があるため、さらに読み込もうとする前にエラーが発生しました。

0
Gem Taylor

getline()プログラムのメモリ管理を少し軽減するためにバッファを再割り当てします。

しかし、実際には、これにより、大量のメモリが割り当てられる可能性があります。それが問題になる場合は、メモリを暗黙的に割り当てない関数を使用するために、追加の手順を実行する必要があります。

0