web-dev-qa-db-ja.com

scanfがCでバッファオーバーフローを引き起こすのを防ぐ方法は?

私はこのコードを使用します:

while ( scanf("%s", buf) == 1 ){

ランダムな長さの文字列を渡すことができるように、可能なバッファオーバーフローを防ぐ最良の方法は何でしょうか?

たとえば、次を呼び出すことで入力文字列を制限できることを知っています。

while ( scanf("%20s", buf) == 1 ){

しかし、ユーザーが入力したものは何でも処理できるようにしたいと思います。または、scanfを使用してこれを安全に実行できず、fgetsを使用する必要がありますか?

73
goe

Gccを使用している場合、GNU拡張a指定子を使用して、入力を保持するためにscanf()がメモリを割り当てることができます。

int main()
{
  char *str = NULL;

  scanf ("%as", &str);
  if (str) {
      printf("\"%s\"\n", str);
      free(str);
  }
  return 0;
}

編集:ジョナサンが指摘したように、指定子が異なる可能性があるため、scanf manページを参照する必要があります(%m)コンパイル時に特定の定義を有効にする必要がある場合があります。

29
John Ledbetter

ほとんどの場合、fgetssscanfの組み合わせが機能します。もう1つは、入力が適切にフォーマットされている場合、独自のパーサーを作成することです。また、2番目の例を安全に使用するには少し変更する必要があることに注意してください。

_#define LENGTH          42
#define str(x)          # x
#define xstr(x)         str(x)

/* ... */ 
int nc = scanf("%"xstr(LENGTH)"[^\n]%*[^\n]", array); 
_

上記は、改行(_\n_)文字を含まない入力ストリームを破棄します。これを使用するには、getchar()を追加する必要があります。また、ストリームの終わりに到達したかどうかを確認します。

_if (!feof(stdin)) { ...
_

そしてそれはそれについてです。

9
dirkgently

scanf(3)とそのバリアントを直接使用すると、多くの問題が発生します。通常、ユーザーと非対話型のユースケースは、入力行の観点から定義されます。十分なオブジェクトが見つからない場合、より多くの行で問題を解決できる場合はまれですが、それがscanfのデフォルトモードです。 (ユーザーが最初の行に数字を入力することを知らなかった場合、2番目と3番目の行はおそらく役に立たないでしょう。)

少なくともfgets(3)であれば、プログラムに必要な入力行数がわかっていて、バッファオーバーフローはありません...

4
DigitalRoss

入力の長さを制限することは間違いなく簡単です。ループを使用して、一度に少しずつ読み取り、必要に応じて文字列のスペースを再割り当てすることにより、任意の長さの入力を受け入れることができます...

しかし、それは多くの作業であるため、ほとんどのCプログラマーは任意の長さで入力を切り捨てます。これはすでに知っていると思いますが、fgets()を使用しても、任意の量のテキストを受け入れることはできません。制限を設定する必要があります。

1
Mark Bessey

文字列に必要なメモリを割り当てる関数を作成するのはそれほど面倒ではありません。それは少し前に書いた小さなC関数です。私はいつもそれを使って文字列を読み込みます。

読み取り文字列を返すか、メモリエラーが発生した場合はNULLを返します。ただし、文字列をfree()し、常に戻り値を確認する必要があることに注意してください。

#define BUFFER 32

char *readString()
{
    char *str = malloc(sizeof(char) * BUFFER), *err;
    int pos;
    for(pos = 0; str != NULL && (str[pos] = getchar()) != '\n'; pos++)
    {
        if(pos % BUFFER == BUFFER - 1)
        {
            if((err = realloc(str, sizeof(char) * (BUFFER + pos + 1))) == NULL)
                free(str);
            str = err;
        }
    }
    if(str != NULL)
        str[pos] = '\0';
    return str;
}
1
user2247995