2016-03-24 10 views
0

Cでは、宣言時に長さを与えることができる配列があることを理解しています。それらの長さの宣言が他のプログラマーにとって、単に文字のバッファ長以上の読み込みを禁止することによってコードを保護するためにコンパイラを作成できるかどうかを知りたいのです。私が文字列を読み込むと、それは単に続行され、読み込みたいバッファの後に宣言された変数に格納されたデータを上書きし始めます。安全にデータを読み込む方法はありますか?Cはバッファオーバーフローをどのように扱いますか?

char arr[5];                 
char buff[5] = "cat";                                   
printf("The buffer holds: %s\n", buff);          
printf("Input a word to be held in \"arr\": ");        

scanf("%s", arr);               

printf("The array holds: %s\n", arr);          
printf("The buffer holds: %s\n", buff);          
printf("%c\n", arr[9]);  

編曲に読み込ま文字列が十分に長い場合は、「猫」は上書きされ、コンパイルフラグのどれもが何かをするように見えるん(私がコンパイル-Wextra -Wall -Werror -std = C99)のみ不平を言うのはバングラドです。 Cで安全な配列コードを書くにはどうすればよいですか?

+1

あなたは規律ある方法で行動します。 'C'標準は、コンパイラベンダーがあなたを守る義務がないことを指定しています。境界からの書き込みは、未定義の動作です。 – StoryTeller

+0

'-O2'を追加しましたか? –

+1

lm gt fy ... 'safe scanf' –

答えて

3

ある意味では、C言語自体はあなたを保護することも、配列の境界を越えてあなたを守ることもできません。より正確には、Cコンパイラは境界チェックを実行する必要はありませんが、それは許可されています。 (ほとんどのコンパイラは、その権限を利用して、非常に少数のはデフォルトでこれを行っています。。)

たとえば、あなたが書く場合:

int arr[10]; 
arr[20] = 42; 

動作は未定義です。それはあなたのプログラムがクラッシュすることを意味しません。エラーがになるか、またはが検出されないことを意味するものではありません。これは、ISO C標準、この規格は何 要件

典型的に課さないそのため移植性や、誤ったプログラムの構築物または誤ったデータの の使用時に

行動を、引用することであり、 Cコンパイラはおそらくarrのベースアドレスをとり、そのオフセットに20 * sizeof (int)のオフセットを加えたコードを生成し、その結果の場所に42を格納しようとします。明示的または暗黙的なチェックがなければ、これは他のデータ構造を破壊する可能性があり、プロセスが所有するメモリに書き込むことはできますが、他の目的には使用しないか、プログラムを終了する可能性があります。 (それとも#include <stdjoke.h>それは悪魔があなたの鼻の外に飛んで作ることができます。)

しかし準拠Cコンパイラ「インデックスが範囲に0〜9であることを確認するためのコードを追加し、それがにISN場合、いくつかの賢明な行動を取ることができますt。 Cは境界チェックを禁止しません。それだけでは必要ありません。

この特定のケースでは、コンパイル時に配列アクセスが範囲外であることを検出することは可能ですが、必須ではありません。そのため、コンパイラはコンパイル時の警告を発行できます。 (これは、実行時までインデックス値がわからない場合は不可能です。)

最終的には、境界外アクセスを避ける責任はプログラマにあります。コンパイラがあなたのためにそれをチェックすると仮定しないでください。

0

Cは、配列の末尾を越えないように保護していません。しかし、それを検出する方法があります。 (これはエラーのみを与えないのgccすなわち打ち鳴らすと連携)以下のコンパイル時のオプションを使用して、このコード

#include <assert.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#define ARRAY_SIZE 100 

int main(void) { 
    size_t i = 0; 
    char arr1[ARRAY_SIZE]; 
    char * arr2 = malloc(ARRAY_SIZE); 
    for(i = 0; i < 200; i++) { 
    arr1[i] = '1'; 
    arr2[i] = '2'; 
    } 

    for(i = 0; i < 200; i++) { 
    printf("%zu arr1[i]=%c \n", i, arr1[i]); 
    printf("%zu arr2[i]=%c \n", i, arr2[i]); 
    } 
    return 0; 
} 

を試してみて、このポスト

Setting up a bounds-protected array

を参照してください

gcc -O3 -Wall -std=c11 -pedantic array_overflow_at_03.c 

その後、してみてくださいそれを使用して

gcc -Wall -std=c11 -pedantic array_overflow_at_03.c 

これを行う各方法にはメリットがあり、アプリケーションのニーズによってどちらを使用するかが決まります。

+1

あなたの参考に感謝、ハリー。問題は、この方法が事後であること、すなわち不正アクセスが発生した後であることである。事実の間、メモリ管理システム(ハードウェア)のみを使用して保護することができます。しかし、どのように各変数を保護することができますか?おそらく、すべての違法アクセスをチェックすることは不可能です。そして、いつアクセスが違法ですか? –

+0

ハ、素敵な人! i == 101で、iが49( '1')にリセットされ、ループが永久に実行されるとき、 'i'は' arr1 [i] = '1';で上書きされます。 –

1

Cは「プログラマは最高の知っている」とこれは、Cは非常に高速である理由、それはすべてのチェックを行う必要はありませんです

「私はあなたが手に保持されていない」の理念に従います。安全なユーザー入力の場合

、あなたの線に沿って

何かのfgetsを使用することができます。

fgets(arr, sizeof(arr), stdin); 

arrが指定したサイズまでの入力を保持します。詳細については、fgetsのマニュアルページをお勧めします。 http://linux.die.net/man/3/fgets

stdinからすべての入力を取得するには、これを複数回呼び出す必要があります。

+0

代わりの方法をありがとう!これは、私がエラーチェックを最小限に抑えるためにクリックするだけのボタンをユーザーに提供したいというこのようなものになりがちです... 代わりに、C++を文字列やストリングストリームなどに使用します。 – guptashark

0

Cの配列サイズは、アレイに予約するメモリの量をコンパイラに指示します。配列の境界を越えているかどうかをチェックするコードは挿入されません。 int a[5];のサイズ '5'は、コンパイルされたプログラムにはどこにも格納されません。これはソースコード内にのみ存在します。ソースコードを見ることができる他のプログラマーはそれを見ることができます。他に誰もできません。

Cはあなたが何をしているのか確認しておらず(Lyle Rollemanの答えを参照)、Cはバッファオーバーランを「検出」しません。その結果、動作は未定義となります(いわゆる "未定義動作"またはUB)。多くの場合、スタックは上書きされ、スタック上には呼び出し元への戻りアドレスがあります。これは上書きされ、現在の関数が復帰したいときは、どこにもジャンプしません(スタックを慎重に上書きするハッカーからの "スタックエクスプロイト"によってこの動作が使用されるため、どこかにジャンプします) 。

関連する問題