From c4225e124f9e11bb8411c4d8be70a01b449ffc12 Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Fri, 5 Apr 2019 20:38:43 +0000 Subject: [PATCH] Fix PR41395 - __cxa_vec_new may overflow in allocation size calculation. llvm-svn: 357814 --- libcxxabi/src/cxa_vector.cpp | 56 ++++++-- .../cxa_vec_new_overflow_PR41395.pass.cpp | 122 ++++++++++++++++++ 2 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 libcxxabi/test/cxa_vec_new_overflow_PR41395.pass.cpp diff --git a/libcxxabi/src/cxa_vector.cpp b/libcxxabi/src/cxa_vector.cpp index 2d89787f4edd..418b40226cad 100644 --- a/libcxxabi/src/cxa_vector.cpp +++ b/libcxxabi/src/cxa_vector.cpp @@ -13,6 +13,9 @@ #include "cxxabi.h" #include // for std::terminate +#include // for std::bad_alloc + +#include "abort_message.h" namespace __cxxabiv1 { @@ -107,6 +110,32 @@ namespace { #pragma mark --Externally visible routines-- #endif +namespace { +_LIBCXXABI_NORETURN +void throw_bad_array_new_length() { +#ifndef _LIBCXXABI_NO_EXCEPTIONS + throw std::bad_array_new_length(); +#else + abort_message("__cxa_vec_new failed to allocate memory"); +#endif +} + +size_t calculate_allocation_size_or_throw(size_t element_count, + size_t element_size, + size_t padding_size) { + const size_t element_heap_size = element_count * element_size; + if (element_heap_size / element_count != element_size) + throw_bad_array_new_length(); + + const size_t allocation_size = element_heap_size + padding_size; + if (allocation_size < element_heap_size) + throw_bad_array_new_length(); + + return allocation_size; +} + +} // namespace + extern "C" { // Equivalent to @@ -121,7 +150,6 @@ __cxa_vec_new(size_t element_count, size_t element_size, size_t padding_size, } - // Given the number and size of elements for an array and the non-negative // size of prefix padding for a cookie, allocate space (using alloc) for // the array preceded by the specified padding, initialize the cookie if @@ -142,12 +170,13 @@ _LIBCXXABI_FUNC_VIS void * __cxa_vec_new2(size_t element_count, size_t element_size, size_t padding_size, void (*constructor)(void *), void (*destructor)(void *), void *(*alloc)(size_t), void (*dealloc)(void *)) { - const size_t heap_size = element_count * element_size + padding_size; - char * const heap_block = static_cast ( alloc ( heap_size )); - char *vec_base = heap_block; - - if ( NULL != vec_base ) { - st_heap_block2 heap ( dealloc, heap_block ); + const size_t heap_size = calculate_allocation_size_or_throw( + element_count, element_size, padding_size); + char* const heap_block = static_cast(alloc(heap_size)); + char* vec_base = heap_block; + + if (NULL != vec_base) { + st_heap_block2 heap(dealloc, heap_block); // put the padding before the array elements if ( 0 != padding_size ) { @@ -170,12 +199,13 @@ _LIBCXXABI_FUNC_VIS void * __cxa_vec_new3(size_t element_count, size_t element_size, size_t padding_size, void (*constructor)(void *), void (*destructor)(void *), void *(*alloc)(size_t), void (*dealloc)(void *, size_t)) { - const size_t heap_size = element_count * element_size + padding_size; - char * const heap_block = static_cast ( alloc ( heap_size )); - char *vec_base = heap_block; - - if ( NULL != vec_base ) { - st_heap_block3 heap ( dealloc, heap_block, heap_size ); + const size_t heap_size = calculate_allocation_size_or_throw( + element_count, element_size, padding_size); + char* const heap_block = static_cast(alloc(heap_size)); + char* vec_base = heap_block; + + if (NULL != vec_base) { + st_heap_block3 heap(dealloc, heap_block, heap_size); // put the padding before the array elements if ( 0 != padding_size ) { diff --git a/libcxxabi/test/cxa_vec_new_overflow_PR41395.pass.cpp b/libcxxabi/test/cxa_vec_new_overflow_PR41395.pass.cpp new file mode 100644 index 000000000000..67b060e5de3e --- /dev/null +++ b/libcxxabi/test/cxa_vec_new_overflow_PR41395.pass.cpp @@ -0,0 +1,122 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: libcxxabi-no-exceptions + +#include "cxxabi.h" +#include +#include + +void dummy_ctor(void*) { assert(false && "should not be called"); } +void dummy_dtor(void*) { assert(false && "should not be called"); } + +void *dummy_alloc(size_t) { assert(false && "should not be called"); } +void dummy_dealloc(void*) { assert(false && "should not be called"); } +void dummy_dealloc_sized(void*, size_t) { assert(false && "should not be called"); } + + +bool check_mul_overflows(size_t x, size_t y) { + size_t tmp = x * y; + if (tmp / x != y) + return true; + return false; +} + +bool check_add_overflows(size_t x, size_t y) { + size_t tmp = x + y; + if (tmp < x) + return true; + + return false; +} + +void test_overflow_in_multiplication() { + const size_t elem_count = std::size_t(1) << (sizeof(std::size_t) * 8 - 2); + const size_t elem_size = 8; + const size_t padding = 0; + assert(check_mul_overflows(elem_count, elem_size)); + + try { + __cxxabiv1::__cxa_vec_new(elem_count, elem_size, padding, dummy_ctor, + dummy_dtor); + assert(false && "allocation should fail"); + } catch (std::bad_array_new_length const&) { + // OK + } catch (...) { + assert(false && "unexpected exception"); + } + + try { + __cxxabiv1::__cxa_vec_new2(elem_count, elem_size, padding, dummy_ctor, + dummy_dtor, &dummy_alloc, &dummy_dealloc); + assert(false && "allocation should fail"); + } catch (std::bad_array_new_length const&) { + // OK + } catch (...) { + assert(false && "unexpected exception"); + } + + try { + __cxxabiv1::__cxa_vec_new3(elem_count, elem_size, padding, dummy_ctor, + dummy_dtor, &dummy_alloc, &dummy_dealloc_sized); + assert(false && "allocation should fail"); + } catch (std::bad_array_new_length const&) { + // OK + } catch (...) { + assert(false && "unexpected exception"); + } +} + +void test_overflow_in_addition() { + const size_t elem_size = 4; + const size_t elem_count = static_cast(-1) / 4u; +#if defined(_LIBCXXABI_ARM_EHABI) + const size_t padding = 8; +#else + const size_t padding = sizeof(std::size_t); +#endif + assert(!check_mul_overflows(elem_count, elem_size)); + assert(check_add_overflows(elem_count * elem_size, padding)); + try { + __cxxabiv1::__cxa_vec_new(elem_count, elem_size, padding, dummy_ctor, + dummy_dtor); + assert(false && "allocation should fail"); + } catch (std::bad_array_new_length const&) { + // OK + } catch (...) { + assert(false && "unexpected exception"); + } + + + try { + __cxxabiv1::__cxa_vec_new2(elem_count, elem_size, padding, dummy_ctor, + dummy_dtor, &dummy_alloc, &dummy_dealloc); + assert(false && "allocation should fail"); + } catch (std::bad_array_new_length const&) { + // OK + } catch (...) { + assert(false && "unexpected exception"); + } + + try { + __cxxabiv1::__cxa_vec_new3(elem_count, elem_size, padding, dummy_ctor, + dummy_dtor, &dummy_alloc, &dummy_dealloc_sized); + assert(false && "allocation should fail"); + } catch (std::bad_array_new_length const&) { + // OK + } catch (...) { + assert(false && "unexpected exception"); + } +} + +int main(int, char**) { + test_overflow_in_multiplication(); + test_overflow_in_addition(); + + return 0; +}