I apologize for this rather localized question, but I was hoping to get someone else's view on it to make sure I'm not doing something obviously wrong.
I believe I've run into a bug in either the Visual C++ Runtime library, or somewhere in Microsoft's implementation of std::stringstream
. The issue only manifests itself with a combination of when:
imbue()
is called to change the locale on a stringstream
, andoperator new
is used which returns a pointer offset from the base address returned by malloc()
used to allocate the block.I've been able to reproduce this with the following minimal test case:
#include <sstream>
static void *localMalloc(size_t bytes)
{
unsigned char *ptr = static_cast<unsigned char *>( malloc(bytes + 64) );
ptr += 64;
printf("malloc of %d bytes: %ph\n", bytes, ptr);
return ptr;
}
void *operator new(size_t bytes) { return localMalloc(bytes); }
void *operator new[](size_t bytes) { return localMalloc(bytes); }
void operator delete(void *ptr) throw() { /* do nothing */ }
void operator delete[](void *ptr) throw() { /* do nothing */ }
struct DecimalSeparator : std::numpunct<char>
{
char do_decimal_point() const
{
return '.';
}
};
int main()
{
std::stringstream ss;
ss.imbue(std::locale(std::locale(), new DecimalSeparator));
ss << 5; // <-- is_block_type_valid(header->_block_use) assertion failure here
return 0;
}
If either:
ptr += 64;
line in localMalloc()
orss.imbue()
call in main()
are commented out, the code works as expected, and the assertion does not happen.
I have attempted to step into the code using the debugger as much as I possibly can, but I am currently unable to pinpoint the location at where the code fails in the STL as Visual Studio dumps me into raw disassembly mode after stepping out of basic_stringbuf::overflow()
making debugging nearly impossible. As far as I can tell, I haven't seen any invalid memory writes outside of the memory being allocated, so I'm not completely sure where the CRT is checking the heap or why it thinks the pointer is invalid.
Note that operator delete
is intentionally ignoring the free in this test case for brevity. It makes no difference if free()
is properly called on the memory block or not.
So far, tests on the following compilers and platforms have resulted in:
Does anyone see anything odd here that I missed?
This is more than likely not a bug, but instead your version of delete
is not being called, and instead the Visual Studio's debug runtime library's version of the global delete
is called. Having two or more versions of the global delete
operator in the same program is undefined behavior.
From this reference (Global replacements), the behavior is stated to be undefined when this occurs.
From the C++ ISO standard:
3.7.4 Dynamic storage duration [basic.stc.dynamic]
//...§2 The library provides default definitions for the global allocation and deallocation functions. Some global allocation and deallocation functions are replaceable (18.6.1). A C++ program shall provide at most one definition of a replaceable allocation or deallocation function.
Running with Visual Studio 2015 using the release version Visual Studio runtime library does not produce this error, and the replacement global delete
does in fact get called.