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

            Bug ID: 123421
           Summary: Warning about too long initializer-string miscounts
                    initializer length when nonstring attribute is used
           Product: gcc
           Version: 15.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: gcc at basmevissen dot nl
  Target Milestone: ---

Hi all,

When compiling code that uses an uint8_t[] to store non-string data, I noticed
the warning GCC issues possibly miscounts the size of the initializer data:

$ cat nonstring_data.c 
#include <stdint.h>

uint8_t my_nonstring_data[DATA_SIZE] __attribute__((nonstring)) = "DATA";
uint8_t my_array_data[DATA_SIZE] = { 'D', 'A', 'T', 'A' };
uint8_t my_string_data[DATA_SIZE+1] = "DATA";

For data size 4, everything is fine:

$ gcc -Wall -Wextra -fanalyzer -DDATA_SIZE=4 -c nonstring_data.c
$

When data size is 3, the following warnings are emitted:

$ gcc -Wall -Wextra -fanalyzer -DDATA_SIZE=3 -c nonstring_data.c
nonstring_data.c:7:67: warning: initializer-string for array of ‘unsigned char’
is too long (5 chars into 3 available)
    7 | uint8_t my_nonstring_data[DATA_SIZE] __attribute__((nonstring)) =
"DATA";
      |                                                                  
^~~~~~
nonstring_data.c:8:53: warning: excess elements in array initializer
    8 | uint8_t my_array_data[DATA_SIZE] = { 'D', 'A', 'T', 'A' };
      |                                                     ^~~
nonstring_data.c:8:53: note: (near initialization for ‘my_array_data’)
nonstring_data.c:9:39: warning: initializer-string for array of ‘unsigned char’
truncates NUL terminator but destination lacks ‘nonstring’ attribute (5 chars
into 4 available) [-Wunterminated-string-initialization]
    9 | uint8_t my_string_data[DATA_SIZE+1] = "DATA";
      | 

It appears strange to me that the first warning reads "warning:
initializer-string for array of ‘unsigned char’ is too long (5 chars into 3
available)". I would expect that due to the nonstring attribute, the "DATA" is
seen as 4 chars non-string data and hence the warning would read "... (4 chars
into 3 available)".

The root cause might be that the attribute should be given to the initialized
instead of the initializer, as previously suggested by Kees Cook in
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117178#c7. Then the initializer
would no longer be a NUL terminated string, but plain data. 

That is not to say the attribute to the variable is not useful, as it helps in
trapping cases where the nonstring variable is passed to printf() and friends:

Add to the code example:

void my_func(void)
{
        printf("%s\n", my_nonstring_data);
}

$ gcc -Wall -Wextra -fanalyzer -DDATA_SIZE=4 -c nonstring_data.c
nonstring_data.c: In function ‘my_func’:
nonstring_data.c:14:9: warning: ‘__builtin_puts’ argument 1 declared attribute
‘nonstring’ [-Wstringop-overread]
   14 |         printf("%s\n", my_nonstring_data);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
nonstring_data.c:8:9: note: argument ‘my_nonstring_data’ declared here
    8 | uint8_t my_nonstring_data[DATA_SIZE] __attribute__((nonstring)) =
"DATA";
      |         ^~~~~~~~~~~~~~~~~

What are your thoughts on this?

Best Regards,

Bas.

Reply via email to