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/cp/ChangeLog: 2016-04-13 Martin Sebor 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. 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, tree, vec **, 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..e626875 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); } @@ -6016,6 +6026,10 @@ check_initializer (tree decl, tree init, int flags, vec **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)); @@ -6167,7 +6181,9 @@ check_initializer (tree decl, tree init, int flags, vec **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 @@ -6181,6 +6197,33 @@ check_initializer (tree decl, tree init, int flags, vec **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 *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 *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** 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