STR11-C. 文字列リテラルで初期化される文字配列のサイズを指定しない
C 標準上、配列変数の宣言は、配列のサイズを指定する方法と、初期化リテラルを指定する方法のどちらでも行える。初期化リテラルを指定する場合、その文字数によって暗黙に要素の数を指定している。その場合のサイズは、初期化リテラルに含まれる文字に null 終端文字を加えた文字数である。
配列変数が宣言されるとき、文字列リテラルで初期化されると同時に、文字列リテラル中の文字数が配列のサイズとして明示的に指定されることがよくある。C 標準 [ISO/IEC 9899:2011] セクション 6.7.9 第 14 段落には次のように記載されている。
文字型の配列は単純文字列リテラルまたはUTF−8 文字列リテラルによって初期化することができ、オプションで波括弧で囲むこともできる。文字列リテラルの連続バイト(スペースの余裕がある場合、または配列のサイズがわからない場合、null 終端文字を含む)により、配列の要素が初期化される。
ただし、文字列を null 終端バイト文字列として使用することを意図している場合、配列では null 終端文字が想定されていないため、文字列を格納するには 1 文字分不足する。このように宣言された文字の配列を取り扱う際には注意が必要であり、null 終端バイト文字列と想定してしまうと、脆弱性を引き起こす可能性がある。
より良いアプローチは、文字列リテラルで初期化される文字列のサイズを指定しないことである。コンパイラは自動的に null 終端文字を含む文字列リテラル全体に対して十分なスペースを割り当てる。このルールは「ARR02-C. 初期化子が暗黙的にサイズを定義する場合であっても、配列のサイズは明示的に指定する」の例外に該当する。
違反コード
以下のコードでは、配列が保持できるより 1 文字長い文字列リテラルを使って文字配列を初期化している('\0'
終端文字を考慮すると文字列リテラルの長さは配列のサイズより 1 大きい)。
const char s[3] = "abc";
s
のサイズは 3 であるが、文字列リテラルのサイズは 4 である。s
は適切に null 終端されていないため、この配列を null 終端バイト文字列として使用するすべての操作は脆弱性につながる可能性がある(「STR32-C. 文字列は null 終端させる」を参照)。
処理系固有の詳細
以下のコードでは、MSVC 2008 でコンパイルエラーが発生する。GCC 4.3.3 では警告なしでコンパイルされる。C 標準で規定されているとおり、null 終端文字なしの 3 文字配列が生成される。
適合コード
以下の適合コードでは、配列宣言の中で文字配列のサイズを指定していない。配列のサイズが省略されると、コンパイラは null 終端文字分を含む、文字列リテラル全体を格納するのに十分な記憶域を割り当てる。
const char s[] = "abc";
文字列リテラルのサイズが変更されても常に適切な配列のサイズが得られるため、このアプローチの方が好ましい。
例外
STR11-EX0: null 終端文字を含まない文字配列の作成を意図している場合、null 文字を除く要素の数だけ正確に納まるように初期化することは許されるが、推奨はされない。たとえば 'a'
、'b'
、'c'
の 3 文字だけを含む配列を作成する場合のより適切なアプローチは、次のように各文字リテラルを別々の要素として宣言することである。
char s[3] = { 'a', 'b', 'c' }; /* 文字列ではない */
文字配列が null 終端文字列でない場合、ソースコード中にコメントをつけるか、あるいはその旨を明文化するべきである。
STR11-EX1: 文字配列が初期化に使用した文字リテラルより大きくなければならない場合、配列のサイズを明示しておくこともできる。このことは、プログラムの実行中に配列の内容が変わる可能性がある場合に特に重要である。
char s[10] = "abc"; strcpy(&s[3], "def");
リスク評価
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
STR11-C |
高 |
中 |
低 |
P18 |
L1 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Compass/ROSE |
|
|
|
ECLAIR |
1.1 |
araydecl |
実装済み |
PRQA QA-C | 8.1 | 1312 | 部分的に実装済み |
Splint |
V. 3.1.1 |
|
|
関連するガイドライン
CERT C++ Secure Coding Standard | STR36-CPP. Do not specify the bound of a character array initialized with a string literal |
ISO/IEC TR 24772:2013 | String Termination [CJM] |
参考資料
[ECTC 1998] | Section A.8, "Character Array Initialization" |
[ISO/IEC 9899:2011] | Section 6.7.9, "Initialization" |
[Seacord 2013] | Chapter 2, "Strings" |
翻訳元
これは以下のページを翻訳したものです。
STR11-C. Do not specify the bound of a character array initialized with a string literal (revision 64)