2012-08-31 5 views
6

データを格納するために使用される固定サイズのバイナリバッファがいくつかあります。 そして、memcpyは、バッファを別のバッファにコピーするために使用されます。ソースバッファは宛先バッファよりも大きい可能性があるためです。バッファオーバーフローがあるかどうかをどのように検出できますか?memcpyのバッファオーバーフローを防ぐには?

+1

検出?あなたは目的地のバッファーサイズを知っていますか?このmemcpy(src、dst、sizeof(dst))のようなコードを書いてください – BSen

+0

ソースバッファとデスティネーションバッファのサイズを比較し、どちらが大きいかを確認してください。 – SingerOfTheFall

+1

@BSen '' sizeof''はポインタのサイズを与えるだけです。 – juanchopanza

答えて

8

ソースバッファにどれくらいのデータがあり、ターゲットバッファでどれだけの空き容量があるかを知る必要があります。

ソースバッファからコピーするすべてのデータのターゲットバッファに十分な領域がない場合は、memcpy()を呼び出しないでください。 (ソースがターゲットよりも大きい場合は、データを切り捨ててもよいかどうかを判断する必要があります)。

わからない場合は、コードを書き換えて十分なスペースがあることを確認します。それ以外の場合は、安全ではありません。

ソースバッファとターゲットバッファが重複する可能性がある場合は、memcpy()ではなくmemmove()を使用する必要があります。

C++では、最初にmemcpy()を使用してください。これはC++ではなくCスタイルの操作です。

+0

ありがとう。 C++でメモリコピーを行う正しい方法は何ですか? –

+1

@MichaelD:データを 'std :: vector <>'に格納し、単に 'vector2 = vector1'を使用してください。 – MSalters

+0

ベクターにデータを挿入するにはどうしたらいいですか? push_backを使用してバイトごとに日付を挿入しますか? –

3

srcとdestのバッファサイズは常に把握してください。

void *memcpy(void *dest, const void *src, size_t n); 

nsrcまたはdestサイズよりも最大になることはありません。

0

場合たとえば、あなたが持っている:

先4つのバイトサイズ

ソース5つのバイトサイズ

あなたは最大で、4つのバイト先のバッファにコピーすることを確認することができます

size_t getCopySize(size_t sourceSize, size_t destSize) 
{ 
    return (destSize <= sourceSize ? destSize : sourceSize); 
} 
memcpy(destination, source, getCopySize(sizeof(source),sizeof(destination))); 

アプリケーションでは、残りのデータが後でコピーされることを確認したり、一部のデータを無視できる場合はスキップすることもできます。

4

バッファオーバーフローが存在するかどうかを検出するにはどうすればよいですか?

私はあなたに3つまたは4つの選択肢があると思います。


第1の選択肢は、memcpyに対して「安全な」機能を提供することです。これは私の所持するコードの中で私が必要とするものであり、私は定期的にそれを監査します。また、すべてのパラメータが検証され、すべてのパラメータがアサートされている必要があります。

アサーションは自己デバッグコードを作成します。私は開発者にコードを書くことを望みます。私は彼らがデバッグ時間を浪費することを望んでいません。だから私は彼らにデバッグするコードを書く必要があります。 ASSERTはまた、物事をうまく文書化するので、ドキュメンテーションを吟味することもできます。リリースビルドでは、ASSERTはプリプロセッサマクロによって削除されます。

errno_t safe_memcpy(void* dest, size_t dsize, void* src, size_t ssize, size_t cnt) 
{ 
    ASSERT(dest != NULL); 
    ASSERT(src != NULL); 
    ASSERT(dsize != 0); 
    ASSERT(ssize != 0); 
    ASSERT(cnt != 0); 

    // What was the point of this call? 
    if(cnt == 0) 
     retrn 0; 

    if(dest == NULL || src == NULL) 
     return EINVALID; 

    if(dsize == 0 || ssize == 0) 
     return EINVALID; 

    ASSERT(dsize <= RSIZE_MAX); 
    ASSERT(ssize <= RSIZE_MAX); 
    ASSERT(cnt <= RSIZE_MAX); 

    if(dsize > RSIZE_MAX || ssize > RSIZE_MAX || cnt > RSIZE_MAX) 
     return EINVALID; 

    size_t cc = min(min(dsize, ssize), cnt); 
    memmove(dest, src, cc); 

    if(cc != cnt) 
     return ETRUNCATE; 

    return 0; 
} 

あなたsafe_memcpy戻っ非0の場合、不正なパラメータまたは潜在的なバッファオーバーフローのようなエラーが発生しました。


第2の選択肢は、Cスタンダードによって提供される「より安全な」機能を使用することです。 CはISO/IEC TR 24731-1, Bounds Checking Interfacesを介してより安全な機能を備えています。適合プラットフォームでは、gets_ssprintf_sと呼ぶことができます。 (文字列が常にNULLであることを保証するなど)一貫性のある戻り値(成功した場合は0、errno_tなど)を提供します。

errno_t err = memcpy_s(dest, dsize, src, cnt); 
... 

残念ながら、gccとglibcはC標準に準拠していません。境界検査インターフェイスと呼ばれるUlrich Drepper(glibcの管理者の1人)は、"horribly inefficient BSD crap"の境界チェックインターフェイスと呼ばれ、決して追加されませんでした。


第3の選択肢は、プラットフォームの「より安全な」インタフェースが存在する場合はそのインタフェースを使用することです。 Windowsでは、ISO/IEC TR 24731-1, Bounds Checking Interfacesの場合と同じです。また、String Safeライブラリもあります。

アップルとBSDでは、memcpyの「より安全な」機能はありません。しかし、あなたはstrlcpy,strlcat、お友達のようなより安全な文字列関数を持っています。


Linuxでは、4番目の選択肢はFORTIFY_SOURCEです。 FORTIFY_SOURCEは、memcpy,strcpygetsなどのリスクの高い機能の「より安全な」バリアントを使用します。コンパイラは、宛先バッファサイズを推測できるときに、より安全なバリアントを使用します。コピーが宛先バッファ・サイズを超える場合、プログラムはabort()を呼び出します。コンパイラが宛先バッファサイズを推測できない場合、「より安全な」バリアントは使用されません。

テストのためにFORTIFY_SOURCEを無効にするには、プログラムを-U_FORTIFY_SOURCEまたは-D_FORTIFY_SOURCE=0でコンパイルする必要があります。

関連する問題