https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106436
Bug ID: 106436 Summary: -Wanalyzer-null-dereference false positive suggests data corruption in GCC Product: gcc Version: 12.1.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: analyzer Assignee: dmalcolm at gcc dot gnu.org Reporter: eggert at cs dot ucla.edu Target Milestone: --- Created attachment 53347 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=53347&action=edit Compile with -O2 -S -fanalyzer (GCC 12 x86-64) to reproduce the bug. I found this problem when compiling Gnu Tar with GCC 12.1.1 20220507 (Red Hat 12.1.1-1) on x86-64. Compile the attached program t.i with: gcc -O2 -S -fanalyzer t.i The output is below. The first diagnostic is a false positive. Attempting to silence it by inserting "if (!listed_loc) abort ();" before line 13975 causes unrelated diagnostics to change, suggesting that there's some sort of confusion in the internal data structure that GCC uses to represent the program. t.i: In function ‘optloc_eq’: t.i:12193:8: warning: dereference of NULL ‘a’ [CWE-476] [-Wanalyzer-null-dereference] 12193 | if (a->source != b->source) | ~^~~~~~~~ ‘main’: events 1-4 | |14348 | main (int argc, char **argv) | | ^~~~ | | | | | (1) entry to ‘main’ |...... |14373 | if (stdopen ()) | | ~ | | | | | (2) following ‘false’ branch... |...... |14383 | allocated_archive_names = 10; | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (3) ...to here |...... |14400 | decode_options (argc, argv); | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (4) calling ‘decode_options’ from ‘main’ | +--> ‘decode_options’: events 5-7 | |13764 | decode_options (int argc, char **argv) | | ^~~~~~~~~~~~~~ | | | | | (5) entry to ‘decode_options’ |...... |13767 | struct option_locus loc = { OPTS_COMMAND_LINE, 0, 0, 0 }; | | ~~~ | | | | | (6) ‘loc.prev’ is NULL |...... |13890 | parse_default_options (&args); | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (7) calling ‘parse_default_options’ from ‘decode_options’ | +--> ‘parse_default_options’: events 8-10 | |13721 | parse_default_options (struct tar_args *args) | | ^~~~~~~~~~~~~~~~~~~~~ | | | | | (8) entry to ‘parse_default_options’ |...... |13725 | struct option_locus loc = { OPTS_ENVIRON, "TAR_OPTIONS", 0, 0 }; | | ~~~ | | | | | (9) ‘loc.prev’ is NULL | | (10) ‘loc.prev’ is NULL | <------+ | ‘decode_options’: events 11-20 | |12176 | return option_class[id]; | | ~~~~~~~~~~~~~~~~ | | | | | (17) ...to here | | (18) ‘0’ is NULL | | (19) ‘0’ is NULL |...... |13890 | parse_default_options (&args); | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (11) returning to ‘decode_options’ from ‘parse_default_options’ |13891 | |13892 | if (argp_parse (&argp, argc, argv, 0x08, &idx, &args)) | | ~ | | | | | (12) following ‘false’ branch... |13893 | exit (2); |13894 | if (args.o_option) | | ~~~~~~~~~~~~~ | | | | | (13) ...to here |...... |13970 | if (listed_incremental_option | | ~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (14) following ‘true’ branch... |13971 | && (0 <= (newer_mtime_option).tv_nsec)) | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | | | (15) ...to here | | (16) following ‘true’ branch... |...... |13975 | if (optloc_eq (listed_loc, newer_loc)) | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (20) calling ‘optloc_eq’ from ‘decode_options’ | +--> ‘optloc_eq’: events 21-22 | |12191 | optloc_eq (struct option_locus *a, struct option_locus *b) | | ^~~~~~~~~ | | | | | (21) entry to ‘optloc_eq’ |12192 | { |12193 | if (a->source != b->source) | | ~~~~~~~~~ | | | | | (22) dereference of NULL ‘a’ | t.i: In function ‘get_date_or_file’: t.i:12354:23: warning: leak of ‘p’ [CWE-401] [-Wanalyzer-malloc-leak] 12354 | args->textual_date = p; | ~~~~~~~~~~~~~~~~~~~^~~ ‘parse_opt’: events 1-4 | |12682 | parse_opt (int key, char *arg, struct argp_state *state) | | ^~~~~~~~~ | | | | | (1) entry to ‘parse_opt’ |...... |12686 | switch (key) | | ~~~~~~ | | | | | (2) following ‘case 152:’ branch... |...... |12946 | case MTIME_OPTION: | | ~~~~ | | | | | (3) ...to here |12947 | get_date_or_file (args, "--mtime", arg, &mtime_option); | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (4) calling ‘get_date_or_file’ from ‘parse_opt’ | +--> ‘get_date_or_file’: events 5-13 | |12303 | get_date_or_file (struct tar_args *args, const char *rpl_option, | | ^~~~~~~~~~~~~~~~ | | | | | (5) entry to ‘get_date_or_file’ |...... |12307 | ((void) ( | | ~~~~~~~~~ |12308 | str | | ~~~ |12309 | ), 0) | | ~~~~~ |12310 | != 0 | | ~~~~ |12311 | || | | ~~ | | | | | (6) following ‘false’ branch... |12312 | (( | | ~~ |12313 | *str | | ~~~~ |12314 | ) == '/') | | ~~~~~~~~~ |12315 | | | |12316 | || *str == '.') | | ~~~~~~~~~~~~~~ | | | | | (7) ...to here | | (8) following ‘false’ branch... |...... |12332 | if (! parse_datetime (ts, str, | | ~ ~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | | | (9) ...to here | | (10) following ‘true’ branch... |12333 | ((void *)0) | | ~~~~~~~~~~~ |12334 | )) | | ~ |...... |12349 | struct textual_date *p = xmalloc (sizeof (*p)); | | ~~~~~~~~~~~~~~~~~~~~~ | | | | | (11) ...to here | | (12) allocated here |...... |12354 | args->textual_date = p; | | ~~~~~~~~~~~~~~~~~~~~~~ | | | | | (13) ‘p’ leaks here; was allocated at (12) | t.i: In function ‘decode_options’: t.i:13862:35: warning: leak of ‘xstrdup(&buffer)’ [CWE-401] [-Wanalyzer-malloc-leak] 13862 | opt = find_argp_option (&argp, *letter); | ^~~~~~~ ‘decode_options’: events 1-7 | |13764 | decode_options (int argc, char **argv) | | ^~~~~~~~~~~~~~ | | | | | (1) entry to ‘decode_options’ |...... |13831 | if (argc > 1 && argv[1][0] != '-') | | ~ | | | | | (2) following ‘true’ branch... |...... |13842 | buffer[0] = '-'; | | ~~~~~~~~~~~~~~~ | | | | | (3) ...to here |...... |13856 | for (letter = *in++; *letter; letter++) | | ~ | | | | | (4) following ‘true’ branch... |...... |13860 | buffer[1] = *letter; | | ~~~~~~~~~~~~~~~~~~~ | | | | | (5) ...to here |13861 | *out++ = xstrdup (buffer); | | ~~~~~~~~~~~~~~~~ | | | | | (6) allocated here |13862 | opt = find_argp_option (&argp, *letter); | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (7) calling ‘find_argp_option’ from ‘decode_options’ | +--> ‘find_argp_option’: events 8-10 | |13655 | find_argp_option (struct argp *ap, int key) | | ^~~~~~~~~~~~~~~~ | | | | | (8) entry to ‘find_argp_option’ |...... |13663 | if (!p && ap->children) | | ~ | | | | | (9) following ‘false’ branch (when ‘p’ is non-NULL)... |...... |13672 | return p; | | ~ | | | | | (10) ...to here | <------+ | ‘decode_options’: events 11-17 | |13856 | for (letter = *in++; *letter; letter++) | | ~ | | | | | (14) following ‘true’ branch... |...... |13860 | buffer[1] = *letter; | | ~~~~~~~~~~~~~~~~~~~ | | | | | (15) ...to here |13861 | *out++ = xstrdup (buffer); | | ~~~~~~~~~~~~~~~~ | | | | | (16) allocated here |13862 | opt = find_argp_option (&argp, *letter); | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | | | (17) ‘xstrdup(&buffer)’ leaks here; was allocated at (16) | | (11) returning to ‘decode_options’ from ‘find_argp_option’ |13863 | if (opt && opt->arg) | | ~ ~~~~~~~~ | | | | | | | (13) ...to here | | (12) following ‘true’ branch (when ‘opt’ is non-NULL)... |