8

整数オーバーフローの場合の結果は(unsigned int) * (int)? unsignedまたはintoperator[]配列インデックス演算子 ( ) は、char*:intなどにどのような型をとりunsigned intますか?

私は次の関数を監査していましたが、突然この質問が発生しました。この関数には 17 行目に脆弱性があります。

// Create a character array and initialize it with init[] 
// repeatedly. The size of this character array is specified by 
// w*h.
char *function4(unsigned int w, unsigned int h, char *init)
{
    char *buf;
    int i;

    if (w*h > 4096)
        return (NULL);

    buf = (char *)malloc(4096+1);
    if (!buf)
        return (NULL);

    for (i=0; i<h; i++)
        memcpy(&buf[i*w], init, w);  // line 17

    buf[4096] = '\0';

    return buf;
}

wと の両方を考慮してくださいh。非常に大きな符号なし整数です。9 行目の乗算は、検証に合格する可能性があります。

問題は 17 行目です。掛け算: 結果が の場合int i、積が負になる可能性があり、結果として の前の位置にアクセスすることになります。結果が の場合、積は常に正になり、 の後の位置にアクセスすることになります。unsigned int wintbufunsigned intbuf

これを正当化するコードを書くのは難しいです:intは大きすぎます。誰かがこれについてアイデアを持っていますか?

製品のタイプを特定するドキュメントはありますか? 私はそれを検索しましたが、これまでのところ何も見つかりませんでした。

コンパイルされたオブジェクトファイルでは、それらは単なるバイトであるため、脆弱性に関する限り、(unsigned int) * (int)生成するunsigned intかどうかは問題ではないと思います。int次のコードは、製品のタイプに関係なく同じように機能します。

unsigned int x = 10;
int y = -10;

printf("%d\n", x * y);  // print x * y in signed integer
printf("%u\n", x * y);  // print x * y in unsigned integer

したがって、乗算が返す型は問題ではありません。int消費者関数が取るかどうかが重要ですunsigned

ここでの問題は、その機能がどれほど悪いか、または機能を改善するためにどのように機能を改善するかではありません。この機能には間違いなく脆弱性があります。問題は、標準から規定された動作に基づいた、関数の正確な動作に関するものです。

4

13 に答える 13

2

あなたの質問に答えるために:intとunsigned intを乗算する式のタイプは、C /C++ではunsignedintになります。

暗黙の質問に答えるために、整数演算で発生する可能性のあるオーバーフローに対処する1つの適切な方法はIntSafe、Microsoftの""ルーチンセットを使用することです。

http://blogs.msdn.com/michael_howard/archive/2006/02/02/523392.aspx

SDKで利用可能であり、インライン実装が含まれているため、別のプラットフォームを使用している場合にそれらが何をしているのかを調べることができます。

于 2009-04-06T15:31:56.393 に答える
2

w と h を制限して、w * h がオーバーフローしないようにします。

于 2009-04-06T15:08:59.867 に答える
2

あなたの場合、タイプw*iは署名されていません。標準を正しく読んだ場合、ルールは、オペランドがより大きな型(符号付き)、または符号付き型に対応する符号なし型(unsigned intあなたの場合)に変換されることです。

ただし、署名されていない場合でも、ラップアラウンド ( の前にメモリに書き込むbuf)を防ぐことp[-1]はできませんp[-1u]。とにかく、あなたの場合、 と の両方buf[-1]buf[big unsigned number]未定義の動作になるため、署名付き/未署名の質問はそれほど重要ではありません。

署名済み/未署名は他のコンテキストで重要であることに注意してください。未定義の動作がない場合でも、と(int)(x*y/2)のタイプに応じて異なる結果が得られます。xy

I would solve your problem by checking for overflow on line 9; since 4096 is a pretty small constant and 4096*4096 doesn't overflow on most architectures (you need to check), I'd do

if (w>4096 || h>4096 || w*h > 4096)
     return (NULL);

This leaves out the case when w or h are 0, you might want to check for it if needed.

In general, you could check for overflow like this:

if(w*h > 4096 || (w*h)/w!=h || (w*h)%w!=0)
于 2009-04-06T15:38:13.227 に答える
2

C/C++ では、p[n]表記法は実際には書き込みのショートカット*(p+n)であり、このポインター演算では符号が考慮されます。Sop[-1]は有効で、 の直前の値を参照します*p

したがって、ここでは符号が本当に重要です。整数を使用した算術演算子の結果は、標準で定義された一連の規則に従います。これは整数昇格と呼ばれます。

このページをチェックしてください: INT02-C. 整数変換規則を理解する

于 2009-04-06T15:40:06.517 に答える
1

2つの変更により安全になります。

if (w >= 4096 || h >= 4096 || w*h > 4096)  return NULL;

...

unsigned i;

バッファの終わりを超えて書き込みまたは読み取りを行うことも、それほど悪い考えではないことにも注意してください。したがって、問題はi wが負になるかどうかではなく、0 <= i h + w<=4096が成り立つかどうかです。

