https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128131
Bug ID: 128131
Summary: __builtin_object_size of strndup result is too small,
valid code aborts with _FORTIFY_SOURCE
Product: gcc
Version: 13.1.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: lzy20000419 at outlook dot com
Target Milestone: ---
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char b[4] = "abcd"; /* no terminating NUL */
int main (void)
{
char *p = strndup (b, 4); /* allocates 5 bytes: "abcd\0" */
if (!p) return 2;
printf ("bos0=%zu bdos0=%zu\n", __builtin_object_size (p, 0),
__builtin_dynamic_object_size (p, 0));
strcpy (p, "abcd"); /* 5 bytes into a 5-byte block */
puts (p);
free (p);
return 0;
}
```
$ gcc -O2 -D_FORTIFY_SOURCE=2 t.c && ./a.out
*** buffer overflow detected ***: terminated
Aborted
There is also a -Wstringop-overflow warning, "writing 5 bytes into a region of
size 4". Expected: prints abcd. strndup does not need a NUL within the first n
bytes of s, so the result here is 5 bytes, but __builtin_object_size (p, 0) is
4.
A second case: with a run-time bound of SIZE_MAX, n + 1 wraps to 0, so
```c
__attribute__((noipa)) char *dup_limited (const char *s, size_t limit)
{ char *p = strndup (s, limit); if (p) strcpy (p, s); return p; }
... dup_limited ("hello", SIZE_MAX);
```
aborts with -O2 -D_FORTIFY_SOURCE=3, and there is no warning.
It fails with 13.1 through 16.2 and trunk. 12.2 returns -1 (no strndup
support), so it doesn't abort there.
https://godbolt.org/z/o6sjfaenh (left gcc 12.2, right trunk)
Disclaimer: This test case was generated by a fuzzer, and the report was
written with LLM assistance.