https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96792
Bug ID: 96792 Summary: Analyzer assumes pointer is NULL, even though pointer was dereferenced earlier Product: gcc Version: 11.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: analyzer Assignee: dmalcolm at gcc dot gnu.org Reporter: vries at gcc dot gnu.org Target Milestone: --- I build gdb/gdbserver master with gcc-11 (gcc-11 (SUSE Linux) 11.0.0 20200824 (experimental) [revision 0d166f4a8773a43d925be006e713b7d81626ddb9]) and CFLAGS/CXXFLAGS="-Wall -O0 -g -fanalyzer". I ran into a warning I thought was interesting in gdb/block.c. I've minimized it into this test-case: ... $ cat test.c #define NULL (void *)0 struct block { void *function; const struct block *superblock; }; struct global_block { struct block block; void *compunit_symtab; }; extern const struct block *block_global_block (const struct block *block); void * block_objfile (const struct block *block) { const struct global_block *global_block; if (block->function != NULL) return block->function; global_block = (struct global_block *) block_global_block (block); return global_block->compunit_symtab; } const struct block * block_global_block (const struct block *block) { if (block == NULL) return NULL; while (block->superblock != NULL) block = block->superblock; return block; } ... The analyzer shows: ... $ gcc-11 -fanalyzer -c test.c In function ‘block_objfile’: test.c:26:22: warning: dereference of NULL ‘global_block’ [CWE-690] [-Wanalyzer-null-dereference] 26 | return global_block->compunit_symtab; | ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~ ‘block_objfile’: events 1-4 | | 18 | block_objfile (const struct block *block) | | ^~~~~~~~~~~~~ | | | | | (1) entry to ‘block_objfile’ |...... | 22 | if (block->function != NULL) | | ~ | | | | | (2) following ‘false’ branch... |...... | 25 | global_block = (struct global_block *) block_global_block (block); | | ~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (3) ...to here | | (4) calling ‘block_global_block’ from ‘block_objfile’ | +--> ‘block_global_block’: events 5-6 | | 30 | block_global_block (const struct block *block) | | ^~~~~~~~~~~~~~~~~~ | | | | | (5) entry to ‘block_global_block’ | 31 | { | 32 | if (block == NULL) | | ~ | | | | | (6) following ‘true’ branch (when ‘block’ is NULL)... | ‘block_global_block’: event 7 | | 1 | #define NULL (void *)0 | | ^ | | | | | (7) ...to here test.c:33:12: note: in expansion of macro ‘NULL’ | 33 | return NULL; | | ^~~~ | ‘block_global_block’: event 8 | | 1 | #define NULL (void *)0 | | ^ | | | | | (8) ‘0’ is NULL test.c:33:12: note: in expansion of macro ‘NULL’ | 33 | return NULL; | | ^~~~ | <------+ | ‘block_objfile’: events 9-10 | | 25 | global_block = (struct global_block *) block_global_block (block); | | ^~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (9) return of NULL to ‘block_objfile’ from ‘block_global_block’ | 26 | return global_block->compunit_symtab; | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (10) dereference of NULL ‘global_block’ | ... The interesting bit to me is that event 6 asserts that block == NULL. However, at event 2, we test block->function != NULL, which we cannot do without block != NULL.