[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