diff --git a/include/my_alloc.h b/include/my_alloc.h index 5bf45bbdcef..5c2a55b216c 100644 --- a/include/my_alloc.h +++ b/include/my_alloc.h @@ -142,6 +142,7 @@ struct MEM_ROOT { * The returned pointer will always be 8-aligned. */ void *Alloc(size_t length) MY_ATTRIBUTE((malloc)) { + assert(length <= ALIGN_SIZE(length)); length = ALIGN_SIZE(length); // Skip the straight path if simulating OOM; it should always fail. @@ -321,8 +322,8 @@ struct MEM_ROOT { /** * Allocate a new block of at least “minimum_length” bytes; usually more. * This holds no matter how many bytes are free in the current block. - * The new black will always become the current block, ie., the next call - * to Peek() will return the newlyy allocated block. (This is different + * The new block will always become the current block, ie., the next call + * to Peek() will return the newly allocated block. (This is different * from Alloc(), where it is possible to allocate a new block that is * not made into the current block.) * diff --git a/mysys/my_alloc.cc b/mysys/my_alloc.cc index 6aa46e4a0d0..7f5b8e3d17e 100644 --- a/mysys/my_alloc.cc +++ b/mysys/my_alloc.cc @@ -83,7 +83,14 @@ MEM_ROOT::Block *MEM_ROOT::AllocBlock(size_t wanted_length, } } + // Any successful allocation should have enough memory to satisfy + // minimum_length. + if (unlikely(length < minimum_length)) { + length = ALIGN_SIZE(minimum_length); + } + const size_t bytes_to_alloc = length + ALIGN_SIZE(sizeof(Block)); + assert(length < bytes_to_alloc); Block *new_block = static_cast( my_malloc(m_psi_key, bytes_to_alloc, MYF(MY_WME | ME_FATALERROR))); if (new_block == nullptr) { @@ -97,6 +104,8 @@ MEM_ROOT::Block *MEM_ROOT::AllocBlock(size_t wanted_length, // Make the default block size 50% larger next time. // This ensures O(1) total mallocs (assuming Clear() is not called). if (!MEM_ROOT_SINGLE_CHUNKS) { + // Note: Block size could be zero by zerofill as in mysql_init(). + assert(m_block_size <= m_block_size + m_block_size / 2); m_block_size += m_block_size / 2; } return new_block; @@ -151,6 +160,7 @@ bool MEM_ROOT::ForceNewBlock(size_t minimum_length) { if (MEM_ROOT_SINGLE_CHUNKS) { assert(m_block_size == m_orig_block_size); } + assert(m_block_size <= ALIGN_SIZE(m_block_size)); Block *new_block = AllocBlock(/*wanted_length=*/ALIGN_SIZE(m_block_size), minimum_length); // Will modify block_size. if (new_block == nullptr) return true; diff --git a/unittest/gunit/my_alloc-t.cc b/unittest/gunit/my_alloc-t.cc index b5d480d018b..fda8a09812b 100644 --- a/unittest/gunit/my_alloc-t.cc +++ b/unittest/gunit/my_alloc-t.cc @@ -202,6 +202,16 @@ TEST_F(MyAllocTest, RawInterface) { // The value should still be there. EXPECT_STREQ("12345", store_ptr); + + // Get a new block to satisfy more than the current block size (512 * 1.5^2). + EXPECT_FALSE(alloc.ForceNewBlock(2048)); + block = alloc.Peek(); + EXPECT_EQ(2048, block.second - block.first); + + // This should give an assertion because ALIGN_SIZE(ULONG_MAX) overflows. + // MEM_ROOT alloc2(PSI_NOT_INSTRUMENTED, ULONG_MAX); + // EXPECT_TRUE(alloc2.Alloc(10) != nullptr); + // EXPECT_EQ(ALIGN_SIZE(10),alloc2.allocated_size()); } TEST_F(MyAllocTest, ArrayAllocInitialization) {