Search code examples
cmemory-leaksmallocforkvalgrind

Memory leaks in child process after fork() is called in parent, why?


Mac OS X 10.13.4. Running this program triggers memory leak signals in Valgrind and leaks system call:

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

char    *ft_strcpy(char *s1, const char *s2)
{
    size_t  cur;

    cur = -1;
    while (s2[++cur] != '\0')
        s1[cur] = s2[cur];
    s1[cur] = '\0';
    return (s1);
}

void    ptr_test()
{
    char    *ptr;

    ptr = (char *)malloc(sizeof(char) * 15);
    printf("FIRST: %p\n", ptr);
    ft_strcpy(ptr, "Hello, World!");
    printf("SECOND: %p\n\n", ptr);
    free(ptr);
}

int     main(void)
{
    pid_t   pid;

    pid = fork();
    while(1)
        ptr_test();
    if (pid != 0)
        wait(NULL);
    return (0);
}

leaks sys call:

Process:         main [98746]
Path:            /nfs/2018/p/patrisor/Desktop/ptr/main
Load Address:    0x1095a2000
Identifier:      main
Version:         ???
Code Type:       X86-64
Parent Process:  main [98687]

Date/Time:       2019-09-22 05:28:32.273 -0700
Launch Time:     2019-09-22 05:28:23.881 -0700
OS Version:      Mac OS X 10.13.4 (17E199)
Report Version:  7
Analysis Tool:   /usr/bin/leaks

Physical footprint:         280K
Physical footprint (peak):  280K
----

leaks Report Version: 3.0
Process 98746: 176 nodes malloced for 27 KB
Process 98746: 4 leaks for 4128 total leaked bytes.

Leak: 0x7f9d9ad00040  size=16  zone: DefaultMallocZone_0x1095a7000
Leak: 0x7f9d9ad00050  size=16  zone: DefaultMallocZone_0x1095a7000
Leak: 0x7f9d9b800800  size=2048  zone: DefaultMallocZone_0x1095a7000
Leak: 0x7f9d9b801000  size=2048  zone: DefaultMallocZone_0x1095a7000

Valgrind:

==1388== LEAK SUMMARY:
==1388==    definitely lost: 32 bytes in 2 blocks
==1388==    indirectly lost: 4,096 bytes in 2 blocks
==1388==      possibly lost: 72 bytes in 3 blocks
==1388==    still reachable: 215 bytes in 7 blocks
==1388==         suppressed: 27,726 bytes in 170 blocks
==1388==
==1388== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 13 from 13)

Originally I suspected it was an issue with free() not working with the correct malloced addresses. However, when I manually scanned the memory usage of the process, no significant increase in it's usage occurred during runtime. Later I suspected it was the child process not ending correctly, and maybe creating zombie processes. The leak happens somewhere in the beginning, where free is called. Is there a problem with the function itself, or...? I seem to be very lost.

Any guesses?


Solution

  • the following contains comments on problems with the code:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    
    /* 
     * strongly suggest:
     * #include <string.h> and
     * eliminating this function and in ptr_test()
     * strcpy( ptr, "Hello, World! );
     */
    char    *ft_strcpy(char *s1, const char *s2)
    {
        size_t  cur;
    
        /* this causes an implicit conversion to unsigned
         * so the value will be seen as a VERY LARGE number
         * suggest initialize to 0 then use: 
         * while( s2[cur++] != '\0' )
         */
        cur = -1;         
        while (s2[++cur] != '\0')
            s1[cur] = s2[cur];
        s1[cur] = '\0';
        return (s1);
    }
    
    void    ptr_test()
    {
        char    *ptr;
    
        /*
         * returned value from malloc has type `void*` 
         * which can be assigned to any pointer
         * casting just clutters the code, 
         * making it more difficult to understand, debug, etc
         * suggest removing that cast.
         */
        /* sizeof( char ) is defined in the c standard as 1
         * multiplying anything by 1 has no effect
         * suggest removing that expresson
         */
        /*
         * when calling any of the heap allocation functions
         * always check (!=NULL) the returned value
         * to assure the operation was successful
         * if not successful, call 'perror( "malloc failed" );
         * to output your error message and the text reason
         * the system thinks the error occurred to 'stderr'
         */
        ptr = (char *)malloc(sizeof(char) * 15); 
    
        printf("FIRST: %p\n", ptr);
    
        ft_strcpy(ptr, "Hello, World!");
        printf("SECOND: %p\n\n", ptr);
        free(ptr);
    }
    
    int     main(void)
    {
        pid_t   pid;
    
        pid = fork();
    
        /*
         * this runs 'ptr_test()' FOREVER
         * and in the fork() error condition FOREVER
         * and in the child process  FOREVER
         * and in the parent process FOREVER
         * 
         * note: 'exit()' and EXIT_SUCCESS are from
         *     the header file: `stdlib.h`
         * Suggest:
         * if( !pid )
         * { // then child process
         *     for( int i=0; i<1024; i++ )
         *         ptr_test();
         *     exit( EXIT_SUCCESS );
         */
        while(1)
            ptr_test();
    
        /* there are three types of returned values from 'fork()'
         * <0  means an error occurred
         * ==0 means in the child process
         * >0  means in the parent process
         * 
         * a test for '!=0' will 'catch' the error AND the parent
         * however, there is no child process for the parent to 'wait()' 
         * strongly suggest testing for each condition separately
         * I.E.
         * if( pid < 0 )
         *     //handle error
         *     perror( "fork failed" );
         * else if( !pid )
         *     // handle child process ...
         *     exit( EXIT_ SUCCESS );
         * else
         *     // handle parent process
         *     wait( NULL );
         */
        if (pid != 0)
    
            /* the prototype for 'wait()' is missing
             * strongly suggest adding the header files:
             * #include <sys/types.h>
             * #include <sys/wait.h>
             */
            wait(NULL);
        return (0);
    }