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.