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

Reply via email to