From 5acc654e380797bbf402bc3a0a67f9a6ac4c2a83 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Thu, 18 Jun 2020 12:00:36 -0600 Subject: [PATCH] Avoid warning for memset writing over multiple members. Resolves: PR middle-end/95667 - unintended warning for memset writing across multiple members PR middle-end/92814 - missing -Wstringop-overflow writing into a dynamically allocated flexible array member gcc/ChangeLog: PR middle-end/95667 PR middle-end/92814 * builtins.c (compute_objsize): Remove call to compute_builtin_object_size and instead compute conservative sizes directly here. gcc/testsuite/ChangeLog: PR middle-end/95667 PR middle-end/92814 * gcc.dg/Wstringop-overflow-25.c: Remove xfails. * gcc.dg/Wstringop-overflow-39.c: New test. --- gcc/builtins.c | 48 +++----- gcc/testsuite/gcc.dg/Wstringop-overflow-25.c | 8 +- gcc/testsuite/gcc.dg/Wstringop-overflow-39.c | 118 +++++++++++++++++++ 3 files changed, 140 insertions(+), 34 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Wstringop-overflow-39.c diff --git a/gcc/builtins.c b/gcc/builtins.c index caab188e81c..4754602e0ec 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -4009,31 +4009,6 @@ static bool compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, const vr_values *rvals /* = NULL */) { - if (ostype == 0) - { - /* Use BOS only for raw memory functions like memcpy to get - the size of the largest enclosing object. */ - tree off = NULL_TREE; - unsigned HOST_WIDE_INT size; - if (compute_builtin_object_size (ptr, ostype, &size, &pref->ref, &off)) - { - if (off) - { - offset_int offset = wi::to_offset (off); - pref->offrng[0] += offset; - pref->offrng[1] += offset; - - /* compute_builtin_object_size() returns the remaining - size in PTR. Add the offset to it to get the full - size. */ - pref->sizrng[0] = pref->sizrng[1] = size + offset; - } - else - pref->sizrng[0] = pref->sizrng[1] = size; - return true; - } - } - const bool addr = TREE_CODE (ptr) == ADDR_EXPR; if (addr) ptr = TREE_OPERAND (ptr, 0); @@ -4058,18 +4033,28 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, if (code == COMPONENT_REF) { + tree field = TREE_OPERAND (ptr, 1); + if (ostype == 0) { /* For raw memory functions like memcpy bail if the size of the enclosing object cannot be determined. */ - access_ref tmpref; tree ref = TREE_OPERAND (ptr, 0); - if (!compute_objsize (ref, ostype, &tmpref, visited, rvals) - || !tmpref.ref) + if (!compute_objsize (ref, ostype, pref, visited, rvals) + || !pref->ref) return false; + + /* Otherwise, use the size of the enclosing object and add + the offset of the member to the offset computed so far. */ + tree offset = byte_position (field); + if (TREE_CODE (offset) != INTEGER_CST) + return false; + offset_int off = wi::to_offset (offset); + pref->offrng[0] += off; + pref->offrng[1] += off; + return true; } - tree field = TREE_OPERAND (ptr, 1); /* Bail if the reference is to the pointer itself (as opposed to what it points to). */ if (!addr && POINTER_TYPE_P (TREE_TYPE (field))) @@ -4147,8 +4132,11 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, orng[0] *= sz; orng[1] *= sz; - if (TREE_CODE (eltype) == ARRAY_TYPE) + if (ostype && TREE_CODE (eltype) == ARRAY_TYPE) { + /* Execpt for the permissive raw memory functions which + use the size of the whole object determined above, + use the size of the referenced array. */ pref->sizrng[0] = pref->offrng[0] + orng[0] + sz; pref->sizrng[1] = pref->offrng[1] + orng[1] + sz; } diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-25.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-25.c index 01807207acc..109a1dd9127 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overflow-25.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-25.c @@ -370,14 +370,14 @@ NOIPA void test_strcpy_malloc_flexarray (void) size_t r_2_3 = UR (2, 3); T (char, S (0), r_0_1); - T (char, S (1), r_0_1); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" { xfail *-*-* } } + T (char, S (1), r_0_1); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" } T (char, S (0), r_1_2); T (char, S (1), r_1_2); - T (char, S (2), r_1_2); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" { xfail *-*-* } } + T (char, S (2), r_1_2); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" } T (char, S (0), r_2_3); T (char, S (2), r_2_3); - T (char, S (3), r_2_3); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" { xfail *-*-* } } - T (char, S (9), r_2_3); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" { xfail *-*-* } } + T (char, S (3), r_2_3); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" } + T (char, S (9), r_2_3); // { dg-warning "\\\[-Wstringop-overflow" "pr92814" } } diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-39.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-39.c new file mode 100644 index 00000000000..f83646a3721 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-39.c @@ -0,0 +1,118 @@ +/* PR middle-end/95667 - unintended warning for memset writing across multiple + members + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +extern void sink (void*); + +struct S1 { char a[3], b[5]; }; + +void warn_strcpy_s1 (void) +{ + struct S1 *p = __builtin_malloc (sizeof *p); + char s[] = "1234567"; + __builtin_strcpy (p->a, s); // { dg-warning "\\\[-Wstringop-overflow" } + sink (p); +} + +void nowarn_memset_s1 (void) +{ + struct S1 *p = __builtin_malloc (sizeof *p); + __builtin_memset (p->a, 0, 8); // { dg-bogus "\\\[-Wstringop-overflow" } + sink (p); +} + +struct S2 { char a[2], b[2][2], c[3]; }; + +void nowarn_memset_s2 (void) +{ + struct S2 *p = __builtin_malloc (sizeof *p); + + __builtin_memset (p->a, 0, sizeof *p); + sink (p); + + __builtin_memset (p->b, 0, 7); + sink (p); + + __builtin_memset (&p->b[0], 0, 7); + sink (p); + + __builtin_memset (&p->b[1], 0, 5); + sink (p); + + __builtin_memset (&p->b[0][0], 0, 7); + sink (p); + + __builtin_memset (&p->b[0][1], 0, 6); + sink (p); + + __builtin_memset (&p->b[1][0], 0, 5); + sink (p); + + __builtin_memset (&p->b[1][1], 0, 4); + sink (p); +} + +void warn_memset_s2 (void) +{ + const unsigned n = sizeof (struct S2); + struct S2 *p = __builtin_malloc (n); + + /* These should trigger -Wstringop-overflow rather than -Warray-bounds + but the main purpose of the test is to verify the absence of warnings + above so the exact warning for these overflwing calls isn't important + here. */ + + __builtin_memset (p->a, 0, n + 1); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } + sink (p); + + __builtin_memset (p->b, 0, 8); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } + sink (p); + + __builtin_memset (&p->b[0], 0, 8); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } + sink (p); + + __builtin_memset (&p->b[0][0], 0, 8); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } + sink (p); + + __builtin_memset (&p->b[1], 0, 6); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } + sink (p); + + __builtin_memset (&p->b[0][1], 0, 7); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } + sink (p); +} + +void nowarn_vl_struct (unsigned n) +{ + if (n < 3 || 5 < n) + n = 3; + + struct V { char a[3], b[n], c[7]; } v; + + __builtin_memset (v.a, 0, 15); + sink (&v); + + __builtin_memset (v.b, 0, 12); + sink (&v); + + __builtin_memset (v.c, 0, 7); + sink (&v); + + __builtin_memset (v.a, 0, 16); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } + sink (&v); + + __builtin_memset (v.b, 0, 13); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } + sink (&v); + + /* The &V.C argument is represented as a variable offset from + the beginning of the allocated object and there's no good + way to figure out from its variable offset that it's base + is the C member: + s.1_12 = __builtin_alloca_with_align (prephitmp_24, 8); + _9 = s.1_12 + prephitmp_27; + __builtin_memset (_9, 0, 2); + */ + + __builtin_memset (v.c, 0, 8); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr?????" { xfail *-*-* } } + sink (&v); +} -- 2.30.2