From 31e924c52f430d81f030a2fa9f60b73a5a0d2126 Mon Sep 17 00:00:00 2001 From: Martin Sebor <msebor@redhat.com> Date: Fri, 17 Sep 2021 10:36:54 -0600 Subject: [PATCH] Better handle MIN/MAX_EXPR of unrelated objects [PR102200]. Resolves: PR middle-end/102200 - ICE on a min of a decl and pointer in a loop gcc/ChangeLog: PR middle-end/102200 * pointer-query.cc (access_ref::inform_access): Handle MIN/MAX_EXPR. (handle_min_max_size): Change argument. Store original SSA_NAME for operands to potentially distinct (sub)objects. (compute_objsize_r): Adjust call to the above. gcc/testsuite/ChangeLog: PR middle-end/102200 * gcc.dg/Wstringop-overflow-62.c: Adjust text of an expected note. * gcc.dg/Warray-bounds-89.c: New test. * gcc.dg/Wstringop-overflow-74.c: New test. * gcc.dg/Wstringop-overflow-75.c: New test. * gcc.dg/Wstringop-overflow-76.c: New test. --- gcc/pointer-query.cc | 62 ++++++-- gcc/testsuite/gcc.dg/Warray-bounds-89.c | 139 +++++++++++++++++ gcc/testsuite/gcc.dg/Wstringop-overflow-62.c | 2 +- gcc/testsuite/gcc.dg/Wstringop-overflow-74.c | 22 +++ gcc/testsuite/gcc.dg/Wstringop-overflow-75.c | 133 +++++++++++++++++ gcc/testsuite/gcc.dg/Wstringop-overflow-76.c | 148 +++++++++++++++++++ 6 files changed, 496 insertions(+), 10 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Warray-bounds-89.c create mode 100644 gcc/testsuite/gcc.dg/Wstringop-overflow-74.c create mode 100644 gcc/testsuite/gcc.dg/Wstringop-overflow-75.c create mode 100644 gcc/testsuite/gcc.dg/Wstringop-overflow-76.c diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc index 4ad28796e576..83b1f0fc8664 100644 --- a/gcc/pointer-query.cc +++ b/gcc/pointer-query.cc @@ -1087,6 +1087,34 @@ access_ref::inform_access (access_mode mode) const else if (gimple_nop_p (stmt)) /* Handle DECL_PARM below. */ ref = SSA_NAME_VAR (ref); + else if (is_gimple_assign (stmt) + && (gimple_assign_rhs_code (stmt) == MIN_EXPR + || gimple_assign_rhs_code (stmt) == MAX_EXPR)) + { + /* MIN or MAX_EXPR here implies a reference to a known object + and either an unknown or distinct one (the latter being + the result of an invalid relational expression). Determine + the identity of the former and point to it in the note. + TODO: Consider merging with PHI handling. */ + access_ref arg_ref[2]; + tree arg = gimple_assign_rhs1 (stmt); + compute_objsize (arg, /* ostype = */ 1 , &arg_ref[0]); + arg = gimple_assign_rhs2 (stmt); + compute_objsize (arg, /* ostype = */ 1 , &arg_ref[1]); + + /* Use the argument that references a known object with more + space remaining. */ + const bool idx + = (!arg_ref[0].ref || !arg_ref[0].base0 + || (arg_ref[0].base0 && arg_ref[1].base0 + && (arg_ref[0].size_remaining () + < arg_ref[1].size_remaining ()))); + + arg_ref[idx].offrng[0] = offrng[0]; + arg_ref[idx].offrng[1] = offrng[1]; + arg_ref[idx].inform_access (mode); + return; + } } if (DECL_P (ref)) @@ -1463,15 +1491,18 @@ pointer_query::dump (FILE *dump_file, bool contents /* = false */) } /* A helper of compute_objsize_r() to determine the size from an assignment - statement STMT with the RHS of either MIN_EXPR or MAX_EXPR. */ + statement STMT with the RHS of either MIN_EXPR or MAX_EXPR. On success + set PREF->REF to the operand with more or less space remaining, + respectively, if both refer to the same (sub)object, or to PTR if they + might not, and return true. Otherwise, if the identity of neither + operand can be determined, return false. */ static bool -handle_min_max_size (gimple *stmt, int ostype, access_ref *pref, +handle_min_max_size (tree ptr, int ostype, access_ref *pref, ssa_name_limit_t &snlim, pointer_query *qry) { - tree_code code = gimple_assign_rhs_code (stmt); - - tree ptr = gimple_assign_rhs1 (stmt); + const gimple *stmt = SSA_NAME_DEF_STMT (ptr); + const tree_code code = gimple_assign_rhs_code (stmt); /* In a valid MAX_/MIN_EXPR both operands must refer to the same array. Determine the size/offset of each and use the one with more or less @@ -1479,7 +1510,8 @@ handle_min_max_size (gimple *stmt, int ostype, access_ref *pref, determined from the other instead, adjusted up or down as appropriate for the expression. */ access_ref aref[2] = { *pref, *pref }; - if (!compute_objsize_r (ptr, ostype, &aref[0], snlim, qry)) + tree arg1 = gimple_assign_rhs1 (stmt); + if (!compute_objsize_r (arg1, ostype, &aref[0], snlim, qry)) { aref[0].base0 = false; aref[0].offrng[0] = aref[0].offrng[1] = 0; @@ -1487,8 +1519,8 @@ handle_min_max_size (gimple *stmt, int ostype, access_ref *pref, aref[0].set_max_size_range (); } - ptr = gimple_assign_rhs2 (stmt); - if (!compute_objsize_r (ptr, ostype, &aref[1], snlim, qry)) + tree arg2 = gimple_assign_rhs2 (stmt); + if (!compute_objsize_r (arg2, ostype, &aref[1], snlim, qry)) { aref[1].base0 = false; aref[1].offrng[0] = aref[1].offrng[1] = 0; @@ -1517,6 +1549,13 @@ handle_min_max_size (gimple *stmt, int ostype, access_ref *pref, *pref = aref[i1]; else *pref = aref[i0]; + + if (aref[i0].ref != aref[i1].ref) + /* If the operands don't refer to the same (sub)object set + PREF->REF to the SSA_NAME from which STMT was obtained + so that both can be identified in a diagnostic. */ + pref->ref = ptr; + return true; } @@ -1537,6 +1576,10 @@ handle_min_max_size (gimple *stmt, int ostype, access_ref *pref, pref->offrng[0] = aref[i0].offrng[0]; pref->offrng[1] = aref[i0].offrng[1]; } + + /* Replace PTR->REF with the SSA_NAME to indicate the expression + might not refer to the same (sub)object. */ + pref->ref = ptr; return true; } @@ -2009,8 +2052,9 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref, if (code == MAX_EXPR || code == MIN_EXPR) { - if (!handle_min_max_size (stmt, ostype, pref, snlim, qry)) + if (!handle_min_max_size (ptr, ostype, pref, snlim, qry)) return false; + qry->put_ref (ptr, *pref); return true; } diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-89.c b/gcc/testsuite/gcc.dg/Warray-bounds-89.c new file mode 100644 index 000000000000..2604f65e6d5e --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-bounds-89.c @@ -0,0 +1,139 @@ +/* Verify warnings and notes for MIN_EXPRs involving either pointers + to distinct objects or one to a known object and the other to + an unknown one. The relational expressions are strictly invalid + but that should be diagnosed by a separate warning. + { dg-do compile } + { dg-options "-O2 -Warray-bounds -Wno-stringop-overflow" } */ + +/* Verify the note points to the larger of the two objects and mentions + the offset into it (alhough the offset would ideally be a part of + the warning). */ +extern char a3[3]; +extern char a5[5]; // { dg-message "at offset 5 into object 'a5' of size 5" "note" } + +void min_a3_a5 (int i) +{ + char *p = a3 + i; + char *q = a5 + i; + + /* The relational expression below is invalid and should be diagnosed + by its own warning independently of -Warray-bounds. */ + char *d = p < q ? p : q; + + d[4] = 0; + + /* Verify the type in the warning corresponds to the larger of the two + objects. */ + d[5] = 0; // { dg-warning "subscript 5 is outside array bounds of 'char\\\[5]'" } +} + + +// Same as above but with the larger array as the first MIN_EXPR operand. +extern char b4[4]; +extern char b6[6]; // { dg-message "at offset 6 into object 'b6' of size 6" "note" } + +void min_b6_b4 (int i) +{ + char *p = b6 + i; + char *q = b4 + i; + char *d = p < q ? p : q; + + d[5] = 0; + d[6] = 0; // { dg-warning "subscript 6 is outside array bounds of 'char\\\[6]'" } +} + + +/* Same as above but with the first MIN_EXPR operand pointing to an unknown + object. */ +extern char c7[7]; // { dg-message "at offset 7 into object 'c7' of size 7" "note" } + +void min_p_c7 (char *p, int i) +{ + char *q = c7 + i; + char *d = p < q ? p : q; + + d[6] = 0; + d[7] = 0; // { dg-warning "subscript 7 is outside array bounds of 'char\\\[7]'" } +} + + +/* Same as above but with the second MIN_EXPR operand pointing to an unknown + object. */ +extern char d8[8]; // { dg-message "at offset 8 into object 'd8' of size 8" "note" } + +void min_d8_p (char *q, int i) +{ + char *p = d8 + i; + char *d = p < q ? p : q; + + d[7] = 0; + d[8] = 0; // { dg-warning "subscript 8 is outside array bounds of 'char\\\[8]'" } +} + + +/* The following are diagnosed by -Wstringop-overflow but, as a result + of PR 101374, not by -Warray-bounds. */ + +struct A3_5 +{ + char a3[3]; + char a5[5]; // { dg-message "at offset 5 into object 'a5' of size 5" "note" { xfail *-*-* } } +}; + +void min_A3_A5 (int i, struct A3_5 *pa3_5) +{ + char *p = pa3_5->a3 + i; + char *q = pa3_5->a5 + i; + + char *d = p < q ? p : q; + + // d[4] = 0; + d[5] = 0; // { dg-warning "subscript 5 is outside array bounds of 'char\\\[5]'" "pr??????" { xfail *-*-* } } +} + + +struct B4_B6 +{ + char b4[4]; + char b6[6]; // { dg-message "at offset 6 into object 'b6' of size 6" "note" { xfail *-*-* } } +}; + +void min_B6_B4 (int i, struct B4_B6 *pb4_b6) +{ + char *p = pb4_b6->b6 + i; + char *q = pb4_b6->b4 + i; + char *d = p < q ? p : q; + + d[5] = 0; + d[6] = 0; // { dg-warning "subscript 6 is outside array bounds of 'char\\\[6]'" "pr??????" { xfail *-*-* } } +} + + +struct C7 +{ + char c7[7]; // { dg-message "at offset 7 into object 'c7' of size 7" "note" { xfail *-*-* } } +}; + +void min_p_C7 (char *p, int i, struct C7 *pc7) +{ + char *q = pc7->c7 + i; + char *d = p < q ? p : q; + + d[6] = 0; + d[7] = 0; // { dg-warning "subscript 7 is outside array bounds of 'char\\\[7]'" "pr??????" { xfail *-*-* } } +} + + +struct D8 +{ + char d8[8]; // { dg-message "at offset 8 into object 'd8' of size 8" "note" { xfail *-*-* } } +}; + +void min_D8_p (char *q, int i, struct D8 *pd8) +{ + char *p = pd8->d8 + i; + char *d = p < q ? p : q; + + d[7] = 0; + d[8] = 0; // { dg-warning "subscript 8 is outside array bounds of 'char\\\[8]'" "pr??????" { xfail *-*-* } } +} diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c index 318d9bd1f94a..4b6d1ab83c5d 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c @@ -117,7 +117,7 @@ void test_min (void) { /* Exercise a pointer pointing to a known object plus constant offset with one pointing to an unknown object. */ - char a6[6]; // { dg-message ": destination object 'a6'" "note" } + char a6[6]; // { dg-message "(at offset 1 into )?destination object 'a6'" "note" } char *p1 = ptr; char *p2 = a6 + 1; char *q = MIN (p1, p2); diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-74.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-74.c new file mode 100644 index 000000000000..bacec964d36b --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-74.c @@ -0,0 +1,22 @@ +/* PR middle-end/102200 - ICE on a min of a decl and pointer in a loop + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +typedef __SIZE_TYPE__ size_t; + +extern char a[], n; + +void f (void) +{ + char *p = a; + size_t end = 1; + + while (n) + { + if (p < (char*)end) + *p = ';'; + + if (p > (char*)&end) + p = (char*)&end; + } +} diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-75.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-75.c new file mode 100644 index 000000000000..0b242e8562d3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-75.c @@ -0,0 +1,133 @@ +/* Verify warnings and notes for MIN_EXPRs involving either pointers + to distinct objects or one to a known object and the other to + an unknown one. The relational expressions are strictly invalid + but that should be diagnosed by a separate warning. + { dg-do compile } + { dg-options "-O2 -Wno-array-bounds" } */ + +/* Verify the note points to the larger of the two objects and mentions + the offset into it (although the offset might be better included in + the warning). */ +extern char a3[3]; +extern char a5[5]; // { dg-message "at offset 5 into destination object 'a5' of size 5" "note" } + +void min_a3_a5 (int i) +{ + char *p = a3 + i; + char *q = a5 + i; + + /* The relational expression below is invalid and should be diagnosed + by its own warning independently of -Wstringop-overflow. */ + char *d = p < q ? p : q; + + d[4] = 0; + d[5] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +// Same as above but with the larger array as the first MIN_EXPR operand. +extern char b4[4]; +extern char b6[6]; // { dg-message "at offset 6 into destination object 'b6' of size 6" "note" } + +void min_b6_b4 (int i) +{ + char *p = b6 + i; + char *q = b4 + i; + char *d = p < q ? p : q; + + d[5] = 0; + d[6] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +/* Same as above but with the first MIN_EXPR operand pointing to an unknown + object. */ +extern char c7[7]; // { dg-message "at offset 7 into destination object 'c7' of size 7" "note" } + +void min_p_c7 (char *p, int i) +{ + char *q = c7 + i; + char *d = p < q ? p : q; + + d[6] = 0; + d[7] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +/* Same as above but with the second MIN_EXPR operand pointing to an unknown + object. */ +extern char d8[8]; // { dg-message "at offset 8 into destination object 'd8' of size 8" "note" } + +void min_d8_p (char *q, int i) +{ + char *p = d8 + i; + char *d = p < q ? p : q; + + d[7] = 0; + d[8] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +struct A3_5 +{ + char a3[3]; + char a5[5]; // { dg-message "at offset 5 into destination object 'a5' of size 5" "note" } +}; + +void min_A3_A5 (int i, struct A3_5 *pa3_5) +{ + char *p = pa3_5->a3 + i; + char *q = pa3_5->a5 + i; + + char *d = p < q ? p : q; + + // d[4] = 0; + d[5] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +struct B4_B6 +{ + char b4[4]; + char b6[6]; // { dg-message "at offset 6 into destination object 'b6' of size 6" "note" } +}; + +void min_B6_B4 (int i, struct B4_B6 *pb4_b6) +{ + char *p = pb4_b6->b6 + i; + char *q = pb4_b6->b4 + i; + char *d = p < q ? p : q; + + d[5] = 0; + d[6] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +struct C7 +{ + char c7[7]; // { dg-message "at offset 7 into destination object 'c7' of size 7" "note" } +}; + +void min_p_C7 (char *p, int i, struct C7 *pc7) +{ + char *q = pc7->c7 + i; + char *d = p < q ? p : q; + + d[6] = 0; + d[7] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +struct D8 +{ + char d8[8]; // { dg-message "at offset 8 into destination object 'd8' of size 8" "note" } +}; + +void min_D8_p (char *q, int i, struct D8 *pd8) +{ + char *p = pd8->d8 + i; + char *d = p < q ? p : q; + + d[7] = 0; + d[8] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-76.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-76.c new file mode 100644 index 000000000000..18191a1aa5ea --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-76.c @@ -0,0 +1,148 @@ +/* Verify warnings and notes for MAX_EXPRs involving either pointers + to distinct objects or one to a known object and the other to + an unknown one. Unlike for the same object, for unrelated objects + the expected warnings and notes are the same as for MIN_EXPR: when + the order of the objects in the address space cannot be determined + the larger of them is assumed to be used. (This is different for + distinct struct members where the order is given.) + The relational expressions are strictly invalid but that should be + diagnosed by a separate warning. + { dg-do compile } + { dg-options "-O2 -Wno-array-bounds" } */ + +#define MAX(p, q) ((p) > (q) ? (p) : (q)) + +/* Verify that even for MAX_EXPR and like for MIN_EXPR, the note points + to the larger of the two objects and mentions the offset into it + (although the offset might be better included in the warning). */ +extern char a3[3]; +extern char a5[5]; // { dg-message "at offset 5 into destination object 'a5' of size 5" "note" } + +void max_a3_a5 (int i) +{ + char *p = a3 + i; + char *q = a5 + i; + + /* The relational expression below is invalid and should be diagnosed + by its own warning independently of -Wstringop-overflow. */ + char *d = MAX (p, q); + + d[2] = 0; + d[3] = 0; + d[4] = 0; + d[5] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +// Same as above but with the larger array as the first MAX_EXPR operand. +extern char b4[4]; +extern char b6[6]; // { dg-message "at offset 6 into destination object 'b6' of size 6" "note" } + +void max_b6_b4 (int i) +{ + char *p = b6 + i; + char *q = b4 + i; + char *d = MAX (p, q); + + d[3] = 0; + d[4] = 0; + d[5] = 0; + d[6] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +/* Same as above but with the first MAX_EXPR operand pointing to an unknown + object. */ +extern char c7[7]; // { dg-message "at offset 7 into destination object 'c7' of size 7" "note" } + +void max_p_c7 (char *p, int i) +{ + char *q = c7 + i; + char *d = MAX (p, q); + + d[6] = 0; + d[7] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +/* Same as above but with the second MIN_EXPR operand pointing to an unknown + object. */ +extern char d8[8]; // { dg-message "at offset 8 into destination object 'd8' of size 8" "note" } + +void max_d8_p (char *q, int i) +{ + char *p = d8 + i; + char *d = MAX (p, q); + + d[7] = 0; + d[8] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +struct A3_5 +{ + char a3[3]; // { dg-message "at offset 3 into destination object 'a3' of size 3" "pr??????" { xfail *-*-* } } + char a5[5]; // { dg-message "at offset 5 into destination object 'a5' of size 5" "note" } +}; + +void max_A3_A5 (int i, struct A3_5 *pa3_5) +{ + char *p = pa3_5->a3 + i; + char *q = pa3_5->a5 + i; + + char *d = MAX (p, q); + + d[2] = 0; + d[3] = 0; // { dg-warning "writing 1 byte into a region of size 0" "pr??????" { xfail *-*-* } } + d[4] = 0; + d[5] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +struct B4_B6 +{ + char b4[4]; + char b6[6]; // { dg-message "at offset 6 into destination object 'b6' of size 6" "note" } +}; + +void max_B6_B4 (int i, struct B4_B6 *pb4_b6) +{ + char *p = pb4_b6->b6 + i; + char *q = pb4_b6->b4 + i; + char *d = MAX (p, q); + + d[3] = 0; + d[4] = 0; + d[5] = 0; + d[6] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +struct C7 +{ + char c7[7]; // { dg-message "at offset 7 into destination object 'c7' of size 7" "note" } +}; + +void max_p_C7 (char *p, int i, struct C7 *pc7) +{ + char *q = pc7->c7 + i; + char *d = MAX (p, q); + + d[6] = 0; + d[7] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} + + +struct D8 +{ + char d8[8]; // { dg-message "at offset 8 into destination object 'd8' of size 8" "note" } +}; + +void max_D8_p (char *q, int i, struct D8 *pd8) +{ + char *p = pd8->d8 + i; + char *d = MAX (p, q); + + d[7] = 0; + d[8] = 0; // { dg-warning "writing 1 byte into a region of size 0" } +} -- GitLab