diff options
author | Martin Sebor <msebor@redhat.com> | 2021-03-08 13:37:21 -0700 |
---|---|---|
committer | Martin Sebor <msebor@redhat.com> | 2021-03-08 13:37:21 -0700 |
commit | f3daa6c0fd8d79ae45eac2dd0f274da1aa71c958 (patch) | |
tree | b3ab03035bc62990c57b38a1dccf441011dd1209 /gcc | |
parent | 7f5ff78ff3f971c11ec67f422b2fd34281db9123 (diff) | |
download | gcc-f3daa6c0fd8d79ae45eac2dd0f274da1aa71c958.zip gcc-f3daa6c0fd8d79ae45eac2dd0f274da1aa71c958.tar.gz gcc-f3daa6c0fd8d79ae45eac2dd0f274da1aa71c958.tar.bz2 |
PR middle-end/98266 - bogus array subscript is partly outside array bounds on virtual inheritance
gcc/ChangeLog:
PR middle-end/98266
* gimple-array-bounds.cc (inbounds_vbase_memaccess_p): New function.
(array_bounds_checker::check_array_bounds): Call it.
gcc/testsuite/ChangeLog:
PR middle-end/98266
* g++.dg/warn/Warray-bounds-15.C: New test.
* g++.dg/warn/Warray-bounds-18.C: New test.
* g++.dg/warn/Warray-bounds-19.C: New test.
* g++.dg/warn/Warray-bounds-20.C: New test.
* g++.dg/warn/Warray-bounds-21.C: New test.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/gimple-array-bounds.cc | 52 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Warray-bounds-15.C | 33 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Warray-bounds-18.C | 167 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Warray-bounds-19.C | 110 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Warray-bounds-20.C | 68 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/warn/Warray-bounds-21.C | 111 |
6 files changed, 540 insertions, 1 deletions
diff --git a/gcc/gimple-array-bounds.cc b/gcc/gimple-array-bounds.cc index d7fd2c7..54f3205 100644 --- a/gcc/gimple-array-bounds.cc +++ b/gcc/gimple-array-bounds.cc @@ -890,6 +890,50 @@ array_bounds_checker::check_addr_expr (location_t location, tree t) } } +/* Return true if T is a reference to a member of a base class that's within + the bounds of the enclosing complete object. The function "hacks" around + problems discussed in pr98266 and pr97595. */ + +static bool +inbounds_vbase_memaccess_p (tree t) +{ + if (TREE_CODE (t) != COMPONENT_REF) + return false; + + tree mref = TREE_OPERAND (t, 0); + if (TREE_CODE (mref) != MEM_REF) + return false; + + /* Consider the access if its type is a derived class. */ + tree mreftype = TREE_TYPE (mref); + if (!RECORD_OR_UNION_TYPE_P (mreftype) + || !TYPE_BINFO (mreftype)) + return false; + + /* Compute the size of the referenced object (it could be dynamically + allocated). */ + access_ref aref; // unused + tree refop = TREE_OPERAND (mref, 0); + tree refsize = compute_objsize (refop, 1, &aref); + if (!refsize || TREE_CODE (refsize) != INTEGER_CST) + return false; + + /* Compute the byte offset of the member within its enclosing class. */ + tree fld = TREE_OPERAND (t, 1); + tree fldpos = byte_position (fld); + if (TREE_CODE (fldpos) != INTEGER_CST) + return false; + + /* Compute the byte offset of the member with the outermost complete + object by adding its offset computed above to the MEM_REF offset. */ + tree refoff = TREE_OPERAND (mref, 1); + tree fldoff = int_const_binop (PLUS_EXPR, fldpos, refoff); + + /* Return true if the member offset is less than the size of the complete + object. */ + return tree_int_cst_lt (fldoff, refsize); +} + /* Callback for walk_tree to check a tree for out of bounds array accesses. The array_bounds_checker class is passed in DATA. */ @@ -919,8 +963,14 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree, else if (TREE_CODE (t) == ADDR_EXPR) { checker->check_addr_expr (location, t); - *walk_subtree = FALSE; + *walk_subtree = false; } + else if (inbounds_vbase_memaccess_p (t)) + /* Hack: Skip MEM_REF checks in accesses to a member of a base class + at an offset that's within the bounds of the enclosing object. + See pr98266 and pr97595. */ + *walk_subtree = false; + /* Propagate the no-warning bit to the outer expression. */ if (warned) TREE_NO_WARNING (t) = true; diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-15.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-15.C new file mode 100644 index 0000000..455f3a0 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-15.C @@ -0,0 +1,33 @@ +/* PR middle-end/98266 - bogus array subscript is partly outside array + bounds on virtual inheritance + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +#if __cplusplus < 201103L +// This matters for the test case. +# define noexcept throw () +#endif + +struct A +{ + virtual ~A () noexcept; + const char *s; +}; + +struct B: virtual A { }; +struct C: virtual A { }; // { dg-bogus "\\\[-Warray-bounds" } + +struct D: virtual B, virtual C +{ + D (const char*); +}; + +void sink (void*); +void sink (D); + + +// Verify that accesses to the table aren't diagnosed. +void test_vtbl () +{ + sink (D ("")); +} diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-18.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-18.C new file mode 100644 index 0000000..53d93cf --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-18.C @@ -0,0 +1,167 @@ +/* PR middle-end/98266 - bogus array subscript is partly outside array + bounds on virtual inheritance + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +struct A +{ + int ai, aj, aa[2]; + + virtual ~A (); +}; + +struct B: virtual A { }; +struct C: virtual A { }; + +void sink (void*); + +struct C1: virtual A +{ + int c2i, c2j, c2a[2]; + + C1 (); + ~C1 () + { // { dg-bogus "\\\[-Warray-bounds" } + c2i = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + c2j = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + c2a[0] = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + c2a[1] = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + c2a[2] = __LINE__; // { dg-warning "\\\[-Warray-bounds" } + } +}; + +struct D1: virtual B, virtual C1 +{ + D1 (); +}; + +void sink (void*); + +/* Verify that only out of bounds accesses to members of an ordinary base + class are diagnosed. Use direct array accesses. */ +void test_vmem_base_ctor_arryaccess () +{ + D1 d2; + sink (&d2); +} + + +struct C2: virtual A +{ + int c3a[2]; + + C2 (); + ~C2 () + { // { dg-bogus "\\\[-Warray-bounds" } + int *p = c3a; + *p++ = __LINE__; + *p++ = __LINE__; + *p++ = __LINE__; // { dg-warning "\\\[-Warray-bounds" } + } +}; + +struct D2: virtual B, virtual C2 +{ + D2 (); +}; + +/* Verify that only out of bounds accesses to members of an ordinary base + class are diagnosed. Use pointer accesses. */ +void test_vmem_base_dtor_ptraccess () +{ + D2 d3; + sink (&d3); +} + + +struct C3: virtual A // { dg-bogus "\\\[-Warray-bounds" } +{ + int i, j, a[2]; + + C3 (); +}; + +struct D3: virtual B, virtual C3 +{ + D3 () + { // { dg-bogus "\\\[-Warray-bounds" } + i = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + j = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + a[0] = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + a[1] = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + a[2] = __LINE__; // { dg-warning "\\\[-Warray-bounds" } + } +}; + +/* Verify that only out of bounds accesses to members of an ordinary base + class made in the ctor of a derived class are diagnosed. Use direct + array accesses. */ +void test_vmem_derived_ctor_arryaccess () +{ + D3 d4; + sink (&d4); +} + + +struct D4: virtual B, virtual C3 +{ + D4 () + { // { dg-bogus "\\\[-Warray-bounds" } + int *p = a; + *p++ = __LINE__; + *p++ = __LINE__; + *p++ = __LINE__; // { dg-warning "\\\[-Warray-bounds" } + } +}; + +/* Verify that only out of bounds accesses to members of an ordinary base + class made in the ctor of a derived class are diagnosed. Use pointer + accesses. */ +void test_vmem_derived_ctor_ptraccess () +{ + D4 d5; + sink (&d5); +} + + +struct D5: virtual B, virtual C3 // { dg-bogus "\\\[-Warray-bounds" } +{ + ~D5 () + { + i = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + j = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + a[0] = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + a[1] = __LINE__; // { dg-bogus "\\\[-Warray-bounds" } + a[2] = __LINE__; // { dg-warning "\\\[-Warray-bounds" } + } +}; + +/* Verify that only out of bounds accesses to members of an ordinary base + class made in the dtor of a derived class are diagnosed. Use pointer + accesses. */ +void test_vmem_derived_dtor_arryaccess () +{ + D5 d6; + sink (&d6); +} + + +struct D6: virtual B, virtual C3 // { dg-bogus "\\\[-Warray-bounds" } +{ + ~D6 () + { + int *p = a; + *p++ = __LINE__; + *p++ = __LINE__; + *p++ = __LINE__; // { dg-warning "\\\[-Warray-bounds" } + } +}; + +/* Verify that only out of bounds accesses to members of an ordinary base + class made in the dtor of a derived class are diagnosed. Use pointer + accesses. */ +void test_vmem_derived_dtor_ptraccess () +{ + D6 d7; + sink (&d7); +} diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-19.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-19.C new file mode 100644 index 0000000..e5fabe9 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-19.C @@ -0,0 +1,110 @@ +/* PR middle-end/98266 - bogus array subscript is partly outside array + bounds on virtual inheritance + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +void* operator new (__SIZE_TYPE__, void *p) { return p; } +void* operator new[] (__SIZE_TYPE__, void *p) { return p; } + + +struct A +{ + virtual ~A (); + int ai; +}; + +struct B: virtual A { }; + + +// Exercise access to base members by ctor of the most derived class. + +struct C1: virtual A // { dg-bogus "\\\[-Warray-bounds" } +{ + int c1i; + C1 (); +}; + +struct D1: virtual B, virtual C1 +{ + D1 () { ai = 0; c1i = 1; }; +}; + +void sink (void*); + +void nowarn_derived_ctor_access_decl () +{ + D1 d1; + sink (&d1); +} + +void nowarn_derived_ctor_access_new () +{ + D1 *p = new D1; + sink (p); +} + +void nowarn_derived_ctor_access_placement_new () +{ + char a[sizeof (D1)]; + D1 *p = new (a) D1; + sink (p); +} + +void nowarn_derived_ctor_access_new_array () +{ + D1 *p = new D1[2]; + sink (p); +} + +void nowarn_derived_ctor_access_placement_new_array () +{ + char a[sizeof (D1) * 2]; + D1 *p = new (a) D1[2]; + sink (p); +} + + +// Exercise access to base members by ctor of the second most derived class. + +struct C2: virtual A +{ + int c2i; + ~C2 () { ai = 0; c2i = 1; } // { dg-bogus "\\\[-Warray-bounds" +}; + +struct D2: virtual B, virtual C2 +{ + D2 (); +}; + +void nowarn_base_dtor_access_decl () +{ + D2 d2; + sink (&d2); +} + +void nowarn_base_dtor_access_new () +{ + D2 *p = new D2; + sink (p); +} + +void nowarn_base_dtor_access_placement_new () +{ + char a[sizeof (D2)]; + D2 *p = new (a) D2; + sink (p); +} + +void nowarn_base_dtor_access_new_array () +{ + D2 *p = new D2[2]; + sink (p); +} + +void nowarn_base_dtor_access_placement_new_array () +{ + char a[sizeof (D2) * 2]; + D2 *p = new (a) D2[2]; + sink (p); +} diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-20.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-20.C new file mode 100644 index 0000000..e142ea1 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-20.C @@ -0,0 +1,68 @@ +/* PR middle-end/98266 - bogus array subscript is partly outside array + bounds on virtual inheritance + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +void* operator new (__SIZE_TYPE__, void *p) { return p; } +void* operator new[] (__SIZE_TYPE__, void *p) { return p; } + + +struct A +{ + int ai; + virtual ~A (); +}; + +struct B: virtual A { }; + +struct C: virtual A +{ + int ci; + C (); +}; + +struct D1: virtual B, virtual C +{ + /* The warning would ideally point to the assignment but instead points + to the opening brace. */ + D1 () + { // { dg-warning "\\\[-Warray-bounds" "brace" } + ci = 0; // { dg-warning "\\\[-Warray-bounds" "assign" { xfail *-*-* } } + } +}; + +void sink (void*); + +void warn_derived_ctor_access_new_decl () +{ + char a[sizeof (D1)]; // { dg-message "referencing 'a'" "note" } + char *p = a; + ++p; + D1 *q = new (p) D1; + sink (q); +} + +void warn_derived_ctor_access_new_alloc () +{ + char *p = (char*)operator new (sizeof (D1)); // { dg-message "referencing an object of size \\d+ allocated by 'void\\\* operator new\\\(" "note" } + ++p; + D1 *q = new (p) D1; + sink (q); +} + +void warn_derived_ctor_access_new_array_decl () +{ + char b[sizeof (D1) * 2]; // { dg-message "referencing 'b'" "note" } + char *p = b; + ++p; + D1 *q = new (p) D1[2]; + sink (q); +} + +void warn_derived_ctor_access_new_array_alloc () +{ + char *p = new char[sizeof (D1) * 2]; // { dg-message "referencing an object of size \\d+ allocated by 'void\\\* operator new \\\[]\\\(" "note" } + ++p; + D1 *q = new (p) D1[2]; + sink (q); +} diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-21.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-21.C new file mode 100644 index 0000000..57bb98b --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-21.C @@ -0,0 +1,111 @@ +/* PR middle-end/98266 - bogus array subscript is partly outside array + bounds on virtual inheritance + Same as Warray-bounds-19.C with nonvirtual inheritance. + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +void* operator new (__SIZE_TYPE__, void *p) { return p; } +void* operator new[] (__SIZE_TYPE__, void *p) { return p; } + + +struct A +{ + virtual ~A (); + int ai; +}; + +struct B: A { }; + + +// Exercise access to base members by ctor of the most derived class. + +struct C1: A +{ + int c1i; + C1 (); +}; + +struct D1: B, C1 +{ + D1 () { B::ai = 0; C1::ai = 1; c1i = 2; }; +}; + +void sink (void*); + +void nowarn_derived_ctor_access_decl () +{ + D1 d1; + sink (&d1); +} + +void nowarn_derived_ctor_access_new () +{ + D1 *p = new D1; + sink (p); +} + +void nowarn_derived_ctor_access_placement_new () +{ + char a[sizeof (D1)]; + D1 *p = new (a) D1; + sink (p); +} + +void nowarn_derived_ctor_access_new_array () +{ + D1 *p = new D1[2]; + sink (p); +} + +void nowarn_derived_ctor_access_placement_new_array () +{ + char a[sizeof (D1) * 2]; + D1 *p = new (a) D1[2]; + sink (p); +} + + +// Exercise access to base members by ctor of the second most derived class. + +struct C2: A +{ + int c2i; + ~C2 () { ai = 0; c2i = 1; } +}; + +struct D2: B, C2 +{ + D2 (); +}; + +void nowarn_base_dtor_access_decl () +{ + D2 d2; + sink (&d2); +} + +void nowarn_base_dtor_access_new () +{ + D2 *p = new D2; + sink (p); +} + +void nowarn_base_dtor_access_placement_new () +{ + char a[sizeof (D2)]; + D2 *p = new (a) D2; + sink (p); +} + +void nowarn_base_dtor_access_new_array () +{ + D2 *p = new D2[2]; + sink (p); +} + +void nowarn_base_dtor_access_placement_new_array () +{ + char a[sizeof (D2) * 2]; + D2 *p = new (a) D2[2]; + sink (p); +} |