CL.FFM.ASSIGNFreeing freed memory due to missing operatorClass-level checkers produce recommendations based on Scott Meyer's rules for effective C++ class construction. CL.FFM.ASSIGN 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 do not define an assignment operator. When there is no implementation of the assignment operator, the C++ compiler auto-generates an assignment operator wherever it is needed, but that compiler-provided implementation is always explicitly shallow. Copying an object 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, which can lead to unexpected results in all types of program. 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 riskIn 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 preventionTo 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 example1 #include <iostream> 2 using namespace std; 3 class C{ 4 char *data; 5 C(const C&){} 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; 16 c1 = c2; 17 return 1; 18 } Output: Calling delete for 0x602030 Calling delete for 0x602030 In this example class 'C' uses dynamic memory allocation for 'C::data', but doesn't define operator=. As a result, when line 16 of function 'main()' is executed, both 'c1.data' and 'c2.data' have the same value and each calls operator 'delete[ ]' when destroyed, the same pointer being deleted twice. In this case, CL.FFM.ASSIGN has found a typical example of freed memory being freed again due to the operator= implementation. Fixed code exampleIn order to fix this problem, operator= should be defined. Depending on the situation, different implementations of operator= can be used.
1 class C{ // ... 2 C& operator=(const C& src){ 3 if (&src == this) return *this; 4 delete[] data; 5 data = new char[10]; 6 memcpy(data, src.data, 10); 7 return *this; 8 } // ... 9 };
1 class C{ // ... 2 C& operator=(const C& src){ 3 if (&src == this) return *this; 4 memcpy(data, src.data, 10); 5 return *this; 6 } // ... 8 };
1 class C{ // ... 2 private: 3 C& operator=(const C&){ return *this;} // ... 4 }; Related checkersExternal guidanceExtensionThis checker can be extended through the Klocwork knowledge base. See Tuning C/C++ analysis for more information. |