EXP19-C. if、for、while 文の本体は波括弧で囲む
if
、for
、while
文に対しては、本体に単一の文しか含まれていない場合でも、必ず開始と終了の波括弧を使用すること。
if
、while
、for
文をマクロの中で使用する場合、マクロ定義の最後をセミコロンで終端すべきではない(「PRE11-C. マクロ定義をセミコロンで終端しない」を参照)。
波括弧を使用することでコードの一貫性が保たれ、読みやすさも向上する。より重要なことは、文が 1 つしかない本体に新たに文を追加するときに、字下げがその構造に対する強固な(しかし誤解を招きやすい)目安となるため、波括弧を付けるのを忘れがちな点である。
また、波括弧を用いると、複数にわたる文からなるマクロを正しく展開するのにも役立つ。複数にわたる文からなるマクロは do-while
ループで包むべきだ(「PRE10-C. 複数にわたる文からなるマクロは do-while ループで包む」を参照)。しかし、do-while
ループがなくても、波括弧を使用することでマクロが意図したとおりに展開されることを保証できる。
違反コード
次のコード例は、波括弧を付けずに if
文を使用してユーザの認証を行っている。
int login;
if (invalid_login())
login = 0;
else
login = 1;
上記のコードにログインが有効かどうかを判定するデバッグ文を追加したときに、プログラマが開始と終了の波括弧を追加し忘れたとする。
int login;
if (invalid_login())
login = 0;
else
printf("Login is valid\n"); /* デバッグ行がここに追加される */
login = 1; /* この行はログインが有効かどうかを問わず常に実行される */
コードの字下げが原因で、意図した通りにコードが機能しないということがわかりにくいため、セキュリティの侵害につながる可能性がある。
適合コード
次の適合コードでは、本体が単一の文であっても開始と終了の波括弧を使用している。
int login;
if (invalid_login()) {
login = 0;
} else {
login = 1;
}
違反コード
次のコード例では、if
と else
の本体を波括弧で囲まずに、if
文の中で if
文を入れ子にしている。
int privileges;
if (invalid_login())
if (allow_guests())
privileges = GUEST;
else
privileges = ADMINISTRATOR;
字下げが原因で、プログラマはユーザのログインが有効な場合に限りユーザに管理者権限が与えられると考えてしまうかもしれない。しかし、else
文は実際には内側の if
文と結び付けられている。
int privileges;
if (invalid_login())
if (allow_guests())
privileges = GUEST;
else
privileges = ADMINISTRATOR;
これはセキュリティの抜け道であり、ログインが無効のユーザでも管理者権限を取得できてしまう可能性がある。
適合コード
次の適合コードは、波括弧を追加することであいまいさを排除し、権限が正しく割り当てられることを保証している。
int privileges;
if (invalid_login()) {
if (allow_guests()) {
privileges = GUEST;
}
} else {
privileges = ADMINISTRATOR;
}
違反コード (empty block)
以下のコードは、本体を持たない while
文である。
while (invalid_login());
invalid_login()
が、ログイン失敗の警告表示などのような副作用を何も発生させないものであったならば、「MSC12-C. プログラムに対して作用しないコードを検出して削除する」にも違反していることになる。
適合コード (empty block)
以下の適合コードでは、空のブロックを置くことでプログラマーの意図を明示している。
while (invalid_login()) {}
リスク評価
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
EXP19-C |
中 |
中 |
中 |
P8 |
L2 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
Astrée |
24.04
|
compound-ifelse compound-loop |
Fully checked |
Axivion Bauhaus Suite |
7.2.0 |
CertC-EXP19 | Fully implemented |
Helix QAC |
2024.2 |
C2212 |
|
Klocwork |
2024.2
|
MISRA.IF.NO_COMPOUND MISRA.STMT.NO_COMPOUND |
|
LDRA tool suite |
9.7.1
|
11 S, 12 S, 428 S | Fully Implemented |
Parasoft C/C++test |
2023.1
|
CERT_C-EXP19-a |
The statement forming the body of a 'switch', 'while', 'do...while' or 'for' statement shall be a compound statement |
PC-lint Plus |
1.4 |
9012 |
Fully supported |
Polyspace Bug Finder |
R2024a |
CERT C: Rec. EXP19-C |
Checks for iteration or selection statement body not enclosed in braces (rec. fully covered) |
PVS-Studio |
7.31 |
V563, V628, V640, V705 |
|
RuleChecker |
24.04
|
compound-ifelse compound-loop |
Fully checked |
SonarQube C/C++ Plugin |
3.11
|
S121 |
|
関連する脆弱性
CVE-2014-1266 was due, in large part, to failing to follow this recommendation. There is a spurious "goto fail" statement on line 631 of sslKeyExchange.c. This "goto" gets executed unconditionally, even though it is indented as if it were part of the preceding "if" statement. As a result, the call to sslRawVerify (which performs the actual signature verification) is rendered dead code. [ImperialViolet 2014]. If the body of the "if" statement had been enclosed in braces, then this defect likely would not have happened.
関連するガイドライン
MISRA C:2012 | Rule 15.6 (required) |
参考資料
[GNU 2010] | Coding Standards, Section 5.3, "Clean Use of C Constructs" |
翻訳元
これは以下のページを翻訳したものです。
EXP19-C. Use braces for the body of an if, for, or while statement (revision 92)