STR34-C. 文字データをより大きなサイズの整数型に変換するときは事前に unsigned char 型に変換する
符号付き文字データは、より大きな符号付きの型に代入したり変換したりする前に、 unsigned char
型に変換しなくてはならない。char
型データは、コンパイラ実装に依存し、signed char
型あるいは unsigned char
型のいずれかに解釈される。つまり、それらの型と同じ値の範囲、バイナリ表現、動作となるように扱われる。したがって、このルールは signed char
型および (単なる) char
型データの両方に適用するべきである。
このルールは、文字データが負の値として解釈される可能性のある値を含む場合のみに適用される。たとえば、char
型が 2 の補数の 8 ビット値で表現される場合、+127 よりも大きな値の文字はすべて負の値として解釈される。
このルールは、「STR37-C. 文字処理関数への引数は unsigned char として表現できなければならない」を汎用化したものである。
違反コード
以下のコード例は、bash バージョン 1.14.6 以前で見つかった脆弱なコードから抜粋した。この脆弱性について CERT Advisory CA-1996-22 が発行されたが、脆弱性は、bash のソースコードの parse.y
モジュールにある yy_string_get()
関数のなかで、string
ポインタが参照する文字データが符号拡張されたことに起因する。
static int yy_string_get() { register char *string; register int c; string = bash_input.location.string; c = EOF; /* 文字列が存在しないか空の場合、EOF を返す */ if (string && *string) { c = *string++; bash_input.location.string = string; } return (c); }
string
変数を使って解析すべきコマンドラインを含む文字列を走査している。このポインタから1文字取得し、int
型の変数に格納している。char
型をデフォルトで signed char
型として扱うコンパイラでは、この値は int
型の変数に割り当てられるとき符号拡張される。文字コード 255 (10 進数、2 の補数表記では −1) は符号拡張の結果 −1 として変数 c
に代入され、EOF
と区別がつかなくなる。
この問題は、以下のように string
変数を unsigned char
型として明示的に宣言することで修正された。
static int yy_string_get() { register unsigned char *string; register int c; string = bash_input.location.string; c = EOF; /* 文字列が存在しないか空の場合、EOF を返す */ if (string && *string) { c = *string++; bash_input.location.string = string; } return (c); }
ただしこのコード例は、「STR04-C. 基本文字集合にある文字を表すには単なる char を使用する」に違反している。
適合コード
以下のコード例では、式 *string++
の結果は (unsigned char)
型にキャストされた上で int
型の変数 c
に代入されている。
static int yy_string_get() { register char *string; register int c; string = bash_input.location.string; c = EOF; /* 文字列が存在しないか空の場合、EOF を返す */ if (string && *string) { /* unsigned 型にキャストする */ c = (unsigned char)*string++; bash_input.location.string = string; } return (c); }
違反コード
以下のコード例では、整数拡張のため、*s
から unsigned int
へのキャストによって値が UCHAR_MAX
を超過し、配列のインデックスが有効な範囲内にあるという VOID の保証に関数が違反することになり、未定義の動作につながる可能性がある。
static const char table[UCHAR_MAX] = { /* ... /* }; int first_not_in_table(const char *str) { const char *s = str; for (; *s; ++s) { if (table[(unsigned)*s] != *s) return s - str; return -1; }
適合コード
以下のコード例では、char
値が暗黙的に大きな符号付き整数型に拡張される前に、この値を unsigned char
にキャストする。
static const char table[UCHAR_MAX] = { /* ... /* }; ptrdiff_t first_not_in_table(const char *str) { const char *s = str; for (; *s; ++s) { if (table[(unsigned char)*s] != *s) return s - str; return -1; }
リスク評価
コード上はごく小さなエラーだが、きわめて幅広い範囲の深刻な脆弱性を招く可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
STR34-C |
中 |
中 |
中 |
P8 |
L2 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Compass/ROSE |
「INT07-C. 数値には符号の有無が明示された文字型を使用する」の違反をチェックするときにこのルールの違反を検出することができる。 |
||
ECLAIR |
1.1 |
charcast |
実装済み |
Fortify SCA |
5.0 |
CERT C Rule Pack を使ってこのルールの違反を検出できる。 |
|
GCC |
2.95 以降 |
配列インデックスとして使用される |
|
LDRA tool suite |
V. 8.5.4 |
434 S |
実装済み |
PRQA QA-C | 8.1 | 3704 | 実装済み |
関連する脆弱性
CVE-2009-0887 はこのルールへの違反の結果である。Linux PAM (バージョン 1.0.3 まで) では、strtok
に libpam
を実装すると、配列のインデックスとして使用できるように文字が整数にキャストされる(基本的に符号付き)。攻撃者はこの脆弱性を悪用し、ASCII 以外の文字列を入力して、キャストで負のインデックスを生成し、配列の範囲外のメモリにアクセスすることが可能である [xorl 2009]。
関連するガイドライン
CERT C++ Secure Coding Standard | STR34-CPP. Cast characters to unsigned types before converting to larger integer sizes |
ISO/IEC TS 17961 (ドラフト) | Conversion of signed characters to wider integer types before a check for EOF [signconv] |
MISRA-C | Rule 6.1 |
MITRE CWE | CWE-704, Incorrect type conversion or cast |
参考資料
[xorl 2009 ] | CVE-2009-0887: Linux-PAM Signedness Issue |
翻訳元
これは以下のページを翻訳したものです。
STR34-C. Cast characters to unsigned char before converting to larger integer sizes (revision 58)