[RFA] PR c++/38699
Dodji Seketeli
dodji@redhat.com
Thu Oct 29 17:19:00 GMT 2009
Hello,
Consider this short code snipet that crashes G++:
struct A
{
const char* p;
};
void
foo ()
{
__builtin_offsetof (struct A, p[0]); /* <-- crashes here. */
}
As Jakub pointed out in a comment of the PR, that code is invalid because if we
have:
static struct A t;
then &(t.p[0]) should evaluate to a constant address for
offsetof (struct A, p[0]) to be valid. And this is not the case.
Incidentaly, __builtin_offsetof (struct A, p[2]) is not valid either and
crashes GCC as well, not just G++.
If struct A is defined as
struct A
{
const char [10];
};
We compile __builtin_offsetof (struct A, p[0]) fine but we do not warn for
for __builtin_offsetof (struct A, p[12]), even with -Warray-bounds.
I think fold_offsetof_1 in gcc/c-common.c could perform some
sanity checks while folding the offsetof expression.
The patch below follows that approach to reject the invalid code refered to by
the PR and issues a warning for the out of bounds array access.
Tested against trunk and 4.4.
Comments ?
Dodji
commit 4f30738b6294e6091e8b3d4675df132a8bf0cd51
Author: Dodji Seketeli <dodji@redhat.com>
Date: Thu Oct 29 10:50:57 2009 +0100
Fix PR c++/38699
gcc/ChangeLog:
PR c++/38699
* c-common.c (fold_offsetof_1): Issue errors when the member designator of
the offsetoff expression is not legitimate.
gcc/testsuite/ChangeLog:
* g++.dg/other/offsetof6.C: New test.
* g++.dg/other/offsetof7.C: Likewise.
* gcc.dg/torture/builtin-offsetof.c: Likewise.
diff --git a/gcc/c-common.c b/gcc/c-common.c
index 8a6d15b..b40c9a8 100644
--- a/gcc/c-common.c
+++ b/gcc/c-common.c
@@ -8341,6 +8341,41 @@ fold_offsetof_1 (tree expr, tree stop_ref)
case NOP_EXPR:
case INDIRECT_REF:
+ if (TREE_CODE (expr) == INDIRECT_REF)
+ {
+ tree r = TREE_OPERAND (expr, 0);
+
+ /* look through a potential NON_LVALUE_EXPR.
+ We won't try to use this expression in an
+ lvalue context anyway. */
+ if (TREE_CODE (r) == NON_LVALUE_EXPR)
+ r = TREE_OPERAND (r, 0);
+
+ if ((TREE_CODE (r) == COMPONENT_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (r, 1))) == POINTER_TYPE)
+ ||
+ TREE_CODE (r) == POINTER_PLUS_EXPR)
+ {
+ /* We are using offsetof to take the address of the result of a
+ pointer dereferencing expression. That is not legitimate.
+
+ In other words we are trying something like:
+ struct A
+ {
+ char *p;
+ };
+ void f ()
+ {
+ __builtin_offsetof(struct A, p[1]);
+ }
+ But the C spec says that if t is of type A, then
+ &(t.p[1])" should evaluate to a constant address.
+ And &(t.p[1]) does not evaluate to a constant address here.
+ */
+ error ("cannot apply %<offsetof%> to a non constant address");
+ return error_mark_node;
+ }
+ }
base = fold_offsetof_1 (TREE_OPERAND (expr, 0), stop_ref);
gcc_assert (base == error_mark_node || base == size_zero_node);
return base;
@@ -8376,6 +8411,16 @@ fold_offsetof_1 (tree expr, tree stop_ref)
}
t = convert (sizetype, t);
off = size_binop (MULT_EXPR, TYPE_SIZE_UNIT (TREE_TYPE (expr)), t);
+
+ /* Check if the offset goes beyond the upper bound of the array. */
+ {
+ tree nelts = array_type_nelts (TREE_TYPE (TREE_OPERAND (expr, 0)));
+ HOST_WIDE_INT index = int_cst_value (t);
+ if (index > int_cst_value (nelts))
+ warning (OPT_Warray_bounds,
+ "index %ld denotes an offset greater than size of %qT",
+ index, TREE_TYPE (TREE_OPERAND (expr, 0)));
+ }
break;
case COMPOUND_EXPR:
diff --git a/gcc/testsuite/g++.dg/other/offsetof6.C b/gcc/testsuite/g++.dg/other/offsetof6.C
new file mode 100644
index 0000000..6d04e34
--- /dev/null
+++ b/gcc/testsuite/g++.dg/other/offsetof6.C
@@ -0,0 +1,29 @@
+// Contributed by Dodji Seketeli <dodji@redhat.com>
+// Origin PR c++/38699
+// { dg-options "-Warray-bounds" }
+// { dg-do compile }
+
+struct A
+{
+ const char *p;
+};
+
+struct B
+{
+ char p[10];
+ A a;
+};
+
+void
+f0 ()
+{
+ __builtin_offsetof(A, p); // OK
+ __builtin_offsetof(A, p[0]); // { dg-error "non constant address" }
+ __builtin_offsetof(B, p[0]); // OK
+ __builtin_offsetof(B, p[9]); // OK
+ __builtin_offsetof(B, p[10]); // { dg-warning "greater than size" }
+ __builtin_offsetof(B, a.p); // OK
+ __builtin_offsetof(B, a.p[0]); // { dg-error "non constant address" }
+}
+
+
diff --git a/gcc/testsuite/g++.dg/other/offsetof7.C b/gcc/testsuite/g++.dg/other/offsetof7.C
new file mode 100644
index 0000000..b77d1b9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/other/offsetof7.C
@@ -0,0 +1,26 @@
+// Contributed by Dodji Seketeli <dodji@redhat.com>
+// Origin PR c++/38699
+// { dg-do compile }
+
+template<class T>
+struct A
+{
+ const T *p;
+};
+
+struct B
+{
+ A<int> a;
+};
+
+template class A<char>;
+
+void
+f0 ()
+{
+ __builtin_offsetof(A<char>, p); // OK
+ __builtin_offsetof(A<char>, p[1]); // { dg-error "non constant address" }
+ __builtin_offsetof(B, a.p); // OK
+ __builtin_offsetof(B, a.p[1]); // { dg-error "non constant address" }
+}
+
diff --git a/gcc/testsuite/gcc.dg/torture/builtin-offsetof.c b/gcc/testsuite/gcc.dg/torture/builtin-offsetof.c
new file mode 100644
index 0000000..db31fb3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/builtin-offsetof.c
@@ -0,0 +1,28 @@
+// Contributed by Dodji Seketeli <dodji@redhat.com>
+// Origin PR c++/38699
+// { dg-option "-Warray-bounds" }
+// { dg-do compile }
+
+struct A
+{
+ const char *p;
+};
+
+struct B
+{
+ char p[10];
+ struct A a;
+};
+
+void
+f0 ()
+{
+ __builtin_offsetof(struct A, p); // OK
+ __builtin_offsetof(struct A, p[0]); // { dg-error "non constant address" }
+ __builtin_offsetof(struct B, p[0]); // OK
+ __builtin_offsetof(struct B, p[9]); // OK
+ __builtin_offsetof(struct B, p[10]); // { dg-warning "greater than size" }
+ __builtin_offsetof(struct B, a.p); // OK
+ __builtin_offsetof(struct B, a.p[0]); // { dg-warning "non constant address" }
+}
+
More information about the Gcc-patches
mailing list