From f92a504fdd943527687faf9557e0b39ff7fe6125 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 5 Oct 2020 15:16:58 +0100 Subject: [PATCH] libstdc++: Make allocators throw bad_array_new_length on overflow [LWG 3190] std::allocator and std::pmr::polymorphic_allocator should throw std::bad_array_new_length from their allocate member functions if the number of bytes required cannot be represented in std::size_t. libstdc++-v3/ChangeLog: * config/abi/pre/gnu.ver: Add new symbol. * include/bits/functexcept.h (__throw_bad_array_new_length): Declare new function. * include/ext/malloc_allocator.h (malloc_allocator::allocate): Throw bad_array_new_length for impossible sizes (LWG 3190). * include/ext/new_allocator.h (new_allocator::allocate): Likewise. * include/std/memory_resource (polymorphic_allocator::allocate) (polymorphic_allocator::allocate_object): Use new function, __throw_bad_array_new_length. * src/c++11/functexcept.cc (__throw_bad_array_new_length): Define. * testsuite/20_util/allocator/lwg3190.cc: New test. --- libstdc++-v3/config/abi/pre/gnu.ver | 3 ++ libstdc++-v3/include/bits/functexcept.h | 3 ++ libstdc++-v3/include/ext/malloc_allocator.h | 10 +++- libstdc++-v3/include/ext/new_allocator.h | 10 +++- libstdc++-v3/include/std/memory_resource | 6 +-- libstdc++-v3/src/c++11/functexcept.cc | 4 ++ .../testsuite/20_util/allocator/lwg3190.cc | 53 +++++++++++++++++++ 7 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/allocator/lwg3190.cc diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 87a48a21f53..6a2b2da33f5 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -2322,6 +2322,9 @@ GLIBCXX_3.4.29 { # std::__atomic_futex_unsigned_base::_M_futex_wait_until_steady _ZNSt28__atomic_futex_unsigned_base26_M_futex_wait_until_steady*; + # std::__throw_bad_array_new_length() + _ZSt28__throw_bad_array_new_lengthv; + } GLIBCXX_3.4.28; # Symbols in the support library (libsupc++) have their own tag. diff --git a/libstdc++-v3/include/bits/functexcept.h b/libstdc++-v3/include/bits/functexcept.h index 52eef2bb2c6..f6079e2a535 100644 --- a/libstdc++-v3/include/bits/functexcept.h +++ b/libstdc++-v3/include/bits/functexcept.h @@ -51,6 +51,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void __throw_bad_alloc(void) __attribute__((__noreturn__)); + void + __throw_bad_array_new_length(void) __attribute__((__noreturn__)); + // Helper for exception objects in void __throw_bad_cast(void) __attribute__((__noreturn__)); diff --git a/libstdc++-v3/include/ext/malloc_allocator.h b/libstdc++-v3/include/ext/malloc_allocator.h index 366c766f25b..dd45470c456 100644 --- a/libstdc++-v3/include/ext/malloc_allocator.h +++ b/libstdc++-v3/include/ext/malloc_allocator.h @@ -102,8 +102,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Tp* allocate(size_type __n, const void* = 0) { - if (__n > this->_M_max_size()) - std::__throw_bad_alloc(); + if (__builtin_expect(__n > this->_M_max_size(), false)) + { + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3190. allocator::allocate sometimes returns too little storage + if (__n > (std::size_t(-1) / sizeof(_Tp))) + std::__throw_bad_array_new_length(); + std::__throw_bad_alloc(); + } _Tp* __ret = 0; #if __cpp_aligned_new diff --git a/libstdc++-v3/include/ext/new_allocator.h b/libstdc++-v3/include/ext/new_allocator.h index 2e21a98409f..a43c8d9b6fb 100644 --- a/libstdc++-v3/include/ext/new_allocator.h +++ b/libstdc++-v3/include/ext/new_allocator.h @@ -102,8 +102,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_NODISCARD _Tp* allocate(size_type __n, const void* = static_cast(0)) { - if (__n > this->_M_max_size()) - std::__throw_bad_alloc(); + if (__builtin_expect(__n > this->_M_max_size(), false)) + { + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3190. allocator::allocate sometimes returns too little storage + if (__n > (std::size_t(-1) / sizeof(_Tp))) + std::__throw_bad_array_new_length(); + std::__throw_bad_alloc(); + } #if __cpp_aligned_new if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) diff --git a/libstdc++-v3/include/std/memory_resource b/libstdc++-v3/include/std/memory_resource index 3db22978294..6491179a7c5 100644 --- a/libstdc++-v3/include/std/memory_resource +++ b/libstdc++-v3/include/std/memory_resource @@ -168,8 +168,8 @@ namespace pmr allocate(size_t __n) __attribute__((__returns_nonnull__)) { - if (__n > (__gnu_cxx::__int_traits::__max / sizeof(_Tp))) - _GLIBCXX_THROW_OR_ABORT(bad_array_new_length()); + if ((__gnu_cxx::__int_traits::__max / sizeof(_Tp)) < __n) + std::__throw_bad_array_new_length(); return static_cast<_Tp*>(_M_resource->allocate(__n * sizeof(_Tp), alignof(_Tp))); } @@ -195,7 +195,7 @@ namespace pmr allocate_object(size_t __n = 1) { if ((__gnu_cxx::__int_traits::__max / sizeof(_Up)) < __n) - _GLIBCXX_THROW_OR_ABORT(bad_array_new_length()); + std::__throw_bad_array_new_length(); return static_cast<_Up*>(allocate_bytes(__n * sizeof(_Up), alignof(_Up))); } diff --git a/libstdc++-v3/src/c++11/functexcept.cc b/libstdc++-v3/src/c++11/functexcept.cc index d43167d6f88..b5da1746c09 100644 --- a/libstdc++-v3/src/c++11/functexcept.cc +++ b/libstdc++-v3/src/c++11/functexcept.cc @@ -53,6 +53,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __throw_bad_alloc() { _GLIBCXX_THROW_OR_ABORT(bad_alloc()); } + void + __throw_bad_array_new_length() + { _GLIBCXX_THROW_OR_ABORT(bad_array_new_length()); } + void __throw_bad_cast() { _GLIBCXX_THROW_OR_ABORT(bad_cast()); } diff --git a/libstdc++-v3/testsuite/20_util/allocator/lwg3190.cc b/libstdc++-v3/testsuite/20_util/allocator/lwg3190.cc new file mode 100644 index 00000000000..955f05b22a1 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/allocator/lwg3190.cc @@ -0,0 +1,53 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-do run { target c++11 } } + +#include +#include +#include +#include + +// LWG 3190. std::allocator::allocate sometimes returns too little storage + +void +test01() +{ + struct A { char biiiiig[1 << 16]; }; + std::allocator a; + try + { + std::size_t max = std::numeric_limits::max() / sizeof(A); + A* p = a.allocate(max + 1); + throw p; + } +#if __cplusplus >= 201103L + catch (const std::bad_array_new_length&) + { + } +#endif + catch (const std::bad_alloc&) + { + VERIFY( __cplusplus < 201103L ); + } +} + +int +main() +{ + test01(); +}