This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCH for c++/69509 and c++/69516 (infinite recursion with invalid VLAs in constexpr functions)


This patch fixes some problems with VLAs and constexprs.  (The runtime aspect
of this matter is being tracked in PR69517.)  It does two things:

1) Changes build_vec_init slightly to prevent the compiler from getting into
   infinite recursion.  Currently, we emit the following sequence when we're
   initializing e.g. n = 1; int a[n] = { 1, 2 }; (cleanup_point junk omitted):

  int a[0:(sizetype) (SAVE_EXPR <(ssizetype) n + -1>)];
  (void) (int[0:(sizetype) (SAVE_EXPR <(ssizetype) n + -1>)] *)   int * D.2256;
  (void) (D.2256 = (int *) &a)
    int * D.2257;
  (void) (D.2257 = D.2256)
    long int D.2258;
  (void) (D.2258 = (long int) (SAVE_EXPR <(ssizetype) n + -1>)) // == 0
  (void) (*D.2257 = 1)
  (void)  ++D.2257 
  (void)  --D.2258	// == -1
  (void) (*D.2257 = 2)
  (void)  ++D.2257
  (void)  --D.2258	// == -2

  while (1) 
    {   
      if (D.2258 == -1)	// never triggers
	goto <D.2261>;
      (void) (*D.2257 = 0) 
      (void)  ++D.2257
      (void)  --D.2258;
    }   
  <D.2261>:;
  ...

  So we first write the elements from the initializer and then we default-initialize
  any remaining elements.  But the iterator == -1 check is never true for invalid
  code, which means the compiler will get stuck in the while loop forever, leading
  to crash -- it tries to cxx_eval_* the body of the loop over and over again.

  Fixed by changing == -1 into <= -1; this should only ever happen for invalid code,
  but we don't want to ICE in any case.

  This also "fixes" the problem in PR69517 -- the program could get into infinite
  recursion as well, because it was evaluating the sequence above at runtime. But
  since it's invoking UB, it doesn't matter much.

2) Moves the check for "array subscript out of bound" a tad above, because for
   invalid VLAs we can't rely on the bool "found" -- e.g. for the example above
   it will find all indexes in the initializer, so "found" is true, which means
   we wouldn't get to the out-of-bounds check below.

Hopefully I'm making sense.  

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2016-01-28  Marek Polacek  <polacek@redhat.com>

	PR c++/69509
	PR c++/69516
	* constexpr.c (cxx_eval_array_reference): Give the "array subscript
	out of bound" error earlier.
	* init.c (build_vec_init): Change NE_EXPR into GT_EXPR.  Update the
	commentary.

	* g++.dg/ext/constexpr-vla2.C: New test.
	* g++.dg/ext/constexpr-vla3.C: New test.
	* g++.dg/ubsan/vla-1.C: Remove dg-shouldfail.

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 57595a4..b076991 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -1833,6 +1833,19 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
       return t;
     }
 
+  tree nelts = array_type_nelts_top (TREE_TYPE (ary));
+  /* For VLAs, the number of elements won't be an integer constant.  */
+  nelts = cxx_eval_constant_expression (ctx, nelts, false, non_constant_p,
+					overflow_p);
+  VERIFY_CONSTANT (nelts);
+  if (!tree_int_cst_lt (index, nelts))
+    {
+      if (!ctx->quiet)
+	error ("array subscript out of bound");
+      *non_constant_p = true;
+      return t;
+    }
+
   bool found;
   if (TREE_CODE (ary) == CONSTRUCTOR)
     {
@@ -1846,37 +1859,23 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 
   if (!found)
     {
-      tree nelts = array_type_nelts_top (TREE_TYPE (ary));
-      /* For VLAs, the number of elements won't be an integer constant.  */
-      nelts = cxx_eval_constant_expression (ctx, nelts, false, non_constant_p,
-					    overflow_p);
-      VERIFY_CONSTANT (nelts);
-      if (tree_int_cst_lt (index, nelts))
+      if (TREE_CODE (ary) == CONSTRUCTOR
+	  && CONSTRUCTOR_NO_IMPLICIT_ZERO (ary))
 	{
-	  if (TREE_CODE (ary) == CONSTRUCTOR
-	      && CONSTRUCTOR_NO_IMPLICIT_ZERO (ary))
-	    {
-	      /* 'ary' is part of the aggregate initializer we're currently
-		 building; if there's no initializer for this element yet,
-		 that's an error. */
-	      if (!ctx->quiet)
-		error ("accessing uninitialized array element");
-	      *non_constant_p = true;
-	      return t;
-	    }
-
-	  /* If it's within the array bounds but doesn't have an explicit
-	     initializer, it's value-initialized.  */
-	  tree val = build_value_init (elem_type, tf_warning_or_error);
-	  return cxx_eval_constant_expression (ctx, val,
-					       lval,
-					       non_constant_p, overflow_p);
+	  /* 'ary' is part of the aggregate initializer we're currently
+	     building; if there's no initializer for this element yet,
+	     that's an error.  */
+	  if (!ctx->quiet)
+	    error ("accessing uninitialized array element");
+	  *non_constant_p = true;
+	  return t;
 	}
 
-      if (!ctx->quiet)
-	error ("array subscript out of bound");
-      *non_constant_p = true;
-      return t;
+      /* If it's within the array bounds but doesn't have an explicit
+	 initializer, it's value-initialized.  */
+      tree val = build_value_init (elem_type, tf_warning_or_error);
+      return cxx_eval_constant_expression (ctx, val, lval, non_constant_p,
+					   overflow_p);
     }
 
   if (TREE_CODE (ary) == CONSTRUCTOR)
diff --git gcc/cp/init.c gcc/cp/init.c
index 9f7b614..976ada8 100644
--- gcc/cp/init.c
+++ gcc/cp/init.c
@@ -4008,7 +4008,7 @@ build_vec_init (tree base, tree maxindex, tree init,
 		&& (num_initialized_elts
 		    == tree_to_shwi (maxindex) + 1))))
     {
-      /* If the ITERATOR is equal to -1, then we don't have to loop;
+      /* If the ITERATOR is lesser or equal to -1, then we don't have to loop;
 	 we've already initialized all the elements.  */
       tree for_stmt;
       tree elt_init;
@@ -4016,7 +4016,7 @@ build_vec_init (tree base, tree maxindex, tree init,
 
       for_stmt = begin_for_stmt (NULL_TREE, NULL_TREE);
       finish_for_init_stmt (for_stmt);
-      finish_for_cond (build2 (NE_EXPR, boolean_type_node, iterator,
+      finish_for_cond (build2 (GT_EXPR, boolean_type_node, iterator,
 			       build_int_cst (TREE_TYPE (iterator), -1)),
 		       for_stmt, false);
       elt_init = cp_build_unary_op (PREDECREMENT_EXPR, iterator, 0,
diff --git gcc/testsuite/g++.dg/ext/constexpr-vla2.C gcc/testsuite/g++.dg/ext/constexpr-vla2.C
index e69de29..6cb1f70 100644
--- gcc/testsuite/g++.dg/ext/constexpr-vla2.C
+++ gcc/testsuite/g++.dg/ext/constexpr-vla2.C
@@ -0,0 +1,21 @@
+// PR c++/69509
+// { dg-do compile { target c++14 } }
+
+constexpr int
+fn_bad (int n)
+{
+  __extension__ int a [n] = { 0 };
+  int z = a [0] + (n ? fn_bad (n - 1) : 0);
+  return z;
+}
+
+constexpr int
+fn_ok (int n)
+{
+  __extension__ int a [n] = { 0 };
+  int z = a [0] + (n > 1 ? fn_ok (n - 1) : 0);
+  return z;
+}
+
+constexpr int i1 = fn_ok (3);
+constexpr int i2 = fn_bad (3); // { dg-error "array subscript out of bound" }
diff --git gcc/testsuite/g++.dg/ext/constexpr-vla3.C gcc/testsuite/g++.dg/ext/constexpr-vla3.C
index e69de29..ba4eb50 100644
--- gcc/testsuite/g++.dg/ext/constexpr-vla3.C
+++ gcc/testsuite/g++.dg/ext/constexpr-vla3.C
@@ -0,0 +1,14 @@
+// PR c++/69516
+// { dg-do compile { target c++14 } }
+
+constexpr int
+foo (int n)
+{
+  __extension__ int a[n] = { 1, 2, 3, 4, 5, 6 };
+  int z = 0;
+  for (int i = 0; i <= n; ++i)
+    z += a[i];
+  return z;
+}
+
+constexpr int n = foo (3); // { dg-error "array subscript out of bound" }
diff --git gcc/testsuite/g++.dg/ubsan/vla-1.C gcc/testsuite/g++.dg/ubsan/vla-1.C
index e7f2494..311cdb1 100644
--- gcc/testsuite/g++.dg/ubsan/vla-1.C
+++ gcc/testsuite/g++.dg/ubsan/vla-1.C
@@ -1,6 +1,5 @@
 // { dg-do run }
 // { dg-options "-Wno-vla -fsanitize=undefined" }
-// { dg-shouldfail "ubsan" }
 // { dg-output "index 1 out of bounds" }
 
 void f(int i) {

	Marek


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]