The buffer overflow detection for multi-char stores uses the size of a source array even when what's actually being accessed (read and stored) is a pointer to the array. That leads to incorrect warnings in some cases.
The attached patch corrects the function that computes the size of the access to set it to that of a pointer instead if the source is an address expression. Tested on x86_64-linux. Martin
PR middle-end/93829 - bogus -Wstringop-overflow on memcpy of a struct with a pointer member from another with a long string gcc/testsuite/ChangeLog: PR middle-end/93829 * gcc.dg/Wstringop-overflow-32.c: New test. gcc/ChangeLog: PR middle-end/93829 * tree-ssa-strlen.c (count_nonzero_bytes): Set the size to that of a pointer in the outermost ADDR_EXPRs. diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index 9a88a85b07c..1cdb581a51f 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -4587,12 +4587,15 @@ int ssa_name_limit_t::next_ssa_name (tree ssa_name) /* Determines the minimum and maximum number of leading non-zero bytes in the representation of EXP and set LENRANGE[0] and LENRANGE[1] - to each. Sets LENRANGE[2] to the total number of bytes in - the representation. Sets *NULTREM if the representation contains - a zero byte, and sets *ALLNUL if all the bytes are zero. + to each. + Sets LENRANGE[2] to the total size of the access (which may be less + than LENRANGE[1] when what's being referenced by EXP is a pointer + rather than an array). + Sets *NULTREM if the representation contains a zero byte, and sets + *ALLNUL if all the bytes are zero. OFFSET and NBYTES are the offset into the representation and - the size of the access to it determined from a MEM_REF or zero - for other expressions. + the size of the access to it determined from an ADDR_EXPR (i.e., + a pointer) or MEM_REF or zero for other expressions. Uses RVALS to determine range information. Avoids recursing deeper than the limits in SNLIM allow. Returns true on success and false otherwise. */ @@ -4692,7 +4695,13 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset, } if (TREE_CODE (exp) == ADDR_EXPR) - exp = TREE_OPERAND (exp, 0); + { + /* If the size of the access hasn't been determined yet it's that + of a pointer. */ + if (!nbytes) + nbytes = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (exp))); + exp = TREE_OPERAND (exp, 0); + } if (TREE_CODE (exp) == SSA_NAME) { @@ -4788,9 +4797,10 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset, return false; if (!nbytes) - /* If NBYTES hasn't been determined earlier from MEM_REF, - set it here. It includes all internal nuls, including - the terminating one if the string has one. */ + /* If NBYTES hasn't been determined earlier, either from ADDR_EXPR + (i.e., it's the size of a pointer), or from MEM_REF (as the size + of the access), set it here to the size of the string, including + all internal and trailing nuls if the string has any. */ nbytes = nchars - offset; prep = TREE_STRING_POINTER (exp) + offset; diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-32.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-32.c new file mode 100644 index 00000000000..e5939567a4d --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-32.c @@ -0,0 +1,51 @@ +/* PR middle-end/93829 - bogus -Wstringop-overflow on memcpy of a struct + with a pointer member from another with a long string + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +extern void* memcpy (void*, const void*, __SIZE_TYPE__); + +#define S40 "0123456789012345678901234567890123456789" + +const char s40[] = S40; + +struct S +{ + const void *p, *q, *r; +} s, sa[2]; + + +void test_lit_decl (void) +{ + struct S t = { 0, S40, 0 }; + + memcpy (&s, &t, sizeof t); // { dg-bogus "-Wstringop-overflow" } +} + +void test_str_decl (void) +{ + struct S t = { 0, s40, 0 }; + + memcpy (&s, &t, sizeof t); // { dg-bogus "-Wstringop-overflow" } +} + + +void test_lit_ssa (int i) +{ + if (i < 1) + i = 1; + struct S *p = &sa[i]; + struct S t = { 0, S40, 0 }; + + memcpy (p, &t, sizeof t); // { dg-bogus "-Wstringop-overflow" } +} + +void test_str_ssa (int i) +{ + if (i < 1) + i = 1; + struct S *p = &sa[i]; + struct S t = { 0, s40, 0 }; + + memcpy (p, &t, sizeof t); // { dg-bogus "-Wstringop-overflow" } +}