EN JP CN

CL.FFM.COPY

CL.FFM.COPY

コピー コンストラクタの欠落による解放されたメモリの解放

クラスレベルのチェッカーは有効な C++ のための Scott Meyer 規則クラス構築に基づいて推奨を通知します。

CL.FFM.COPY は Scott Meyer の 11 項「動的割り当て済みメモリを持つクラス用のコピーコンストラクタと代入演算子の宣言」に基づくものです。このチェッカーは、動的に割り当てられたデータメンバーを含むが、コピーコンストラクタが定義されていないクラスを検出します。

コピーコンストラクタが実装されていない場合、C++ コンパイラは必要に応じてコピーコンストラクタを自動生成します。しかし、そのコンパイラが提供する実装は常に形式的なものにすぎません。

データメンバーを管理するようにコピーが明示的にコード化されていない場合、初期化により、または値渡しの関数呼び出しの使用のいずれかによってオブジェクトをコピーすると動的に割り当てられる単一のデータメンバーを参照する 2 つのオブジェクトができます。ポインターが値によって単純にコピーされる形式的なコピー操作では、オブジェクトのペアが生成され、その両方が同じ基底のヒープメモリをポイントします。そのヒープメモリ上で実行するどの操作も、その操作への参照を維持する両方のオブジェクトに影響します。この結果、マルチスレッドアプリケーションでは同期問題を、すべてのタイプのプログラムで予期しない結果を引き起こす可能性があります。

この特定のチェッカーが参照する状況では、すでに解放されたメモリを解放する可能性があり、これは共通の基底の割り当てを共有している、そのような 2 つのオブジェクトが範囲外になる場合に発生します。

脆弱性とリスク

この状況では、通常は、範囲外になる最初のオブジェクトが、現在他のオブジェクトと共有されているバッファを含めて、関連するすべてのヒープメモリを解放します。2 番目のオブジェクトが範囲外になると、独自のメモリリソースと見なしたメモリを解放しようとする結果、すでに解放しているメモリにアクセスすることになり、最悪の場合、ヒープを破壊する可能性があります。

軽減と防止

この問題に対処するには、動的に割り当てられたデータメンバーを含むクラスに対しては常に代入演算子を明示的に実装し、そしてこの代入演算子がこれらのデータメンバーを実質的にコピーするようにします。

脆弱コード例

     
1    #include <iostream>
2    using namespace std;
3    class C{
4      char* data;
5      C& operator=(const C&) {return *this;}
6    public:
7      C() { data = new char[10]; }
8      ~C(){
9        cout << "Calling delete for " << (void *)data << endl;
10        delete[] data;
11      }
12    };
13    int main(){
14      C c1;
15      C c2 = c1;
16      return 1;
17    }


Output:

Calling delete for 0x602010
Calling delete for 0x602010

この例では、15 行目でコピーコンストラクタが呼び出されます。'C' でコピーコンストラクタが定義されていないため、コンパイラはすべての値を 1 つのインスタンスから他のインスタンスにコピーするコピーコンストラクタを生成します。その結果、'c1.data' と'c2.data' は両方とも同じ値になり、デストラクタが呼び出されるときに 2 回削除することになります。この場合、CL.FFM.COPY は、動的に割り当てられたデータメンバーを含むが、コピーコンストラクタが定義されていないクラスの典型的な例を検出しました。

修正コード例

この問題を修正するために、状況に応じて以下の 2 つの異なった実装のいずれかを使用できます。

一般には、コピーコンストラクタを実質的に定義する必要があります。

1    class C{
// ...
2      C(const C& src){
3        data = new char[10];
4        memcpy(data, src.data, 10);
5      }
// ...
6    };

インスタンスのコピーを想定しない場合は、コピーコンストラクタを private として宣言する必要があります。

1    class C{
// ...
2    private:
3      C(const C& src){ /* do not create copies */ }
// ...
4    };

拡張機能

このチェッカーは、Klocwork knowledge base (ナレッジベース) を利用して拡張できます。詳細については、C/C++ 解析のチューニングを参照してください。