Correctly handle zero-sized allocations in polymorphic memory resources.

This commit is contained in:
Ion Gaztañaga
2026-07-10 15:17:20 +02:00
parent 9bf2ace3ec
commit 3f69b6f47f
5 changed files with 122 additions and 0 deletions
+5
View File
@@ -145,6 +145,11 @@ void* monotonic_buffer_resource::do_allocate(std::size_t bytes, std::size_t alig
#endif
}
//A zero-sized request must still work correctly so treat it as a 1-byte allocation
if(!bytes){
bytes = 1u;
}
//See if there is room in current buffer
std::size_t aligner = 0u;
if(this->remaining_storage(alignment, aligner) < bytes){
+23
View File
@@ -116,6 +116,11 @@ void test_new_delete_resource()
#endif
mr->deallocate(addr, 16, 1);
BOOST_TEST(memcount == allocation_count);
//A zero-sized allocation must still work
void *const zero_addr = mr->allocate(0, 1);
mr->deallocate(zero_addr, 0, 1);
BOOST_TEST(memcount == allocation_count);
}
#endif //BOOST_CONTAINER_ASAN
@@ -143,6 +148,24 @@ void test_null_memory_resource()
BOOST_TEST(bad_allocexception_thrown == true);
if(p)
mr->deallocate(p, 1, 1);
//A zero-sized allocation must also fail (the standard permits a zero
//request to fail, and null_memory_resource always throws).
bool zero_bad_alloc_thrown = false;
void *pz = 0;
BOOST_CONTAINER_TRY{
pz = mr->allocate(0, 1);
}
BOOST_CONTAINER_CATCH(std::bad_alloc&) {
zero_bad_alloc_thrown = true;
}
BOOST_CONTAINER_CATCH(...) {
}
BOOST_CONTAINER_CATCH_END
BOOST_TEST(zero_bad_alloc_thrown == true);
if(pz)
mr->deallocate(pz, 0, 1);
#endif //BOOST_NO_EXCEPTIONS
}
+37
View File
@@ -360,6 +360,42 @@ void test_do_allocate()
BOOST_TEST(mrl.m_info.size() == 0u);
}
void test_do_allocate_zero()
{
//Regression test: after exhausting an external buffer, a zero-sized
//allocation requesting an alignment that needs padding used to underflow
//remaining_storage() (m_current_buffer_size wrapped around to SIZE_MAX)
//because the padding alone did not fit in the current buffer. In addition,
//a zero-sized allocation must return a non-null pointer to distinct storage.
memory_resource_logger mrl;
//An 8-aligned buffer of 7 bytes reproduces the reported scenario.
boost::move_detail::aligned_storage<8u, 8u>::type storage;
const std::size_t buffer_size = 7u;
{
monotonic_buffer_resource m(&storage, buffer_size, &mrl);
BOOST_TEST(m.remaining_storage(1u) == buffer_size);
//Exhaust the buffer with a byte-aligned allocation
void *const p_full = m.allocate(buffer_size, 1u);
BOOST_TEST(p_full != 0);
BOOST_TEST(m.remaining_storage(1u) == 0u);
BOOST_TEST(mrl.m_info.size() == 0u);
//A zero-sized allocation on an exhausted buffer must still work
void *const p_zero1 = m.allocate(0u, 1u);
BOOST_TEST(p_zero1 != 0);
BOOST_TEST(m.remaining_storage(1u) < m.next_buffer_size());
//A zero-sized allocation with a stricter alignment must honor it
void *const p_zero2 = m.allocate(0u, 4u);
BOOST_TEST((std::size_t(p_zero2) % 4u) == 0u);
BOOST_TEST(m.remaining_storage(1u) < m.next_buffer_size());
}
//All upstream memory must have been released
BOOST_TEST(mrl.m_mismatches == 0u);
BOOST_TEST(mrl.m_info.size() == 0u);
}
void test_do_deallocate()
{
memory_resource_logger mrl;
@@ -477,6 +513,7 @@ int main()
test_upstream_resource();
test_do_allocate();
test_do_allocate_zero();
test_do_deallocate();
test_do_is_equal();
test_release();
+37
View File
@@ -390,6 +390,42 @@ void test_do_allocate_deallocate()
BOOST_TEST(mrl.m_info.size() == 0u);
}
template<class PoolResource>
void test_do_allocate_zero()
{
//A zero-sized allocation must return a valid, non-null pointer that can be
//deallocated, and successive zero-sized allocations must return distinct
//blocks (pooled allocations never alias live blocks).
memory_resource_logger mrl;
{
derived_from_pool_resource<PoolResource> dmbr(&mrl);
//Zero bytes is served from the smallest pool
void *p0 = dmbr.do_allocate(0u, 1u);
BOOST_TEST(p0 != 0);
void *p1 = dmbr.do_allocate(0u, 1u);
BOOST_TEST(p1 != 0);
//Distinct live blocks must not alias
BOOST_TEST(p0 != p1);
//Both come from the smallest pool (index 0)
BOOST_TEST(dmbr.pool_index(0u) == 0u);
//Round-trip deallocation must not report mismatches
dmbr.do_deallocate(p1, 0u, 1u);
dmbr.do_deallocate(p0, 0u, 1u);
//Deallocated zero-sized blocks are cached, not returned to upstream
BOOST_TEST(dmbr.pool_cached_blocks(0u) >= 1u);
//A max-aligned zero-sized allocation must also succeed
void *p2 = dmbr.do_allocate(0u, memory_resource::max_align);
BOOST_TEST(p2 != 0);
BOOST_TEST((std::size_t(p2) % memory_resource::max_align) == 0u);
dmbr.do_deallocate(p2, 0u, memory_resource::max_align);
}
//All memory must have been released back to upstream on destruction
BOOST_TEST(mrl.m_mismatches == 0u);
BOOST_TEST(mrl.m_info.size() == 0u);
}
template<class PoolResource>
void test_do_is_equal()
{
@@ -487,6 +523,7 @@ void test_pool_resource()
test_options_constructor<PoolResource>();
test_options<PoolResource>();
test_do_allocate_deallocate<PoolResource>();
test_do_allocate_zero<PoolResource>();
test_do_is_equal<PoolResource>();
test_release<PoolResource>();
test_destructor<PoolResource>();
+20
View File
@@ -188,6 +188,15 @@ void test_do_allocate_deallocate()
//new_allocator, high alignment
mr.deallocate(mr.allocate(16, max_alignment_value*4u), 16, max_alignment_value*4u);
//new_allocator, zero size, low alignment: must return a non-null pointer
void *pz = mr.allocate(0, 1);
mr.deallocate(pz, 0, 1);
//new_allocator, zero size, high alignment
void *pza = mr.allocate(0, max_alignment_value*4u);
BOOST_TEST((std::size_t(pza) % (max_alignment_value*4u)) == 0u);
mr.deallocate(pza, 0, max_alignment_value*4u);
}
{
typedef resource_adaptor<std ::allocator<int> > new_resource_alloc_t;
@@ -199,6 +208,17 @@ void test_do_allocate_deallocate()
//std::allocator, high alignment
mr.deallocate(mr.allocate(16, max_alignment_value*4u), 16, max_alignment_value*4u);
//std::allocator, zero size, low alignment: must return a non-null pointer
void *pz = mr.allocate(0, 1);
mr.deallocate(pz, 0, 1);
//std::allocator, zero size, high alignment: must return a non-null,
//correctly aligned pointer
void *pza = mr.allocate(0, max_alignment_value*4u);
BOOST_TEST(pza != 0);
BOOST_TEST((std::size_t(pza) % (max_alignment_value*4u)) == 0u);
mr.deallocate(pza, 0, max_alignment_value*4u);
}
}