INT30-C. 符号無し整数の演算結果がラップアラウンドしないようにする
C標準規格の6.2.5節、第9パラグラフには次のように規定されている [ISO/IEC 9899:2011]。
符号無しオペランドを含む計算は、決してオーバーフローしない。すなわち、結果を符号無し整数型で表現できないときは、その型で表現しうる最大値より1 だけ大きい数を法とする剰余を結果とする。
このような動作は俗に "unsigned integer wrapping" (符号無し整数のラップアラウンド)と呼ばれている。符号無し整数の演算は、結果の値がその整数型の整数表現で表現できない場合にラップアラウンドする。次の表にラップアラウンドが発生しうる演算子を示す。
演算子 |
ラップアラウンド |
演算子 |
ラップアラウンド |
演算子 |
ラップアラウンド |
演算子 |
ラップアラウンド |
---|---|---|---|---|---|---|---|
|
Yes |
|
Yes |
|
Yes |
|
No |
|
Yes |
|
Yes |
|
No |
|
No |
|
Yes |
|
No |
|
No |
|
No |
|
No |
|
No |
|
No |
|
No |
|
No |
|
Yes |
|
No |
|
No |
|
Yes |
|
No |
|
No |
|
No |
|
Yes |
|
No |
|
No |
|
No |
|
No |
|
No |
|
No |
|
No |
|
Yes |
|
No |
|
Yes |
|
No |
以降のセクションでは、符号無し整数のラップアラウンドが発生する可能性のある演算について詳しく見ていく。int
より小さな整数型に対する演算には、整数拡張 (integer promotion) が適用される。算術演算が行われる前に通常の算術型変換 (usual arithmetic conversion) が適用され、オペランドの型を共通の型へ暗黙的に変換する場合もある。セキュアな算術演算を実装する前に、C言語の整数変換のルールを理解しておくべきである(「INT02-C. 整数変換のルールを理解する」を参照)。
整数値は、特に次のいずれかの目的で利用する場合には、ラップアラウンドさせてはならない。
- ポインタ演算の整数オペランド (配列インデックスを含む)
- 可変長配列の宣言における代入式
[]
に先立つ後置式 (postix expression) もしくは配列オブジェクトの要素を指し示す[]
の中の式size_t
型やrsize_t
型を持つ関数の引数 (たとえばメモリ割当て関数の引数)- セキュリティ上重要な意味を持つコード
C 言語標準はアトミック整数型に対する演算は、read-modify-write 操作であり、通常の整数型と同じ表現を持つことを定めている。そのため、アトミック符号無し型のラップアラウンドは通常の符号無し整数と同様、防止もしくは検知されるべきである。
加算
加算は、2つの算術型オペランド同士、あるいは、オブジェクト型へのポインタと整数型の間で行われる。このルールは2つの算術型オペランドの加算にのみ適用される。(ポインタ演算に関しては「ARR37-C. 配列以外のオブジェクトを指すポインタに対して整数の加算や減算を行わない」および「ARR30-C. 境界外を指すポインタや配列添字を生成したり使用したりしない」を参照)。
インクリメントは1の加算に等しい。
違反コード
次の違反コード例では、符号無しオペランド ui_a
と ui_b
の加算時に符号無し整数のラップアラウンドが発生する可能性がある。この動作を想定していない場合、ラップアラウンドした値を使って不十分なメモリ領域を割り当ててしまうなど、攻撃可能な脆弱性につながる可能性がある。
void func(unsigned int ui_a, unsigned int ui_b) { unsigned int usum = ui_a + ui_b; /* ... */ }
適合コード (事前条件のテスト)
次の適合コードでは、加算する前にオペランドをテストし、符号無しラップアラウンドが発生しないことを保証している。
#include <limits.h> void func(unsigned int ui_a, unsigned int ui_b) { unsigned int usum; if (UINT_MAX - ui_a < ui_b) { /* エラー処理 */ } else { usum = ui_a + ui_b; } /* ... */ }
適合コード (事後条件のテスト)
このコードは事後条件のテストを行い、符号無し整数の加算結果 usum
が第1オペランドよりも小さくないことを確認している。
void func(unsigned int ui_a, unsigned int ui_b) { unsigned int usum = ui_a + ui_b; if (usum < ui_a) { /* エラー処理 */ } /* ... */ }
減算
減算は、算術型オペランド同士、適合するオブジェクト型の修飾版または非修飾版へのポインタ同士、またはオブジェクト型へのポインタと整数型の間で行われる。このルールは算術型オペランド同士の減算に適用される。(ポインタ演算に関しては「ARR36-C. 異なる配列を指す2つのポインタに対して減算や比較を行わない」「ARR37-C. 配列以外のオブジェクトを指すポインタに対して整数の加算や減算を行わない」「ARR30-C. 境界外を指すポインタや配列添字を生成したり使用したりしない」を参照。)
違反コード
次の違反コードは、符号無しオペランド ui_a
と ui_b
の減算時に符号無し整数のラップアラウンドを引き起こす可能性がある。この動作を想定していない場合、攻撃可能な脆弱性につながる恐れがある。
void func(unsigned int ui_a, unsigned int ui_b) { unsigned int udiff = ui_a - ui_b; /* ... */ }
適合コード (事前条件のテスト)
次の適合コードでは、減算を行う前に符号無しオペランドの値を確認することで、符号無しラップアラウンドが起こらないことを保証している。
void func(unsigned int ui_a, unsigned int ui_b) { unsigned int udiff; if (ui_a < ui_b){ /* エラー処理 */ } else { udiff = ui_a - ui_b; } /* ... */ }
適合コード (事後条件のテスト)
次のコードは事後条件のテストを行い、符号無し減算の結果である udiff
が被減数より大きくないことを確認している。
void func(unsigned int ui_a, unsigned int ui_b) { unsigned int udiff = ui_a - ui_b; if (udiff > ui_a) { /* エラー処理 */ } /* ... */ }
乗算
乗算は、2つの算術型オペランド間で行われる。
違反コード
Mozilla Foundation セキュリティアドバイザリ 2007-01 は、Mozilla Scalable Vector Graphics (SVG) ビューアのヒープバッファオーバーフローの脆弱性について説明している。この脆弱性は、signed int
型の値 pen->num_vertices
と size_t
型の値 sizeof(cairo_pen_vertex_t)
の乗算時に、符号無し整数のラップアラウンドが発生することが原因で作り込まれた [VU#551436]。 signed int
型オペランドを size_t
型に変換してから乗算することで、2つの符号無し size_t
整数を使って乗算が行われるようにしている。る(「INT02-C. 整数変換のルールを理解する」を参照)。
pen->num_vertices = _cairo_pen_vertices_needed( gstate->tolerance, radius, &gstate->ctm ); pen->vertices = malloc( pen->num_vertices * sizeof(cairo_pen_vertex_t) );
符号無し整数のラップアラウンドは不十分なサイズのメモリ割り当てを引き起こす恐れがある。
適合コード
次の適合コードでは、乗算するオペランドを確認することで符号無し整数のラップアラウンドが発生しないことを保証している。
pen->num_vertices = _cairo_pen_vertices_needed( gstate->tolerance, radius, &gstate->ctm ); if (pen->num_vertices > SIZE_MAX / sizeof(cairo_pen_vertex_t)) { /* エラー処理 */ } pen->vertices = malloc( pen->num_vertices * sizeof(cairo_pen_vertex_t) );
例外
INT30-EX1: プログラムを適切に実行するために必要な場合には、符号無し整数の演算で剰余(ラップアラウンド)してもよい。ただしその場合は、変数宣言やその変数を使った各整数演算を行うコードに、符号無し整数のラップアラウンドを想定していることをコメントしておくべきである。
INT30-EX2: コンパイル時にラップアラウンドが発生しないことがわかる場合、ラップアラウンドのチェックは省略してもよい。したがって、次に挙げる符号無し整数に対する演算はチェックの必要がない。
- コンパイル時の2つの定数に対する演算
- 変数と0に対する演算(0による除算や剰余は除く)
- その型の最大値から任意の値を減算する場合。たとえば、
UINT_MAX
からunsigned int
の全ての値は安全に減算することができる。 - あらゆる変数に対する1の乗算
- 除数が0でない除算や剰余
- ある型の最大値をその型の精度以下の数で右シフトする場合。たとえば
UINT_MAX >> x
は0 <= x < 32
が成立する限り有効 (unsigned int
の精度が32ビットであると仮定)
INT30-EX3. 左シフト演算子は2つの整数型オペランドをとる。符号無し左シフト <<
は剰余動作 (ラップアラウンド) することがある。この動作は通常想定されており、また言語規格上規定された動作であるため、例外として取り扱う。左シフトの使用例については、「INT34-C. 負のビット数、あるいはオペランドのビット数以上シフトしない」を参照。
リスク評価
整数のラップアラウンドがバッファオーバーフローにつながり、攻撃者に任意のコードを実行される可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
INT30-C |
高 |
高 |
高 |
P9 |
L2 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
|
|
演算前に必ずチェックを行うことでこのルールの違反を検知できる。INT30-EX2 の適用は慎重に行い、本質的に危険な演算を例外として認めないこと。たとえば、2つの符号無し整数の加算のラップアラウンドチェックにおいて、オペランドの一方を |
|
Coverity | 6.5 | INTEGER_OVERFLOW | 実装済み |
5.0 |
|
CERT C Rule Pack を使用することでこのルールを検出できる |
|
PRQA QA-C | 8.1 |
2910 (C) |
部分的に実装済み |
関連する脆弱性
CVE-2009-1385 はこのルールの違反が原因で発生した。バッファの長さから値をチェックせずに減算し、減算したバイト数分のデータを別のバッファに追加していた [xorl 2009]。これによりバッファオーバーフローが発生し、任意のコード実行が可能になった。
Rafal Wojtczuk の Linux kernel vmsplice
exploit は符号無し整数のラップアラウンドが原因で発生したバッファオーバーフローの脆弱性を使っている [Wojtczuk 2008]。
関連するガイドライン
CERT C Secure Coding Standard |
INT02-C. Understand integer conversion rules |
CERT C++ Secure Coding Standard | INT30-CPP. Ensure that unsigned integer operations do not wrap |
ISO/IEC TR 24772:2013 | Arithmetic Wrap-around Error [FIF] |
MITRE CWE | CWE-190, Integer Overflow or Wraparound |
参考資料
[Dowd 2006] | Chapter 6, "C Language Issues" ("Arithmetic Boundary Conditions," pp. 211–223) |
[ISO/IEC 9899:2011] | 6.2.5, "Types" |
[Seacord 2013b] | Chapter 5, "Integer Security" |
[Viega 2005] | Section 5.2.7, "Integer Overflow" |
[VU#551436] | |
[Warren 2002] | Chapter 2, "Basics" |
[Wojtczuk 2008] | |
[xorl 2009] | "CVE-2009-1385: Linux Kernel E1000 Integer Underflow" |
翻訳元
これは以下のページを翻訳したものです。
INT30-C. Ensure that unsigned integer operations do not wrap (revision 113)