メインコンテンツ

boolean 型オペランドの演算が無効な可能性があります

boolean 型オペランドの精度を超過するか、任意の値になる可能性がある演算

説明

この欠陥は、boolean 型オペランドが算術演算、関係演算、またはビット演算で使用されており、かつ次の場合に発生します。

  • boolean 型オペランドがトラップ表現をもつ。メモリ内での boolean 型のサイズは 1 以上のアドレス可能単位 (char のサイズ) です。boolean 型は、値 true (1) または false (0) を表現するために 1 ビットのみを必要とします。メモリ内での boolean 型オペランドの表現にはパディング ビットが含まれます。そのメモリ表現は、true でも false でもない値、つまりトラップ表現になる場合があります。

  • 演算の結果が boolean 型オペランドの精度を超過する可能性がある。

たとえば、以下のコード スニペットについて考えます。

bool_v >> 2

  • bool_v の値が true (1) または false (0) である場合、このビット単位シフトは bool_v の 1 ビット精度を超過し、その結果は常に 0 になります。

  • bool_v がトラップ表現をもつ場合、演算の結果は任意の値になります。

boolean 型オペランドの演算が無効な可能性がありますでは、以下の場合、欠陥が報告されません。

  • 演算の結果が精度オーバーフローにならない。たとえば、0x010x00 を使用した & または | ビット演算。

  • boolean 型オペランドがトラップ表現をもつことができない。たとえば、0 または 1 になる定数式や、true または false に評価される比較。

リスク

boolean 型オペランドに対する算術演算、関係演算、ビット演算は、オペランドの精度を超過する可能性があり、boolean 値として使用すると予期しない結果を引き起こす場合があります。トラップ表現をもつ boolean 型のオペランドに対する演算では、任意の値が返される可能性があります。

修正方法

boolean 型のオペランドに対し、次の演算以外の演算を実行することを避けます。

  • 代入演算 (=)。

  • 等式演算 (== または !=)。

  • 論理演算 (&&||、または !)。

すべて展開する

#include <stdio.h>
#include <stdbool.h>

#define BOOL _Bool

int arr[2] = {1, 2};

int func(BOOL b)
{
    return arr[b];
}

int main(void)
{
    BOOL b;
    char* ptr = (char*)&b;
    *ptr = 64;
    return func(b);
}

この例では、boolean 型のオペランド b を、2 つの要素をもつ配列の配列インデックスとして func で使用しています。使用するコンパイラと最適化フラグによっては、値 b0 にも 1 にもならない場合があります。たとえば、Linux® Debian 8 で gcc version 4.9 を最適化フラグ -O0 を指定して使用した場合、b の値は 64 となり、バッファー オーバーフローを引き起こします。

修正 — boolean 型オペランドの最下位ビット値のみを使用

1 つの修正方法として、unsigned int 型の変数 b0 を使用して、boolean 型オペランドの最下位ビットの値のみを取得します。boolean 型オペランドがトラップ表現をもつ場合でも、このビットの値は [0..1] の範囲内になります。

#include <stdio.h>
#include <stdbool.h>

#define BOOL _Bool

int arr[2] = {1, 2};

int func(BOOL b)
{
    unsigned int b0 = (unsigned int)b;
    b0 &= 0x1;
    return arr[b0];
}

int main(void)
{
    BOOL b;
    char* ptr = (char*)&b;
    *ptr = 64;
    return func(b);
} 

多くの場合、トラップ表現はコード内で以前に発生した問題の結果です。そのような問題の例として、以下が挙げられます。

  • 初期化されていない bool 型変数。

  • lvalue 式を使用して bool 型オブジェクトの一部を変更したことによる二次的影響。

  • 他の型の最終格納値をもつ union 型からの bool メンバーの読み取り。

したがって、C++ コードであっても boolean セマンティクスに従うのがベスト プラクティスとなります。

#include <iostream>

template <typename T>
bool less_or_equal(const T& x, const T& y)
{
    std::cout << "INTEGER VERSION" << '\n';
    return x <= y;
}
bool b1 = true, b2 = false;
int i1 = 2, i2 = 3;

int main()
{
    std::cout << std::boolalpha;
    std::cout << "less_or_equal(" << b1 << ',' << b2 << ") = " << less_or_equal<bool>(b1, b2) << '\n';
    std::cout << "less_or_equal(" << i1 << ',' << i2 << ") = " << less_or_equal<int>(11, 12) << '\n';
    return 0;
}

この例では、関数テンプレート less_or_equal は変数 x が y 以下であるかどうかを評価します。この関数に boolean 型を渡すと、オペランドのメモリ表現 (パディング ビットを含む) が 1 でも 0 でもない場合、<= 演算の結果は任意の値となる可能性があります。

修正 — 関数テンプレートを boolean 型向けに特殊化

1 つの修正方法として、関数テンプレートを boolean 型向けに特殊化します。特殊化された関数テンプレートでは、論理演算 (||) を使用して boolean 型のオペランドを比較します。

#include <iostream>

template <typename T>
bool less_or_equal(const T& x, const T& y)
{
    std::cout << "INTEGER VERSION" << '\n';
    return x <= y;
}

template<>
bool less_or_equal<bool>(const bool& x, const bool& y)
{
    std::cout << "BOOLEAN VERSION" << '\n';
    return !x || y;
}

bool b1 = true, b2 = false;
int i1 = 2, i2 = 3;

int main()
{
    std::cout << std::boolalpha;
    std::cout << "less_or_equal(" << b1 << ',' << b2 << ") = " << less_or_equal<bool>(b1, b2) << '\n';
    std::cout << "less_or_equal(" << i1 << ',' << i2 << ") = " << less_or_equal<int>(11, 12) << '\n';
    return 0;
}

結果情報

グループ: 数値
言語: C | C++
既定値: オフ
コマンド ライン構文: INVALID_OPERATION_ON_BOOLEAN
影響度:

バージョン履歴

R2018b で導入