malloc hook for memory leak detection

Record the hook form using malloc, write a small demo, and record the problems encountered

1. Implementation code:

   CMakeLists.txt and corresponding memory_leak.cpp file

cmake_minimum_required(VERSION 3.14)
project(demo)

set(_SRC
    memory_leak.cpp)

add_library(memory_leak SHARED ${_SRC})
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <execinfo.h>

//实际内存申请的函数
extern void *__libc_malloc(size_t size);
int enable_malloc_hook = 1;
 
extern void __libc_free(void* p);
int enable_free_hook = 1;

void *malloc(size_t size) 
{
	if (enable_malloc_hook) 
    {
		enable_malloc_hook = 0;
        
        void *p = __libc_malloc(size); //重载达到劫持后 实际内存申请
		// void *caller = __builtin_return_address(0); // 0
		
		// char buff[128] = {0};
		// sprintf(buff, "./mem/%p.mem", p);

        // FILE *fp = fopen(buff, "w");
		// // fprintf(fp, "[+%p] --> addr:%p, size:%ld\n", caller, p, size);

        // void* trace[16];
        // char** messages = (char**)NULL;
        // int i, trace_size = 0;
        // trace_size  = backtrace(trace, 16);
        // messages    = backtrace_symbols(trace, trace_size);
        // for (i=0; i<trace_size; ++i) 
        // {
        //     fprintf(fp, "[bt][%s]\n", messages[i]);
        // }

		// fflush(fp);
        
		fclose(fp); //free
		printf("=====Malloc: %p\n", p);

		enable_malloc_hook = 1;
		
        return p;
	} 
    else 
    {
		return __libc_malloc(size);
	}

	return NULL;
}

void free(void *p) 
{
	if (NULL == p)   // 这里加这个判断是因为backtrace函数的调用不知道为什么会调用free并传递的是0x0指针
    {
        return;
    }

    if (enable_free_hook) 
    {
        printf("=====free: %p\n", p);

		enable_free_hook = 0;
 
		char buff[128] = {0};
		sprintf(buff, "./mem/%p.mem", p);
 
		if (unlink(buff) < 0) 
        { // no exist
			printf("double free: %p\n", p);
		}
        __libc_free(p);
        
		// rm -rf p.mem
		enable_free_hook = 1;
	} 
    else 
    {
		__libc_free(p);
	}
}

Test function:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <thread>
#include <chrono>
int main()
{

    // while (true)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        void * ptr1 = malloc(10);
    }

    return 0;
}

How to use:

        export LD_PRELOAD=./libmemory_leak.so

        ./demo

2. Having a problem

  •  Why memory_leak uses cpp follow-up, directly executes the demo and crashes directly

        Segmentation fault (core dumped)

        But there is no problem compiling with the .c suffix

        reason:

                It should be related to the signature of CPP, compile directly with memory_leak.cpp, and use

        extern "C"

        {

                //all code

        }

        Use OK

  • The above code can be run directly, but why gdb crashes, it feels like a recursive call, but the recursive limit has been added to the logic, and the information of the secondary release will be repeatedly output

        Directly nest the source code of memory_leak.cpp in main.cpp, then you can use gdb, why?

        In actual use, it should be carried out by using an independent dynamic library and then LD_PRELOAD

        

  • If you want to directly gdb ./demo | tee log.txt, check whether it is really released twice through log.txt

 It can be seen that malloc was not called before the first free, why is free called without calling malloc?

 Guess: In addition to the system free, there are other resource free functions that are covered, but this resource is not applied for through malloc?

3. Good solution

Here are the detection schemes that have been implemented:

    GitHub - efficios/memleak-finder: Simple memory leak finder.

You can make custom modifications on this basis to meet your own engineering needs.

However, in the process of using this code, I encountered some problems

3.1. Program stuck

        If the library is directly applied to the project code, the program will be stuck. Check the stack and find:

The first stack scene:       

 It feels like this thread has been waiting here, is the lock occupied all the time?

Second pass stack scene:

Block the interface directly:

add_mh(void *ptr, size_t alloc_size, const void *caller)

del_mh(void *ptr, const void *caller)

The program can run normally ()

        Looking at the complete stack, you will find that several threads are competing to lock mh_mutex, but there is only one lock here. How does it feel like a deadlock?

        Why shield the above two interfaces, it will be fine?

        

3.2. False positives output a lot of leaks

After the program is started, a lot of leaks are output, but I don’t feel like a leak at the moment, and I need to confirm it later

Guess you like

Origin blog.csdn.net/bocai1215/article/details/130160501