このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。
非トリビアルなクラス オブジェクトのバイト演算
値の表現が不適切に初期化または比較される可能性がある
説明
この欠陥は、C 標準ライブラリ関数を使用して、非トリビアルまたは非標準のレイアウト クラス型オブジェクトに対してバイト単位演算を実行した場合に発生します。トリビアルおよび標準のレイアウト クラスに関する定義については、C++ Standard (ISO/IEC 14882:2017)、[class]、段落 6 および 7 をそれぞれ参照してください。
チェッカーは以下の場合に欠陥を報告します。
次の関数を使用して非トリビアルなクラス型オブジェクトを初期化またはコピーする。
std::memset
std::memcpy
std::strcpy
std::memmove
クラス型がトリビアルであるかどうかを確認するには、type-traits ライブラリ関数
std::is_trivial
を次のように使用します。オブジェクト型がトリビアル コピー可能 (#include <iostream> #include <type_traits> class trivialClass {}; void checkTrivial(){ static_assert(std::is_trivial<trivialClass>::value, "Class is not trivial"); }
std::is_trivially_copyable<>:value
) であることを確認するだけでは十分ではありません。プログラムの後半でオブジェクトを使用する場合、トリビアル コピー可能オブジェクトでは、クラスの不変性の維持が保証されません。次の関数を使用して非標準のレイアウト クラス型オブジェクトを比較する。
std::memcmp
std::strcmp
不完全なクラスは非トリビアルである可能性があることに注意してください。
チェッカーは、バイト単位演算がエイリアスを介して実行された場合には欠陥を報告しません。たとえば、このコードによるバイト比較演算およびバイト コピー演算では欠陥が報告されません。このバイト演算では、dptr
および sptr
を使用しています。これらは非トリビアルまた非標準のレイアウト クラス オブジェクト d
および s
のエイリアスです。
void func(NonTrivialNonStdLayout *d, const NonTrivialNonStdLayout *s) { void* dptr = (void*)d; const void* sptr = (void*)s; // ... // ... // ... if (!std::memcmp(dptr, sptr, sizeof(NonTrivialNonStdLayout))) { (void)std::memcpy(dptr, sptr, sizeof(NonTrivialNonStdLayout)); // ... } }
リスク
非トリビアルまたは非標準のレイアウト クラス型オブジェクトに対して、C 標準ライブラリ関数を使用してバイト比較演算を実行すると、実装の詳細による予期しない値につながる可能性があります。オブジェクト表現は、実装の詳細 (プライベートおよびパブリック メンバーの順序など)、またはそのオブジェクトを表すバーチャル関数ポインター テーブルの使用に依存します。
非トリビアルまたは非標準のレイアウト クラス型オブジェクトに対して、C 標準ライブラリ関数を使用してバイト設定演算を実行すると、実装の詳細が変更される可能性があります。この演算により、プログラムの異常動作やコード実行の脆弱性につながる可能性があります。たとえば、メンバー関数のアドレスが上書きされた場合、その関数を呼び出すと予期しない関数が呼び出されます。
修正方法
非トリビアルまたは非標準のレイアウト クラス型オブジェクトにバイト演算を実行するには、C 標準ライブラリ関数ではなく、次の特殊な C++ メンバー関数を使用します。
C 標準ライブラリ関数 | C++ メンバー関数 |
---|---|
| クラスのコンストラクター |
| クラスのコピー コンストラクター クラスの移動コンストラクター コピー代入演算子 移動代入演算子 |
|
|
例
結果情報
グループ: オブジェクト指向 |
言語: C++ |
既定値: オフ |
コマンド ライン構文: MEMOP_ON_NONTRIVIAL_OBJ |
影響度: Medium |
バージョン履歴
R2019b で導入