Commit 342fac95 by Martin Sebor Committed by Martin Sebor

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
parent 2ecc0c83
2016-04-13 Martin Sebor <msebor@redhat.com>
PR c++/69517
PR c++/70019
PR c++/70588
* doc/extend.texi (Variable Length): Document C++ specifics.
2016-04-13 Jakub Jelinek <jakub@redhat.com>
PR c++/70641
......
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.
......
......@@ -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);
......
......@@ -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);
......
......@@ -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
......
......@@ -1638,14 +1638,48 @@ foo (int n)
You can use the function @code{alloca} to get an effect much like
variable-length arrays. The function @code{alloca} is available in
many other C implementations (but not in all). On the other hand,
variable-length arrays are more elegant.
variable-length arrays are available in GCC for all targets and
provide type safety.
There are other differences between these two methods. Space allocated
with @code{alloca} exists until the containing @emph{function} returns.
The space for a variable-length array is deallocated as soon as the array
name's scope ends, unless you also use @code{alloca} in this scope.
You can also use variable-length arrays as arguments to functions:
Unlike GCC, G++ instruments variable-length arrays (@xref{Variable Length})
with checks for erroneous uses: when a variable-length array object is
created its runtime bounds are checked to detect non-positive values,
integer overflows, sizes in excess of SIZE_MAX / 2 bytes, and excess
initializers. When an erroneous variable-length array is detected
the runtime arranges for an exception to be thrown that matches a handler
of type @code{std::bad_array_length}.
Also unlike GCC, G++ allows variable-length arrays to be initialized.
However, unlike initializer lists for ordinary multidimensional arrays,
those for multidimensional variable-length arrays must be enclosed in
pairs of curly braces delimiting each sequence of values to use to
initialize each subarray. Initializer lists that aren't unambiguously
enclosed in braces are rejected with an error. For example, in the
following function, the initializer list for the ordinary @code{array}
is accepted even though it isn't fully enclosed in braces. The same
initializer list, however, wouldn't be accepted for a multidimensional
variable-length array. To initialize the variable-length array @code{vla},
the elements of the subarray @code{vla[m]} must be enclosed in braces
as shown. As with ordinary arrays, elements that aren't initialized
explicitly are default-initialized.
@smallexample
void
foo (int m, int n)
@{
int array[2][3] = @{ 1, 2, 4, 5, 6 @};
int vla[m][n] = @{ @{ 1, 2 @}, @{ 4, 5, 6 @} @};
@}
@end smallexample
In C programs (but not in C++) variable-length arrays can also be declared
as function arguments:
@smallexample
struct entry
......
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.
2016-04-13 Jakub Jelinek <jakub@redhat.com>
PR c++/70641
......
......@@ -87,18 +87,24 @@ fn12 (void)
int
main (void)
{
fn1 ();
fn2 ();
fn3 ();
fn4 ();
fn5 ();
fn6 ();
fn7 ();
fn8 ();
fn9 ();
fn10 ();
fn11 ();
fn12 ();
#if __cplusplus
# define TRY(stmt) do { try { stmt; } catch (...) { } } while (0)
#else
# define TRY(stmt) stmt
#endif
TRY (fn1 ());
TRY (fn2 ());
TRY (fn3 ());
TRY (fn4 ());
TRY (fn5 ());
TRY (fn6 ());
TRY (fn7 ());
TRY (fn8 ());
TRY (fn9 ());
TRY (fn10 ());
TRY (fn11 ());
TRY (fn12 ());
return 0;
}
......
// Test to verify that variable length arrays the product of whose constant
// bounds overflows or exceeds the implementation-defined limit are diagnosed.
// { dg-do compile { target c++11 } }
// { dg-additional-options "-Wno-error=vla" }
#define INT_MAX __INT_MAX__
#define LONG_MAX __LONG_MAX__
#define SIZE_MAX __SIZE_MAX__
typedef __SIZE_TYPE__ size_t;
#define MAX (SIZE_MAX / 2)
void test (int x)
{
const size_t amax = MAX;
// The following are valid and shouldn't elicit a bounds overflow warning.
{
char a [x][amax]; // { dg-warning "forbids" }
(void)a;
}
{
char a [amax][x]; // { dg-warning "forbids" }
(void)a;
}
// The following is invalid and should be diagnosed. Unfortunately,
// when the VLA maximum size is (SIZE_MAX / 2), G++ also issues
// a (bogus) -Woverflow because it computes the array bound in
// a signed type (ssize_t) instead of size_t, in addition to
// rejecting the declaration with error: size of array ‘a’ is too
// large, before the VLA constant bound check has had a chance to
// see it. So the test is disabled.
// {
// char a [x][amax + 1];
// (void)a;
// }
{
char a [x][x][amax]; // { dg-warning "forbids" }
(void)a;
}
{
char a [x][amax][x]; // { dg-warning "forbids" }
(void)a;
}
{
char a [amax][x][x]; // { dg-warning "forbids" }
(void)a;
}
{
char a [2][x][amax]; // { dg-warning "forbids|exceeds maximum" }
(void)a;
}
{
// Unfortunately, the following is rejected with a different error
// earlier during parsing and before the VLA checking gets to see
// it: error: size of array ‘a’ is too large
// Ditto for other multidimensional VLAs where the overflow occurs
// in the computation of the product of adjacent constant bounds.
// char a [x][amax][amax];
// char b [x][2][amax];
// That error above also leads to the following error when using
// the variable below.
// error:’ was not declared in this scope
// (void)a;
}
{
char a [amax][x][amax]; // { dg-warning "forbids|exceeds maximum" }
(void)a;
}
{
char a [amax][amax][x]; // { dg-warning "forbids|exceeds maximum" }
(void)a;
}
{
struct A256 { __attribute__ ((aligned (256))) char a; };
enum {
M = 1024,
N = MAX / (sizeof (A256) * M)
};
A256 a [x][M][x][N]; // { dg-warning "forbids" }
(void)a;
A256 b [2][x][M][x][N]; // { dg-warning "forbids|exceeds maximum" }
(void)b;
}
}
// PR c++/70019 - VLA size overflow not detected
// Compile-time test to verify that attempting to initialize a VLA with
// a string that's longer than the VLA's constant bound is diagnosed at
// compile time. For a runtime version of the test see vla13.C.
// { dg-do run }
// { dg-additional-options "-Wno-vla" }
void test (int n)
{
char a1[n][1] = { { "a" } }; // { dg-error "initializer-string for array of chars is too long" }
(void)a1;
char a2[1][n] = { { "a" } };
(void)a2;
char a3[n][1][1] = { { { "a" } } }; // { dg-error "initializer-string for array of chars is too long" }
(void)a3;
char a4[1][1][n] = { { { "a" } } };
(void)a4;
char a5[1][n][1] = { { { "a" } } }; // { dg-error "initializer-string for array of chars is too long" }
(void)a5;
char a6[n][1][n] = { { { "a" } } };
(void)a6;
wchar_t a7[n][1] = { { L"a" } }; // { dg-error "initializer-string for array of chars is too long" }
(void)a7;
wchar_t a8[1][n] = { { L"a" } };
(void)a8;
wchar_t a9[n][1][1] = { { { L"a" } } }; // { dg-error "initializer-string for array of chars is too long" }
(void)a9;
wchar_t a10[1][1][n] = { { { L"a" } } };
(void)a10;
wchar_t a11[][n][1] = { { { L"a" } } }; // { dg-error "initializer-string for array of chars is too long" }
(void)a11;
wchar_t a12[n][1][n] = { { { L"a" } } };
(void)a12;
}
// Test for throwing bad_array_length on invalid array length.
// { dg-do run { target c++14 } }
// { dg-additional-options "-Wno-vla" }
namespace std
{
struct exception
{
virtual ~exception ();
virtual const char* what () const throw ();
};
}
int f(int i)
{
int ar[i]{1,2,3,4};
return ar[i-1];
}
void g(int i)
{
int ar[i];
ar[0] = 42;
}
int main()
{
int ok = 0;
f(4); // OK
try {
f(3); // too small
}
catch (std::exception &e) {
++ok;
}
try { g(-24); } // negative
catch (std::exception &e) {
++ok;
}
if (ok != 2)
__builtin_abort ();
}
......@@ -3,5 +3,5 @@
void foo(int i)
{
int x[][i] = { 0 };
int x[][i] = { { 0 } };
}
// { dg-do run }
// { dg-options "-Wno-vla -fsanitize=undefined" }
// Disable exceptions to prevent the erroneous initializer from
// throwing before the sanitizer instrumentation has detected
// the problem.
// { dg-options "-Wno-vla -fno-exceptions -fsanitize=undefined" }
// { dg-output "index 1 out of bounds" }
void f(int i) {
......
2016-04-13 Martin Sebor <msebor@redhat.com>
PR c++/69517
* testsuite/25_algorithms/rotate/moveable2.cc: Make sure VLA
upper bound is positive.
2016-04-13 Jonathan Wakely <jwakely@redhat.com>
* include/bits/c++config (_GLIBCXX_BEGIN_NAMESPACE_EMPTY_TYPES,
......
......@@ -44,7 +44,8 @@ template<typename Con>
{
bool test __attribute__((unused)) = true;
rvalstruct array[length];
/* Make sure the VLA upper bound is positive. */
rvalstruct array[length + 1];
for(int i = 0; i < length; ++i)
array[i] = i;
Con con(array, array + length);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment