STR30-C. 文字列リテラルを変更しない
文字列リテラルとは、二重引用符で囲まれた0個以上のマルチバイト文字の並びである(たとえば "xyz"
)。ワイド文字列リテラルも同じであるが、接頭辞 'L
' が頭に付く(たとえば、L"xyz"
)。
コンパイルの時、文字の並びと null 終端文字を格納するのに十分な長さを持つ静的記憶域期間の配列を作成するために、文字列リテラルが使用される(複数の文字列リテラルを定義している場合)。これらの配列がどの程度区別されるかは不定である。プログラムが文字列リテラルを変更しようとした場合の動作は未定義である。通常は、文字列リテラルは読み取り専用メモリに格納されるため、アクセス違反を引き起こすことが多い。C 標準 [ISO/IEC 9899:2011] 附属書 J 「未定義の動作」の 33 を参照すること。
ライブラリ関数 strpbrk()
、strchr()
、strrchr()
、wcspbrk()
、wcschr()
、wcsrchr()
の返り値と const
文字のポインタ(または配列)は文字列リテラルとして扱うものとする。
文字列リテラルを変更しようとしないこと。名前付きの文字配列を使用し、変更可能な文字列を取得すること。
違反コード
以下のコードでは、char
型ポインタ p
は文字列リテラルのアドレスに初期化される。文字列リテラルを変更しようとすると、未定義の動作となる。
char *p = "string literal"; p[0] = 'S';
適合コード
配列を初期化する場合、文字列リテラルは配列内の文字の初期値と配列のサイズを指定する(「STR36-C. 文字列リテラルで初期化される文字配列のサイズを指定しない」を参照)。このコードは、文字列リテラルのコピーを、文字配列 a
に割り当てられた領域に作成する。a
に格納された文字列は安全に変更することができる。
char a[] = "string literal"; a[0] = 'S';
違反コード
以下のコード例では、POSIX 関数 mkstemp()
の(非 const
へのポインタ)パラメータに文字列リテラルが渡され、文字列リテラルの文字が変更されている。
char *fname; fname = mkstemp("/tmp/edXXXXXX");
適合コード
文字列リテラルを渡す代わりに、名前付き配列を使用する。
static char fname[] = "/tmp/edXXXXXX"; mkstemp(fname);
違反コード (strrchr()
の結果)
以下のコード例では、pathname
によって参照されるオブジェクトを変更するために、strrchr()
関数の非 const
char*
の結果が使用される。ポインタは文字列リテラルを参照するため、変更の影響は未定義で、オブジェクトが読み取り専用メモリに格納された場合にプロセス用に SIGSEGV
などのシグナルが生成される可能性がある。
const char* get_dirname(const char* pathname) { char* slash; slash = strrchr(pathname, '/'); if (slash) *slash = '\0'; /* 未定義の動作 */ return pathname; } int main() { puts(get_dirname(__FILE__)); return 0; }
適合コード (strrchr()
の結果)
以下の適合コードでは、strrchr()
などの標準の C ライブラリ関数を呼び出してこのようなオブジェクトへの非 const
ポインタを取得することが可能な場合でも、const
オブジェクトの変更を避ける。関数に const オブジェクトを渡す get_dirname()
の呼び出し元のリスクを低減するため、引数は非 const
ポインタとして宣言されている。文字列リテラルから非 const
char*
への変換は言語によって許可されているが、このようなコードに対して適合コンパイラによって診断メッセージが発行されることがある。「EXP05-C. const 修飾をキャストではずさない」も参照すること。
char* get_dirname(char* pathname) { char* slash; slash = strrchr(pathname, '/'); if (slash) *slash = '\0'; return pathname; } int main() { char pathname[] = __FILE__; /* get_dirname(__FILE__) の呼び出しにより診断メッセージが発行されることがある */ puts(get_dirname(pathname)); return 0; }
リスク評価
文字列リテラルの変更は、プログラムの異常終了やサービス運用妨害(DoS)攻撃を引き起こす可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
STR30-C |
低 |
高 |
低 |
P9 |
L2 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Compass/ROSE |
このルールの単純な違反を検出できる。 |
||
LDRA tool suite |
V. 8.5.4 |
157 S |
部分的に実装済み |
PRQA QA-C | 8.1 | 0556 | 部分的に実装済み |
Splint |
V. 3.1.1 |
|
関連するガイドライン
CERT C++ Secure Coding Standard | STR30-CPP. Do not attempt to modify string literals |
ISO/IEC TS 17961 (ドラフト) | Modifying string literals [strmod] |
参考資料
[Summit 1995] | comp.lang.c FAQ list, Question 1.32 |
[Plum 1991] | Topic 1.26, "Strings—String Literals" |
翻訳元
これは以下のページを翻訳したものです。
STR30-C. Do not attempt to modify string literals (revision 69)