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│ ╰────────────────╯ ╰─────────────────────╯