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?