DCL30-C. 適切な記憶域期間でオブジェクトを宣言する
すべてのオブジェクトは、その生存期間を決定する記憶域期間(storage duration)をもつ。記憶域期間には、静的記憶域期間(static)、スレッド記憶域期間(thread)、自動記憶域期間(automatic)、割付け記憶域期間(allocated) の4種類がある。
C標準 [ISO/IEC 9899:2011]の6.2.4、パラグラフ2には次のように記載されている。
オブジェクトの生存期間(life time)とは、オブジェクトに対して記憶域の確保が保証されている、プログラム実行の一部分をいう。オブジェクトは、生存期間を通じて存在し、一定のアドレスをもち、最後に格納された値を保持する。生存期間外でオブジェクトを参照したときの動作は未定義とする。ポインタの値は、そのポインタが指すオブジェクトが生存期間の最後に到達すると、不定になる。
生存期間外でオブジェクトにアクセスしないこと。生存期間外でのオブジェクトへのアクセスは未定義の動作であり、攻撃可能な脆弱性へとつながる可能性がある(C標準の附属書J、未定義動作9も参照のこと)。
違反コード(異なる記憶域期間)
以下の違反コード例では、静的記憶域期間をもつ変数 p
に、自動記憶域期間をもつ変数 c_str
のアドレスを割り当てている。dont_do_this
()
の実行が終了すると c_str
の生存期間も終了し、p
が保持している c_str
のアドレスは無効な値になる。
#include <stdio.h>
const char *p;
void dont_do_this(void) {
const char c_str[] = "This will change";
p = c_str; /* 危険! */
}
void innocuous(void) {
printf("%s\n", p);
}
int main(void) {
dont_do_this();
innocuous();
return 0;
}
適合コード(同じ記憶域期間の指定)
以下の適合コードでは、p
と str
は同じ記憶域期間となるよう宣言されており、p
が this_is_OK()
の外で 不定な値をとることはない。
void this_is_OK(void) {
const char c_str[] = "Everything OK";
const char *p = c_str;
/* ... */
}
/* c_str の生存期間外では p へもアクセスできない */
または p
と c_str
の両方とも静的記憶域期間で宣言する方法もある。
適合コード(異なる記憶域期間)
p
を 静的記憶域期間 で、c_str
をより短期の記憶域期間で定義する必要がある場合、c_str
が無効になる前に p
に NULL
を設定するという方法がある。こうすれば p
が 不定な値をとることは防げるが、p
を参照する際は必ずその値が NULL
かどうかを確認しなければならない。
const char *p;
void is_this_OK(void) {
const char c_str[] = "Everything OK?";
p = c_str;
/* ... */
p = NULL;
}
違反コード (返り値)
以下の違反コード例では、関数 init_array()
は自動記憶域期間をもつ文字配列のポインタを返す。この返り値を受け取った呼び出し元がアクセスしてしまうかもしれない。
char *init_array(void) {
char array[10];
/* 配列を初期化 */
return array;
}
コンパイラによっては、このコード例のように関数が自動記憶域期間をもつオブジェクトへのポインタを返した場合に警告メッセージを出すものもある。コンパイル時には高い警告レベルを設定し、出力されるすべての警告メッセージに対処すべきである。(MSC00-C. 高い警告レベルでのコンパイルで警告が出ないようにするを参照。)
適合コード (返り値)
上記のコード例をどのように修正するかは、プログラマがなにをしようとしているのかによる。array
の値を init_array()
の外でも使いたい場合には、array
を別の場所で宣言し、init_array()
に引数として渡すことで、必要な動作を実現できる。
#include <stddef.h>
void init_array(char *array, size_t len) {
/* 配列を初期化 */
return;
}
int main(void) {
char array[10];
init_array(array, sizeof(array) / sizeof(array[0]));
/* ... */
return 0;
}
違反コード(出力引数)
以下の違反コード例において関数 squirrel_away()
は、ローカル変数 local
へのポインタを、引数として渡された ptr_param
が指す位置に格納する。squirrel_away()
から戻った後、ptr_param
ポインタが指している変数は生存期間を過ぎている。
void squirrel_away(char **ptr_param) {
char local[10];
/* 配列を初期化 */
*ptr_param = local;
}
void rodent(void) {
char *ptr;
squirrel_away(&ptr);
/* 変数 ptr は生存しているが、その値は無効 */
}
適合コード (出力引数)
以下の適合コードでは、変数 local
は静的記憶域期間をもつため、rodent()
関数内で ptr
を通じて local
配列を参照しても問題ない。
char local[10];
void squirrel_away(char **ptr_param) {
/* 配列を初期化 */
*ptr_param = local;
}
void rodent(void) {
char *ptr;
squirrel_away(&ptr);
/* 変数 ptr とその値はこの範囲内で有効 */
}
リスク評価
生存期間外でオブジェクトを参照している場合、任意のコード実行につながる可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
DCL30-C |
高 |
中 |
高 |
P6 |
L2 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Astrée |
19.04
|
pointered-deallocation | Fully checked |
Axivion Bauhaus Suite |
6.9.0 |
CertC-DCL30 | Fully implemented |
CodeSonar |
5.0p0
|
LANG.STRUCT.RPL | ローカル変数へのポインタを返す |
Compass/ROSE |
|
|
このルールの違反を検出できる。ローカル変数へのポインタを返したことを自動的に検出する。より一般的な場合、例えばローカル変数を指す静的ポインタが当該変数の生存期間外までその値を保持してしまう、などの検出は難しい。 |
2017.07
|
RETURN_LOCAL |
関数がローカルスタック変数へのポインタを返す多くのパターンを検出する。ただし、このルールへの違反をすべて検出できるわけではないため、さらなる検証が必要である。 |
|
Klocwork |
2018
|
LOCRET.RET |
|
LDRA tool suite |
9.7.1
|
42 D, 77 D, 71 S, 565 S |
Enhanced Enforcement |
Parasoft C/C++test |
10.4.1
|
CERT_C-DCL30-a |
The address of an object with automatic storage shall not be returned from a function |
Polyspace Bug Finder |
R2018a |
ローカル変数へのポインタがその変数の範囲外へ出る The address of an object with automatic storage shall not be copied to another object that persists after the first object has ceased to exist |
|
PRQA QA-C |
9.5
|
3217, 3225, 3230, 4140 |
部分的に実装済み |
PRQA QA-C++ |
4.3
|
2515, 2516, 2527, 2528, 4026, 4624, 4629 |
|
PVS-Studio |
6.23 |
V506, V507, V558, V623, V723, V738 |
|
Splint |
3.1.1
|
|
|
関連するガイドライン
Taxonomy |
Taxonomy item |
Relationship |
---|---|---|
CERT C コーディングスタンダード | MSC00-C. 高い警告レベルでのコンパイルで警告が出ないようにする | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT C | EXP54-CPP. Do not access an object outside of its lifetime | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TR 24772:2013 | Dangling References to Stack Frames [DCM] | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TS 17961 | Escaping of the address of an automatic object [addrescape] | Prior to 2018-01-12: CERT: Unspecified Relationship |
MISRA C:2012 | Rule 18.6 (required) | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT-CWE Mapping Notes
Key here for mapping notes
CWE-562 and DCL30-C
DCL30-C = Union( CWE-562, list) where list =
- Assigning a stack pointer to an argument (thereby letting it outlive the current function
参考資料
[Coverity 2007] |
|
[ISO/IEC 9899:2011] | 6.2.4, "Storage Durations of Objects" |
翻訳元
これは以下のページを翻訳したものです。
DCL30-C. Declare objects with appropriate storage durations (revision 212)