From f36a8168f04dfbde9d4c64421c1058975b28ff9a Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Fri, 11 Sep 2020 09:40:45 -0600 Subject: [PATCH] Move/correct offset adjustment (PR middle-end/96903). Resolves: PR middle-end/96903 - bogus warning on memcpy at negative offset from array end gcc/ChangeLog: PR middle-end/96903 * builtins.c (compute_objsize): Remove incorrect offset adjustment. (compute_objsize): Adjust offset range here instead. gcc/testsuite/ChangeLog: PR middle-end/96903 * gcc.dg/Wstringop-overflow-42.c:: Add comment. * gcc.dg/Wstringop-overflow-43.c: New test. --- gcc/builtins.c | 20 ++- gcc/testsuite/gcc.dg/Wstringop-overflow-42.c | 4 + gcc/testsuite/gcc.dg/Wstringop-overflow-43.c | 178 +++++++++++++++++++ 3 files changed, 194 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Wstringop-overflow-43.c diff --git a/gcc/builtins.c b/gcc/builtins.c index 97f1a184dc6..8b9a4a4d948 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -4372,12 +4372,6 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, orng[0] = wi::to_offset (TYPE_MIN_VALUE (ptrdiff_type_node)); orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node)); } - else if (wi::lts_p (orng[1], orng[0])) - /* The upper bound is less than the lower bound when the integer - operand is the result of signed integer conversion to sizetype, - as in P + OFF + CST where OFF > 0. - Correct just the upper bound. */ - orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node)); pref->offrng[0] += orng[0]; pref->offrng[1] += orng[1]; @@ -4403,7 +4397,8 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, return false; } -/* Convenience wrapper around the above. */ +/* A "public" wrapper around the above. Clients should use this overload + instead. */ static tree compute_objsize (tree ptr, int ostype, access_ref *pref, @@ -4420,6 +4415,15 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, if (!success) return NULL_TREE; + if (pref->offrng[1] < pref->offrng[0]) + { + if (pref->offrng[1] < 0 + && pref->sizrng[1] <= pref->offrng[0]) + return size_zero_node; + + return wide_int_to_tree (sizetype, pref->sizrng[1]); + } + if (pref->offrng[0] < 0) { if (pref->offrng[1] < 0) @@ -4428,7 +4432,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, pref->offrng[0] = 0; } - if (pref->sizrng[1] < pref->offrng[0]) + if (pref->sizrng[1] <= pref->offrng[0]) return size_zero_node; return wide_int_to_tree (sizetype, pref->sizrng[1] - pref->offrng[0]); diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-42.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-42.c index 21a675ab7c7..4bb22f2ecd3 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overflow-42.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-42.c @@ -36,7 +36,11 @@ void cpy_sl_1_max (long i, const char *s) void cpy_ul_1_max (unsigned long i, const char *s) { if (i < 1) i = 1; + d = strcpy (a + i, s); // { dg-warning "writing 1 or more bytes into a region of size 0" } + + /* Because of integer wraparound the offset's range is [1, 0] so + the overflow isn't diagnosed (yet). */ d = strcpy (a + i + 1, s); // { dg-warning "writing 1 or more bytes into a region of size 0" "" { xfail *-*-* } } } diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-43.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-43.c new file mode 100644 index 00000000000..3ac5a88e4b0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-43.c @@ -0,0 +1,178 @@ +/* PR 96903 - bogus warning on memcpy at negative offset from array end + { dg-do compile } + { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */ + +#include "range.h" + +#define INT_MAX __INT_MAX__ +#define INT_MIN -(INT_MAX - 1) +#define UINT_MAX (2U * INT_MAX + 1) + +typedef __SIZE_TYPE__ size_t; + +void* memset (void *, int, size_t); + +void sink (void*, ...); + +extern char a11[11]; +struct S { char a11[11], b; }; +extern struct S sa11; + +#define T2(dst, off1, off2, n) do { \ + char *_p0 = dst; \ + char *_p1 = _p0 + (off1); \ + char *_p2 = _p1 + (off2); \ + memset (_p2, 0, n); \ + sink (dst, _p0, _p1, _p2); \ + } while (0); + +#define T1(dst, off, n) T2 (dst, off, 0, n) + + +void nowarn_memset_array_cst (void) +{ + char *p = &a11[11]; + + T1 (p, -11, 11); + T1 (p, -10, 10); + T1 (p, -9, 9); + T1 (p, -8, 8); + T1 (p, -3, 3); + T1 (p, -2, 2); + T1 (p, -1, 1); + T1 (p, 0, 0); + + T2 (p, -6, -5, 11); + T2 (p, -6, -4, 10); + T2 (p, -6, -3, 9); + T2 (p, -6, -2, 8); + T2 (p, -6, -1, 7); + T2 (p, -5, -6, 11); + T2 (p, -5, -5, 10); +} + +void nowarn_memset_array_rng_int (void) +{ + char *p = &a11[11]; + + int i11 = SR (11, INT_MAX); + int i10 = SR (10, INT_MAX); + int i9 = SR ( 9, INT_MAX); + int i3 = SR ( 3, INT_MAX); + int i2 = SR ( 2, INT_MAX); + int i1 = SR ( 1, INT_MAX); + int i0 = SR ( 0, INT_MAX); + + int m11 = SR (INT_MIN, -11); + int m10 = SR (INT_MIN, -10); + int m9 = SR (INT_MIN, -9); + int m3 = SR (INT_MIN, -3); + int m2 = SR (INT_MIN, -2); + int m1 = SR (INT_MIN, -1); + int m0 = SR (INT_MIN, -0); + + T1 (p, m11, i11); + T1 (p, m10, i10); + T1 (p, m9, i9); + T1 (p, m3, i3); + T1 (p, m2, i2); + T1 (p, m1, i1); + T1 (p, m0, i0); + + T1 (p, m11, i11); + T1 (p, m10, i10); + T1 (p, m9, i9); + T1 (p, m3, i3); + T1 (p, m2, i2); + T1 (p, m1, i1); + T1 (p, m0, i0); +} + + +void nowarn_memset_array_rng (void) +{ + char *p = &a11[11]; + + T2 (p, SR (-11, -10), SR ( -2, -1), UR (11, 12)); + T2 (p, SR (-10, -9), SR ( -1, 0), UR (11, 13)); + T2 (p, SR ( -9, -8), SR ( -2, -1), UR (11, 14)); + T2 (p, SR ( -8, -7), SR ( -3, -2), UR (11, 15)); + T2 (p, SR ( -7, -6), SR ( -4, -3), UR (11, 16)); + T2 (p, SR ( -6, -5), SR ( -5, -4), UR (11, 17)); + T2 (p, SR ( -5, -4), SR ( -6, -5), UR (11, 18)); + T2 (p, SR ( -4, -3), SR ( -7, -6), UR (11, 19)); + T2 (p, SR ( -3, -2), SR ( -8, -7), UR (11, INT_MAX)); + T2 (p, SR ( -2, -1), SR ( -9, -8), UR (11, UINT_MAX)); + T2 (p, SR ( -1, 0), SR (-10, -9), UR (11, DIFF_MAX)); + T2 (p, SR ( 0, 1), SR (-11, -10), UR (11, SIZE_MAX)); + + T2 (p, SR (DIFF_MIN, -10), SR (DIFF_MIN, -1), UR (10, 12)); + + T2 (p, SR (-11, -10), SR ( -3, -1), UR (10, 12)) + T2 (p, SR (-11, -10), SR ( -3, -1), UR (10, 12)) +} + + +void warn_memset_array_rng (void) +{ + char *p = &a11[11]; + size_t n11_12 = UR (11, 12); + size_t n10_12 = UR (10, 12); + + T2 (p, SR (-11, -10), SR ( -3, -2), n11_12); // { dg-warning "writing between 11 and 12 bytes into a region of size 0" } + T2 (p, SR (-11, -10), SR ( -3, -2), n10_12); // { dg-warning "writing between 10 and 12 bytes into a region of size 0" } +} + + +void nowarn_memset_anti_range (void) +{ + size_t n11 = UR (11, SIZE_MAX); + + char *p = &a11[11]; + + T1 (p, (int)SAR (INT_MIN, -12), n11); + T1 (p, (int)SAR ( -13, -13), n11); + T1 (p, (int)SAR ( -13, -12), n11); + T1 (p, (int)SAR ( -10, 1), n11); + T1 (p, (int)SAR ( -10, 11), n11); + T1 (p, (int)SAR ( -10, INT_MAX), n11); + T1 (p, (int)SAR ( -1, -1), n11); + T1 (p, (int)SAR ( -1, 0), n11); + T1 (p, (int)SAR ( -1, 11), n11); + T1 (p, (int)SAR ( -1, INT_MAX), n11); + + T1 (p, SAR (DIFF_MIN, -12), n11); + T1 (p, SAR ( -13, -13), n11); + T1 (p, SAR ( -13, -12), n11); + T1 (p, SAR ( -10, 1), n11); // { dg-bogus "-Wstringop-overflow" } + T1 (p, SAR ( -10, 11), n11); // { dg-bogus "-Wstringop-overflow" } + T1 (p, SAR ( -10, DIFF_MAX), n11); + T1 (p, SAR ( -1, -1), n11); // { dg-bogus "-Wstringop-overflow" } + T1 (p, SAR ( -1, 0), n11); // { dg-bogus "-Wstringop-overflow" } + T1 (p, SAR ( -1, 11), n11); // { dg-bogus "-Wstringop-overflow" } + T1 (p, SAR ( -1, DIFF_MAX), n11); +} + +void warn_memset_reversed_range (void) +{ + size_t n11 = UR (11, SIZE_MAX); + + char *p = &a11[11]; + + /* The below is represented as a true anti-range as opposed to a range + with reversed bounds and the former aren't handled. */ + T1 (p, SAR (INT_MIN, -11), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail *-*-* } } + + /* The following are represented as ordinary ranges with reversed bounds + and those are handled. */ + T1 (p, SAR (INT_MIN, 11), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" } + /* In ILP32 the offset in the following has no range info associated + with it. */ + T1 (p, SAR (INT_MIN, 1), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail ilp32 } } + T1 (p, SAR (INT_MIN, 0), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" } + /* Also represented as a true anti-range. */ + T1 (p, SAR ( -12, -11), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail *-*-* } } + T1 (p, SAR ( -12, -1), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" } + T1 (p, SAR ( -11, 0), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" } + T1 (p, SAR ( -11, 11), n11); // { dg-warning "writing 11 or more bytes into a region of size 0" } +} -- 2.30.2