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

安全・安心なIT社会のための、国内・国際連携を支援する

お問い合わせ 採用情報 サイトマップ English

Home > ラーニング > セキュアコーディング > C セキュアコーディングスタンダード > 02. 宣言と初期化 (DCL)

最終更新: 2014-03-14

DCL31-C. 識別子は宣言してから使用する


DCL31-C. 識別子は宣言してから使用する

C 標準では変数と関数を暗黙的に型宣言することができる。暗黙的な宣言を行うと型チェックが緩くなり、予期せ動作、あるいは誤ったプログラムの動作につながったり、セキュリティ上の脆弱性を引き起こすことも少なくない。

C 標準では型識別子が強制され、暗黙的な関数宣言は禁じられている。ただし、処理系は、警告メッセージを出力後、(エラーにせず)暗黙的な宣言が行われていると仮定してコンパイルを続けてもよい。これは、暗黙的な型宣言を行っている既存のプログラムを考慮してのことである。

違反コード (暗黙的な int)

C 標準では、オブジェクトの宣言時に型指定子を省略することが認められている。省略された場合、型は signed int 型として定義される。

暗黙的な int 型の指定に依存しないこと。C 標準 [ISO/IEC 9899:2011] セクション 6.7.2 には、次のように記載されている。

それぞれの宣言の宣言指定子列の中で、またはそれぞれの struct 宣言および型名の型指定子型修飾子の並びの中で、少なくとも 1 つの型指定子を指定しなければならない。

ルールに違反した以下のコード例では、型指定子を省略している。

extern foo;

C 処理系の多くはこの制約違反に対して警告メッセージを出さない。C 処理系の多くは、このような宣言は暗黙的に int 型を指定しているとみなし処理を続ける。

適合コード

以下の適合コードでは、型指定子を明示的に指定している。

extern int foo;
違反コード (暗黙的な関数宣言)

暗黙的な関数宣言を行ってはならない。すべての関数は、その呼び出し前に明示的に宣言されている必要がある。C89 では、明示的なプロトタイプを持たない関数が呼び出された場合、コンパイラが暗黙的な関数宣言を行う。

C90 は次のように定めている。

関数呼び出しにおいて、括弧で囲まれた引数リストの前に付く式が単一の識別子のみで構成され、この識別子の宣言が見えない場合、この関数呼び出し内の最も内側のブロックに extern int identifier(); という宣言があるものとして、暗黙的に識別子が宣言される。

C99 以降の処理系は暗黙的な関数宣言を行わない。

コンパイラの中には、関数呼び出し時にその関数の宣言が見えない場合、次のような暗黙的な宣言が行われているものとみなすものもある。

extern int func();

しかし、C 標準に適合するには、関数呼び出し前に、各関数を明示的にプロトタイプ宣言する必要がある。ルールに違反したこのコード例では、main() 内で foo() 関数を呼び出す前にそのプロトタイプ宣言が行われていない。

int main(void) {
  int c = foo();
  printf("%d\n", c);
  return 0;
}

int foo(int a) {
  return a;
}

コンパイラは foo() の型が extern int foo() であると暗黙的に想定するため、引数が欠落していることを検知できない。

適合コード (暗黙的な関数宣言)

以下の適合コードでは、foo() のプロトタイプ宣言を関数呼び出しの前に行っている。

int foo(int);

int main(void) {
  int c = foo(0);
  printf("%d\n", c);
  return 0;
}

int foo(int a) {
  return a;
}

関数宣言については「DCL07-C. 関数宣言子には適切な型情報を含める」を参照。

違反コード (暗黙的な戻り値の型)

同様に、返り値の型を明示しない関数を宣言してはならない。関数が整数値を返す場合、返り値を明示的に int 型として宣言すること。意味のある値を返さない場合は、void 型として宣言すること。

foo(void) {
  return UINT_MAX;
}

int main(void) {
  long long c = foo();
  printf("%lld\n", c);
  return 0;
}

コンパイラは foo() が(符号付きの) int 型の値を返すと想定するため、UINT_MAX は誤って −1 に変換される。

適合コード (暗黙的な戻り値の型)

以下の適合コードでは、foo() の返り値の型を明示的に unsigned int 型として定義している。

unsigned int foo(void) {
  return UINT_MAX;
}

int main(void) {
  long long c = foo();
  printf("%lld\n", c);
  return 0;
}
リスク評価

実際のコードで型指定子を省略されている例はまれである。また、たとえ省略されたとしても生じる結果はそれほど深刻ではなく、プログラムが異常終了する程度であろう。

ルール

深刻度

可能性

修正コスト

優先度

レベル

DCL31-C

P3

L3

自動検出

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

 

ECLAIR

1.1

decltype

実装済み

GCC

V. 4.3.5

 

-Wimplicit フラグと -Wreturn-type フラグが指定されている場合にこのルールの違反を検出できる。

Klocwork

V. 9.1

IF_MISS_DECL RETVOID.IMPLICIT

 

LDRA tool suite

V. 8.5.4

24 D
20 S
326 S

実装済み

PRQA QA-C 8.1

0434 (C)
1302
2050
2051
3335

実装済み
関連するガイドライン
ISO/IEC TR 24772:2013 Subprogram Signature Mismatch [OTR]
MISRA-C  
参考資料
[ISO/IEC 9899:2011] Section 6.7.2, "Type Specifiers"
[Jones 2008]  
翻訳元

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

DCL31-C. Declare identifiers before using them (revision 69)

Top へ