https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113963

            Bug ID: 113963
           Summary: analyzer-null-dereference, analyzer-malloc-leak false
                    alarms in Gnulib savedir.c
           Product: gcc
           Version: 13.2.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 57441
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=57441&action=edit
test program with line number directives

I ran into this problem when compiling programs using Gnulib's lib/savedir.c
code.

This is gcc (GCC) 13.2.1 20231205 (Red Hat 13.2.1-6) on x86-64. Compile the
attached programs with:

gcc -O2 -fanalyzer -S savedir.i
gcc -O2 -fanalyzer -S savedis.i

Two problems. First, although savedis.i is merely a copy of savedir.i with line
number directives removed, the second command outputs an extra
-Wanalyzer-null-argument warning that the first command does not. Surely the
presence of line number directives should not affect which warnings are issued.

Second and more important, all the warnings are false positives. The pointers
can't possibly be null when the sizes are nonzero, and there is no memory leak.

The source files savedir.i and savedis.i are attached, compressed.

Here are the incorrect warnings that I get:

$ gcc -O2 -fanalyzer -S savedir.i
savedir.c: In function ‘streamsavedir’:
savedir.c:135:42: warning: dereference of NULL ‘entries’ [CWE-476]
[-Wanalyzer-null-dereference]
  135 |               entries[entries_used].name = used;
      |               ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
  ‘streamsavedir’: event 1
    |
    |savedir.c:106:6:
    |  106 |   if (dirp == NULL)
    |      |      ^
    |      |      |
    |      |      (1) following ‘false’ branch (when ‘dirp’ is non-NULL)...
    |
  ‘streamsavedir’: event 2
    |
    |cc1:
    | (2): ...to here
    |
  ‘streamsavedir’: events 3-5
    |
    |savedir.c:116:10:
    |  116 |       if (! dp)
    |      |          ^
    |      |          |
    |      |          (3) following ‘false’ branch (when ‘dp’ is non-NULL)...
    |......
    |  121 |       entry = dp->d_name;
    |      |       ~~~~~~~~~~~~~~~~~~
    |      |             |
    |      |             (4) ...to here
    |  122 |       if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] !=
