Search code examples
cmemory-leaksforkvalgrind

Fork after malloc causing memory leak


I'm working on a system programming project that requires forking N process. The issue is I'm keeping the PID's in a dynamicly allocated array and valgrind with "--leak-check=full --show-leak-kinds=all" flags shows this PID array pointer as possible leak.

pid_t *pid;

//...globals & prototypes

int main(int argc, char *argv[])
{

    //... Input parsing

    // Create actor process=========================================
    pid = malloc((_N + _V + _C + 1) * sizeof(pid_t *)); // LINE VALGRIND IS POINTING STILL REACHABLE
    for (int i = 0; i < _N + _V + _C + 1; i++)
    {
        pid[i] = fork();
        if (pid[i] == 0)
            break;
    }
    // ======================================== Create actor process

    // Parent process ====================================================
    if (parent_pid == getpid())
    {

        // Wait for all the childeren=====================================
        for (int i = 0; i < _N + _V + _C + 1 || exit_requested != 0; i++)
        {
            int status;
            if (waitpid(pid[i], &status, 0) == -1)
            {
                errExit("waitpid");
            }
            
        }
        // =====================================Wait for all the childeren

        // Free resources
        free(pid);

        //.. destroy semaphores

        shm_unlink(SHARED_LINK);
    }

    // Child processes ===================================================
    else
    {
        for (int i = 0; i < _N + _V + _C + 1; i++)
        {
            if (i >= 0 && i < _N && pid[i] == 0)
            {
                producer(_I, shared_data, i);
            }
            else if (i >= _N && i < _N + _V && pid[i] == 0)
            {
                mid_consumer(shared_data, i - _N);
            }
            else if (i >= _N + _V && i < _N + _V + _C && pid[i] == 0)
            {
                end_consumer(shared_data, i - _N - _V);
            }
        }
    }
    // ===================================================================
    return 0;
}

Here is the valgrind output

==8056== Memcheck, a memory error detector
==8056== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8056== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==8056== Command: ./program -n 2 -v 2 -c 4 -b 81 -t 2 -i cold_storage.txt
==8056== Parent PID: 8055
==8056== 
==8061== 
==8061== HEAP SUMMARY:
==8061==     in use at exit: 72 bytes in 1 blocks
==8061==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8061== 
==8061== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8061==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8061==    by 0x10925D: main (main.c:169)
==8061== 
==8061== LEAK SUMMARY:
==8061==    definitely lost: 0 bytes in 0 blocks
==8061==    indirectly lost: 0 bytes in 0 blocks
==8061==      possibly lost: 0 bytes in 0 blocks
==8061==    still reachable: 72 bytes in 1 blocks
==8061==         suppressed: 0 bytes in 0 blocks
==8061== 
==8061== For counts of detected and suppressed errors, rerun with: -v
==8061== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8059== 
==8059== HEAP SUMMARY:
==8059==     in use at exit: 72 bytes in 1 blocks
==8059==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8059== 
==8057== 
==8062== 
==8057== HEAP SUMMARY:
==8057==     in use at exit: 72 bytes in 1 blocks
==8057==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8057== 
==8062== HEAP SUMMARY:
==8062==     in use at exit: 72 bytes in 1 blocks
==8062==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8062== 
==8059== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8059==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8059==    by 0x10925D: main (main.c:169)
==8059== 
==8059== LEAK SUMMARY:
==8059==    definitely lost: 0 bytes in 0 blocks
==8059==    indirectly lost: 0 bytes in 0 blocks
==8059==      possibly lost: 0 bytes in 0 blocks
==8059==    still reachable: 72 bytes in 1 blocks
==8059==         suppressed: 0 bytes in 0 blocks
==8059== 
==8059== For counts of detected and suppressed errors, rerun with: -v
==8059== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8062== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8057== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8062==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8057==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8062==    by 0x10925D: main (main.c:169)
==8062== 
==8057==    by 0x10925D: main (main.c:169)
==8062== LEAK SUMMARY:
==8057== 
==8062==    definitely lost: 0 bytes in 0 blocks
==8057== LEAK SUMMARY:
==8062==    indirectly lost: 0 bytes in 0 blocks
==8057==    definitely lost: 0 bytes in 0 blocks
==8062==      possibly lost: 0 bytes in 0 blocks
==8057==    indirectly lost: 0 bytes in 0 blocks
==8062==    still reachable: 72 bytes in 1 blocks
==8057==      possibly lost: 0 bytes in 0 blocks
==8062==         suppressed: 0 bytes in 0 blocks
==8057==    still reachable: 72 bytes in 1 blocks
==8062== 
==8057==         suppressed: 0 bytes in 0 blocks
==8062== For counts of detected and suppressed errors, rerun with: -v
==8057== 
==8062== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8057== For counts of detected and suppressed errors, rerun with: -v
==8057== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8063== 
==8063== HEAP SUMMARY:
==8063==     in use at exit: 72 bytes in 1 blocks
==8063==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8063== 
==8063== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8063==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8063==    by 0x10925D: main (main.c:169)
==8063== 
==8063== LEAK SUMMARY:
==8063==    definitely lost: 0 bytes in 0 blocks
==8063==    indirectly lost: 0 bytes in 0 blocks
==8063==      possibly lost: 0 bytes in 0 blocks
==8063==    still reachable: 72 bytes in 1 blocks
==8063==         suppressed: 0 bytes in 0 blocks
==8063== 
==8063== For counts of detected and suppressed errors, rerun with: -v
==8063== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8058== 
==8058== HEAP SUMMARY:
==8058==     in use at exit: 72 bytes in 1 blocks
==8058==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8058== 
==8058== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8058==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8058==    by 0x10925D: main (main.c:169)
==8058== 
==8058== LEAK SUMMARY:
==8058==    definitely lost: 0 bytes in 0 blocks
==8058==    indirectly lost: 0 bytes in 0 blocks
==8058==      possibly lost: 0 bytes in 0 blocks
==8058==    still reachable: 72 bytes in 1 blocks
==8058==         suppressed: 0 bytes in 0 blocks
==8058== 
==8058== For counts of detected and suppressed errors, rerun with: -v
==8058== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8064== 
==8064== HEAP SUMMARY:
==8064==     in use at exit: 72 bytes in 1 blocks
==8064==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8064== 
==8064== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8064==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8064==    by 0x10925D: main (main.c:169)
==8064== 
==8064== LEAK SUMMARY:
==8064==    definitely lost: 0 bytes in 0 blocks
==8064==    indirectly lost: 0 bytes in 0 blocks
==8064==      possibly lost: 0 bytes in 0 blocks
==8064==    still reachable: 72 bytes in 1 blocks
==8064==         suppressed: 0 bytes in 0 blocks
==8064== 
==8064== For counts of detected and suppressed errors, rerun with: -v
==8064== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8060== 
==8060== HEAP SUMMARY:
==8060==     in use at exit: 72 bytes in 1 blocks
==8060==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8060== 
==8060== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8060==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8060==    by 0x10925D: main (main.c:169)
==8060== 
==8060== LEAK SUMMARY:
==8060==    definitely lost: 0 bytes in 0 blocks
==8060==    indirectly lost: 0 bytes in 0 blocks
==8060==      possibly lost: 0 bytes in 0 blocks
==8060==    still reachable: 72 bytes in 1 blocks
==8060==         suppressed: 0 bytes in 0 blocks
==8060== 
==8060== For counts of detected and suppressed errors, rerun with: -v
==8060== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8065== 
==8065== HEAP SUMMARY:
==8065==     in use at exit: 72 bytes in 1 blocks
==8065==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8065== 
==8065== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8065==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8065==    by 0x10925D: main (main.c:169)
==8065== 
==8065== LEAK SUMMARY:
==8065==    definitely lost: 0 bytes in 0 blocks
==8065==    indirectly lost: 0 bytes in 0 blocks
==8065==      possibly lost: 0 bytes in 0 blocks
==8065==    still reachable: 72 bytes in 1 blocks
==8065==         suppressed: 0 bytes in 0 blocks
==8065== 
==8065== For counts of detected and suppressed errors, rerun with: -v
==8065== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8056== 
==8056== HEAP SUMMARY:
==8056==     in use at exit: 0 bytes in 0 blocks
==8056==   total heap usage: 1 allocs, 1 frees, 72 bytes allocated
==8056== 
==8056== All heap blocks were freed -- no leaks are possible
==8056== 
==8056== For counts of detected and suppressed errors, rerun with: -v
==8056== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

