`<format>`: `_malloc_dbg` allocations freed with `delete` in `std::format` (debug builds)
Describe the bug
In MSVC STL debug builds, std::format appears to allocate temporary buffers using _malloc_dbg (or another internal heap allocation method) but frees them using the delete operator. This is a violation of C++ memory management rules, as memory allocated with _malloc_dbg must be freed with _free_dbg, and memory allocated with new must be freed with delete.
This behavior can lead to undefined behavior, including heap corruption or crashes, especially when using debug builds with CRT memory checks enabled.
Command-line test case
C:\Temp>type repro.cpp
#include <crtdbg.h>
#include <iostream>
#include <string>
#include <format>
#include <new>
// Custom new/delete to detect misuse
void* operator new(std::size_t size)
{
void* p = std::malloc(size);
if (!p) throw std::bad_alloc{};
std::cout << "[new] size = " << size << "p = " << p << std::endl;
return p;
}
void operator delete(void* p) noexcept
{
std::cout << "[delete] ptr = " << p << std::endl;
std::free(p);
}
static std::locale myLocale("en_US.UTF-8");
int main()
{
int value = 123456;
std::cout << "Calling std::format..." << std::endl;
std::string formatted = std::format(myLocale, "{:L}", value); // simple format
std::cout << "Result: " << formatted << std::endl;
return 0;
}
C:\Temp>cl /std:c++20 /MDd /Zi /EHsc repro.cpp
<I dont know what the template wants from me here?>
Expected behavior
The output should have a delete line matching every new line, but there is an extra delete line, which was never newed. This is the output:
[new] size = 16p = 0000017AFF503990
[delete] ptr = 0000017AFF503990
Calling std::format...
[new] size = 16p = 0000017AFF521C10
[new] size = 16p = 0000017AFF522700
[delete] ptr = 0000017AFF521C60
[delete] ptr = 0000017AFF522700
Result: 123,456
[delete] ptr = 0000017AFF521C10
You can see there is one extra delete.
if you change the string formatting line to something like:
std::string formatted = std::string("") + std::to_string(value); // simple format
then the news and deletes match up
Possibly duplicate of #160.
Possibly duplicate of #160.
Nope. _malloc_dbg is a red herring. What's happening is that std::format calls std::numpunct<char>::do_grouping in the runtime DLL, which returns a string created using the DLL's operator new. The string is later destroyed inside std::format, using the replaced operator delete.
I think this is by design, albeit surprising.
Ugh, separately compiled facets strike again.
I don't see a way around this before vNext.