aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2016-04-13 23:26:41 +0000
committerMartin Sebor <msebor@gcc.gnu.org>2016-04-13 17:26:41 -0600
commit342fac9537c6a75e65fe62943ba84b81bddede3f (patch)
tree1d55385c5ee07d32a08d9279dcb595632d96e73c /gcc/cp
parent2ecc0c837bf2ac1309f41d30b7b30595331a6eb6 (diff)
downloadgcc-342fac9537c6a75e65fe62943ba84b81bddede3f.zip
gcc-342fac9537c6a75e65fe62943ba84b81bddede3f.tar.gz
gcc-342fac9537c6a75e65fe62943ba84b81bddede3f.tar.bz2
PR c++/69517 - [5/6 regression] SEGV on a VLA with excess initializer elements
PR c++/69517 - [5/6 regression] SEGV on a VLA with excess initializer elements PR c++/70019 - VLA size overflow not detected PR c++/70588 - SIGBUS on a VLA larger than SIZE_MAX / 2 gcc/testsuite/ChangeLog: 2016-04-13 Martin Sebor <msebor@redhat.com> PR c++/69517 PR c++/70019 PR c++/70588 * c-c++-common/ubsan/vla-1.c (main): Catch exceptions. * g++.dg/cpp1y/vla11.C: New test. * g++.dg/cpp1y/vla12.C: New test. * g++.dg/cpp1y/vla13.C: New test. * g++.dg/cpp1y/vla14.C: New test. * g++.dg/cpp1y/vla3.C: Restore deleted test. * gcc/testsuite/g++.dg/init/array24.C: Fully brace VLA initializer. * g++.dg/ubsan/vla-1.C: Disable exceptions. gcc/cp/ChangeLog: 2016-04-13 Martin Sebor <msebor@redhat.com> PR c++/69517 PR c++/70019 PR c++/70588 * 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. gcc/doc/ChangeLog: 2016-04-13 Martin Sebor <msebor@redhat.com> PR c++/69517 PR c++/70019 PR c++/70588 * extend.texi (Variable Length): Document C++ specifics. libstdc++-v3/ChangeLog: 2016-04-13 Martin Sebor <msebor@redhat.com> PR c++/69517 * testsuite/25_algorithms/rotate/moveable2.cc: Make sure VLA upper bound is positive. From-SVN: r234966
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/ChangeLog16
-rw-r--r--gcc/cp/cp-tree.h2
-rw-r--r--gcc/cp/decl.c45
-rw-r--r--gcc/cp/init.c315
-rw-r--r--gcc/cp/typeck2.c4
5 files changed, 379 insertions, 3 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index c9929b63..866b4f2 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,19 @@
+2016-04-13 Martin Sebor <msebor@redhat.com>
+
+ PR c++/69517
+ PR c++/70019
+ PR c++/70588
+ * 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.
+
2016-04-13 Jason Merrill <jason@redhat.com>
Warn about empty parameter ABI with -Wabi=9.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8d721c7..87e3ea0 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5950,6 +5950,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,
@@ -5971,6 +5972,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 7099199..42e853f 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5896,6 +5896,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);
}
@@ -6048,6 +6058,10 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
/* There is no way to make a variable-sized class type in GNU C++. */
gcc_assert (TREE_CONSTANT (TYPE_SIZE (type)));
+ /* Initializer exression used to check invalid VLA bounds and excess
+ initializer elements. */
+ tree saved_init_for_vla_check = NULL_TREE;
+
if (init && BRACE_ENCLOSED_INITIALIZER_P (init))
{
int init_len = vec_safe_length (CONSTRUCTOR_ELTS (init));
@@ -6199,7 +6213,9 @@ 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));
- init = NULL;
+
+ saved_init_for_vla_check = init;
+ init = NULL_TREE;
}
}
else
@@ -6213,6 +6229,33 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
check_for_uninitialized_const_var (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), saved_init_for_vla_check);
+
+ 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);
+ }
+ }
+
if (init && init != error_mark_node)
init_code = build2 (INIT_EXPR, type, decl, 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