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.
This commit is contained in:
Martin Sebor 2021-03-08 13:37:21 -07:00
parent 7f5ff78ff3
commit f3daa6c0fd
6 changed files with 540 additions and 1 deletions

View File

@ -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;

View File

@ -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 (""));
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}