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.

Reply via email to