https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113606
Bug ID: 113606 Summary: -Wanalyzer-infinite-recursion false positive on code involving strstr, memset, strnlen and -D_FORTIFY_SOURCE Product: gcc Version: 14.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: analyzer Assignee: dmalcolm at gcc dot gnu.org Reporter: dmalcolm at gcc dot gnu.org Target Milestone: --- Taking the following from this downstream bug report: https://bugzilla.redhat.com/show_bug.cgi?id=2260398 Create str.c: ``` #define _POSIX_C_SOURCE 200809L #include <stddef.h> #include <stdio.h> #include <string.h> static char* strredact(char *str, const char *sub, const char c) { char *p; if (!str) return NULL; if (!sub) return str; p = strstr(str, sub); if (!c || !p) return str; (void)memset(p, c, strnlen(sub, strlen(str))); return strredact(str, sub, c); } int main (void) { char string[] = "This_is_a_string."; return printf("%s\n", strredact(string, "_", ' ')); } ``` Actual Results (with trunk aka gcc 14): $ gcc -fanalyzer -Werror -O str.c $ gcc -fanalyzer -Werror -O -D_FORTIFY_SOURCE=2 str.c str.c: In function ‘strredact’: str.c:16:10: error: infinite recursion [CWE-674] [-Werror=analyzer-infinite-recursion] 16 | return strredact(str, sub, c); | ^~~~~~~~~~~~~~~~~~~~~~ ‘strredact’: events 1-9 | | 8 | strredact(char *str, const char *sub, const char c) | | ^~~~~~~~~ | | | | | (1) entry to ‘strredact’ |...... | 11 | if (!str) return NULL; | | ~ | | | | | (2) following ‘false’ branch (when ‘str’ is non-NULL)... | 12 | if (!sub) return str; | | ~ | | | | | (3) ...to here | | (4) following ‘false’ branch (when ‘sub’ is non-NULL)... | 13 | p = strstr(str, sub); | | ~~~~~~~~~~~~~~~~ | | | | | (5) ...to here | | (6) when ‘strstr’ returns non-NULL | 14 | if (!c || !p) return str; | | ~ | | | | | (7) following ‘false’ branch... | 15 | (void)memset(p, c, strnlen(sub, strlen(str))); | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (8) ...to here | 16 | return strredact(str, sub, c); | | ~~~~~~~~~~~~~~~~~~~~~~ | | | | | (9) calling ‘strredact’ from ‘strredact’ | +--> ‘strredact’: events 10-18 | | 8 | strredact(char *str, const char *sub, const char c) | | ^~~~~~~~~ | | | | | (10) initial entry to ‘strredact’ |...... | 11 | if (!str) return NULL; | | ~ | | | | | (11) following ‘false’ branch (when ‘str’ is non-NULL)... | 12 | if (!sub) return str; | | ~ | | | | | (12) ...to here | | (13) following ‘false’ branch (when ‘sub’ is non-NULL)... | 13 | p = strstr(str, sub); | | ~~~~~~~~~~~~~~~~ | | | | | (14) ...to here | | (15) when ‘strstr’ returns non-NULL | 14 | if (!c || !p) return str; | | ~ | | | | | (16) following ‘false’ branch... | 15 | (void)memset(p, c, strnlen(sub, strlen(str))); | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (17) ...to here | 16 | return strredact(str, sub, c); | | ~~~~~~~~~~~~~~~~~~~~~~ | | | | | (18) calling ‘strredact’ from ‘strredact’ | +--> ‘strredact’: events 19-20 | | 8 | strredact(char *str, const char *sub, const char c) | | ^~~~~~~~~ | | | | | (19) recursive entry to ‘strredact’; previously entered at (10) | | (20) apparently infinite recursion | cc1: all warnings being treated as errors Expected Results: $ gcc -fanalyzer -Werror -O str.c $ gcc -fanalyzer -Werror -O -D_FORTIFY_SOURCE=2 str.c (no output) Affects trunk. Doesn't affect gcc 13.2 Reproduced on Godbolt, see https://godbolt.org/z/ebsq7WhxG https://godbolt.org/z/Tn7oe1EbG - a slightly more minimized example