Home > ラーニング > セキュアコーディング > C セキュアコーディングスタンダード > 01. プリプロセッサ (PRE)
マクロ置換リストは括弧で囲み、優先度の低い演算子をその周囲の式から保護すべきである。
以下に示す CUBE() マクロ定義は置換リストを括弧で囲んでいない。
#define CUBE(X) (X) * (X) * (X) int i = 3; int a = 81 / CUBE(i);
そのため、次のような呼び出し
int a = 81 / CUBE(i);
は以下のように展開される。
int a = 81 / i * i * i;
これは以下のように評価される。
int a = ((81 / i) * i) * i); /* 243 に評価される */
これは意図した動作ではない。
置換リストを括弧で囲むことで、CUBE() マクロはこの手の呼び出しに対して正しく展開される。
#define CUBE(X) ((X) * (X) * (X)) int i = 3; int a = 81 / CUBE(i);
この解決法は「PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う」に違反している。インライン関数として実装する方がよい。
以下のコード例では、EOF を -1 として定義している。マクロ置換リストは、単項否定演算子 - の後に整数リテラル 1 が続く。
#define EOF -1
/* ... */
if (getchar() EOF) {
/* ... */
}
このコード例では、プログラマは、本来getchar() != EOFと書くべき条件文で、うっかり比較演算子を書き忘れてしまっている。(「MSC02-C. Avoid errors of omission」を参照) マクロが展開されると、条件式は誤ってバイナリ演算 getchar()-1として評価されてしまう。これは、もちろんプログラマの意図したところではないが、構文的には正しい。この例はまた、「DCL00-C. イミュータブルなオブジェクトは const 修飾する」にも違反していることに注意。
EOF の定義で -1 を括弧で囲んでやれば、マクロ展開は正しく評価される。
#define EOF (-1)
一度この変更を行えば、このコード例はコンパイルできない。 マクロが展開されると、条件式は getchar() (-1) になるが、これはもはや構文的には正しくない。EOF のあとには空白が必要なことに注意。空白がないと関数形式マクロになってしまう(-1は仮引数にはなりえないのでマクロとして正しい形式になっていない)。
以下の解決法では、「DCL00-C. イミュータブルなオブジェクトは const 修飾する」に適合するよう、マクロ定義を列挙定数に置き換えている。
enum { EOF = -1 };
/* ... */
if (getchar() != EOF) {
/* ... */
}
PRE02-EX1: 単一の識別子もしくは関数呼び出しに展開されるマクロは、周囲の式の中の演算子の優先順位の影響を受けない。したがって、そのようなマクロの置換リストには括弧をつけなくてもよい。
#define MY_PID getpid()
PRE02-EX2: 配列の添字演算子 [] を使った配列参照、. 演算子や ->演算子を使って構造体のメンバや共用体オブジェクトを意味する式に展開されるマクロ。
#define NEXT_FREE block->next_free #define CID customer_record.account.cid #define TOOFAR array[MAX_ARRAY_SIZE]
マクロ置換リストを括弧で囲まないと予期せぬ結果を引き起こすおそれがある。
| レコメンデーション | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
|---|---|---|---|---|---|
| PRE02-C | 中 | 中 | 低 | P12 | L1 |
LDRA tool suite V 7.6.0 はこのレコメンデーションの違反を検出することができる。