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)
                  |

Reply via email to