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 astringstream
, and- A custom global
operator new
is used which returns a pointer offset from the base address returned bymalloc()
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:
- The
ptr += 64;
line inlocalMalloc()
or - The
ss.imbue()
call inmain()
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:
- VS2015 Update 2: fails
- VS2015 Update 3: fails
- VS2015 Update 3 + KB3165756: fails
- clang-703.0.31 on OSX: OK
Does anyone see anything odd here that I missed?