https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128593
Bug ID: 128593
Summary: strlen_pass::handle_builtin_stxncpy_strncat bypasses
maybe_diag_stxncpy_trunc when strncpy bound is derived
from strlen(src), emitting false-positive
-Wstringop-truncation on __attribute__((nonstring))
destinations
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: 220245569 at seu dot edu.cn
CC: 220255623 at seu dot edu.cn, jianhao.xu at seu dot edu.cn
Target Milestone: ---
Target: *-*-*
Reproducer
----------
$ cat trigger.c
typedef __SIZE_TYPE__ size_t;
struct Rec {
char name[16] __attribute__((nonstring));
};
char g_dst[16] __attribute__((nonstring));
void copy_member(struct Rec *r, const char *s) {
size_t n = __builtin_strlen(s);
__builtin_strncpy(r->name, s, n);
}
void copy_global(const char *s) {
size_t n = __builtin_strlen(s);
__builtin_strncpy(g_dst, s, n);
}
Compile command / flags:
podman exec defuzz-toolchain-daemon /opt/gcc16/bin/x86_64-linux-gnu-gcc -O2
-Wall -Wstringop-truncation -S -o /dev/null /tmp/t5.c
Observed output (GCC 16.2.0):
/tmp/t5.c: In function 'copy_member':
/tmp/t5.c:9:5: warning: '__builtin_strncpy' output truncated before terminating
nul copying as many bytes from a string as its length [-Wstringop-truncation]
9 | __builtin_strncpy(r->name, s, n);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/t5.c:8:16: note: length computed here
8 | size_t n = __builtin_strlen(s);
| ^~~~~~~~~~~~~~~~~~~
/tmp/t5.c: In function 'copy_global':
/tmp/t5.c:13:5: warning: '__builtin_strncpy' output truncated before
terminating nul copying as many bytes from a string as its length
[-Wstringop-truncation]
13 | __builtin_strncpy(g_dst, s, n);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expected behavior:
strncpy(dst, s, strlen(s)) into a destination declared with
__attribute__((nonstring)) must not emit -Wstringop-truncation, because
nonstring character arrays are explicitly intended to store non-NUL-terminated
character sequences.
Root cause
----------
Source location: gcc/tree-ssa-strlen.cc:3282-3328
(strlen_pass::handle_builtin_stxncpy_strncat) vs 2965-2973
(maybe_diag_stxncpy_trunc)
Distinct from DF-G-054 (which is get_attr_nonstring_decl in tree.cc:15519
failing on MEM_REF with a non-zero offset s.a + 4). In
gcc/tree-ssa-strlen.cc:2965-2973, maybe_diag_stxncpy_trunc calls
get_attr_nonstring_decl (dst, & dstref) and suppresses -Wstringop-truncation
when the destination is annotated with __attribute__((nonstring)). However, in
strlen_pass::handle_builtin_stxncpy_strncat (gcc/tree-ssa-strlen.cc:3282-3328),
when the bound argument len of strncpy/stpncpy is recorded in strlen_to_stridx
(i.e. len = strlen(src)), execution enters an earlier 'if (unsigned *len_stridx
= strlen_to_stridx.get (len))' branch that emits OPT_Wstringop_truncation
('output truncated before terminating nul copying as many bytes from a string
as its length') directly at lines 3312 and 3325 without ever calling
maybe_diag_stxncpy_trunc or checking get_attr_nonstring_decl (dst).
Consequently, even a plain top-level char g_dst[16] __attribute__((nonstring))
or zero-offset struct member r->name triggers a false-positive
-Wstringop-truncation warning.
Impact:
False-positive -Wstringop-truncation diagnostic (breaking -Werror builds) on
idiomatic bounded copies into __attribute__((nonstring)) buffers when the bound
is strlen(src).
Suggested Fix
-------------
In gcc/tree-ssa-strlen.cc:3282-3328
(strlen_pass::handle_builtin_stxncpy_strncat), check '!get_attr_nonstring_decl
(dst)' before issuing the OPT_Wstringop_truncation warning on the
strlen_to_stridx branch.