[Bug analyzer/103217] New: analyzer false positive on leak warning when using indirect strdup

npfhrotynz-ptnqh.myvf at noclue dot notk.org gcc-bugzilla@gcc.gnu.org
Sat Nov 13 00:30:36 GMT 2021


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103217

            Bug ID: 103217
           Summary: analyzer false positive on leak warning when using
                    indirect strdup
           Product: gcc
           Version: 11.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: analyzer
          Assignee: dmalcolm at gcc dot gnu.org
          Reporter: npfhrotynz-ptnqh.myvf at noclue dot notk.org
  Target Milestone: ---

Inlining both the reproducer and fanalyzer warning as they are small enough:

----------
#include <getopt.h>
#include <stdlib.h>
#include <string.h>

char *xstrdup(const char *src) {
        char *val = strdup(src);
        if (!val)
                abort();
        return val;
}

int main(int argc, char *argv[]) {
        char *one = NULL, *two = NULL;
        int rc;

        while ((rc = getopt(argc, argv, "a:b:")) != -1) {
                switch (rc) {
                case 'a':
                        free(one);
                        one = xstrdup(optarg);
                        break;
                case 'b':
                        free(two);
                        two = xstrdup(optarg);
                        break;
                }
        }
        free(one);
        free(two);
        return 0;
}
----------

----------
$ gcc -fanalyzer -o t t.c
t.c: In function ‘main’:
cc1: warning: leak of ‘val’ [CWE-401] [-Wanalyzer-malloc-leak]
  ‘main’: events 1-4
    |
    |t.c:13:5:
    |   13 | int main(int argc, char *argv[]) {
    |      |     ^~~~
    |      |     |
    |      |     (1) entry to ‘main’
    |......
    |   17 |         while ((rc = getopt(argc, argv, "a:b:")) != -1) {
    |      |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |                                                  |
    |      |                                                  (2) following
‘true’ branch (when ‘rc != -1’)...
    |   18 |                 switch (rc) {
    |      |                 ~~~~~~
    |      |                 |
    |      |                 (3) ...to here
    |......
    |   25 |                         two = xstrdup(optarg);
    |      |                               ~~~~~~~~~~~~~~~
    |      |                               |
    |      |                               (4) calling ‘xstrdup’ from ‘main’
    |
    +--> ‘xstrdup’: events 5-9
           |
           |    6 | char *xstrdup(const char *src) {
           |      |       ^~~~~~~
           |      |       |
           |      |       (5) entry to ‘xstrdup’
           |    7 |         char *val = strdup(src);
           |      |                     ~~~~~~~~~~~
           |      |                     |
           |      |                     (6) allocated here
           |    8 |         if (!val)
           |      |            ~
           |      |            |
           |      |            (7) assuming ‘val’ is non-NULL
           |      |            (8) following ‘false’ branch (when ‘val’ is
non-NULL)...
           |    9 |                 abort();
           |   10 |         return val;
           |      |                ~~~
           |      |                |
           |      |                (9) ...to here
           |
    <------+
    |
  ‘main’: event 10
    |
    |   25 |                         two = xstrdup(optarg);
    |      |                               ^~~~~~~~~~~~~~~
    |      |                               |
    |      |                               (10) returning to ‘main’ from
‘xstrdup’
    |
  ‘main’: event 11
    |
    |cc1:
    | (11): ‘val’ leaks here; was allocated at (6)
    |
----------

As far as I see the conditions seem to be:
 - there have to be at least two cases and two variables, adding more than two
cases leave the error only on second one; interverting the two makes the error
stay on 2nd. Similarly, using the same variable in both cases makes the error
go away.
 - it has to be strdup, replacing strdup with malloc makes the error go away.
 - it has to be indirected, calling strdup() directly in main (with the same
check/abort) makes the error go away. explicit "inline" attribute does not
change behaviour.
 - it doesn't have to be getopt, but there has to be a function call e.g.
replacing getopt() with a locally defined iteration function keeps the error,
but checking argc/argv directly in the loop makes the error disappear

What's also interesting is the event 11 and "(11): ‘val’ leaks here" that
points to... nothing at all? There's no line number or any code quoted to refer
to. I'm a bit at a loss as to what this could mean, exiting from main? where?


More information about the Gcc-bugs mailing list