https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108036
Bug ID: 108036
Summary: Spurious warning for zero-sized array parameters to a
function
Product: gcc
Version: 12.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c
Assignee: unassigned at gcc dot gnu.org
Reporter: colomar.6.4.3 at gmail dot com
Target Milestone: ---
It's interesting to pass pointers to one past the end of an array to a
function, acting as a sentinel value that serves as an alternative to the size
of the buffer. It helps chaining string copy functions, for example:
char *
ustr2stpe(char *dst, const char *restrict src, size_t n, char past_end[0])
{
bool trunc;
char *end;
ptrdiff_t len;
if (dst == past_end)
return past_end;
trunc = false;
len = strnlen(src, n);
if (len > past_end - dst - 1) {
len = past_end - dst - 1;
trunc = true;
}
end = mempcpy(dst, src, len);
*end = '\0';
return trunc ? past_end : end;
}
However, if you use array syntax for it, which clarifies where it points to,
the GCC complains, not at the function implementation, but at call site:
#define nitems(arr) (sizeof((arr)) / sizeof((arr)[0]))
int
main(void)
{
char pre[4] = "pre.";
char *post = ".post";
char *src = "some-long-body.post";
char dest[100];
char *p, *past_end;
past_end = dest + nitems(dest);
p = dest;
p = ustr2stpe(p, pre, nitems(pre), past_end);
p = ustr2stpe(p, src, strlen(src) - strlen(post), past_end);
p = ustr2stpe(p, "", 0, past_end);
if (p == past_end)
fprintf(stderr, "truncation\n");
puts(dest); // "pre.some-long-body"
}
$ cc -Wall -Wextra ustr2stpe.c
ustr2stpe.c: In function ‘main’:
ustr2stpe.c:43:13: warning: ‘ustr2stpe’ accessing 1 byte in a region of size 0
[-Wstringop-overflow=]
43 | p = ustr2stpe(p, pre, nitems(pre), past_end);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ustr2stpe.c:43:13: note: referencing argument 4 of type ‘char[0]’
ustr2stpe.c:10:1: note: in a call to function ‘ustr2stpe’
10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char
past_end[0])
| ^~~~~~~~~
ustr2stpe.c:44:13: warning: ‘ustr2stpe’ accessing 1 byte in a region of size 0
[-Wstringop-overflow=]
44 | p = ustr2stpe(p, src, strlen(src) - strlen(post), past_end);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ustr2stpe.c:44:13: note: referencing argument 4 of type ‘char[0]’
ustr2stpe.c:10:1: note: in a call to function ‘ustr2stpe’
10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char
past_end[0])
| ^~~~~~~~~
ustr2stpe.c:45:13: warning: ‘ustr2stpe’ accessing 1 byte in a region of size 0
[-Wstringop-overflow=]
45 | p = ustr2stpe(p, "", 0, past_end);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ustr2stpe.c:45:13: note: referencing argument 4 of type ‘char[0]’
ustr2stpe.c:10:1: note: in a call to function ‘ustr2stpe’
10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char
past_end[0])
| ^~~~~~~~~
ustr2stpe.c:43:13: warning: ‘ustr2stpe’ accessing 1 byte in a region of size 0
[-Wstringop-overflow=]
43 | p = ustr2stpe(p, pre, nitems(pre), past_end);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ustr2stpe.c:43:13: note: referencing argument 4 of type ‘char[0]’
ustr2stpe.c:10:1: note: in a call to function ‘ustr2stpe’
10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char
past_end[0])
| ^~~~~~~~~
ustr2stpe.c:44:13: warning: ‘ustr2stpe’ accessing 1 byte in a region of size 0
[-Wstringop-overflow=]
44 | p = ustr2stpe(p, src, strlen(src) - strlen(post), past_end);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ustr2stpe.c:44:13: note: referencing argument 4 of type ‘char[0]’
ustr2stpe.c:10:1: note: in a call to function ‘ustr2stpe’
10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char
past_end[0])
| ^~~~~~~~~
ustr2stpe.c:45:13: warning: ‘ustr2stpe’ accessing 1 byte in a region of size 0
[-Wstringop-overflow=]
45 | p = ustr2stpe(p, "", 0, past_end);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ustr2stpe.c:45:13: note: referencing argument 4 of type ‘char[0]’
ustr2stpe.c:10:1: note: in a call to function ‘ustr2stpe’
10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char
past_end[0])
| ^~~~~~~~~
The warnings are invalid. While it's true that I'm referencing a pointer of
size 0, it's false that I'm "accessing 1 byte" in that region. I guess this is
all about the bogus design of 'static' in ISO C, where you can have an array
parameter of size 0, which is very useful in cases like this one.
See the original report in the mailing list, where Richard Biener had some
guess of what might be the reason:
<https://gcc.gnu.org/pipermail/gcc/2022-December/240230.html>