Debugging and Fixing Mem Leaks – Part 3

Introduction

In earlier blog, Debugging and Fixing Mem Leaks – Part 1 – My sky (freewindcode.com), I explained about Direct and Indirect memory leak.

Various causes of memory leaks were discussed:

  • failure to create a separate event to handle deallocating memory
  • mem leak due to reassigning an argument pointer inside a function and failing to deallocate the original heap mem when the function exits.
  • failure to create virtual destructor.
  • forget to deregister an observer in Observer pattern design. (also called lapse listener problem)

In this blog, I discuss about another cause:

  • failure to deallocate member pointer in class destructor.

In other words, it is failure to deallocate resources in wrapper class. Depending on the scenario, we may have Direct or Indirect Leaks.

Direct Mem Leak by failing to deallocate resources in Wrapper class

Wrapper class is any class that hold resources, such as a pointer to some memory locations. Wrapper class may allocate the resources sometime during its life, such as in its constructor.

If the wrapper object is deallocated, we need to explicitly deallocate the memory location at the resource that it holds; otherwise, the resource is deleted, but the memory location is not free up. Direct leak will occur.

The following program illustrates such scenarios:

#include <iostream>
using namespace std;

class Resource {
    public:
    Resource() { cout<<"Resource constructor"<<endl; }
    ~Resource() { cout<<"Resource destructor"<<endl; }
};

class MyWrapperClass {\
    public:
    MyWrapperClass() { 
        cout<<"MyWrapperClass constructor"<<endl;
        res_ = new Resource(); 
    }
    ~MyWrapperClass(){ 
       cout<<"MyWrapperClass destructor" <<endl; 
       // fail to delete res_
    }
    private:
    Resource * res_;
};

int main(){
    cout<<"Size of Resouce: "<<sizeof(Resource)<<endl;
    MyWrapperClass * cl = new MyWrapperClass();
    delete cl;
    return 0;
}

Console log:

  • Resource pointer res_ was created and memory was allocated at line 15.
  • However, when MyWrapperClass is deleted, the destructor doesn't call to deallocate the memory location pointed by res_ pointer.
  • While the pointer res_ is removed, the memory location pointed by res is not deallocated. This causes direct leak. The size of the direct leak is the same as the size of Resource.
Size of Resouce: 1
MyWrapperClass constructor
Resource constructor
MyWrapperClass destructor
Program stderr

=================================================================
==1==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 1 byte(s) in 1 object(s) allocated from:
    #0 0x555a6fd4e1cd in operator new(unsigned long) /root/llvm-project/compiler-rt/lib/asan/asan_new_delete.cpp:95:3
    #1 0x555a6fd5059f in MyWrapperClass::MyWrapperClass() /app/example.cpp:15:16
    #2 0x555a6fd50506 in main /app/example.cpp:27:31
    #3 0x7f2e0af5f082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)

SUMMARY: AddressSanitizer: 1 byte(s) leaked in 1 allocation(s)

Indirect leak in wrapper class

The following illustrates a scenario where indirect leak occurs.

  • MyClass pointer, math, is created, and memory is allocated at where it points to.
  • The pointer is then reassigned, it is pointed to another location, which is allocated. (Yeah, I changed class a lot). So we have 2 memory locations now.
  • MyClass itself has a private pointer, resource_, type Resource. Thus, for each memory location that was created, there is another section of memory that corresponds to Resource and is accessible by the private pointer resource_;
  • When MyClass pointer, math, is deleted, only the later memory location is deleted.
  • The memory section that resource_ points to, in the earlier memory location section is not deleted, thus it is considered indirect leak.
#include <iostream>
using namespace std;

typedef struct Resource{
    int fee;  // 4 bytes
};

class MyClass {
    public:
    MyClass( string teacher, Resource * res ) : 
    teacher_(teacher),
    res_(res) { }
     ~MyClass() { 
        // delete res_; // cause indirect leak
    }  
    private:
    string teacher_;
    Resource * res_;
};

int main(){
    cout<<"size of My Class "<<sizeof(MyClass)<<endl;
    cout<<"size of Resource "<<sizeof(Resource)<<endl;
    Resource * res = new Resource;
    res->fee=100;
    MyClass * cl = new MyClass("Tom", res);  
    cl = new MyClass ("Joh", res);
    
    delete cl;
}

Error log:

  • the direct leak is the first memory location that was allocated in line 23.
  • the indirect leak is the encapsulating memory area within the first memory location that could be accessible by pointer res, which was created in line 21
  • Note that Resource has size 8 bytes, and the indirect leak size is the same.
  • The MyClass size is 40 bytes, and direct leak size is also 40 bytes
size of My Class 40
size of Resource 4
Program stderr

=================================================================
==1==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 40 byte(s) in 1 object(s) allocated from:
    #0 0x7f7b12086518 in operator new(unsigned long) (/opt/compiler-explorer/gcc-13.2.0/lib64/libasan.so.8+0xdb518) (BuildId: 5ce9c09d3612315d01d50bcaafaea176e7ddab77)
    #1 0x40246e in main /app/example.cpp:26
    #2 0x7f7b11a04082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)

Indirect leak of 4 byte(s) in 1 object(s) allocated from:
    #0 0x7f7b12086518 in operator new(unsigned long) (/opt/compiler-explorer/gcc-13.2.0/lib64/libasan.so.8+0xdb518) (BuildId: 5ce9c09d3612315d01d50bcaafaea176e7ddab77)
    #1 0x402416 in main /app/example.cpp:24
    #2 0x7f7b11a04082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)

SUMMARY: AddressSanitizer: 44 byte(s) leaked in 2 allocation(s).

Leave a Reply

Your email address will not be published. Required fields are marked *