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

            Bug ID: 120213
           Summary: analyzer report a false positive
           Product: gcc
           Version: 15.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: raffaellobertini at gmail dot com
  Target Milestone: ---

compile this code:

```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define NUM_CHARS 1

void split_string(const char* str)
{
    const size_t l = strnlen(str, 2);
    if (l > NUM_CHARS)
    {
        char s[NUM_CHARS + 1];
        memcpy(s, str, NUM_CHARS);
        s[NUM_CHARS] = '\0';
        split_string(s);
        split_string(&str[NUM_CHARS]);
        return;
    }

    printf("%s", str);
}

int main()
{
    split_string("H");
    return 0;
}

```

using a cmake conifg of:
```
add_executable(split-string src/split-string.c)
target_compile_options(split-string
        PRIVATE "-Wall" "-Wextra"
        $<$<COMPILE_LANGUAGE:C>: -fanalyzer>
    )
```


the output reported is a warning: "warning: stack-based buffer over-read
[CWE-126]"

Also the output seems a little bit corrupted:

```
[build]    13 |         memcpy(s, str, NUM_CHARS);
[build]       |         ^~~~~~~~~~~~~~~~~~~~~~~~~
[build]   'main': events 1-2
[build]     Γöé
[build]     Γöé   23 | int main()
[build]     Γöé      |     ^~~~
[build]     Γöé      |     |
[build]     Γöé      |     (1) entry to 'main'
[build]     Γöé   24 | {
[build]     Γöé   25 |     split_string("H");
[build]     Γöé      |     ~~~~~~~~~~~~~~~~~
[build]     Γöé      |     |
[build]     Γöé      |     (2) calling 'split_string' from 'main'
[build]     Γöé
[build]     ΓööΓöÇΓöÇ> 'split_string': events 3-7
[build]            Γöé
[build]            Γöé    7 | void split_string(const char* str)
[build]            Γöé      |      ^~~~~~~~~~~~
[build]            Γöé      |      |
[build]            Γöé      |      (3) entry to 'split_string'
[build]            Γöé......
[build]            Γöé   10 |     if (l > NUM_CHARS)
[build]            Γöé      |        ~
[build]            Γöé      |        |
[build]            Γöé      |        (5) following 'true' branch (when 'l >
1')... ΓöÇ>ΓöÇΓöÉ
[build]            Γöé      |                                                  
      Γöé
[build]            Γöé      |                                                  
      Γöé
[build]            Γöé     
|ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
[build]            Γöé   11 |Γöé    {
[build]            Γöé   12 |Γöé        char s[NUM_CHARS + 1];
[build]            Γöé      |Γöé             ~
[build]            Γöé      |Γöé             |
[build]            Γöé      |Γöé             (4) capacity: 2 bytes
[build]            Γöé   13 |Γöé        memcpy(s, str, NUM_CHARS);
[build]            Γöé      |Γöé        ~~~~~~~~~~~~~~~~~~~~~~~~~
[build]            Γöé      |Γöé        |
[build]            Γöé      |ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ>(6) ...to here
[build]            Γöé   14 |         s[NUM_CHARS] = '\0';
[build]            Γöé   15 |         split_string(s);
[build]            Γöé      |         ~~~~~~~~~~~~~~~
[build]            Γöé      |         |
[build]            Γöé      |         (7) calling 'split_string' from
'split_string'
[build]            Γöé
[build]            ΓööΓöÇΓöÇ> 'split_string': events 8-12
[build]                   Γöé
[build]                   Γöé    7 | void split_string(const char* str)
[build]                   Γöé      |      ^~~~~~~~~~~~
[build]                   Γöé      |      |
[build]                   Γöé      |      (8) entry to 'split_string'
[build]                   Γöé......
[build]                   Γöé   10 |     if (l > NUM_CHARS)
[build]                   Γöé      |        ~
[build]                   Γöé      |        |
[build]                   Γöé      |        (10) following 'true' branch (when
'l > 1')... ΓöÇ>ΓöÇΓöÉ
[build]                   Γöé      |                                           
              Γöé
[build]                   Γöé      |                                           
              Γöé
[build]                   Γöé     
|ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ

[...]

```

It looks like the analyzer is assuming the branch of `if (l> NUM_CHARS)` true
even when is false, basically doesn't logically follow the first 2 lines of the
spit_string function.

At least it looks like so to me.

Reply via email to