Home > ラーニング > セキュアコーディング > C セキュアコーディングスタンダード > 01. プリプロセッサ (PRE)
マクロを使ってソースコードをより読みやすくすることはよくあることだ。単一の文からなるマクロ定義は do()-while() で包む必要はないが(「PRE10-C. 複数にわたる文からなるマクロは do-while ループで包む」を参照)、マクロ定義の最後をセミコロンで終端すべきではない。セミコロンが必要であるなら、マクロ展開後に追加されるべきである。うっかりセミコロンを挿入してしまうと、プログラムの制御フローを変更してしまう可能性がある。
この問題を回避する他の方法は、関数形式マクロよりもインライン関数やスタティック関数を使うことである。(「PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う」も参照)
一般に、プログラマは単一の文からなるマクロ定義の末尾にはセミコロンをつけないようにすべきだ。セミコロンをつける必要がある場合、マクロを呼び出すコードの側で責任をもってセミコロンをつけるべきである。
以下のコード例は、プログラムに for ループを提供するマクロ定義を行っている。このマクロはループが実行される回数をあらわす整数引数をとる。プログラマは間違って、マクロ定義の最後にセミコロンをつけてしまっている。
#define FOR_LOOP(n) for(i=0; i<(n); i++);
int i;
FOR_LOOP(3)
{
puts("Inside for loop\n");
}
プログラマは次のような出力が得られることを期待している:
Inside for loop Inside for loop Inside for loop
しかし、マクロ定義の最後にセミコロンがあるため、プログラム中の for ループは空文をもつことになり、"Inside for loop" は一度だけ出力される。基本的に、マクロ定義の最後にあるセミコロンはプログラムの制御フローを変更する。
上記のような例が実際のコードで使われることはないかもしれないが、マクロ定義中のセミコロンが持つ影響について示している。
以下の解決法は、最後にセミコロンがないマクロ定義を書き、セミコロンをつけるかどうかの判断はそのマクロを使うユーザにゆだねることである。
#define FOR_LOOP(n) for(i=0; i<(n); i++)
int i;
FOR_LOOP(3)
{
puts("Inside for loop\n");
}
以下のコードでは、プログラマは第一引数の値を1インクリメントし、その結果の値を第二引数 max の値で剰余している。
#define INCREMENT(x, max) ((x) = ((x) + 1) % (max)); int index = 0; int value; value = INCREMENT(index, 10) + 2;
このコードでは、プログラマは index をインクリメントし、その結果の値に2を足すことを意図している。しかし残念ながら、結果の値は index をインクリメントした値に等しくなる。マクロの最後にセミコロンが存在するからである。コンパイラは '+ 2;' を別の文として扱う。ユーザがコンパイルエラーをうけとることはない。コンパイル時に警告オプションを有効にしていなければ、マクロのセミコロンの影響が開発の早い段階で検出されることはないだろう。
以下の解決法は、マクロ定義の最後にセミコロンをつけないで書いておき、セミコロンをつけるかどうかの判断はマクロを使う者にゆだねることである。
#define INCREMENT(x, max) ((x) = ((x) + 1) % (max))
マクロ定義の最後にセミコロンをつけると、プログラムの制御フローが変わり、その結果予期せぬ動作が引き起こされる恐れがある。
| レコメンデーション | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
|---|---|---|---|---|---|
| PRE11-C | 中 | 中 | 低 | P12 | L1 |
PRE11-C. Do not conclude a single statement macro definition with a semicolon