Julian Zalewski
Julian Zalewski

Reputation: 402

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?

Upvotes: 30

Views: 1981

Answers (2)

Lord Alveric
Lord Alveric

Reputation: 397

Compiler Explorer, same compiler settings. -O3 optimizes all of this code away and just prints the number.

https://godbolt.org/z/r4hnvqd6s

Upvotes: -2

dbush
dbush

Reputation: 224852

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.

Upvotes: 31

Related Questions