したがって、重要なのはタイプではなく、h*iの結果です。たとえば、これが(unsigned)0x80000000であるか(int)0x80000000であるかは関係ありませんが、プログラムはとにかくセグメンテーション違反を起こします。

于 2009-04-06T15:30:26.007 に答える
0

現在のC1Xドラフトには、6.3.1.8の(UNSIGNED TYPE1)X(SIGNED TYPE2)の計算に関する3つの段落があります。通常の算術カバー、N1494、

WG 14:C-プロジェクトのステータスとマイルストーン

それ以外の場合、符号なし整数型のオペランドのランクが他のオペランドの型のランク以上の場合、符号付き整数型のオペランドは符号なし整数型のオペランドの型に変換されます。

それ以外の場合、符号付き整数型のオペランドの型が符号なし整数型のオペランドの型のすべての値を表すことができる場合、符号なし整数型のオペランドは符号付き整数型のオペランドの型に変換されます。

それ以外の場合は、両方のオペランドが、符号付き整数型のオペランドの型に対応する符号なし整数型に変換されます。

したがって、aがunsigned intで、bがintの場合、(a * b)を解析すると、コード(a *(unsigned int)b)が生成されます。b<0またはa*b> UINT_MAXの場合、オーバーフローします。

aがunsignedintで、bのサイズが大きい場合、(a * b)は((long)a *(long)b)を生成する必要があります。a *b>LONG_MAXまたはa*b<LONG_MINの場合はオーバーフローします。

aがunsignedintで、bが同じサイズのlongである場合、(a * b)は((unsigned long)a *(unsigned long)b)を生成する必要があります。b<0またはa*b>ULONG_MAXの場合はオーバーフローします。

「インデクサー」が期待する型に関する2番目の質問では、答えは「整数型」と表示されます。これにより、任意の(符号付き)整数インデックスが可能になります。

6.5.2.1配列の添え字

制約

1式の1つは「オブジェクト型を完成させるためのポインタ」型であり、もう1つの式は整数型であり、結果は「型」型である必要があります。

セマンティクス

2接尾辞式の後に角括弧[]で囲まれた式は、配列オブジェクトの要素の添え字付き指定です。添え字演算子[]の定義は、E1 [E2]が(*((E1)+(E2)))と同じであるということです。二項+演算子に適用される変換規則により、E1が配列オブジェクト(同等に、配列オブジェクトの初期要素へのポインター)であり、E2が整数である場合、E1[E2]はのE2番目の要素を指定します。 E1(ゼロから数える)。

静的分析を実行し、ポインター式が配列変数であり、インデックスが負である可能性がある場合にバッファーオーバーランの可能性について開発者に警告するのは、コンパイラーの責任です。インデックスが正または符号なしの場合でも、配列サイズのオーバーランの可能性に関する警告についても同じことが言えます。

于 2010-09-18T01:11:07.347 に答える
0

i を unsigned int として宣言しないのはなぜですか? その後、問題は解決します。

いずれの場合でも、コードがこれをテストしているため、i*w は <= 4096 であることが保証されているため、オーバーフローすることはありません。

于 2009-04-06T15:07:22.990 に答える
0

memcpy(&buf[i w > -1 ? i w < 4097? i w : 0 : 0], init, w); i w のトリプル計算がパフォーマンスを低下させるとは思わない)

于 2009-04-06T15:11:18.347 に答える
0

w および/または h が十分に大きく、次の検証に合格できる場合、w*h はオーバーフローする可能性があります。

9.      if (w*h > 4096)
10.         return (NULL);

int と unsigned int の混合演算では、int が unsigned int に昇格されます。この場合、'i' の負の値は大きな正の値になります。その場合

&buf[i*w]

範囲外の値にアクセスしています。

于 2009-04-06T15:20:45.547 に答える
0

unsigned 算術演算はモジュラー (またはラップアラウンド) として行われるため、2 つの大きな unsigned int の積は簡単に 4096 未満になる可能性があります。int と unsigned int の乗算は unsigned int になります (C++ 標準のセクション 4.5 を参照)。 .

したがって、大きな w と適切な h の値を指定すると、実際に問題が発生する可能性があります。

整数演算がオーバーフローしないようにすることは困難です。簡単な方法の 1 つは、浮動小数点に変換して浮動小数点乗算を行い、結果が妥当かどうかを確認することです。qwerty が示唆したように、実装で使用できる場合は long long を使用できます。(これは C90 と C++ で共通の拡張機能であり、C99 には存在し、C++0x にも存在します。)

于 2009-04-06T15:21:41.553 に答える
-1

実行しているハードウェアを指定せずに実際に質問に答えるには、わかりません。移植可能にすることを目的としたコードでは、特定の動作に依存するべきではありません。

于 2009-04-06T15:19:26.177 に答える