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

            Bug ID: 114882
           Summary: Two -fanalyzer false positives after realloc grows
                    buffer
           Product: gcc
           Version: 14.0
            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 58062
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=58062&action=edit
Run "gcc -O2 -fanalyzer -S v.i" to see the false positives

This is gcc (GCC) 14.0.1 20240411 (Red Hat 14.0.1-0) on x86-64. I ran into this
problem when compiling GNU Emacs's etags.c program. A simplified version of the
problem is attached.

-fanalyzer issues two false-positive diagnostics for the same source line.
Either GCC's analyzer is confused about how realloc works, or it is confused
about buffer size calculations, or both.

To reproduce the problem, compile the attached program v.i with:

gcc -O2 -fanalyzer -S v.i

This program calls realloc in a common way when growing a buffer. The original
Emacs code doubles the buffer size when reallocating; in this simplified
version the code merely adds 1 to the buffer size when reallocating. Either
way, GCC incorrectly warns about out-of-bounds writes and about accessing
uninitialized storage.

I plan to work around the problem for now by disabling
-Wanalyzer-use-of-uninitialized-value when compiling etags.c.

Here are the diagnostics from the above GCC invocation. They're all false
positives.

v.i: In function ‘oemacs_etags_readline_internal’:
v.i:30:26: warning: use of uninitialized value ‘p[-1]’ [CWE-457]
[-Wanalyzer-use-of-uninitialized-value]
   30 |       if (buffer < p && p[-1] == '\r')
      |                         ~^~~~
  ‘oemacs_etags_readline_internal’: events 1-13
    |
    |   11 |   if (!buffer)
    |      |      ^
    |      |      |
    |      |      (1) following ‘false’ branch (when ‘buffer’ is non-NULL)...
    |......
    |   14 |   char *pend = p + buf_size;
    |      |         ~~~~
    |      |         |
    |      |         (2) ...to here
    |......
    |   18 |       if (p == pend)
    |      |          ~
    |      |          |
    |      |          (3) following ‘true’ branch (when ‘p == pend’)...
    |   19 |         {
    |   20 |           size_t new_buf_size = buf_size + 1;
    |      |                  ~~~~~~~~~~~~
    |      |                  |
    |      |                  (4) ...to here
    |   21 |           if (new_buf_size < buf_size)
    |      |              ~
    |      |              |
    |      |              (5) following ‘false’ branch (when ‘buf_size <=
new_buf_size’)...
    |   22 |             __builtin_abort ();
    |   23 |           buffer = realloc (buffer, new_buf_size);
    |      |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    |      |                    |
    |      |                    (6) ...to here
    |      |                    (7) when ‘realloc’ succeeds, moving buffer
    |      |                    (8) region created on heap here
    |   24 |           if (!buffer)
    |      |              ~
    |      |              |
    |      |              (9) following ‘false’ branch (when ‘buffer’ is
non-NULL)...
    |   25 |             __builtin_abort ();
    |   26 |           p = buffer + buf_size;
    |      |           ~~~~~~~~~~~~~~~~~~~~~
    |      |             |
    |      |             (10) ...to here
    |......
    |   30 |       if (buffer < p && p[-1] == '\r')
    |      |          ~              ~~~~~
    |      |          |               |
    |      |          |               (12) ...to here
    |      |          |               (13) use of uninitialized value ‘p[-1]’
here
    |      |          (11) following ‘true’ branch (when ‘buffer < p’)...
    |
v.i:32:12: warning: heap-based buffer overflow [CWE-122]
[-Wanalyzer-out-of-bounds]
   32 |       *p++ = c;
      |       ~~~~~^~~
  ‘oemacs_etags_readline_internal’: events 1-10
    |
    |   10 |   char *buffer = malloc (buf_size);
    |      |                  ^~~~~~~~~~~~~~~~~
    |      |                  |
    |      |                  (1) capacity: 1 byte
    |   11 |   if (!buffer)
    |      |      ~            
    |      |      |
    |      |      (2) following ‘false’ branch (when ‘buffer’ is non-NULL)...
    |......
    |   14 |   char *pend = p + buf_size;
    |      |         ~~~~      
    |      |         |
    |      |         (3) ...to here
    |......
    |   18 |       if (p == pend)
    |      |          ~        
    |      |          |
    |      |          (4) following ‘false’ branch (when ‘p != pend’)...
    |      |          (8) following ‘false’ branch (when ‘p != pend’)...
    |......
    |   30 |       if (buffer < p && p[-1] == '\r')
    |      |          ~        
    |      |          |
    |      |          (5) ...to here
    |      |          (6) following ‘false’ branch (when ‘buffer >= p’)...
    |      |          (9) ...to here
    |   31 |         break;
    |   32 |       *p++ = c;
    |      |       ~~~~~~~~    
    |      |         |  |
    |      |         |  (10) out-of-bounds write at byte 1 but region ends at
byte 1
    |      |         (7) ...to here
    |
v.i:32:12: note: write of 1 byte to beyond the end of the region
   32 |       *p++ = c;
      |       ~~~~~^~~

                                      ┌──────────────────────────────────┐
                                      │     write of ‘char’ (1 byte)     │
                                      └──────────────────────────────────┘
                                                       │
                                                       │
                                                       v
  ┌──────────────────────────────────┐┌──────────────────────────────────┐
  │ buffer allocated on heap at (1)  ││        after valid range         │
  └──────────────────────────────────┘└──────────────────────────────────┘
  ├────────────────┬─────────────────┤├────────────────┬─────────────────┤
                   │                                   │
          ╭────────┴───────╮                ╭──────────┴──────────╮
          │capacity: 1 byte│                │⚠️  overflow of 1 byte│
          ╰────────────────╯                ╰─────────────────────╯

Reply via email to