I tried freeing the PID array in the forked processes too but it still gives the same error.


Solution

  • This is not a complete answer, but it addresses some concerns.

    First, the pid array memory block is being allocated with the wrong size based on a multiple of sizeof(pid_t *) when it should be the same multiple of sizeof(pid_t). That is a harmless waste of memory if sizeof(pid_t *) is greater than sizeof(pid_t), but would be a buffer overrun in the unlike case that sizeof(pid_t *) is less than sizeof(pid_t).

    Second, the child processes do not free the pid array memory block.

    Third, the child processes loop through all the elements of the pid array memory block looking for pid[i] == 0. I believe this is the child process trying to find its own index. However, the pid array memory block has not been fully initialized in the child process fork, so there could be more than one element with the value 0. Besides, there is no need for the child process to use the pid array at all if all it needs is its index number which it can determine at the time of the fork().

    The version below fixes those issues, but I don't know if it fixes the Valgrind errors.

    pid_t *pid;
    
    //...globals & prototypes
    
    int main(int argc, char *argv[])
    {
        int cid;  // child index
    
        //... Input parsing
    
        // Create actor process=========================================
        pid = malloc((_N + _V + _C + 1) * sizeof(pid_t));
        for (cid = 0; xid < _N + _V + _C + 1; cid++)
        {
            pid[cid] = fork();
            if (pid[cid] == 0)
            {
                free(pid);
                break;
            }
        }
        // ======================================== Create actor process
    
        // Parent process ====================================================
        if (parent_pid == getpid())
        {
    
            // Wait for all the childeren=====================================
            for (int i = 0; i < _N + _V + _C + 1 || exit_requested != 0; i++)
            {
                int status;
                if (waitpid(pid[i], &status, 0) == -1)
                {
                    errExit("waitpid");
                }
                
            }
            // =====================================Wait for all the childeren
    
            // Free resources
            free(pid);
    
            //.. destroy semaphores
    
            shm_unlink(SHARED_LINK);
        }
    
        // Child processes ===================================================
        else
        {
            if (cid >= 0 && cid < _N)
            {
                producer(_I, shared_data, cid);
            }
            else if (cid >= _N && cid < _N + _V)
            {
                mid_consumer(shared_data, cid - _N);
            }
            else if (cid >= _N + _V && cid < _N + _V + _C)
            {
                end_consumer(shared_data, cid - _N - _V);
            }
        }
        // ===================================================================
        return 0;
    }