mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-08 12:10:39 +08:00
Fix PR C++/19351: integer overflow in operator new[]
2012-08-20 Florian Weimer <fweimer@redhat.com> PR c++/19351 * call.c (build_operator_new_call): Add size_check argument and evaluate it. * cp-tree.h (build_operator_new_call): Adjust declaration. * init.c (build_new_1): Compute array size check and apply it. 2012-08-10 Florian Weimer <fweimer@redhat.com> PR c++/19351 * g++.dg/init/new38.C: New test. * g++.dg/init/new39.C: New test. From-SVN: r190546
This commit is contained in:
parent
5592815a88
commit
4a84253c98
@ -1,3 +1,11 @@
|
||||
2012-08-20 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
PR c++/19351
|
||||
* call.c (build_operator_new_call): Add size_check argument and
|
||||
evaluate it.
|
||||
* cp-tree.h (build_operator_new_call): Adjust declaration.
|
||||
* init.c (build_new_1): Compute array size check and apply it.
|
||||
|
||||
2012-08-20 Oleg Endo <olegendo@gcc.gnu.org>
|
||||
|
||||
PR target/50489
|
||||
|
@ -3943,15 +3943,19 @@ build_new_function_call (tree fn, VEC(tree,gc) **args, bool koenig_p,
|
||||
total number of bytes required by the allocation, and is updated if
|
||||
that is changed here. *COOKIE_SIZE is non-NULL if a cookie should
|
||||
be used. If this function determines that no cookie should be
|
||||
used, after all, *COOKIE_SIZE is set to NULL_TREE. If FN is
|
||||
non-NULL, it will be set, upon return, to the allocation function
|
||||
called. */
|
||||
used, after all, *COOKIE_SIZE is set to NULL_TREE. If SIZE_CHECK
|
||||
is not NULL_TREE, it is evaluated before calculating the final
|
||||
array size, and if it fails, the array size is replaced with
|
||||
(size_t)-1 (usually triggering a std::bad_alloc exception). If FN
|
||||
is non-NULL, it will be set, upon return, to the allocation
|
||||
function called. */
|
||||
|
||||
tree
|
||||
build_operator_new_call (tree fnname, VEC(tree,gc) **args,
|
||||
tree *size, tree *cookie_size,
|
||||
tree *size, tree *cookie_size, tree size_check,
|
||||
tree *fn, tsubst_flags_t complain)
|
||||
{
|
||||
tree original_size = *size;
|
||||
tree fns;
|
||||
struct z_candidate *candidates;
|
||||
struct z_candidate *cand;
|
||||
@ -3959,6 +3963,10 @@ build_operator_new_call (tree fnname, VEC(tree,gc) **args,
|
||||
|
||||
if (fn)
|
||||
*fn = NULL_TREE;
|
||||
/* Set to (size_t)-1 if the size check fails. */
|
||||
if (size_check != NULL_TREE)
|
||||
*size = fold_build3 (COND_EXPR, sizetype, size_check,
|
||||
original_size, TYPE_MAX_VALUE (sizetype));
|
||||
VEC_safe_insert (tree, gc, *args, 0, *size);
|
||||
*args = resolve_args (*args, complain);
|
||||
if (*args == NULL)
|
||||
@ -4022,7 +4030,11 @@ build_operator_new_call (tree fnname, VEC(tree,gc) **args,
|
||||
if (use_cookie)
|
||||
{
|
||||
/* Update the total size. */
|
||||
*size = size_binop (PLUS_EXPR, *size, *cookie_size);
|
||||
*size = size_binop (PLUS_EXPR, original_size, *cookie_size);
|
||||
/* Set to (size_t)-1 if the size check fails. */
|
||||
gcc_assert (size_check != NULL_TREE);
|
||||
*size = fold_build3 (COND_EXPR, sizetype, size_check,
|
||||
*size, TYPE_MAX_VALUE (sizetype));
|
||||
/* Update the argument list to reflect the adjusted size. */
|
||||
VEC_replace (tree, *args, 0, *size);
|
||||
}
|
||||
|
@ -4886,7 +4886,7 @@ extern tree build_user_type_conversion (tree, tree, int,
|
||||
extern tree build_new_function_call (tree, VEC(tree,gc) **, bool,
|
||||
tsubst_flags_t);
|
||||
extern tree build_operator_new_call (tree, VEC(tree,gc) **, tree *,
|
||||
tree *, tree *,
|
||||
tree *, tree, tree *,
|
||||
tsubst_flags_t);
|
||||
extern tree build_new_method_call (tree, tree, VEC(tree,gc) **,
|
||||
tree, int, tree *,
|
||||
|
@ -2178,7 +2178,10 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
|
||||
tree pointer_type;
|
||||
tree non_const_pointer_type;
|
||||
tree outer_nelts = NULL_TREE;
|
||||
/* For arrays, a bounds checks on the NELTS parameter. */
|
||||
tree outer_nelts_check = NULL_TREE;
|
||||
bool outer_nelts_from_type = false;
|
||||
double_int inner_nelts_count = double_int_one;
|
||||
tree alloc_call, alloc_expr;
|
||||
/* The address returned by the call to "operator new". This node is
|
||||
a VAR_DECL and is therefore reusable. */
|
||||
@ -2231,7 +2234,22 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
|
||||
{
|
||||
tree inner_nelts = array_type_nelts_top (elt_type);
|
||||
tree inner_nelts_cst = maybe_constant_value (inner_nelts);
|
||||
if (!TREE_CONSTANT (inner_nelts_cst))
|
||||
if (TREE_CONSTANT (inner_nelts_cst)
|
||||
&& TREE_CODE (inner_nelts_cst) == INTEGER_CST)
|
||||
{
|
||||
double_int result;
|
||||
if (mul_double (TREE_INT_CST_LOW (inner_nelts_cst),
|
||||
TREE_INT_CST_HIGH (inner_nelts_cst),
|
||||
inner_nelts_count.low, inner_nelts_count.high,
|
||||
&result.low, &result.high))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("integer overflow in array size");
|
||||
nelts = error_mark_node;
|
||||
}
|
||||
inner_nelts_count = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (complain & tf_error)
|
||||
{
|
||||
@ -2321,7 +2339,56 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
|
||||
|
||||
size = size_in_bytes (elt_type);
|
||||
if (array_p)
|
||||
size = size_binop (MULT_EXPR, size, convert (sizetype, nelts));
|
||||
{
|
||||
/* Maximum available size in bytes. Half of the address space
|
||||
minus the cookie size. */
|
||||
double_int max_size
|
||||
= double_int_lshift (double_int_one, TYPE_PRECISION (sizetype) - 1,
|
||||
HOST_BITS_PER_DOUBLE_INT, false);
|
||||
/* Size of the inner array elements. */
|
||||
double_int inner_size;
|
||||
/* Maximum number of outer elements which can be allocated. */
|
||||
double_int max_outer_nelts;
|
||||
tree max_outer_nelts_tree;
|
||||
|
||||
gcc_assert (TREE_CODE (size) == INTEGER_CST);
|
||||
cookie_size = targetm.cxx.get_cookie_size (elt_type);
|
||||
gcc_assert (TREE_CODE (cookie_size) == INTEGER_CST);
|
||||
gcc_checking_assert (double_int_ucmp
|
||||
(TREE_INT_CST (cookie_size), max_size) < 0);
|
||||
/* Unconditionally substract the cookie size. This decreases the
|
||||
maximum object size and is safe even if we choose not to use
|
||||
a cookie after all. */
|
||||
max_size = double_int_sub (max_size, TREE_INT_CST (cookie_size));
|
||||
if (mul_double (TREE_INT_CST_LOW (size), TREE_INT_CST_HIGH (size),
|
||||
inner_nelts_count.low, inner_nelts_count.high,
|
||||
&inner_size.low, &inner_size.high)
|
||||
|| double_int_ucmp (inner_size, max_size) > 0)
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("size of array is too large");
|
||||
return error_mark_node;
|
||||
}
|
||||
max_outer_nelts = double_int_udiv (max_size, inner_size, TRUNC_DIV_EXPR);
|
||||
/* Only keep the top-most seven bits, to simplify encoding the
|
||||
constant in the instruction stream. */
|
||||
{
|
||||
unsigned shift = HOST_BITS_PER_DOUBLE_INT - 7
|
||||
- (max_outer_nelts.high ? clz_hwi (max_outer_nelts.high)
|
||||
: (HOST_BITS_PER_WIDE_INT + clz_hwi (max_outer_nelts.low)));
|
||||
max_outer_nelts
|
||||
= double_int_lshift (double_int_rshift
|
||||
(max_outer_nelts, shift,
|
||||
HOST_BITS_PER_DOUBLE_INT, false),
|
||||
shift, HOST_BITS_PER_DOUBLE_INT, false);
|
||||
}
|
||||
max_outer_nelts_tree = double_int_to_tree (sizetype, max_outer_nelts);
|
||||
|
||||
size = size_binop (MULT_EXPR, size, convert (sizetype, nelts));
|
||||
outer_nelts_check = fold_build2 (LE_EXPR, boolean_type_node,
|
||||
outer_nelts,
|
||||
max_outer_nelts_tree);
|
||||
}
|
||||
|
||||
alloc_fn = NULL_TREE;
|
||||
|
||||
@ -2384,10 +2451,13 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
|
||||
/* Use a class-specific operator new. */
|
||||
/* If a cookie is required, add some extra space. */
|
||||
if (array_p && TYPE_VEC_NEW_USES_COOKIE (elt_type))
|
||||
{
|
||||
cookie_size = targetm.cxx.get_cookie_size (elt_type);
|
||||
size = size_binop (PLUS_EXPR, size, cookie_size);
|
||||
}
|
||||
size = size_binop (PLUS_EXPR, size, cookie_size);
|
||||
else
|
||||
cookie_size = NULL_TREE;
|
||||
/* Perform the overflow check. */
|
||||
if (outer_nelts_check != NULL_TREE)
|
||||
size = fold_build3 (COND_EXPR, sizetype, outer_nelts_check,
|
||||
size, TYPE_MAX_VALUE (sizetype));
|
||||
/* Create the argument list. */
|
||||
VEC_safe_insert (tree, gc, *placement, 0, size);
|
||||
/* Do name-lookup to find the appropriate operator. */
|
||||
@ -2418,13 +2488,12 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
|
||||
{
|
||||
/* Use a global operator new. */
|
||||
/* See if a cookie might be required. */
|
||||
if (array_p && TYPE_VEC_NEW_USES_COOKIE (elt_type))
|
||||
cookie_size = targetm.cxx.get_cookie_size (elt_type);
|
||||
else
|
||||
if (!(array_p && TYPE_VEC_NEW_USES_COOKIE (elt_type)))
|
||||
cookie_size = NULL_TREE;
|
||||
|
||||
alloc_call = build_operator_new_call (fnname, placement,
|
||||
&size, &cookie_size,
|
||||
outer_nelts_check,
|
||||
&alloc_fn, complain);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,9 @@
|
||||
2012-08-20 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
PR c++/19351
|
||||
* g++.dg/init/new38.C: New test.
|
||||
* g++.dg/init/new39.C: New test.
|
||||
|
||||
2012-08-20 Oleg Endo <olegendo@gcc.gnu.org>
|
||||
|
||||
PR target/50489
|
||||
|
54
gcc/testsuite/g++.dg/init/new38.C
Normal file
54
gcc/testsuite/g++.dg/init/new38.C
Normal file
@ -0,0 +1,54 @@
|
||||
// { dg-do compile }
|
||||
|
||||
void
|
||||
large_array_char(int n)
|
||||
{
|
||||
new char[n]
|
||||
[1ULL << (sizeof(void *) * 4)]
|
||||
[1ULL << (sizeof(void *) * 4)]; // { dg-error "size of array" }
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
large_array_char_template(int n)
|
||||
{
|
||||
new char[n]
|
||||
[1ULL << (sizeof(void *) * 4)]
|
||||
[1ULL << (sizeof(void *) * 4)]; // { dg-error "size of array" }
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
large_array_template1(int n)
|
||||
{
|
||||
new T[n] // { dg-error "size of array is too large" }
|
||||
[(1ULL << (sizeof(void *) * 4)) / sizeof(T)]
|
||||
[1ULL << (sizeof(void *) * 4)];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
large_array_template2(int n)
|
||||
{
|
||||
new T[n] // { dg-error "size of array is too large" }
|
||||
[(1ULL << (sizeof(void *) * 4)) / sizeof(T)]
|
||||
[1ULL << (sizeof(void *) * 4)];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
large_array_template3(int n)
|
||||
{
|
||||
new T[n] // { dg-error "size of array is too large" }
|
||||
[(1ULL << (sizeof(void *) * 4)) / sizeof(T)]
|
||||
[1ULL << (sizeof(void *) * 4)];
|
||||
}
|
||||
|
||||
void
|
||||
call_large_array_template(int n)
|
||||
{
|
||||
large_array_template1<char>(n);
|
||||
large_array_template2<int>(n);
|
||||
large_array_template3<double>(n);
|
||||
}
|
68
gcc/testsuite/g++.dg/init/new39.C
Normal file
68
gcc/testsuite/g++.dg/init/new39.C
Normal file
@ -0,0 +1,68 @@
|
||||
// Testcase for overflow handling in operator new[].
|
||||
// { dg-do run }
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdexcept>
|
||||
|
||||
struct without_new {
|
||||
char bar[256];
|
||||
};
|
||||
|
||||
struct with_new {
|
||||
char bar[256];
|
||||
void *operator new[] (size_t sz)
|
||||
{
|
||||
if (sz != -1)
|
||||
abort ();
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline void
|
||||
test (size_t s)
|
||||
{
|
||||
try {
|
||||
new T[s];
|
||||
abort ();
|
||||
} catch (std::bad_alloc &) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
test_noopt (size_t s) __attribute__((noinline));
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
test_noopt (size_t s)
|
||||
{
|
||||
__asm__ ("");
|
||||
test<T> (s);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
all_tests ()
|
||||
{
|
||||
test<T>(-1);
|
||||
test<T>(size_t(-1) / sizeof (T) + 1);
|
||||
test<T>(size_t(-1) / sizeof (T) + 2);
|
||||
test_noopt<T>(-1);
|
||||
test_noopt<T>(size_t(-1) / sizeof (T) + 1);
|
||||
test_noopt<T>(size_t(-1) / sizeof (T) + 2);
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
try {
|
||||
::operator new(size_t(-1));
|
||||
abort ();
|
||||
} catch (std::bad_alloc &) {
|
||||
}
|
||||
all_tests<without_new> ();
|
||||
all_tests<with_new> ();
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user