JPCERT コーディネーションセンター

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;
}
適合コード(同じ記憶域期間の指定)

以下の適合コードでは、pstr は同じ記憶域期間となるよう宣言されており、pthis_is_OK() の外で 不定な値をとることはない。

void this_is_OK(void) {
  const char c_str[] = "Everything OK";
  const char *p = c_str;
  /* ... */
}
/* c_str の生存期間外では p へもアクセスできない */

または pc_str の両方とも静的記憶域期間で宣言する方法もある。

適合コード(異なる記憶域期間)

p を 静的記憶域期間 で、c_str をより短期の記憶域期間で定義する必要がある場合、c_str が無効になる前に pNULL を設定するという方法がある。こうすれば 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

このルールの違反を検出できる。ローカル変数へのポインタを返したことを自動的に検出する。より一般的な場合、例えばローカル変数を指す静的ポインタが当該変数の生存期間外までその値を保持してしまう、などの検出は難しい。

Coverity

2017.07

RETURN_LOCAL

関数がローカルスタック変数へのポインタを返す多くのパターンを検出する。ただし、このルールへの違反をすべて検出できるわけではないため、さらなる検証が必要である。

Klocwork
2018

LOCRET.ARG
LOCRET.GLOB

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
CERT_C-DCL30-b

The address of an object with automatic storage shall not be returned from a function
The address of an object with automatic storage shall not be assigned to another object that may persist after the first object has ceased to exist

Polyspace Bug Finder

R2018a

Pointer or reference to stack variable leaving scope

MISRA C:2012 Rule 18.6

ローカル変数へのポインタがその変数の範囲外へ出る

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 =

参考資料
[Coverity 2007]
[ISO/IEC 9899:2011] 6.2.4, "Storage Durations of Objects"
翻訳元

これは以下のページを翻訳したものです。

DCL30-C. Declare objects with appropriate storage durations (revision 212)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter