aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2021-09-17 10:36:54 -0600
committerMartin Sebor <msebor@redhat.com>2021-09-17 10:36:54 -0600
commit31e924c52f430d81f030a2fa9f60b73a5a0d2126 (patch)
treebe076ca2c9b45615a2ae1f6a9faf17165e4b23f8 /gcc
parent47e5052b5e15b3018eb56d353cb6330cd2d614a9 (diff)
downloadgcc-31e924c52f430d81f030a2fa9f60b73a5a0d2126.zip
gcc-31e924c52f430d81f030a2fa9f60b73a5a0d2126.tar.gz
gcc-31e924c52f430d81f030a2fa9f60b73a5a0d2126.tar.bz2
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.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/pointer-query.cc62
-rw-r--r--gcc/testsuite/gcc.dg/Warray-bounds-89.c139
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-62.c2
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-74.c22
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-75.c133
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-76.c148
6 files changed, 496 insertions, 10 deletions
diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 4ad2879..83b1f0f 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 0000000..2604f65
--- /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 318d9bd..4b6d1ab 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 0000000..bacec96
--- /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 0000000..0b242e8
--- /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 0000000..18191a1
--- /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" }
+}