INT18-C. より大きいサイズの整数との比較や代入を行う前に、整数式をそのサイズで評価する
演算を含む整数式をより大きいサイズの整数型と比較したり代入する場合、オペランドのいずれかを明示的にキャストすることで整数式をより大きいサイズの型で評価しなければならない。
違反コード
次のコードは、size_t
の値が符号無し32ビットで long long
の値が64ビットであるシステムにおいてルールに違反している。このコード例では、SIZE_MAX
と length + BLOCK_HEADER_SIZE
を比較することでラップアラウンドが発生するかどうか確認している。length
は size_t
型として宣言されているので、加算は32ビットで演算され、結果はラップアラウンドする可能性がある。SIZE_MAX
との比較は常に偽になる。ラップアラウンドが発生すると、malloc()
は mBlock
に十分なメモリ領域を割り当てることができず、バッファオーバーフローにつながってしまう。
#include <stdlib.h> #include <stdint.h> /* SIZE_MAX を使用するため */ enum { BLOCK_HEADER_SIZE = 16 }; void *AllocateBlock(size_t length) { struct memBlock *mBlock; if (length + BLOCK_HEADER_SIZE > (unsigned long long)SIZE_MAX) return NULL; mBlock = (struct memBlock *)malloc( length + BLOCK_HEADER_SIZE ); if (!mBlock) { return NULL; } /* ブロックヘッダにデータを書き込み、データを返す */ return mBlock; }
この条件を検出するコンパイラもある。
適合コード (アップキャスト)
次の解決法では、length
オペランドを unsigned long long
にキャストし、このサイズで加算を行っている。
#include <stdlib.h> #include <stdint.h> enum { BLOCK_HEADER_SIZE = 16 }; void *AllocateBlock(size_t length) { struct memBlock *mBlock; if ((unsigned long long)length + BLOCK_HEADER_SIZE > SIZE_MAX) { return NULL; } mBlock = (struct memBlock *)malloc( length + BLOCK_HEADER_SIZE ); if (!mBlock) { return NULL; } /* ブロックヘッダにデータを書き込み、データを返す */ return mBlock; }
このラップアラウンドのテストは、sizeof(unsigned long long) > sizeof(size_t)
である場合だけ有効である。size_t
と unsigned long long
型が64ビット符号無しの値として表現されている場合には、加算演算の結果を unsigned long long
型の値として表現できない可能性がある。
適合コード (式の再配置)
以下の適合コードでは、length
を SIZE_MAX
から引くことでラップアラウンドが発生しないようにしている。詳しくは「INT30-C. 符号無し整数の演算結果がラップアラウンドしないようにする」を参照。
#include <stdlib.h> #include <stdint.h> enum { BLOCK_HEADER_SIZE = 16 }; void *AllocateBlock(size_t length) { struct memBlock *mBlock; if (SIZE_MAX - length < BLOCK_HEADER_SIZE) return NULL; mBlock = (struct memBlock *)malloc( length + BLOCK_HEADER_SIZE ); if (!mBlock) { return NULL; } /* ブロックヘッダにデータを書き込み、データを返す */ return mBlock; }
違反コード
以下のコードでは、ラップアラウンドを回避しようとして unsigned long long
型変数 alloc
を割り当て、この変数に cBlocks * 16
の結果を代入している。
#include <stdlib.h> #include <limits.h> void *AllocBlocks(size_t cBlocks) { if (cBlocks == 0) { return NULL; } unsigned long long alloc = cBlocks * 16; return (alloc < UINT_MAX) ? malloc(cBlocks * 16) : NULL; }
このコードでは2つの問題が発生する。1つ目の問題は、unsigned long long
が size_t
より少なくとも4ビット大きい実装を想定しているということである。2つ目の問題は、size_t
の値が32ビットで unsigned long long
の値が64ビットでは、C言語規格に従い、2つの32ビット値の乗算結果が32ビットになるということである。乗算でラップアラウンドが発生しても検出されず、式 alloc < UINT_MAX
は常に真になる。
適合コード
以下の解決法では、オペランド cBlocks
が unsigned long long
にアップキャストされているので、乗算はこのサイズで行われる。
#include <stdlib.h> #include <assert.h> #include <limits.h> static_assert( CHAR_BIT * sizeof(unsigned long long) >= CHAR_BIT * sizeof(size_t) + 4, "乗算で発生したラップアラウンドを検出できない" ); void *AllocBlocks(size_t cBlocks) { if (cBlocks == 0) return NULL; unsigned long long alloc = (unsigned long long)cBlocks * 16; return (alloc < UINT_MAX) ? malloc(cBlocks * 16) : NULL; }
ただし、unsigned long long
型が size_t
より少なくとも4ビット大きくないと、ラップアラウンドを防げない。
違反コード (size_t
)
mbstowcs()
関数はマルチバイト文字列をワイド文字列に変換し、変換した文字数を返す。不正なマルチバイト文字に遭遇すると、(size_t)(-1)
を返す。size_t
の実装によっては、mbstowcs()
の戻り値と整数リテラル -1
との比較は期待通りにならない。
#include <stdlib.h> void func(wchar_t *pwcs, const char *restrict s, size_t n) { size_t count_modified = mbstowcs(pwcs, s, n); if (count_modified == -1) { /* エラー処理 */ } }
適合コード (size_t
)
比較を正しく行うには, mbstowcs()
の戻り値を size_t
にキャストした -1
と比較しなくてはならない。
#include <stdlib.h> void func(wchar_t *pwcs, const char *restrict s, size_t n) { size_t count_modified = mbstowcs(pwcs, s, n); if (count_modified == (size_t)-1) { /* エラー処理 */ } }
リスク評価
キャストせずに整数型をより大きなサイズと比較したり代入すると、脆弱性につながり、脆弱なプロセスの実行権限で任意のコードを実行されてしまう可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
INT18-C |
高 |
高 |
中 |
P18 |
L1 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
|
|
Can detect violations of this rule. It should look for patterns of
|
|
Coverity | 6.5 | OVERFLOW_BEFORE_WIDEN | 実装済み |
5.0 |
|
CERT C Rule Pack を使用することでこのルールを検出できる |
|
PRQA QA-C | 8.1 |
1890 |
部分的に実装済み |
関連するガイドライン
CERT C++ Secure Coding Standard | INT35-CPP. Evaluate integer expressions in a larger size before comparing or assigning to that size |
ISO/IEC TR 24772:2013 | Numeric Conversion Errors [FLC] |
MITRE CWE | CWE-681, Incorrect conversion between numeric types CWE-190, Integer overflow (wrap or wraparound) |
参考資料
[Dowd 2006] | Chapter 6, "C Language Issues" |
[Seacord 2013] | Chapter 5, "Integer Security" |
翻訳元
これは以下のページを翻訳したものです。
INT18-C. Evaluate integer expressions in a larger size before comparing or assigning to that size (revision 83)