'\0')
    |      |          ~
    |      |          |
    |      |          (5) following ‘true’ branch...
    |
  ‘streamsavedir’: event 6
    |
    |savedir.c:124:30:
    |  124 |           idx_t entry_size = _D_EXACT_NAMLEN (dp) + 1;
    |
  ‘streamsavedir’: event 7
    |
    |savedir.c:125:14:
    |  125 |           if (allocated - used <= entry_size)
    |      |              ^
    |      |              |
    |      |              (7) following ‘true’ branch...
    |
  ‘streamsavedir’: event 8
    |
    |  126 |             name_space = xpalloc (name_space, &allocated,
    |
  ‘streamsavedir’: events 9-14
    |
    |savedir.c:130:14:
    |  130 |           if (cmp)
    |      |              ^
    |      |              |
    |      |              (9) following ‘true’ branch (when ‘cmp’ is
non-NULL)...
    |  131 |             {
    |  132 |               if (entries_allocated == entries_used)
    |      |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |                  |                  |
    |      |                  |                  (10) ...to here
    |      |                  (11) following ‘false’ branch...
    |......
    |  135 |               entries[entries_used].name = used;
    |      |               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |                      |                   |
    |      |                      (12) ...to here     (14) dereference of NULL
‘entries + (long unsigned int)entries_used * 16’
    |      |                      (13) ‘entries’ is NULL
    |
savedir.c:169:3: warning: leak of ‘name_space’ [CWE-401]
[-Wanalyzer-malloc-leak]
  169 |   free (entries);
      |   ^~~~~~~~~~~~~~
  ‘savedir’: events 1-2
    |
    |  179 | savedir (char const *dir, enum savedir_option option)
    |      | ^~~~~~~
    |      | |
    |      | (1) entry to ‘savedir’
    |......
    |  182 |   if (! dirp)
    |      |      ~
    |      |      |
    |      |      (2) following ‘false’ branch (when ‘dirp’ is non-NULL)...
    |
  ‘savedir’: events 3-5
    |
    |savedir.c:186:26:
    |  186 |       char *name_space = streamsavedir (dirp, option);
    |      |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |                          |
    |      |                          (3) ...to here
    |      |                          (4) allocated here
    |      |                          (5) calling ‘streamsavedir’ from
‘savedir’
    |
    +--> ‘streamsavedir’: event 6
           |
           |savedir.c:96:1:
           |   96 | streamsavedir (DIR *dirp, enum savedir_option option)
           |      | ^~~~~~~~~~~~~
           |      | |
           |      | (6) entry to ‘streamsavedir’
           |
         ‘streamsavedir’: event 7
           |
           |savedir.c:106:6:
           |  106 |   if (dirp == NULL)
           |      |      ^
           |      |      |
           |      |      (7) following ‘false’ branch (when ‘dirp’ is
non-NULL)...
           |
         ‘streamsavedir’: event 8
           |
           |cc1:
           | (8): ...to here
           |
         ‘streamsavedir’: event 9
           |
           |savedir.c:145:6:
           |  145 |   if (errno != 0)
           |      |      ^
           |      |      |
           |      |      (9) following ‘true’ branch...
           |
         ‘streamsavedir’: event 10
           |
           |savedir.c:147:7:
           |  147 |       free (name_space);
           |      |       ^~~~~~~~~~~~~~~~~
           |      |       |
           |      |       (10) ...to here
           |
         ‘streamsavedir’: event 11
           |
           |savedir.c:169:3:
           |  169 |   free (entries);
           |      |   ^~~~~~~~~~~~~~
           |      |   |
           |      |   (11) ‘name_space’ leaks here; was allocated at (4)
           |
$ gcc -O2 -fanalyzer -S savedis.i
In function ‘memcpy’,
    inlined from ‘streamsavedir’ at savedis.i:2993:11:
savedis.i:2502:10: warning: use of NULL ‘name_space’ where non-null expected
[CWE-476] [-Wanalyzer-null-argument]
 2502 |   return __builtin___memcpy_chk (__dest, __src, __len,
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 2503 |      __builtin_object_size (__dest, 0));
      |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ‘streamsavedir’: event 1
    |
    | 2958 |   if (dirp ==
    |      |      ^
    |      |      |
    |      |      (1) following ‘false’ branch (when ‘dirp’ is non-NULL)...
    |
  ‘streamsavedir’: event 2
    |
    |cc1:
    | (2): ...to here
    |
  ‘streamsavedir’: events 3-10
    |
    | 2974 |       if (! dp)
    |      |          ^
    |      |          |
    |      |          (3) following ‘false’ branch (when ‘dp’ is non-NULL)...
    |......
    | 2979 |       entry = dp->d_name;
    |      |       ~~~~~~~~~~~~~~~~~~
    |      |             |
    |      |             (4) ...to here
    | 2980 |       if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] !=
'\0')
    |      |          ~
    |      |          |
    |      |          (5) following ‘true’ branch...
    |......
    | 2983 |                             (strlen ((
    |      |                             ~~~~~~~~~~
    |      |                              |
    |      |                              (6) ...to here
    | 2984 |                             dp
    |      |                             ~~
    | 2985 |                             )->d_name))
    |      |                             ~~~~~~~~~~~
    | 2986 |                                                  + 1;
    | 2987 |           if (allocated - used <= entry_size)
    |      |              ~
    |      |              |
    |      |              (7) following ‘false’ branch...
    |......
    | 2993 |           memcpy (name_space + used, entry, entry_size);
    |      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |           |                  |
    |      |           (8) ...to here     (9) ‘name_space’ is NULL
    |      |           (10) inlined call to ‘memcpy’ from ‘streamsavedir’
    |
    +--> ‘memcpy’: event 11
           |
           | 2502 |   return __builtin___memcpy_chk (__dest, __src, __len,
           |      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           |      |          |
           |      |          (11) argument 1 (‘name_space + (sizetype)used’)
NULL where non-null expected
           | 2503 |      __builtin_object_size (__dest, 0));
           |      |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           |
<built-in>: In function ‘streamsavedir’:
<built-in>: note: argument 1 of ‘__builtin___memcpy_chk’ must be non-null
savedis.i:2999:42: warning: dereference of NULL ‘entries’ [CWE-476]
[-Wanalyzer-null-dereference]
 2999 |               entries[entries_used].name = used;
      |               ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
  ‘streamsavedir’: event 1
    |
    | 2958 |   if (dirp ==
    |      |      ^
    |      |      |
    |      |      (1) following ‘false’ branch (when ‘dirp’ is non-NULL)...
    |
  ‘streamsavedir’: event 2
    |
    |cc1:
    | (2): ...to here
    |
  ‘streamsavedir’: events 3-14
    |
    | 2974 |       if (! dp)
    |      |          ^
    |      |          |
    |      |          (3) following ‘false’ branch (when ‘dp’ is non-NULL)...
    |......
    | 2979 |       entry = dp->d_name;
    |      |       ~~~~~~~~~~~~~~~~~~
    |      |             |
    |      |             (4) ...to here
    | 2980 |       if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] !=
'\0')
    |      |          ~
    |      |          |
    |      |          (5) following ‘true’ branch...
    |......
    | 2983 |                             (strlen ((
    |      |                             ~~~~~~~~~~
    |      |                              |
    |      |                              (6) ...to here
    | 2984 |                             dp
    |      |                             ~~
    | 2985 |                             )->d_name))
    |      |                             ~~~~~~~~~~~
    | 2986 |                                                  + 1;
    | 2987 |           if (allocated - used <= entry_size)
    |      |              ~
    |      |              |
    |      |              (7) following ‘true’ branch...
    | 2988 |             name_space = xpalloc (name_space, &allocated,
    |      |                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |                          |
    |      |                          (8) ...to here
    | 2989 |                                   entry_size - (allocated - used),
    |      |                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    | 2990 |
    |      |
    | 2991 |                                  (9223372036854775807L)
    |      |                                  ~~~~~~~~~~~~~~~~~~~~~~
    | 2992 |                                          - 1, sizeof *name_space);
    |      |                                          ~~~~~~~~~~~~~~~~~~~~~~~~
    | 2993 |           memcpy (name_space + used, entry, entry_size);
    | 2994 |           if (cmp)
    |      |              ~
    |      |              |
    |      |              (9) following ‘true’ branch (when ‘cmp’ is
non-NULL)...
    | 2995 |             {
    | 2996 |               if (entries_allocated == entries_used)
    |      |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |                  |                  |
    |      |                  |                  (10) ...to here
    |      |                  (11) following ‘false’ branch...
    |......
    | 2999 |               entries[entries_used].name = used;
    |      |               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |                      |                   |
    |      |                      (12) ...to here     (14) dereference of NULL
‘entries + (long unsigned int)entries_used * 16’
    |      |                      (13) ‘entries’ is NULL
    |
savedis.i:3037:3: warning: leak of ‘name_space’ [CWE-401]
[-Wanalyzer-malloc-leak]
 3037 |   free (entries);
      |   ^~~~~~~~~~~~~~
  ‘savedir’: events 1-5
    |
    | 3047 | savedir (char const *dir, enum savedir_option option)
    |      | ^~~~~~~
    |      | |
    |      | (1) entry to ‘savedir’
    |......
    | 3050 |   if (! dirp)
    |      |      ~
    |      |      |
    |      |      (2) following ‘false’ branch (when ‘dirp’ is non-NULL)...
    |......
    | 3056 |       char *name_space = streamsavedir (dirp, option);
    |      |                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |                          |
    |      |                          (3) ...to here
    |      |                          (4) allocated here
    |      |                          (5) calling ‘streamsavedir’ from
‘savedir’
    |
    +--> ‘streamsavedir’: events 6-7
           |
           | 2944 | streamsavedir (DIR *dirp, enum savedir_option option)
           |      | ^~~~~~~~~~~~~
           |      | |
           |      | (6) entry to ‘streamsavedir’
           |......
           | 2958 |   if (dirp ==
           |      |      ~
           |      |      |
           |      |      (7) following ‘false’ branch (when ‘dirp’ is
non-NULL)...
           |
         ‘streamsavedir’: event 8
           |
           |cc1:
           | (8): ...to here
           |
         ‘streamsavedir’: events 9-11
           |
           | 3009 |   if (
           |      |      ^
           |      |      |
           |      |      (9) following ‘true’ branch...
           |......
           | 3013 |       free (name_space);
           |      |       ~~~~~~~~~~~~~~~~~
           |      |       |
           |      |       (10) ...to here
           |......
           | 3037 |   free (entries);
           |      |   ~~~~~~~~~~~~~~
           |      |   |
           |      |   (11) ‘name_space’ leaks here; was allocated at (4)
           |

Reply via email to