JPCERT コーディネーションセンター

INT30-C. 符号無し整数の演算結果がラップアラウンドしないようにする

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

un +

No

||

No

+=

Yes

^=

No

un -

Yes

?:

No

以降のセクションでは、符号無し整数のラップアラウンドが発生する可能性のある演算について詳しく見ていく。int より小さな整数型に対する演算には、整数拡張 (integer promotion) が適用される。算術演算が行われる前に通常の算術型変換 (usual arithmetic conversion) が適用され、オペランドの型を共通の型へ暗黙的に変換する場合もある。セキュアな算術演算を実装する前に、C言語の整数変換のルールを理解しておくべきである(「INT02-C. 整数変換のルールを理解する」を参照)。

整数値は、特に次のいずれかの目的で利用する場合には、ラップアラウンドさせてはならない。

C 言語標準はアトミック整数型に対する演算は、read-modify-write 操作であり、通常の整数型と同じ表現を持つことを定めている。そのため、アトミック符号無し型のラップアラウンドは通常の符号無し整数と同様、防止もしくは検知されるべきである。

加算

加算は、2つの算術型オペランド同士、あるいは、オブジェクト型へのポインタと整数型の間で行われる。このルールは2つの算術型オペランドの加算にのみ適用される。(ポインタ演算に関しては「ARR37-C. 配列以外のオブジェクトを指すポインタに対して整数の加算や減算を行わない」および「ARR30-C. 境界外を指すポインタや配列添字を生成したり使用したりしない」を参照)。

インクリメントは1の加算に等しい。

違反コード

次の違反コード例では、符号無しオペランド ui_aui_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_aui_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_verticessize_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: コンパイル時にラップアラウンドが発生しないことがわかる場合、ラップアラウンドのチェックは省略してもよい。したがって、次に挙げる符号無し整数に対する演算はチェックの必要がない。

INT30-EX3. 左シフト演算子は2つの整数型オペランドをとる。符号無し左シフト << は剰余動作 (ラップアラウンド) することがある。この動作は通常想定されており、また言語規格上規定された動作であるため、例外として取り扱う。左シフトの使用例については、「INT34-C. 負のビット数、あるいはオペランドのビット数以上シフトしない」を参照。

リスク評価

整数のラップアラウンドがバッファオーバーフローにつながり、攻撃者に任意のコードを実行される可能性がある。

ルール

深刻度

可能性

修正コスト

優先度

レベル

INT30-C

P9

L2

自動検出(最新の情報はこちら

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

演算前に必ずチェックを行うことでこのルールの違反を検知できる。INT30-EX2 の適用は慎重に行い、本質的に危険な演算を例外として認めないこと。たとえば、2つの符号無し整数の加算のラップアラウンドチェックにおいて、オペランドの一方をUINT_MAXから減算する演算を行うが、この演算自体はラップアラウンドしないのでチェックしなくてよい

Coverity 6.5 INTEGER_OVERFLOW 実装済み

Fortify SCA

5.0

 

CERT C Rule Pack を使用することでこのルールを検出できる

PRQA QA-C 8.1

2910 (C)
2911 (D)
2912 (A)
2913 (S)
3302
3303
3304

部分的に実装済み
関連する脆弱性

CVE-2009-1385 はこのルールの違反が原因で発生した。バッファの長さから値をチェックせずに減算し、減算したバイト数分のデータを別のバッファに追加していた [xorl 2009]。これによりバッファオーバーフローが発生し、任意のコード実行が可能になった。

Rafal Wojtczuk の Linux kernel vmsplice exploit は符号無し整数のラップアラウンドが原因で発生したバッファオーバーフローの脆弱性を使っている [Wojtczuk 2008]。

関連するガイドライン
CERT C Secure Coding Standard

INT02-C. Understand integer conversion rules
ARR30-C. Do not form or use out-of-bounds pointers or array subscripts
ARR36-C. Do not subtract or compare two pointers that do not refer to the same array
ARR37-C. Do not add or subtract an integer to a pointer to a non-array object
CON08-C. Do not assume that a group of calls to independently atomic methods is atomic

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)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter