[libcxx-commits] [libcxxabi] r357814 - Fix PR41395 - __cxa_vec_new may overflow in allocation size calculation.

Eric Fiselier via libcxx-commits libcxx-commits at lists.llvm.org
Fri Apr 5 13:38:44 PDT 2019


Author: ericwf
Date: Fri Apr  5 13:38:43 2019
New Revision: 357814

URL: http://llvm.org/viewvc/llvm-project?rev=357814&view=rev
Log:
Fix PR41395 - __cxa_vec_new may overflow in allocation size calculation.

Added:
    libcxxabi/trunk/test/cxa_vec_new_overflow_PR41395.pass.cpp
Modified:
    libcxxabi/trunk/src/cxa_vector.cpp

Modified: libcxxabi/trunk/src/cxa_vector.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/src/cxa_vector.cpp?rev=357814&r1=357813&r2=357814&view=diff
==============================================================================
--- libcxxabi/trunk/src/cxa_vector.cpp (original)
+++ libcxxabi/trunk/src/cxa_vector.cpp Fri Apr  5 13:38:43 2019
@@ -13,6 +13,9 @@
 #include "cxxabi.h"
 
 #include <exception>        // for std::terminate
+#include <new>              // 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
 }
 
 
-
 // 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<char *> ( 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<char*>(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<char *> ( 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<char*>(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 ) {

Added: libcxxabi/trunk/test/cxa_vec_new_overflow_PR41395.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/test/cxa_vec_new_overflow_PR41395.pass.cpp?rev=357814&view=auto
==============================================================================
--- libcxxabi/trunk/test/cxa_vec_new_overflow_PR41395.pass.cpp (added)
+++ libcxxabi/trunk/test/cxa_vec_new_overflow_PR41395.pass.cpp Fri Apr  5 13:38:43 2019
@@ -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 <new>
+#include <cassert>
+
+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<size_t>(-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;
+}




More information about the libcxx-commits mailing list