diff mbox

69517 - [5/6 regression] SEGV on a VLA with excess initializer elements

Message ID 570ADE43.9080107@gmail.com
State New
Headers show

Commit Message

Martin Sebor April 10, 2016, 11:14 p.m. UTC
Attached is an updated patch with some the changes you suggested.
I also moved the new functions from decl.c to init.c because they
logically seem to belong there.

The suggested changes resulted in introducing the checking code
into split_nonconstant_init() in cp/typeck2.c in addition to
build_vec_init().  I would expect it to be preferable to check
in as few places as possible so I attach an alternate version
of the patch without this proliferation of call (the tests and
the documentation changes are the same so I excluded those).

Let me know which of the two approaches you prefer and/or if
you have any other requests or suggestions.

Martin

On 04/07/2016 06:05 PM, Martin Sebor wrote:
> I've spent a ton of time trying to implement the suggested
> changes (far too much in large part because of my own mistakes)
> but I don't think they will work.  I'll try to clean up what
> I have and post it for review.  I wanted to respond to this
> how in case you have some suggestions or concerns with the
> direction I'm taking in the meantime.
>
>>> But if even a few MB seems too strict, I would find having even
>>> an exceedingly liberal limit (say 1GB) much preferable to none
>>> at all as it makes it possible to exercise boundary conditions
>>> such as the size overflow problem you noted below.
>>
>> That sounds reasonable, as long as users with unusual needs can adjust
>> it with a flag, but even so I'm nervous about doing this in stage 4.  It
>> certainly isn't a regression.
>
> I'm not comfortable adding a new option at this stage.  I'm also
> not sure that an option to impose a static limit is the best
> solution.  It seems that if we go to the trouble of making the limit
> customizable it should be possible to change it without recompiling
> everything (e.g., on ELF, we could check for a weak function and
> call it to get the most up-to-date limit).
>
> Let me restore the 4.9.3 behavior by setting the VLA size limit to
> SIZE_MAX / 2 (that fixes the other regression that I just raised
> in c++/70588 for the record).
>
>>> I don't think modifying build_vec_init() alone would be sufficient.
>>> For example, the function isn't called for a VLA with a constant
>>> bound like this one:
>>>
>>>   int A [2][N] = { 1, 2, 3, 4 };
>>
>> That seems like a bug, due to array_of_runtime_bound_p returning false
>> for that array.
>
> It seems that a complete fix would involve (among other things)
> replacing calls to array_of_runtime_bound_p with
> variably_modified_type_p or similar since the N3639 arrays are
> just a subset of those accepted by G++.  Unfortunately, that has
> other repercussions (e.g., c++70555).
>
> I replaced the call to array_of_runtime_bound_p in build_vec_init
> with one to variably_modified_type_p to get around the above.
> That  works, but it's only good for checking for excess
> initializers in build_vec_init.  It's too late to check for
> overflow in the VLA bounds because by that time the code to
> allocate the stack has already been emitted.
>
>>>> Also, I think we should check for invalid bounds in
>>>> compute_array_index_type, next to the UBsan code.  Checking bounds only
>>>> from cp_finish_decl means that we don't check uses of VLA types other
>>>> than variable declarations.
>
> I don't see how to make this work.  compute_array_index_type
> doesn't have access to the CONSTRUCTOR for the initializer of
> the VLA the initializer hasn't been parsed yet).  Without it
> it's not possible to detect VLA size overflow in cases such
> as in:
>
>      T a [][N] = { { ... }, { ... } };
>
> where the number of top-level elements determines whether or
> not the size of the whole VLA would overflow or exceed the
> maximum.
>
> Given this, I believe the check does need to be implemented
> somewhere in cp_finish_decl or one of the functions it calls
> (such as check_initializer) and emitted before build_vec_init
> is called or the initializer code it creates is emitted.
>
>>>
>>> You mean VLA typedefs?  That's true, though I have consciously
>>> avoided dealing with those.  They're outlawed in N3639 and so
>>> I've been focusing just on variables.  But since GCC accepts
>>> VLA typedefs too I was thinking I would bring them up at some
>>> point in the future to decide what to do about them.
>>
>> And cast to pointer to VLAs.  But for non-variable cases we don't care
>> about available stack, so we wouldn't want your allocation limit to
>> apply.
>
> I don't want to implement it now, but I think the same limit
> should apply in all cases, otherwise code remains susceptible
> to unsigned integer wrapping.  For example:
>
>    extern size_t N;
>    typedef int A [N];
>    int *a = (int*)malloc (sizeof (A));   // possible wraparound
>    a [N - 1] = 0;                        // out-of-bounds write
>
> It seems that the typedef will need to be accepted (in case it's
> unused) but the runtime sizeof would need to do the checking and
> potentially throw.  I haven't thought through the ramifications
> yet.
>
>>
>>> As for where to add the bounds checking code, I also at first
>>> thought of checking the bounds parallel to the UBSan code in
>>> compute_array_index_type() and even tried that approach. The
>>> problem with it is that it only considers one array dimension
>>> at a time, without the knowledge of the others.  As a result,
>>> as noted in sanitizer/70051, it doesn't correctly detect
>>> overflows in the bounds of multidimensional VLAs.
>>
>> It doesn't, but I don't see why it couldn't.  It should be fine to check
>> each dimension for overflow separately; if an inner dimension doesn't
>> overflow, we can go on and consider the outer dimension.
>
> As I explained above, I don't see how to make this work.
>
>>
>> Incidentally, I was wondering if it would make sense to use the
>> overflowing calculation for both TYPE_SIZE and the sanity check when
>> we're doing both.
>
> I'm not sure what you mean here.  Can you elaborate?
>
>>
>>>>> +          /* Avoid instrumenting constexpr functions.  Those must
>>>>> +         be checked statically, and the (non-constexpr) dynamic
>>>>> +         instrumentation would cause them to be rejected.  */
>>
>>>> Hmm, this sounds wrong; constexpr functions can also be called with
>>>> non-constant arguments, and the instrumentation should be folded away
>>>> when evaluating a call with constant arguments.
>>
>>> You're right that constexpr functions should be checked as
>>> well.  Unfortunately, at present, due to c++/70507 the check
>>> (or rather the call to __builtin_mul_overflow) isn't folded
>>> away and we end up with error: call to internal function.
>>
>> Ah, sure.  It should be pretty simple to teach the constexpr code how to
>> handle that built-in.
>
> I'd be glad to do this work but I don't believe I can get it done
> in time for 6.0.
>
> Martin

Comments

Jason Merrill April 12, 2016, 3:43 p.m. UTC | #1
On 04/10/2016 07:14 PM, Martin Sebor wrote:
> +	  if (TREE_CODE (type) == ARRAY_TYPE
> +	      && variably_modified_type_p (type, NULL_TREE)
> +	      && !processing_template_decl)
> +	    {
> +	      /* Statically check for overflow in VLA bounds and build
> +		 an expression that checks at runtime whether the VLA
> +		 is erroneous due to invalid (runtime) bounds.
> +		 Another expression to check for excess initializers
> +		 is built in build_vec_init.  */

Why do this both in check_initializer and then again in cp_finish_decl 
right after the call to check_initializer?

> +      /* Also check to see if the final array size is zero (the size
> +	 is unsigned so the earlier overflow check detects negative
> +	 values as well.  */
> +      tree zerocheck = fold_build2 (EQ_EXPR, boolean_type_node,
> +				    vlasize, size_zero_node);

I'm not sure whether we want this, given that GCC allows zero-length 
arrays in general.  As I recall, with the C++14 stuff whether we checked 
for zero was dependent on flag_iso, though I wasn't particularly happy 
with that.  If you want to check this unconditionally that's fine.

Jason
Martin Sebor April 12, 2016, 5:37 p.m. UTC | #2
On 04/12/2016 09:43 AM, Jason Merrill wrote:
> On 04/10/2016 07:14 PM, Martin Sebor wrote:
>> +      if (TREE_CODE (type) == ARRAY_TYPE
>> +          && variably_modified_type_p (type, NULL_TREE)
>> +          && !processing_template_decl)
>> +        {
>> +          /* Statically check for overflow in VLA bounds and build
>> +         an expression that checks at runtime whether the VLA
>> +         is erroneous due to invalid (runtime) bounds.
>> +         Another expression to check for excess initializers
>> +         is built in build_vec_init.  */
>
> Why do this both in check_initializer and then again in cp_finish_decl
> right after the call to check_initializer?

I would have liked to make just one call but I couldn't find
a good way to do it.

The call to build_vla_check() in check_initializer() is to check
only explicitly initialized VLAs.  The latter function may need
to complete the VLA element type and reshape the initializer so
the call cannot be made earlier.  The function returns the runtime
initializer code so the call cannot be made after it returns.

The call to build_vla_check() in cp_finish_decl() is guarded with
!init and made to check VLAs that are not explicitly initialized.
This call could perhaps be moved into check_initializer() though
it doesn't seem like it logically belongs there (since there is
no initializer).  If it were moved there, it seems to me that it
would require more extensive changes to the function than making
it in cp_finish_decl().

>> +      /* Also check to see if the final array size is zero (the size
>> +     is unsigned so the earlier overflow check detects negative
>> +     values as well.  */
>> +      tree zerocheck = fold_build2 (EQ_EXPR, boolean_type_node,
>> +                    vlasize, size_zero_node);
>
> I'm not sure whether we want this, given that GCC allows zero-length
> arrays in general.  As I recall, with the C++14 stuff whether we checked
> for zero was dependent on flag_iso, though I wasn't particularly happy
> with that.  If you want to check this unconditionally that's fine.

The (sizeof A / sizeof *A) expression is commonly used to compute
the number of elements in an array.  When A is a two-dimensional
VLA with a runtime minor bound of zero, the division is undefined
and usually traps at runtime.  (This is also true for ordinary
arrays but there GCC warns about it with -Wdiv-by-zero.)  Since
we're already doing the runtime checking for VLAs it costs almost
nothing to detect and prevent it and results in arguably safer
code.  So if it's okay with you, my preference is to keep it
without providing a way to disable it.

Martin
Jason Merrill April 12, 2016, 6:17 p.m. UTC | #3
On 04/10/2016 07:14 PM, Martin Sebor wrote:
> The call to build_vla_check() in check_initializer() is to check
> only explicitly initialized VLAs.  The latter function may need
> to complete the VLA element type and reshape the initializer so
> the call cannot be made earlier.  The function returns the runtime
> initializer code so the call cannot be made after it returns.

But this call isn't checking the initializer; we're looking at the 
initializer in build_vec_init now.

> The call to build_vla_check() in cp_finish_decl() is guarded with
> !init and made to check VLAs that are not explicitly initialized.
> This call could perhaps be moved into check_initializer() though
> it doesn't seem like it logically belongs there (since there is
> no initializer).

check_initializer handles implicit (default-) as well as explicit 
initialization.

> If it were moved there, it seems to me that it
> would require more extensive changes to the function than making
> it in cp_finish_decl().

I don't see that; you ought to be able to move the check_initializer 
copy down out of the if/else structure and use that for both cases.

> +/* Build an expression to determine whether the VLA TYPE is erroneous.
> +   INIT is the VLA initializer expression or NULL_TREE when the VLA is
> +   not initialized.  */
> +
> +/* Build an expression to determine whether the VLA TYPE is erroneous.
> +   INIT is the VLA initializer expression or NULL_TREE when the VLA is
> +   not initialized.  */

Duplicate comment.

Jason
diff mbox

Patch

PR c++/69517 - [5/6 regression] SEGV on a VLA with excess initializer elements
PR c++/70019 - VLA size overflow not detected

gcc/cp/ChangeLog:
2016-04-10  Martin Sebor  <msebor@redhat.com>

	PR c++/69517
	PR c++/70019
	* cp-tree.h (throw_bad_array_length, build_vla_check): Declare new
	functions.
	* decl.c (check_initializer, cp_finish_decl): Call them.
	(reshape_init_r): Reject incompletely braced intializer-lists
	for VLAs.
	* init.c (throw_bad_array_length, build_vla_check)
	(build_vla_size_check, build_vla_init_check): Define new functions.
	* typeck2.c (split_nonconstant_init_1): Use variably_modified_type_p()
	to detect a VLA.
	(store_init_value): Same.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index b7b770f..1d726fd 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5978,6 +5978,7 @@  extern tree build_value_init_noctor		(tree, tsubst_flags_t);
 extern tree get_nsdmi				(tree, bool);
 extern tree build_offset_ref			(tree, tree, bool,
 						 tsubst_flags_t);
