INT14-C. 同じデータに対してビット単位の演算と算術演算を行わない
同じデータに対してビット単位の演算と算術演算の両方を行うのは避けること。とくに、ビット単位の演算は、算術値にたいする演算のはやまった最適化として使われることが多い。ビット単位の演算子には、単項演算子~
、ビット演算子 <<
, >>
, &
, ^
および |
がある。このような演算は(言語仕様上は)有効でありコンパイルもできるが、コードの可読性は低下する。変数を数値もしくはビットパターンとして宣言すると、プログラマの意図が明確になり、コードのメンテナンス性もよくなる。
ビットパターンを表現する型を別に定義することで、ビットパターンの集合を数値型から区別することができる。これにより、ビット単位の演算がビットパターンを表現する変数に対してのみ行われることを確認しやすくなる。
typedef uint32_t bitmap32_t; bitmap32_t x = 0x000007f3; x = (x << 2) | 3; /* 2ビット右シフト */
typedef
名前 uintN_t
は、符号無し整数型が N
ビットの幅を持つことを表す。したがって、uint32_t
は符号無し整数を32ビットきっかりで表すことを意味する。ビットマップは符号無し型で宣言すること。「INT13-C. ビット単位の演算子は符号無しオペランドに対してのみ使用する」を参照。
左シフト演算子および右シフト演算子は、数値を2のべき乗で乗算したり除算したりする場合によく使われる。しかし、シフト演算子を使って乗算や除算を最適化すると、コードの移植性や可読性が悪くなる。しかも、2のべき乗の乗算除算をビット単位の演算に最適化することは、ほとんどのコンパイラが当たり前のように行っている。実際、コンパイラの方がプログラマよりも実行環境に詳しいものだ。
違反コード (左シフト)
以下のコード例では、整数 x
にビット単位の演算と算術演算が行われている。この代入式は、整数を2の補数で表現する処理系において、5x + 1
を x
に代入する処理を最適化したものである。
unsigned int x = 50; x += (x << 2) + 1;
これは(言語仕様上は)正当な演算だが、シフト演算の結果は、整数型がどのように表現されているかに依存している、つまり処理系定義である。加えて、コードの可読性が低下している。
適合コード (左シフト)
以下の適合コードでは、x
に関する算術演算を自然に表現するような代入式に書き換え、プログラマの意図をより明確に反映した形にしている。
unsigned int x = 50; x = 5 * x + 1;
このように変更すると、コードレビューをしたときに、演算にラップアラウンドのチェックが必要であることに気づくかもしれない。元のコード例では、これに気づくのは容易ではなかっただろう。
違反コード (右シフト)
以下のコード例では、プログラマは除算を右シフトで最適化したコードにしている。
int x = -50; x >>= 2;
このコードでも除算は正しく行われるかもしれないが、その保証はない。もし x
が符号付きの型であり負の値を持つならば、この演算は処理系定義であり、算術シフトか論理シフトのいずれかで実装される。論理シフトとして実装される場合、整数が1の補数表現あるいは2の補数表現として表現されるならば、最上位ビット(符号を表現する)はゼロにセットされる。こうなると、負であった値が正の値(非常に大きい値かもしれない)になる。詳しくは、「INT13-C. ビット単位の演算子は符号無しオペランドに対してのみ使用する」を参照のこと。
たとえば、x
の内部表現が 0xFFFF FFCE
(2の補数)である場合、算術シフトの結果は 0xFFFF FFF3
(2の補数の−13)になり、論理シフトの結果は 0x3FFF FFF3
(2の補数の1,073,741,811)になる。
適合コード (右シフト)
以下の適合コードでは、右シフトを除算におきかえている。
int x = -50; x /= 4;
こうすれば、結果の値はプログラマの期待通りになるだろう。
リスク評価
同じ変数に対してビット演算と算術演算の両方を行うと、プログラマの意図が曖昧になるし、コードの可読性も低下する。そうなると、セキュリティ上の欠陥をなくしたりデータの完全性を保証するためにどのようなチェックを行わなければならないか、コードのセキュリティ監査やメンテナンスを行う者が判断するのが困難になる。
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
INT14-C |
中 |
低 |
中 |
P4 |
L3 |
自動検出
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Compass/ROSE |
|
|
このレコメンデーションの違反を検出できる。しかし、同一の式のなかでビット演算と算術演算を行っている場合しか検出できない。 |
Fortify SCA |
5.0 |
|
CERT C Rule Packを使ってこのレコメンデーションの違反を検出できる |
LDRA tool suite |
V. 8.5.4 |
585 S |
実装済み |
関連するガイドライン
CERT C++ Secure Coding Standard | INT14-CPP. Avoid performing bitwise and arithmetic operations on the same data |
Java セキュアコーディングスタンダード CERT/Oracle 版 | NUM01-J. 同一のデータに対してビット演算と算術演算の両方を行わない |
ISO/IEC TR 24772:2013 | Bit Representations [STR] |
MISRA-C | Rule 6.4 Rule 6.5 |
参考資料
[Steele 1977] |
翻訳元
これは以下のページを翻訳したものです。
INT14-C. Avoid performing bitwise and arithmetic operations on the same data (revision 79)