diff --git a/include/fmt/format.h b/include/fmt/format.h index 6b03e857fa066..5d90f443fb008 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -742,9 +742,11 @@ void basic_memory_buffer::grow(size_t size) { #ifdef FMT_FUZZ if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much"); #endif + const size_t max_size = std::allocator_traits::max_size(alloc_); size_t old_capacity = this->capacity(); size_t new_capacity = old_capacity + old_capacity / 2; if (size > new_capacity) new_capacity = size; + else if (new_capacity > max_size) new_capacity = (std::max)(size, max_size); T* old_data = this->data(); T* new_data = std::allocator_traits::allocate(alloc_, new_capacity); diff --git a/test/format-test.cc b/test/format-test.cc index 128b57a2e10ee..bdc3c1c229cf3 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -354,6 +354,33 @@ TEST(MemoryBufferTest, ExceptionInDeallocate) { EXPECT_CALL(alloc, deallocate(&mem2[0], 2 * size)); } +TEST(MemoryBufferTest, AllocatorMaxSize) { + // 160 = 128 + 32 + typedef allocator_max_size, 160> TestAllocator; + basic_memory_buffer buffer; + buffer.resize(128); + bool throws_on_resize = false; + try { + // new_capacity = 128 + 128/2 = 192 > 160 + buffer.resize(160); + } catch (const std::exception &) { + throws_on_resize = true; + } + EXPECT_EQ(throws_on_resize, false); +} + +TEST(MemoryBufferTest, AllocatorMaxSizeOverflow) { + typedef allocator_max_size, 160> TestAllocator; + basic_memory_buffer buffer; + bool throws_on_resize = false; + try { + buffer.resize(161); + } catch (const std::exception &) { + throws_on_resize = true; + } + EXPECT_EQ(throws_on_resize, true); +} + TEST(UtilTest, UTF8ToUTF16) { fmt::detail::utf8_to_utf16 u("лошадка"); EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str()); diff --git a/test/mock-allocator.h b/test/mock-allocator.h index 513a62e8bb32c..af393db0121c6 100644 --- a/test/mock-allocator.h +++ b/test/mock-allocator.h @@ -57,4 +57,24 @@ template class allocator_ref { void deallocate(value_type* p, size_t n) { alloc_->deallocate(p, n); } }; +template +class allocator_max_size: public Allocator { + public: + typedef typename Allocator::value_type value_type; + size_t max_size() const FMT_NOEXCEPT { + return MaxSize; + } + value_type* allocate(size_t n) { + if (n > max_size()) { + throw std::length_error("size > max_size"); + } + return std::allocator_traits::allocate( + *static_cast(this), n); + } + void deallocate(value_type* p, size_t n) { + std::allocator_traits::deallocate( + *static_cast(this), p, n); + } +}; + #endif // FMT_MOCK_ALLOCATOR_H_