简体   繁体   中英

sbrk - Valgrind doesn't report the memory leak

I wrote this little version of malloc (no free ):

#include <cstdio>
#include <cstddef>
#include <unistd.h>

#define word_size sizeof(intptr_t)
#define align(n) ((n + word_size - 1) & ~(word_size - 1))

void* my_malloc(size_t size) {
    void* p = sbrk(0);
    printf("before allocation: %p\n", p);
    if (sbrk(align(size)) == (void*) -1) {
        // failed to allocate memory
        return NULL;
    }
    printf("after allocation: %p\n", sbrk(0));
    return p;
}

int main() {
    int* foo = (int*) my_malloc(1);
    *foo = 100;
    printf("after allocation outside: %p\n", sbrk(0));
}

Here is the output of the code:

before allocation: 0x1796000
after allocation: 0x1796008
after allocation outside: 0x1796008

As you can see, the memory allocated by my_malloc has not been freed. Yet when I run it through valgrind with this:

valgrind --leak-check=yes ./main

I get this:

==1592== Memcheck, a memory error detector
==1592== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1592== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==1592== Command: ./main
==1592== HEAP SUMMARY:
==1592==     in use at exit: 0 bytes in 0 blocks
==1592==   total heap usage: 2 allocs, 2 frees, 73,728 bytes allocated
==1592== 
==1592== All heap blocks were freed -- no leaks are possible
==1592== 
==1592== For counts of detected and suppressed errors, rerun with: -v
==1592== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

As you can see, valgrind didn't find any memory leaks! Am I using valgrind incorrectly, or is this a bug?

The first answer is not very accurate.

Short answer for the OP: Valgrind won't detect a leak in custom memory pools unless you tell it more about your allocator.

Long answer.

When you run valgrind there are two executables that will run. Firstly valgrind itself. This is a small application that will read a few command line arguments, set a few environment variables then look at the guest application to determine its bitness and then execve the appropriate tool, for instance memcheck-x86-freebsd .

The tool does not link with anything . Not libc, not even the libc startup code. The entry point is in assembler that creates a new stack and starts valgrind_main . Since the tool links with nothing, it has to implement for itself all of the C runtime functions that it needs or wants to use. This includes some functions that compilers may implicitly generate. For instance, code that does struct assignment may result in the compiler generating a call to memcpy() . This is what the quoted text is referring to. These functions are also provided by the tool rather than libc. This is not related to the redirection mechanism.

So, redirection. valgrind will have set LD_PRELOAD (or a platform specific version such as LD_32_PRELOAD ). This will point to a 'core' component (such as vgpreload_core-x86-freebsd.so and a tool component such as vgpreload_memcheck-x86-freebsd.so . The tool does the job of the link loader, and will mmap these files into memory. During this process it will read the Elf information and note any "special" functions. These functions use an elaborate name mangling system, and the tool will recognize that, for instance, _vgr10010ZU_libcZdsoZa_malloc is a replacement for malloc in libc . The addresses of these redirection functions are stored and will be used when the guest executes. This mechanism also means that different tools can redirect different functions. For instance, memcheck needs to redirect the malloc and new families of functions whilst DRD and Helgrind need to redirect pthread_* and sema_* .

For the other end of the redirection, the tool will also see the target function(s) when it loads libc (and the executable binary itself, which covers the case of static linking).

The redirection does not fully replace the target function, it just acts as a shim allowing the tool to track what the function has requested.

Now to get right back to sbrk . Valgrind does know about this, but only as a syscall. It will check that the size argument does not come from invalid storage. memcheck won't track the memory as it does with malloc . If you use massif with --pages-as-heap=yes then it will profile the sbrk usage.

If you want memcheck to validate your custom allocation functions then you have to do one of two things.

UPDATE: The default [since Release 3.12.0 (20 October 2016)] has changed to look for replacement functions in the test exe, text updated.

Valgrind checks for memory leaks by linking in its own functions in place of malloc , calloc , realloc , and free . You can see this when valgrind reports a memory leak:

==24877== 8 bytes in 1 blocks are definitely lost in loss record 1 of 1
==24877==    at 0x4C29EA3: malloc (vg_replace_malloc.c:309)
==24877==    by 0x4EC3AA9: strdup (in /usr/lib64/libc-2.17.so)
==24877==    by 0x40058E: main (x1.c:9)

You can see here that the libc version of strdup is called but the valgrind replacement for malloc is called instead of the libc malloc .

You're allocating memory at a lower level using sbrk . Valgrind doesn't intercept that function, so it won't detect a leak. In fact, according to the valgrind documentation , it lists sbrk as a dependency. From section 1.1.4:

To find out which glibc symbols are used by Valgrind, reinstate the link flags -nostdlib -Wl,-no-undefined . This causes linking to fail, but will tell you what you depend on. I have mostly, but not entirely, got rid of the glibc dependencies; what remains is, IMO, fairly harmless. AFAIK the current dependencies are: memset , memcmp , stat , system , sbrk , setjmp and longjmp .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM