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

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

お問い合わせ サイトマップ English

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

  1. HTTPS

02. 宣言と初期化 (DCL)

最終更新: 2010-07-26

DCL07-C. 関数宣言子には適切な型情報を含める

関数宣言子は、返り値の型や引数のリストなどを含む適切な型情報をつけて宣言されなくてはならない。関数指定子に型情報が適切に指定されないと、コンパイラは関数の型情報を適切にチェックすることができない。標準ライブラリを使用する場合、適切な型情報を持つ関数宣言子を取得するもっとも簡単で(かつ推奨される)方法は適切なヘッダファイルをインクルードすることである。

適切な型情報を持たない関数宣言子を含むプログラムをコンパイルしようとすると、典型的には警告が出るが、コンパイルは通る。これらの警告は解決すべきである(「MSC00-C. 高い警告レベルで警告を出さずにコンパイルする」を参照)。

違反コード (NonPrototype-Format Declarators)

以下のコード例では、識別子並びの形を引数の宣言に用いている。

int max(a, b)
int a, b;
{
  return a > b ? a : b;
}

C99 言語規格のセクション6.11 「今後の言語の方針」には「仮引数の識別子並びと宣言並びを別々に与える関数定義(関数原型形式の仮引数の型及び識別子の宣言ではない。)の使用は、廃止予定事項とする」と書かれている。

適合コード (NonPrototype-Format Declarators)

以下の解決法では、int が型指定子であり、max(int a, int b) は関数宣言子であり、中括弧の中のブロックは関数本体である。

int max(int a, int b) {
  return a > b ? a : b;
}
違反コード (関数プロトタイプ)

プロトタイプなしで関数宣言を行うと、コンパイラは正しい数および型の引数が関数に渡されているものと想定して動作する。これにより、プログラマが予期せぬ未定義の動作が引き起こされる恐れがある。

以下のコード例では、file_a.c 中の func() の定義は3つの引数をとるが、実際には2つしか与えられていない。

/* file_a.c source file */
int func(int one, int two, int three) {
  printf("%d %d %d", one, two, three);
  return 1;
}

しかし、file_b.c 中に func() のプロトタイプが存在しないため、コンパイラは正しい数の引数が渡されていると想定し、プログラムスタック上の次の値を第三引数として使用する。

/* file_b.c source file */
func(1, 2);

C99 では暗黙的な関数宣言をC言語からとりのぞいている。[ISO/IEC 9899:1999] しかし、多くのコンパイラは、警告は出すかもしれないが、いまだ暗黙的に宣言された関数のコンパイルを許している。このような警告は解決されるべきである。 (「MSC00-C. 高い警告レベルで警告を出さずにコンパイルする」を参照。)

適合コード (関数プロトタイプ)

以下の解決法は、func()の関数プロトタイプを、それが呼び出される翻訳単位のなかに正しく含めている。また、関数呼び出しを修正し、正しい数の引数を渡している。

/* file_b.c source file */
int func(int, int, int)

func(1, 2, 3);
違反コード (関数ポインタ)

関数ポインタが互換性のない関数を指しているとき、その関数を関数ポインタを通じて呼び出すと、プロセススタックを破壊するかもしれない。呼出し側の関数による、予期せぬデータへのアクセスにつながるかもしれない。

以下のコード例では、関数ポインタ fn_ptr は整数引数を三つとる関数 add() を指している。しかし、fn_ptr は整数引数を二つとると指定されている。fn_ptradd() を参照するようにセットすると、予期せぬプログラムの動作を引き起こす。この例は「DCL35-C. 関数定義と一致しない型で関数を呼び出さない」にも違反している。

int add(int x, int y, int z) {
   return x + y + z;
}

int main (int argc, char *argv[]) {
   int (*fn_ptr) (int, int);
   int res;
   fn_ptr = add;
   res = fn_ptr(2, 3);  /* 間違い */
   /* ... */
   return 0;
}
適合コード (関数ポインタ)

このコードを修正するには、fn_ptr を引数を三つとるように変更すればよい。

int add(int x, int y, int z) {
   return x + y + z;
}

int main(int argc, char *argv[]) {
   int(*fn_ptr) (int, int, int);
   int res;
   fn_ptr = add;
   res = fn_ptr(2, 3, 4);
   /* ... */
   return 0;
}
リスク評価

関数宣言子に型情報を含めないと、予期せぬあるいは意図しないプログラムの動作を引き起こすかもしれない。

レコメンデーション 深刻度 可能性 修正コスト Cost 優先度 レベル
DCL07-C P3 L3
自動検出

LDRA tool suite V 7.6.0 はこのレコメンデーションへの違反を検出できる。

GCC コンパイラは、-Wstrict-prototypes フラグを指定することにより、このレコメンデーションへの違反を検出できる。

参考情報
  • [ISO/IEC 9899:1999] Forward and Section 6.9.1, "Function definitions"
  • [ISO/IEC PDTR 24772] "IHN Type system" and "OTR Subprogram Signature Mismatch"
  • [MISRA 04] Rule 8.2
  • [Spinellis 06] Section 2.6.1, "Incorrect Routine or Arguments"
翻訳元

DCL07-C. Include the appropriate type information in function declarators

Top へ