CL.FFM.COPY

Freeing freed memory due to missing copy constructor

Class-level checkers produce recommendations based on Scott Meyer's rules for effective C++ class construction.

CL.FFM.COPY is based on Scott Meyer's Item 11: Declare a copy constructor and an assignment operator for classes with dynamically allocated memory. This checker looks for classes that contain dynamically allocated data members and don't define a copy constructor.

When there is no implementation of the copy constructor, the C++ compiler auto-generates a copy constructor wherever it is needed, but that compiler-provided implementation is always explicitly shallow.

Copying an object, either through initialization or using a pass-by-value function call, can result in two objects referencing a single dynamically allocated data member if the copy is not explicitly coded to manage those data members. A shallow copy operation in which pointers are simply copied by value results in a pair of objects both pointing to the same underlying heap memory. Any operation performed on that heap memory will affect both objects that are maintaining references to it. This can lead to synchronization problems in multi-threaded applications and unexpected results in all types of programs.

The situation this particular checker references is the potential for freeing already freed memory, which occurs when two such objects sharing a common underlying allocation go out of scope.

Vulnerability and risk

In this situation, the first object to go out of scope will typically release all associated heap memory, including the buffer that is now shared with another object. When that second object goes out of scope, its attempt to free what it believes to be its own memory resources will result in access to already released memory, something that could corrupt the heap in a worst-case scenario.

Mitigation and prevention

To address the problem, always provide an explicit implementation of the assignment operator for classes that contain dynamically allocated data members, and always ensure that the assignment operator performs a deep copy of those data members.

Vulnerable code example

     
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

In this example, the copy constructor is called at line 15. Since 'C' doesn't define a copy constructor, the compiler generates one that copies all values from one instance to another. As a result, both 'c1.data' and 'c2.data' have the same value, and delete it twice when the destructors are called. In this case, CL.FFM.COPY has found a typical example of a class that contains dynamically allocated data members and doesn't define a copy constructor.

Fixed code example

To fix this problem, one of two different implementations can be used, depending on the situation.

Typically, a deep copy constructor should be defined:

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

If the instances are not meant to be copied, the copy constructor should be declared as private:

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

Security training

Application security training materials provided by Secure Code Warrior.

Extension

This checker can be extended through the Klocwork knowledge base. See Tuning C/C++ analysis for more information.