ACE VERSION: 6.1.2
HOST MACHINE and OPERATING SYSTEM: Windows 7 SP1
COMPILER NAME AND VERSION: VS2008 SP1
AREA/CLASS/EXAMPLE AFFECTED: Message_Block::clone
DOES THE PROBLEM AFFECT:
EXECUTION?
The Message_Block::clone method allocates too much memory
SYNOPSIS: When using a specially crafted Message_Block chain, similar to one that might be build by TAO, the close method will allocate too much memory
DESCRIPTION:
The following method builds a message block chain similar to what TAO might build when a method expects to receive an array of strings. It is composed of one data block, of size 1,000,000 bytes and a multitude of Message_Blocks that point into this Data_Block.
When the clone method is called on this chain, each Message_Block will copy the full 1,000,000 bytes buffer instead of only copying the appropriate size from the large data block.
REPEAT BY:
Trying to execute this test in the Message_Block_test.cpp test:
int DoTest2()
{
// prepare data block so it simulates an array of strings...
size_t datalen(1000000);
ACE_Data_Block dataBlock(datalen, ACE_Message_Block::MB_DATA, 0,0, 0, 0, 0);
ACE_Message_Block mainBlock(&dataBlock, ACE_Message_Block::DONT_DELETE, 0);
for (ACE_Message_Block *ptr = &mainBlock; datalen > 0;)
{
#undef min
ACE_CDR::ULong size = 11;
size_t toCopy(std::min(datalen, sizeof(size) + size));
if (toCopy < sizeof(size))
break; // no more space
size = toCopy - sizeof(size);
ACE_OS::memcpy(ptr->wr_ptr(), &size, sizeof(size));
ptr->wr_ptr(sizeof(size));
ACE_OS::memcpy(ptr->wr_ptr(), "1234567890", size);
ptr->wr_ptr(size);
if (size == 11)
{ // create next block only if not last block
ACE_Message_Block *cont = ACE_Message_Block::duplicate(ptr);
cont->rd_ptr(cont->wr_ptr());
ptr->cont(cont);
ptr = cont;
}
datalen -= toCopy;
}
// now, clone the mainblock:
// this should take a lot of memory, probably the program will crash, or on Windows, the system will hang
ACE_Message_Block *clonedBlock = mainBlock.clone();
// release all allocated memory
clonedBlock->release();
mainBlock.cont()->release();
return 0;
}
SAMPLE FIX/WORKAROUND:
The following seems to correct the problem:
--- C:/cvsdata/third-party4.3/ACE_wrappers/ace/Message_Block.cpp mer. sept. 21 10:51:23 2011
+++ C:/cvsdata/third-party4.3/ACE_wrappers/ace/Message_Block - copy.cpp mar. juin 5 12:14:40 2012
@@ -1182,11 +1182,27 @@
{
// Get a pointer to a "cloned"<ACE_Data_Block> (will copy the
// values rather than increment the reference count).
- ACE_Data_Block *db = old_message_block->data_block ()->clone (mask);
+ ACE_Data_Block *oldBlock = old_message_block->data_block();
+ ACE_Data_Block *db(0);
+ ACE_NEW_MALLOC_RETURN (db,
+ static_cast<ACE_Data_Block*> (
+ oldBlock->data_block_allocator()->malloc (sizeof (ACE_Data_Block))),
+ ACE_Data_Block (old_message_block->length(), // size
+ oldBlock->msg_type(), // type
+ 0, // data
+ oldBlock->allocator_strategy(), // allocator
+ oldBlock->locking_strategy(), // locking strategy
+ oldBlock->flags(), // flags
+ oldBlock->data_block_allocator()),
+ 0);
+
if (db == 0)
return 0;
+ db->clr_flags(ACE_Message_Block::DONT_DELETE);
+ ACE_OS::memcpy(db->base(), old_message_block->rd_ptr (), old_message_block->length());
+
if(old_message_block->message_block_allocator_ == 0)
{
ACE_NEW_RETURN (new_message_block,
@@ -1241,8 +1257,7 @@
// Set the read and write pointers in the new <Message_Block> to the
// same relative offset as in the existing <Message_Block>.
- new_message_block->rd_ptr (old_message_block->rd_ptr_);
- new_message_block->wr_ptr (old_message_block->wr_ptr_);
+ new_message_block->wr_ptr (old_message_block->length());
// save the root message block to return
if (new_root_message_block == 0)
new_root_message_block = new_message_block;