整数オーバーフロー
size_t が 32 ビットの処理系で,次の C 言語のコードは,攻撃者が指定できる件数 n に基づいてレコード用の領域を確保し,n 個のレコードを読み込む。n に非常に大きな値を与えたときに起こり得ることとして,適切なものはどれか。
uint32_t n = read_count(); /* 攻撃者が指定 */
rec_t *p = malloc(n * sizeof(rec_t)); /* sizeof(rec_t) == 64 */
for (uint32_t i = 0; i < n; i++)
p[i] = read_rec();- アn * sizeof(rec_t) が 32 ビットの範囲を超えて折り返し,必要な量より小さい領域が確保される。その後 n 個分の書込みで領域を越え,ヒープが破壊される。
- イmalloc は要求サイズが大きすぎると必ず NULL を返すので,確保失敗を検知でき,オーバーフローは起きない。
- ウループ変数 i も uint32_t なので i < n の比較が破綻して無限ループになるだけで,メモリは破壊されない。
- エsizeof(rec_t) は定数なので乗算結果はコンパイル時に確定し,実行時にオーバーフローは起きない。
正解と解説を見る
正解 ア
この問題の基礎知識
- 符号なし整数の演算結果が型の最大値を超えると,上位の桁が失われて小さな値に折り返す(ラップアラウンド)。32 ビットの符号なし整数で表せる最大値は 2 の 32 乗から 1 を引いた値である。
- メモリの確保量の計算で整数オーバーフローが起きると,想定より小さい領域が確保され,その後の書込みが領域の境界を越えてバッファオーバーフロー(ヒープの破壊)につながる。
- malloc は確保に失敗すると NULL を返すが,折り返した後の小さい値が渡されれば確保は成功してしまうので,NULL の検査だけではこの問題を防げない。
- 対策として,乗算の前に n が「確保できる最大サイズ ÷ 要素サイズ」を超えないことを検査するなど,演算の結果がオーバーフローしないことを確かめてから確保する。
アn * sizeof(rec_t) が 32 ビットの範囲を超えて折り返し,必要な量より小さい領域が確保される。その後 n 個分の書込みで領域を越え,ヒープが破壊される。正解
例えば n = 0x04000001 なら積は 0x100000040 で,32 ビットに折り返して 64 バイトしか確保されない。その領域に n 回書き込むので境界外書込みでヒープが壊れる。整数オーバーフローがバッファオーバーフローに連鎖する典型例である。
イmalloc は要求サイズが大きすぎると必ず NULL を返すので,確保失敗を検知でき,オーバーフローは起きない。
折り返しにより malloc には小さな値が渡るので,確保は成功してしまう。巨大な要求が渡るという前提が誤りで,オーバーフローの発生を見落としている。
ウループ変数 i も uint32_t なので i < n の比較が破綻して無限ループになるだけで,メモリは破壊されない。
i は 0 から n まで正しく増え,比較は破綻しない。無限ループではなく,確保不足の領域への境界外書込みが問題である。
エsizeof(rec_t) は定数なので乗算結果はコンパイル時に確定し,実行時にオーバーフローは起きない。
n は実行時に読み込む変数なので,乗算は実行時に行われオーバーフローし得る。定数は一方の因子だけで,積が定数になるわけではない。
検証用の出典出典照合済み
CWE-190 Integer Overflow or Wraparound / CWE-680 Integer Overflow to Buffer Overflow照合の根拠(3 件)
- ア(正解): 確保量の計算で整数オーバーフローが起きると想定より少ない領域が確保され,バッファオーバーフローにつながる
The product performs a calculation to determine how much memory to allocate, but an integer overflow can occur that causes less memory to be allocated than expected, leading to a buffer overflow.
CWE-680 · Description(新しいタブで開きます) - ア(正解)・エ(誤り): sizeof が定数でも件数が大きくなれば実行時の乗算が桁あふれし,非常に小さい領域が確保される
This code intends to allocate a table of size num_imgs, however as num_imgs grows large, the calculation determining the size of the list will eventually overflow ( CWE-190 ). This will result in a very small list to be allocated instead.
CWE-190 · Demonstrative Examples(新しいタブで開きます) - イ(誤り): 桁あふれで小さな値が渡ると malloc は確保に成功し,後続のループがヒープバッファをあふれさせる
Most malloc() implementations will happily allocate a 0-byte buffer, causing the subsequent loop iterations to overflow the heap buffer response.
CWE-190 · Demonstrative Examples(新しいタブで開きます)
検算: n = 0x04000001(67108865)のとき n × 64 = 0x100000040(4294967360)。size_t が 32 ビットなら 2^32 で折り返して 0x40(64)バイトしか確保されない。ウ(誤り): i と n はともに uint32_t で i は 0 から n−1 まで増えるだけなので i < n の比較は破綻せず,無限ループにはならない(i は最大でも n−1 ≤ 2^32−2 で折り返さない)。