Hi, Consider the following example program: #include <stdio.h> #include <unistd.h> int main(void) { struct test_st {}; int fd = 0; int count = 0; struct test_st test_info[16]; count = read(fd, test_info, sizeof(test_info)); return(0); } When compiling it with GCC 13.2 using -D_FORTIFY_SOURCE=3 and -O1, I see: a.c: In function ‘main’: a.c:15:13: warning: ‘read’ writing 1 byte into a region of size 0 overflows the destination [-Wstringop-overflow=] 15 | count = read(fd, test_info, sizeof(test_info)); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a.c:10:20: note: destination object ‘test_info’ of size 0 10 | struct test_st test_info[16]; | ^~~~~~~~~ In file included from /usr/include/unistd.h:1214, from a.c:3: /usr/include/x86_64-linux-gnu/bits/unistd.h:26:1: note: in a call to function ‘read’ declared with attribute ‘access (write_only, 2)’ 26 | read (int __fd, void *__buf, size_t __nbytes) | ^~~~ GCC allows empty structs on C code and they are correctly sized 0, but -Wstringop-overflow still thinks the code is trying to read 1 byte into the array, which is not correct. I know there are a lot of false positives reported against -Wstringop-overflow, but I couldn't find an exact duplicate of this one.
Can you attach the preprocessed source since this depends on glibc's FORTIFY_SOURCE for value of 3 which is only included in newer glibc's?
Created attachment 57431 [details] Preprocessed source Sure thing. Here it is.
From https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-access-function-attribute : >When no size-index argument is specified, the pointer argument must be either null or point to a space that is suitably aligned and large for at least one object of the referenced type (this implies that a past-the-end pointer is not a valid argument). I think this is a bug in glibc and its (mis)use of the access and write only attribute without a size. It has: __attribute__ ((__access__ (__write_only__, 2))) but the documentation for this attribute does not correspond with the above.
POSIX definition of read: https://pubs.opengroup.org/onlinepubs/009604599/functions/read.html >Before any action described below is taken, and if nbyte is zero, the read() function may detect and return errors as described below. In the absence of errors, or if error detection is not performed, the read() function shall return zero and have no other results. So yes it does look like the use of write_only access is incorrect ...
extern ssize_t read (int __fd, void *__buf, size_t __nbytes) __wur __fortified_attr_access (__write_only__, 2, 3); ... /* For _FORTIFY_SOURCE == 3 we use __builtin_dynamic_object_size, which may use the access attribute to get object sizes from function definition arguments, so we can't use them on functions we fortify. Drop the object size hints for such functions. */ # if __USE_FORTIFY_LEVEL == 3 # define __fortified_attr_access(a, o, s) __attribute__ ((__access__ (a, o))) # else # define __fortified_attr_access(a, o, s) __attr_access ((a, o, s)) # endif Yes that is broken. Let me file the glibc issue.
Thanks for the quick analysis. It seems that the following glibc commit dropped size hints from access when FORTIFY_SOURCE=3: https://sourceware.org/git/?p=glibc.git;a=commit;h=e938c02748402c50f60ba0eb983273e7b52937d1 I'll report a bug against glibc and mention this conversation. Thanks.
Ah, OK, I'll let you file the bug, then. Thanks.
Moved to glibc issue: https://sourceware.org/bugzilla/show_bug.cgi?id=31383