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.

Reply via email to