+extern tree throw_bad_array_length              (void);
 extern tree throw_bad_array_new_length		(void);
 extern tree build_new				(vec<tree, va_gc> **, tree, tree,
 						 vec<tree, va_gc> **, int,
@@ -5999,6 +6000,7 @@  extern tree scalar_constant_value		(tree);
 extern tree decl_really_constant_value		(tree);
 extern int diagnose_uninitialized_cst_or_ref_member (tree, bool, bool);
 extern tree build_vtbl_address                  (tree);
+extern tree build_vla_check                     (tree, tree = NULL_TREE);
 
 /* in lex.c */
 extern void cxx_dup_lang_specific_decl		(tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 9260f4c..c5ec0da 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5864,6 +5864,16 @@  reshape_init_r (tree type, reshape_iter *d, bool first_initializer_p,
 	    }
 	}
 
+      if (variably_modified_type_p (type, NULL_TREE))
+	{
+	  /* Require VLAs to have their initializers fully braced
+	     to avoid initializing the wrong elements.  */
+	  if (complain & tf_error)
+	    error ("missing braces around initializer for a variable length "
+		   "array %qT", type);
+	  return error_mark_node;
+	}
+
       warning (OPT_Wmissing_braces, "missing braces around initializer for %qT",
 	       type);
     }
@@ -6167,6 +6177,34 @@  check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	      && PAREN_STRING_LITERAL_P (DECL_INITIAL (decl)))
 	    warning (0, "array %qD initialized by parenthesized string literal %qE",
 		     decl, DECL_INITIAL (decl));
+
+	  if (TREE_CODE (type) == ARRAY_TYPE
+	      && variably_modified_type_p (type, NULL_TREE)
+	      && !processing_template_decl)
+	    {
+	      /* Statically check for overflow in VLA bounds and build
+		 an expression that checks at runtime whether the VLA
+		 is erroneous due to invalid (runtime) bounds.
+		 Another expression to check for excess initializers
+		 is built in build_vec_init.  */
+	      tree check = build_vla_check (TREE_TYPE (decl), init);
+
+	      if (flag_exceptions && current_function_decl
+		  /* Avoid instrumenting constexpr functions for now.
+		     Those must be checked statically, and the (non-
+		     constexpr) dynamic instrumentation would cause
+		     them to be rejected.  See c++/70507.  */
+		  && !DECL_DECLARED_CONSTEXPR_P (current_function_decl))
+		{
+		  /* Use the runtime check only when exceptions are enabled.
+		     Otherwise let bad things happen...  */
+		  check = build3 (COND_EXPR, void_type_node, check,
+				  throw_bad_array_length (), void_node);
+
+		  finish_expr_stmt (check);
+		}
+	    }
+
 	  init = NULL;
 	}
     }
