https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103526
Bug ID: 103526 Summary: -fanalyzer considers memcpy()ed and returned pointer to malloc()ed memory a memory leak Product: gcc Version: 11.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: analyzer Assignee: dmalcolm at gcc dot gnu.org Reporter: guilherme.janczak at yandex dot com Target Milestone: --- Created attachment 51915 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=51915&action=edit The preprocessed file output by '$ egcc -v -save-temps -fanalyzer example.c' I found this while trying -fanalyzer on my hangman game. The attached preprocessed file comes from the "example.c" file as requested by the bug writing guidelines. It seems that my allocator function game_new() triggers a bug. It creates a struct game_state in the stack with the identifier 'tmp', as well as a pointer to the same kind of struct with the identifier 'rval'. It assigns the return value of malloc() to a member of 'tmp', then it allocates storage for 'rval', and then it memcpy()es 'tmp' into the storage 'rval' points to and returns 'rval'. GCC's static analyzer seems to think the pointer inside 'tmp' leaks at the end of the function because it's not smart enough to realize it has been returned through 'rval'. Here's the file "example.c" which triggers this bug: #include <stdlib.h> #include <string.h> struct game_state { const char *word; /* This is a pointer to static storage. */ char *word_state; }; const char *const teststr = "test string"; static struct game_state *game_new(void); static void game_free(struct game_state *); int main(void) { struct game_state *game; if ((game = game_new()) == NULL) exit(1); game_free(game); exit(0); } static struct game_state * game_new(void) { struct game_state tmp = {0}; struct game_state *rval = NULL; size_t wordlen; tmp.word = teststr; wordlen = strlen(tmp.word); if ((tmp.word_state = malloc(wordlen+1)) == NULL) goto err; if ((rval = malloc(sizeof(*rval))) == NULL) goto err; memcpy(rval, &tmp, sizeof(*rval)); return (rval); err: free(tmp.word_state); free(rval); return (NULL); } static void game_free(struct game_state *game) { if (game == NULL) return; free(game->word_state); free(game); } $ egcc -v -save-temps -fanalyzer example.c Using built-in specs. COLLECT_GCC=egcc COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-unknown-openbsd7.0/11.2.0/lto-wrapper Target: x86_64-unknown-openbsd7.0 Configured with: /usr/obj/ports/gcc-11.2.0/gcc-11.2.0/configure --with-stage1-ldflags=-L/usr/obj/ports/gcc-11.2.0/bootstrap/lib --verbose --program-transform-name='s,^,e,' --disable-nls --with-system-zlib --disable-libmudflap --disable-libgomp --disable-libssp --disable-tls --with-gnu-ld --with-gnu-as --enable-threads=posix --enable-wchar_t --with-gmp=/usr/local --enable-languages=c,c++,fortran,objc,ada,d --disable-libstdcxx-pch --enable-default-ssp --enable-default-pie --without-isl --enable-cpp --prefix=/usr/local --sysconfdir=/etc --mandir=/usr/local/man --infodir=/usr/local/info --localstatedir=/var --disable-silent-rules --disable-gtk-doc Thread model: posix Supported LTO compression algorithms: zlib gcc version 11.2.0 (GCC) COLLECT_GCC_OPTIONS='-v' '-save-temps' '-fanalyzer' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a-' /usr/local/libexec/gcc/x86_64-unknown-openbsd7.0/11.2.0/cc1 -E -quiet -v example.c -mtune=generic -march=x86-64 -fanalyzer -fpch-preprocess -o a-example.i ignoring nonexistent directory "/usr/local/lib/gcc/x86_64-unknown-openbsd7.0/11.2.0/../../../../x86_64-unknown-openbsd7.0/include" #include "..." search starts here: #include <...> search starts here: /usr/local/lib/gcc/x86_64-unknown-openbsd7.0/11.2.0/include /usr/local/lib/gcc/x86_64-unknown-openbsd7.0/11.2.0/include-fixed /usr/include End of search list. COLLECT_GCC_OPTIONS='-v' '-save-temps' '-fanalyzer' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a-' /usr/local/libexec/gcc/x86_64-unknown-openbsd7.0/11.2.0/cc1 -fpreprocessed a-example.i -quiet -dumpdir a- -dumpbase example.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -fanalyzer -o a-example.s GNU C17 (GCC) version 11.2.0 (x86_64-unknown-openbsd7.0) compiled by GNU C version 11.2.0, GMP version 6.2.1, MPFR version 4.1.0, MPC version 1.1.0, isl version none GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 GNU C17 (GCC) version 11.2.0 (x86_64-unknown-openbsd7.0) compiled by GNU C version 11.2.0, GMP version 6.2.1, MPFR version 4.1.0, MPC version 1.1.0, isl version none GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: b55fd2c5b9d1ebf92b67661bceac5878 example.c: In function 'game_new': example.c:44:1: warning: leak of 'tmp.word_state' [CWE-401] [-Wanalyzer-malloc-leak] 44 | } | ^ 'game_new': events 1-3 | |example.c:33:31: | 33 | if ((tmp.word_state = malloc(wordlen+1)) == NULL) | | ~ ^~~~~~~~~~~~~~~~~ | | | | | | | (1) allocated here | | (2) assuming 'tmp.word_state' is non-NULL | | (3) following 'false' branch... | 'game_new': events 4-5 | |example.c:35:21: | 35 | if ((rval = malloc(sizeof(*rval))) == NULL) | | ~ ^~~~~~~~~~~~~~~~~~~~~ | | | | | | | (4) ...to here | | (5) following 'false' branch (when 'rval' is non-NULL)... | 'game_new': event 6 | |example.c:37:9: | 37 | memcpy(rval, &tmp, sizeof(*rval)); | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (6) ...to here | 'game_new': event 7 | |example.c:44:1: | 44 | } | | ^ | | | | | (7) 'tmp.word_state' leaks here; was allocated at (1) | COLLECT_GCC_OPTIONS='-v' '-save-temps' '-fanalyzer' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a-' as -v -o a-example.o a-example.s GNU assembler version 2.17 (amd64-unknown-openbsd7.0) using BFD version 2.17 COMPILER_PATH=/usr/local/libexec/gcc/x86_64-unknown-openbsd7.0/11.2.0/:/usr/local/libexec/gcc/x86_64-unknown-openbsd7.0/11.2.0/:/usr/local/libexec/gcc/x86_64-unknown-openbsd7.0/:/usr/local/lib/gcc/x86_64-unknown-openbsd7.0/11.2.0/:/usr/local/lib/gcc/x86_64-unknown-openbsd7.0/ LIBRARY_PATH=/usr/local/lib/gcc/x86_64-unknown-openbsd7.0/11.2.0/:/usr/local/lib/gcc/x86_64-unknown-openbsd7.0/11.2.0/../../../:/usr/lib/ COLLECT_GCC_OPTIONS='-v' '-save-temps' '-fanalyzer' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a.' /usr/local/libexec/gcc/x86_64-unknown-openbsd7.0/11.2.0/collect2 --eh-frame-hdr -e __start -Bdynamic -dynamic-linker /usr/libexec/ld.so -L/usr/lib /usr/lib/crt0.o /usr/lib/crtbegin.o -L/usr/local/lib/gcc/x86_64-unknown-openbsd7.0/11.2.0 -L/usr/local/lib/gcc/x86_64-unknown-openbsd7.0/11.2.0/../../.. a-example.o -lgcc -lc -lgcc /usr/lib/crtend.o COLLECT_GCC_OPTIONS='-v' '-save-temps' '-fanalyzer' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a.'