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

DCL03-C. 定数式の値をテストするには静的アサートを使う

アサートは、脆弱性につながる欠陥を検出し、解消するための有用な診断ツールである。(「MSC11-C. 診断テストはアサートを使って組み込む」を参照)。しかし、実行時 assert() マクロには、実行時のオーバーヘッドをともないかつabort()を呼び出すという制限が存在する。したがって、実行時 assert()マクロが有用なのは、間違った想定を特定するためだけであって、実行時のエラーチェックのためではない。それゆえ、実行時アサートは一般的にサーバープログラムや組込みシステムには向いていない。

静的アサートは C 言語標準で新たに取り入れられた機能であり、次の形式をとる。

static_assert(constant-expression, string-literal);

C 標準 [ISO/IEC 9899:2011] セクション 6.7.10 には、次のように記載されている。

定数式は、整数の定数式でなければならない。定数式の値が 0 と等しくない場合、宣言は効果を発揮しない。そうでなければ、制限違反が発生しており、実装は文字列リテラルのテキストを含む、診断メッセージを生成する。ただし、基本ソース文字セット内にない文字がメッセージに表示される必要はない。

これは、constant-expression が真の場合は何も発生しないが、constant-expression が偽の場合はコンパイル時に string-literal を含むエラーメッセージが出力されることを意味する。

/* パスする */
static_assert(
  sizeof(int) <= sizeof(void*), 
  "sizeof(int) <= sizeof(void*)"
); 

/* 失敗する */
static_assert(
  sizeof(double) <= sizeof(int), 
  "sizeof(double) <= sizeof(int)"
);

C99 では、静的アサートは使用できない。

違反コード

以下のコードでは、assert() マクロを使い、コードが正しく動作するために必須なメモリマップされた構造体の状態を診断している。

struct timer {
  unsigned char MODE;
  unsigned int DATA;
  unsigned int COUNT;
};
 
int func(void) {
  assert(sizeof(struct timer) == sizeof(unsigned char) + sizeof(unsigned int) + sizeof(unsigned int));
}

実行時アサートを使う方が何もしないよりはましだが、関数に組み込まれ、実行されなければ意味がない。つまり、多くの場合、関数が参照する実際の構造体からあまりにかけはなれているのである。診断が行われるのは、実行時のみであり、かつアサートを含むコードパスが実行された場合のみである。

適合コード

定数式のみを含むアサートに関しては、次の例に示すように、プリプロセッサの条件文を使用できる。

struct timer {
  unsigned char MODE;
  unsigned int DATA;
  unsigned int COUNT;
};

#if (sizeof(struct timer) == sizeof(unsigned char) + sizeof(unsigned int) + sizeof(unsigned int))
  #error "Structure must not have any padding"
#endif

#error 指令を使うことで明解な診断メッセージを実現できる。このアプローチを使えばコンパイル時にアサートが評価されるため、実行時のペナルティーはない。

適合コード

可搬性のあるこの適合コードは、static_assertを使用する。

struct timer {
  unsigned char MODE;
  unsigned int DATA;
  unsigned int COUNT;
};

static_assert(sizeof(struct timer) == sizeof(unsigned char) + sizeof(unsigned int) + sizeof(unsigned int),
              "Structure must not have any padding");

静的アサートを使うことで、プログラムが黙って機能しなくなったり実行時エラーを引き起こすかわりに、正しくない想定をコンパイル時に検査することができる。アサートはコンパイル時に行われるため、メモリスペースや時間の実行時コストは発生しない。アサートをファイル有効範囲やブロック有効範囲で使うことで、失敗時に有意味な診断エラーメッセージを出すことができる。

静的アサートの他の利用方法については、「STR07-C. TR 24731 を使用し、文字列操作を行う既存のコードの脅威を緩和する」および「FIO35-C. sizeof(int) == sizeof(char) の場合、EOF およびファイルエラーの検出には feof() と ferror() を使用する」を参照のこと。

リスク評価

静的アサートは脆弱性につながるソフトウェアの欠陥を発見し排除するのに役に立つプログラム診断ツールである。しかし、静的アサートがプログラム中にないからといってコードが間違っているということを意味するわけではない。

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

DCL03-C

P1

L3

自動検出

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

ただ単に assert() 呼び出しを見つけることでこのレコメンデーションの違反を検出できるはず。もし、アサートに関するすべての値がコンパイル時に決定しており、完全に評価できるのであれば、static_assert を使うべき。ただし、ROSE がマクロ呼び出しを認識できるという前提である。

ECLAIR 1.1 macrcall 実装済み

LDRA tool suite

V. 8.5.4

44 S

実装済み

PRQA QA-C 8.1 2741 2742 部分的に実装済み
関連するガイドライン
C++ Secure Coding Standard DCL03-CPP. Use a static assertion to test the value of a constant expression
参考資料
[Becker 2008]  
[Eckel 2007]  
[ISO/IEC 9899:2011] Section 6.7.10, "Static Assertions"
[Jones 2010]  
[Klarer 2004]  
[Saks 2005]  
[Saks 2008]  
翻訳元

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

DCL03-C. Use a static assertion to test the value of a constant expression (revision 94)

Top へ

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