@@ -6809,6 +6847,36 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 	  cleanups = make_tree_vector ();
 	  init = check_initializer (decl, init, flags, &cleanups);
 
+	  if (TREE_CODE (type) == ARRAY_TYPE
+	      && variably_modified_type_p (type, NULL_TREE)
+	      && !processing_template_decl
+	      && !init)
+	    {
+	      /* Statically check for overflow in VLA bounds and build
+		 an expression that checks whether the VLA is erroneous
+		 at runtime.  A runtime check for the bounds of
+		 initialized VLAs along with excess initializer
+		 elements is built in build_vec_init().  */
+	      tree check = build_vla_check (type);
+
+	      if (flag_exceptions
+		  && current_function_decl
+		  /* Avoid instrumenting constexpr functions.  Those must
+		     be checked statically for now since the (non-constexpr)
+		     dynamic instrumentation would cause them to be rejected
+		     due to c++/70507.  */
+		  && !DECL_DECLARED_CONSTEXPR_P (current_function_decl))
+		{
+		  /* Use the runtime check only when exceptions are enabled.
+		     Otherwise let bad things happen (though perhaps emitting
+		     a trap would be appropriate).  */
+		  check = build3 (COND_EXPR, void_type_node, check,
+				  throw_bad_array_length (), void_node);
+
+		  finish_expr_stmt (check);
+		}
+	    }
+
 	  /* Handle:
 
 	     [dcl.init]
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 5997d53..ec19d72 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2262,6 +2262,20 @@  diagnose_uninitialized_cst_or_ref_member (tree type, bool using_new, bool compla
   return diagnose_uninitialized_cst_or_ref_member_1 (type, type, using_new, complain);
 }
 
+/* Call __cxa_throw_bad_array_length to indicate that the size calculation
+   in the bounds of a variable length array overflowed.  */
+
+tree
+throw_bad_array_length (void)
+{
+  tree fn = get_identifier ("__cxa_throw_bad_array_length");
+  if (!get_global_value_if_present (fn, &fn))
+    fn = push_throw_library_fn (fn, build_function_type_list (void_type_node,
+                                                             NULL_TREE));
+
+  return build_cxx_call (fn, 0, NULL, tf_warning_or_error);
+}
+
 /* Call __cxa_bad_array_new_length to indicate that the size calculation
    overflowed.  Pretend it returns sizetype so that it plays nicely in the
    COND_EXPR.  */
@@ -4709,3 +4723,304 @@  build_vec_delete (tree base, tree maxindex,
 
   return rval;
 }
