https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103892
Bug ID: 103892 Summary: -Wanalyzer-double-free false positive when compiling libpipeline Product: gcc Version: 11.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: analyzer Assignee: dmalcolm at gcc dot gnu.org Reporter: cjwatson at ubuntu dot com Target Milestone: --- Created attachment 52109 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=52109&action=edit reduced version of lib/pipeline.c Using gcc (Debian 11.2.0-13) 11.2.0 from Debian unstable, I tried -fanalyzer on https://gitlab.com/cjwatson/libpipeline, and I ran into what looks like a false positive from -Wanalyzer-double-free. I've attached a test case that's reduced as far as I could. There's no double-free here, just fairly straightforward freeing of elements of a tagged union, but it looks like the analyzer is perhaps getting confused by the fact that the freeing is recursive? (The argstr_get_word and pipecmd_new_argstr functions appear entirely superfluous to the problem, and I trimmed down their bodies as far as I could, but if I remove anything else from them then the problem goes away.) $ gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Debian 11.2.0-13' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-11-KdLYb3/gcc-11-11.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-11-KdLYb3/gcc-11-11.2.0/debian/tmp-gcn/usr --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2 Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 11.2.0 (Debian 11.2.0-13) $ gcc -save-temps -fanalyzer -Wall -Wno-analyzer-malloc-leak -Wanalyzer-too-complex -g -O2 -Wall -c t.c -fPIC -DPIC -o t.o t.c: In function ‘pipecmd_free’: cc1: warning: terminating analysis for this program point: callstring: [(SN: 14 -> SN: 9 in pipecmd_free)] before (SN: 6 stmt: 0): <L13>:EN: 67, EN: 77, EN: 87, EN: 97, EN: 107, EN: 117, EN: 127, EN: 137 [-Wanalyzer-too-complex] t.c: At top level: t.c:43:6: warning: analysis bailed out early (131 'after-snode' enodes; 491 enodes) [-Wanalyzer-too-complex] 43 | void pipecmd_free (struct pipecmd *cmd) | ^~~~~~~~~~~~ t.c: In function ‘pipecmd_free’: t.c:56:25: warning: double-‘free’ of ‘*(struct pipecmd_process *)((char *)cmd + 8).argv’ [CWE-415] [-Wanalyzer-double-free] 56 | free (cmdp->argv); | ^~~~~~~~~~~~~~~~~ ‘pipecmd_free’: events 1-4 | | 43 | void pipecmd_free (struct pipecmd *cmd) | | ^~~~~~~~~~~~ | | | | | (1) entry to ‘pipecmd_free’ |...... | 47 | if (!cmd) | | ~ | | | | | (2) following ‘false’ branch (when ‘cmd’ is non-NULL)... |...... | 50 | switch (cmd->tag) { | | ~~~~~~ | | | | | (3) ...to here | | (4) following ‘case 1:’ branch... | ‘pipecmd_free’: event 5 | |cc1: | (5): ...to here | ‘pipecmd_free’: events 6-8 | | 64 | for (i = 0; i < cmds->ncommands; ++i) | | ~~^~~~~~~~~~~~~~~~~ | | | | | (6) following ‘true’ branch... | 65 | pipecmd_free (cmds->commands[i]); | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (7) ...to here | | (8) calling ‘pipecmd_free’ from ‘pipecmd_free’ | +--> ‘pipecmd_free’: events 9-12 | | 43 | void pipecmd_free (struct pipecmd *cmd) | | ^~~~~~~~~~~~ | | | | | (9) entry to ‘pipecmd_free’ |...... | 47 | if (!cmd) | | ~ | | | | | (10) following ‘false’ branch (when ‘cmd’ is non-NULL)... |...... | 50 | switch (cmd->tag) { | | ~~~~~~ | | | | | (11) ...to here | | (12) following ‘case 1:’ branch... | ‘pipecmd_free’: event 13 | |cc1: | (13): ...to here | ‘pipecmd_free’: events 14-16 | | 64 | for (i = 0; i < cmds->ncommands; ++i) | | ~~^~~~~~~~~~~~~~~~~ | | | | | (14) following ‘true’ branch... | 65 | pipecmd_free (cmds->commands[i]); | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (15) ...to here | | (16) calling ‘pipecmd_free’ from ‘pipecmd_free’ | +--> ‘pipecmd_free’: events 17-18 | | 43 | void pipecmd_free (struct pipecmd *cmd) | | ^~~~~~~~~~~~ | | | | | (17) entry to ‘pipecmd_free’ |...... | 47 | if (!cmd) | | ~ | | | | | (18) following ‘true’ branch (when ‘cmd’ is NULL)... | ‘pipecmd_free’: event 19 | |cc1: | (19): ...to here | <------+ | ‘pipecmd_free’: events 20-23 | | 64 | for (i = 0; i < cmds->ncommands; ++i) | | ~~~~~~~~~~~~~~~~~~~ | | | | | (21) following ‘true’ branch... | 65 | pipecmd_free (cmds->commands[i]); | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (20) returning to ‘pipecmd_free’ from ‘pipecmd_free’ | | (22) ...to here | | (23) calling ‘pipecmd_free’ from ‘pipecmd_free’ | +--> ‘pipecmd_free’: events 24-25 | | 43 | void pipecmd_free (struct pipecmd *cmd) | | ^~~~~~~~~~~~ | | | | | (24) entry to ‘pipecmd_free’ |...... | 56 | free (cmdp->argv); | | ~~~~~~~~~~~~~~~~~ | | | | | (25) first ‘free’ here | <------+ | ‘pipecmd_free’: events 26-31 | | 64 | for (i = 0; i < cmds->ncommands; ++i) | | ~~~~~~~~~~~~~~~~~~~ | | | | | (27) following ‘true’ branch... | | (29) following ‘true’ branch... | 65 | pipecmd_free (cmds->commands[i]); | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (26) returning to ‘pipecmd_free’ from ‘pipecmd_free’ | | (28) ...to here | | (30) ...to here | | (31) calling ‘pipecmd_free’ from ‘pipecmd_free’ | +--> ‘pipecmd_free’: events 32-33 | | 43 | void pipecmd_free (struct pipecmd *cmd) | | ^~~~~~~~~~~~ | | | | | (32) entry to ‘pipecmd_free’ |...... | 56 | free (cmdp->argv); | | ~~~~~~~~~~~~~~~~~ | | | | | (33) second ‘free’ here; first ‘free’ was at (25) |