cmalloc

Why does GCC’s static analyser falsely warn that a pointer to an allocated memory block itself stored in an allocated memory block may leak?


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

int main()
{
    int ***new = malloc(sizeof(int **));
    *new = malloc(sizeof(int *));
    **new = malloc(sizeof(int));

    ***new = 2137;
    printf("%i\n", ***new);

    free(**new);
    free(*new);
    free(new);

    return EXIT_FAILURE;
}

This code, when compiled using command gcc -Wall -Wextra -fanalyzer -g -O0 -fsanitize=address,undefined -o test2 test2.c produces output:

test2.c: In function ‘main’:
test2.c:10:7: warning: leak of ‘malloc(4)’ [CWE-401] [-Wanalyzer-malloc-leak]
   10 |     ***new = 2137;
      |       ^~~~
  ‘main’: events 1-2
    |
    |    8 |     **new = malloc(sizeof(int));
    |      |             ^~~~~~~~~~~~~~~~~~~
    |      |             |
    |      |             (1) allocated here
    |    9 | 
    |   10 |     ***new = 2137;
    |      |       ~~~~   
    |      |       |
    |      |       (2) ‘malloc(4)’ leaks here; was allocated at (1)
    |

I have narrowed down my code do something as simple as this, but still cannot find the problem. I know I am not checking malloc errors, doing so does not help, I have removed them to improve clarity. How do I fix this?


Solution

  • This is a bug in the analyzer. If we look closely at the output:

        |
        |    8 |     **new = (int*) malloc(sizeof(int));
        |      |                    ^~~~~~~~~~~~~~~~~~~
        |      |                    |
        |      |                    (1) allocated here
        |    9 | 
        |   10 |     ***new = 2137;
        |      |       ~~~~          
        |      |       |
        |      |       (2) ‘malloc(4)’ leaks here; was allocated at (1)
    

    We can see that the assigned pointer it's checking is not the same one where the leak happens. Specifically, it incorrectly thinks that an assignment to ***new overwrites as assignment to **new.

    To verify, we can run the code through valgrind, which shows there is no memory leak:

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

    When compiling with versions 10 and 11 of gcc with these options, no warnings appear. The warning you show start with version 12 of gcc.