+
+
+/* The implementation of build_vla_check() that recursively builds
+   an expression to determine whether the VLA TYPE is erroneous due
+   either to its bounds being invalid or to integer overflow in
+   the computation of its total size.
+   CHECK is the boolean expression being built, initialized to
+   boolean_false_node.
+   VLASIZE is used internally to pass the incrementally computed
+   size of the VLA object down to its recursive invocations.
+   MAX_VLASIZE is the maximum valid size of the VLA in bytes.
+   CST_SIZE is the product of the VLA's constant dimensions.  */
+
+static tree
+build_vla_size_check (tree check,
+		      tree type,
+		      tree vlasize,
+		      tree max_vlasize,
+		      offset_int *cst_size)
+{
+  tree vmul = builtin_decl_explicit (BUILT_IN_MUL_OVERFLOW);
+
+  tree vlasizeaddr = build_unary_op (input_location, ADDR_EXPR, vlasize, 0);
+
+  bool overflow = false;
+
+  if (TREE_CODE (type) == ARRAY_TYPE)
+    {
+      /* Compute the upper bound of this array type.  */
+      tree inner_nelts = array_type_nelts_top (type);
+      tree inner_nelts_cst = maybe_constant_value (inner_nelts);
+
+      if (TREE_CODE (inner_nelts_cst) == INTEGER_CST)
+	{
+	  /* The upper bound is a constant expression.  Compute the product
+	     of the constant upper bounds seen so far so that overflow can
+	     be diagnosed.  */
+	  offset_int result = wi::mul (wi::to_offset (inner_nelts_cst),
+				       *cst_size, SIGNED, &overflow);
+	  *cst_size = overflow ? 0 : result;
+	}
+
+      /* Check for overflow in the VLAs (runtime) upper bounds.  */
+      tree vflowcheck = build_call_expr (vmul, 3, inner_nelts,
+					 vlasize, vlasizeaddr);
+
+      check = fold_build2 (TRUTH_OR_EXPR, boolean_type_node,
+			   check, vflowcheck);
+
+      /* Recursively check for overflow in the remaining major bounds.  */
+      check = build_vla_size_check (check, TREE_TYPE (type),
+				    vlasize, max_vlasize,
+				    cst_size);
+    }
+  else
+    {
+      /* Get the size of the VLA element type in bytes.  */
+      tree typesize = TYPE_SIZE_UNIT (type);
+
+      /* See if the size, when multipled by the product of the VLA's
+	 constant dimensions, is within range of size_t.  If not,
+	 the VLA is definitely erroneous amd must be diagnosed at
+	 compile time.  */
+      offset_int result = wi::mul (wi::to_offset (typesize), *cst_size,
+				   SIGNED, &overflow);
+      *cst_size = overflow ? 0 : result;
+
+      /* Multiply the (non-constant) VLA size so far by the element size,
+	 checking for overflow, and replacing the value of vlasize with
+	 the product in the absence of overflow.  This size is the total
+	 runtime size of the VLA in bytes.  */
+      tree vflowcheck = build_call_expr (vmul, 3, typesize,
+					 vlasize, vlasizeaddr);
+
+      check = fold_build2 (TRUTH_OR_EXPR, boolean_type_node,
+			   check, vflowcheck);
+
+      /* Check to see if the final VLA size exceeds the maximum.  */
+      tree sizecheck = fold_build2 (LT_EXPR, boolean_type_node,
+				    max_vlasize, vlasize);
+
+      check = fold_build2 (TRUTH_OR_EXPR, boolean_type_node,
+			   check, sizecheck);
+
+      /* Also check to see if the final array size is zero (the size
+	 is unsigned so the earlier overflow check detects negative
+	 values as well.  */
+      tree zerocheck = fold_build2 (EQ_EXPR, boolean_type_node,
+				    vlasize, size_zero_node);
+
+      check = fold_build2 (TRUTH_OR_EXPR, boolean_type_node,
+			   check, zerocheck);
+    }
+
+  /* Diagnose overflow determined at compile time.  */
+  if (overflow)
+    {
+      error ("integer overflow in variable array size");
+      /* Reset to suppress any further diagnostics.  */
+      *cst_size = 0;
+    }
+
+  return check;
+}
+
+/* The implementation of build_vla_check() that recursively builds
+   an expression to determine whether the VLA initializer-list for
+   TYPE is erroneous due to excess initializers.
+   CHECK is the boolean expression being built, initialized to
+   the result of build_vla_size_check().
+   INIT is the VLA initializer expression to check against TYPE.
+   On the first (non-recursive) call, INIT_ELTS is set either to 1,
+   or to the number of elements in the initializer-list for VLAs
+   of unspecified (major) bound.  On subsequent (recursive) calls.
+   it is set to NULL and computed from the number of elements in
+   the (nested) initializer-list.
+*/
+
+static tree
+build_vla_init_check (tree check, tree type, tree init, tree init_elts)
+{
+  if (TREE_CODE (type) == ARRAY_TYPE)
+    {
+      /* Compute the upper bound of this array type unless it has
+	 already been computed by the caller for an array of unspecified
+	 bound, as in 'T a[];'  */
+      tree inner_nelts = init_elts ? init_elts : array_type_nelts_top (type);
+
+      size_t len;
+
+      if (TREE_CODE (init) == CONSTRUCTOR)
+	{
+	  /* The initializer of this array is itself an array.  Build
+	     an expression to check if the number of elements in the
+	     initializer array exceeds the upper bound of the type
+	     of the object being initialized.  */
+	  if (vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (init))
+	    {
+	      len = v->length ();
+	      tree initelts = build_int_cstu (size_type_node, len);
+	      tree initcheck = fold_build2 (LT_EXPR, boolean_type_node,
+					    inner_nelts, initelts);
+
+	      check = fold_build2 (TRUTH_OR_EXPR, boolean_type_node,
+				   check, initcheck);
+
+	      constructor_elt *ce;
+	      HOST_WIDE_INT i;
+
+	      /* Iterate over all non-empty initializers in this array,
+		 recursively building expressions to see if the elements
+		 of each are in excess of the corresponding (runtime)
+		 bound of the array type.  */
+	      FOR_EACH_VEC_SAFE_ELT (v, i, ce)
+		check = build_vla_init_check (check, TREE_TYPE (type),
+					      ce->value, NULL_TREE);
+	    }
+	}
+      else if (TREE_CODE (init) == STRING_CST
+	       && (len = TREE_STRING_LENGTH (init)))
+	{
+	  /* The initializer of this array is a string.  */
+	  tree ctype = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (init)));
+	  len /= TYPE_PRECISION (ctype) / BITS_PER_UNIT;
+
+	  /* A C++ string literal initializer must have at most as many
+	     characters as there are elements in the array, including
+	     the terminating NUL.  */
+	  tree initelts = build_int_cstu (size_type_node, len);
+	  tree initcheck = fold_build2 (LT_EXPR, boolean_type_node,
+					inner_nelts, initelts);
+	  check = fold_build2 (TRUTH_OR_EXPR, boolean_type_node,
+			       check, initcheck);
+	}
+      else if (TREE_CODE (init) == ERROR_MARK)
+	{
+	  // No checking is possible.
+	  check = boolean_false_node;
+	}
+      else
+	{
+	  /* What's this array initializer?  */
+	  gcc_unreachable ();
+	}
+    }
+
+  return check;
+}
+
+/* Build an expression to determine whether the VLA TYPE is erroneous.
+   INIT is the VLA initializer expression or NULL_TREE when the VLA is
+   not initialized.  */
+
+tree
+build_vla_check (tree type, tree init /* = NULL_TREE */)
+{
+  tree check = boolean_false_node;
+
+  /* The product of all constant dimensions of the VLA, initialized
+     to either 1 in the common case or to the number of elements in
+     the VLA's initializer-list for VLAs of unspecified (major)
+     bound.  */
+  offset_int cst_size = 1;
+
+  /* The initial size of the VLA to start the computation of the total
+     size with.  Like CST_SIZE above, initialized to 1 or the number
+     of elements in the VLA's initializer-list for VLAs of unspecified
+     bound.  */
+  tree initial_size = size_one_node;
+
+  /* For a VLA of unspecified (major) bound, the number of elements
+     it is initialized with determined from the initializer-list.  */
+  tree initial_elts = NULL_TREE;
+
+  if (init)
+    {
+      /* Determine the upper bound of the VLA of unspecified bound,
+	 as in 'T a[];' if this is such a VLA.  Such a VLA can be
+	 initialized with any number of elements but the number of
+	 elements so determined must be used to check the total size
+	 of the VLA.  */
+      gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
+
+      if (tree dom = TYPE_DOMAIN (type))
+	if (tree max = TYPE_MAX_VALUE (dom))
+	  if (integer_zerop (max))
+	    {
+	      if (TREE_CODE (init) == CONSTRUCTOR)
+		{
+		  vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (init);
+
+		  /* Since the upper bound of every array must be positive
+		     a VLA with an unspecified major bound must be initized
+		     by a non-empty initializer list.  */
+		  gcc_assert (v != NULL);
+
+		  cst_size = v->length ();
+		}
+	      else if (TREE_CODE (init) == STRING_CST)
+		{
+		  /* The initializer is a (possibly empty) string consisting
+		     at a minumum of one character, the terminating NUL.
+		     This condition implies a definition like
+		       char s [][N] = "";
+		     which is an error but even though it has been diagnosed
+		     by this point the initializer still winds up here.  */
+		  size_t nchars = TREE_STRING_LENGTH (init);
+		  tree ctype = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (init)));
+		  nchars /= TYPE_PRECISION (ctype) / BITS_PER_UNIT;
+
+		  cst_size = nchars + 1;
+		}
+
+	      initial_elts = wide_int_to_tree (size_type_node, cst_size);
+	      initial_size = initial_elts;
+	    }
+    }
+
+  /* Build a variable storing the total runtime size of the VLA and
+     initialize it either to 1 (in the common case) or to the number
+     of topmost elements in the initializer-list when the VLA is
+     an array of unspecified (major) bound.  */
+  tree vlasize = build_decl (input_location,
+			     VAR_DECL, NULL_TREE, sizetype);
+  DECL_ARTIFICIAL (vlasize) = 1;
+  DECL_IGNORED_P (vlasize) = 1;
+  DECL_CONTEXT (vlasize) = current_function_decl;
+  DECL_INITIAL (vlasize) = initial_size;
+  vlasize = pushdecl (vlasize);
+  add_decl_expr (vlasize);
+
+  /* Impose a lenient limit on the size of the biggest VLA in bytes.
+     FIXME: Tighten up the limit to make it more useful and make it
+     configurable for users with unusual requirements.  */
+  tree max_vlasize
+    = fold_build2 (RSHIFT_EXPR, size_type_node,
+		   build_all_ones_cst (size_type_node),
+		   integer_one_node);
+
+  /* Build an expression that checks the runtime bounds of the VLA
+     for invalid values and the total size of the VLA for overflow.  */
+  check = build_vla_size_check (check, type, vlasize, max_vlasize, &cst_size);
+
+  if (wi::ltu_p (wi::to_offset (max_vlasize), cst_size))
+    {
+      /* Issue the warning only in the "topmost" (non-recursive) call
+	 to avoid duplicating diagnostics.  This is only a warning to
+	 allow programs to be portable to more permissive environments.  */
+      warning (OPT_Wvla, "size of variable length array exceeds maximum "
+	       "of %qE bytes",  max_vlasize);
+    }
+
+  if (init)
+    {
+      /* Build an expression that checks the VLA initializer expression
+	 against the type of the VLA for excess elements.  */
+      check = build_vla_init_check (check, type, init, initial_elts);
+    }
+
+  return check;
+}
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index b921689..eba19ca 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -603,7 +603,7 @@  split_nonconstant_init_1 (tree dest, tree init)
       array_type_p = true;
       if ((TREE_SIDE_EFFECTS (init)
 	   && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
-	  || array_of_runtime_bound_p (type))
+	  || variably_modified_type_p (type, NULL_TREE))
 	{
 	  /* For an array, we only need/want a single cleanup region rather
 	     than one per element.  */
@@ -845,7 +845,7 @@  store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
      will perform the dynamic initialization.  */
   if (value != error_mark_node
       && (TREE_SIDE_EFFECTS (value)
-	  || array_of_runtime_bound_p (type)
+	  || variably_modified_type_p (type, NULL_TREE)
 	  || ! reduced_constant_expression_p (value)))
     return split_nonconstant_init (decl, value);
   /* If the value is a constant, just put it in DECL_INITIAL.  If DECL