プログラムには、データを格納するために使用される固定サイズのバイナリバッファがいくつかあり、memcpyはバッファをあるバッファから別のバッファにコピーするために使用されます。ソースバッファがデスティネーションバッファよりも大きい可能性があるため、バッファオーバーフローがあるかどうかをどのように検出できますか?
4 に答える
ソースバッファにあるデータの量と、ターゲットバッファに使用できるスペースの量を知る必要があります。
memcpy()ソースバッファからコピーするすべてのデータに対してターゲットバッファに十分なスペースがない場合は、呼び出さないでください。(ソースがターゲットよりも大きい場合は、データを切り捨ててもよいかどうかを判断する必要があります。)
わからない場合は、コードを書き直して、スペースがどれだけあるかがわかるようにします。そうでなければ、それは安全ではありません。
memmove()ソースバッファとターゲットバッファが重複する可能性がある場合は、ではなくを使用する必要があることに注意してくださいmemcpy()。
C ++では、そもそも使用を検討してくださいmemcpy()。これは、C++ではなくCスタイルの操作です。
バッファオーバーフローがあるかどうかをどのように検出できますか?
私はあなたが3つか4つの選択肢(与えるか取るか)があると思います。
最初の選択肢は、に「安全な」関数を提供することです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、境界チェックインターフェイスを介した「より安全な」機能があります。gets_s準拠するプラットフォームでは、とを呼び出すだけですsprintf_s。これらは、一貫した動作(常に文字列がNULL終了することを保証するなど)と一貫した戻り値(成功時の0やerrno_t)を提供します。
errno_t err = memcpy_s(dest, dsize, src, cnt);
...
残念ながら、gccとglibcはC標準に準拠していません。Ulrich Drepper(glibcメンテナの1人)は、境界チェックインターフェイスを「ひどく非効率的なBSDがらくた」と呼び、追加されることはありませんでした。
3番目の選択肢は、プラットフォームの「より安全な」インターフェースが存在する場合はそれを使用することです。Windowsでは、これはISO / IEC TR 24731-1、境界チェックインターフェイスの場合と同じです。StringSafeライブラリもあります。
AppleとBSDでは、の「より安全な」機能はありませんmemcpy。strlcpyただし、、strlcatやfriendsなどのより安全な文字列関数はあります。
Linuxでは、4番目の選択肢はFORTIFY_SOURCEを使用することです。memcpyFORTIFY_SOURCEは、、、などのリスクの高い関数の「より安全な」バリアントを使用しstrcpyますgets。コンパイラーは、宛先バッファー・サイズを推測できる場合、より安全なバリアントを使用します。コピーが宛先バッファサイズを超える場合、プログラムはを呼び出しますabort()。コンパイラが宛先バッファサイズを推測できない場合、「より安全な」バリアントは使用されません。
テストのためにFORTIFY_SOURCEを無効にするには、-U_FORTIFY_SOURCEまたはを使用してプログラムをコンパイルする必要があります-D_FORTIFY_SOURCE=0。
srcバッファとdestバッファのサイズを常に把握して確認する必要があります。
void *memcpy(void *dest, const void *src, size_t n);
nsrcまたはdestサイズより大きくすることはできません。
たとえば、次の場合:
宛先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)));
アプリケーションに基づいて、残りのデータが後でコピーされることを確認することもできます。または、一部のデータを無視できる場合はスキップすることもできます。