Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

valgrind --trace-children=yes reports leak despite atexit cleanup

I'm trying to avoid false positives with valgrind, but I'm suck with a combination of atexit() and fork(), despite using --trace-children=yes. My code:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

static int * arr;

static void cleanup() {
    free(arr);
    printf("free arr as: %p\n", (void *)arr);
}

int main()
{
    arr = malloc(16 * sizeof(int));
    printf("allocated arr as: %p\n", (void *)arr);
    atexit(cleanup);

    pid_t pid = fork();
    if (pid == -1) {
        exit(1);
    } else if (pid == 0) {
        // child
        _exit(0);
    } else {
        // parent
        exit(0);
    }
}

Command-line:

$ clang -Weverything leak.c 
$ valgrind --trace-children=yes ./a.out 
==3287== Memcheck, a memory error detector
==3287== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3287== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3287== Command: ./a.out
==3287== 
allocated arr as: 0x5202040
free arr as: 0x5202040
==3288== 
==3288== HEAP SUMMARY:
==3288==     in use at exit: 64 bytes in 1 blocks
==3288==   total heap usage: 2 allocs, 1 frees, 1,088 bytes allocated
==3288== 
==3288== LEAK SUMMARY:
==3288==    definitely lost: 0 bytes in 0 blocks
==3288==    indirectly lost: 0 bytes in 0 blocks
==3288==      possibly lost: 0 bytes in 0 blocks
==3288==    still reachable: 64 bytes in 1 blocks
==3288==         suppressed: 0 bytes in 0 blocks
==3288== Rerun with --leak-check=full to see details of leaked memory
==3288== 
==3288== For counts of detected and suppressed errors, rerun with: -v
==3288== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==3287== 
==3287== HEAP SUMMARY:
==3287==     in use at exit: 0 bytes in 0 blocks
==3287==   total heap usage: 2 allocs, 2 frees, 1,088 bytes allocated
==3287== 
==3287== All heap blocks were freed -- no leaks are possible
==3287== 
==3287== For counts of detected and suppressed errors, rerun with: -v
==3287== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Based on the printf() output, it looks like there's no leaks. Can I convince valgrind of this, or should I just add this to my valgrind suppression file?

like image 557
gperciva Avatar asked Feb 09 '17 17:02

gperciva


2 Answers

Based on the printf() output, it looks like there's no leaks. Can I convince valgrind of this, or should I just add this to my valgrind suppression file?

It appears that valgrind is right. If you interpret the printf() output as indicating that there are no leaks, then you're not appreciating the effect of fork().

When you fork a child, it gets a complete copy of its parent's address space. This is typically implemented via copy-on-write pages, but it still constitutes memory belonging to the child. In your case, that includes a copy of dynamically-allocated array arr.

The child exits by calling _exit(), so although it inherits its parent's exit-handler registrations, registered exit handlers are not called in that process. You can tell that is so because you see the output of cleanup() only once. As a result, the copy of arr belonging to the child is never freed, as valgrind tells you.

It's a bit pedantic to call this a memory leak, though. The memory in question is still reachable when the program terminates, at which time it gets reclaimed by the system. It simply is not explicitly freed before termination.

like image 168
John Bollinger Avatar answered Oct 11 '22 11:10

John Bollinger


You are using _exit() in child process. As per _exit's man page :

The function _exit() is like exit(3), but does not call any functions registered with atexit(3) or on_exit(3). 

Change it to exit(0). It should work.

like image 31
ReddyVeeru Avatar answered Oct 11 '22 11:10

ReddyVeeru