std::locale implementation in CRT assumes all facets to be allocated on CRT heap and crashes in destructor in debug mode if a facet was allocated by a custom allocator - by RuleBreaker

Status : 

 


8
0
Sign in
to vote
ID 750951 Comments
Status Active Workarounds
Type Bug Repros 7
Opened 6/26/2012 9:20:19 AM
Access Restriction Public

Description

locale::_Locimp::_Locimp_dtor(_Locimp* _This) (line 169 of %Visual Studio Installation directory%\VC\crt\src\locale0.cpp) deletes all facets using DELETE_CRT macro. This macro calls the function _DebugHeapDelete which, in turn, calls the function free on the facet pointer. The function free (_free_dbg_nolock) checks whether a pointer is in the range of the crt heap and asserts if not. If a facet has been allocated by a custom allocator not on a crt heap, the assert will be triggered.

This crash is reproducible in the debug mode using tbb::scalable_malloc from the open source Thread Building blocks library. For example, overload operator new using tbb::scalable_malloc, create a facet object using the new operator and add it to the global locale - see "Steps to reproduce"


The implemenation of std::locale is now making an implicit assumption that a facet is always allocated on crt heap. This assumption is wrong. At least facets added yb the user should be destroyed by the delete operator.
Sign in to post a comment.
Posted by Microsoft on 10/22/2013 at 1:26 PM
Hi again,

I've fixed this bug, and the fix will be available in the next major version of VC after 2013. Please note that because the fix breaks binary compatibility, it must be shipped in a new major version and cannot be shipped in an Update.

Stephan T. Lavavej
Senior Developer - Visual C++ Libraries
stl@microsoft.com
Posted by Microsoft on 3/26/2013 at 12:35 AM
Hi,

Thanks for reporting this bug. I wanted to let you know what's happening with it. I'm still keeping track of it, but it's been resolved as "Deferred" because we may not have time to fix it in VC12. (Note: VC8 = VS 2005, VC9 = VS 2008, VC10 = VS 2010, VC11 = VS 2012.)

Note: Connect doesn't notify me about comments. If you have any further questions, please E-mail me.

Stephan T. Lavavej
Senior Developer - Visual C++ Libraries
stl@microsoft.com
Posted by dave_fuller on 11/13/2012 at 2:19 PM
Trivially reproducible for me as well, using the following program:

#include <locale>

void* operator new(size_t count)
{
if (count == 0)
     return 0;
void* p = std::malloc(count + 8);
if (!p)
     throw std::bad_alloc();
unsigned char* px = reinterpret_cast<unsigned char*>(p);
return px + 8;
}

void operator delete(void* p) throw()
{
if (p == 0)
     return;
unsigned char* px = reinterpret_cast<unsigned char*>(p);
std::free(px - 8);
}

class facet : public std::codecvt<wchar_t, char, std::mbstate_t>
{
};

int main(void)
{
std::locale loc(std::locale(), new facet);
return 0;
}
Posted by FlamingMike on 8/10/2012 at 7:53 AM
Unfortunatly, this seems to be only happening in debug/x64 builds.

std::facet class defines correct operator new/delete for other targets.

Since facet class is a dllexport/import class, it cannot be resolved without rebuilding the runtime. Doing so break binary compatibility and is not possible at this stage of VS2012.
Posted by FlamingMike on 8/8/2012 at 10:54 AM
Forgot to mention the fact is created on the stack as well:

UTF16Facet myUTF16Facet;
std::locale unicodeLocale(std::locale(), &myUTF16Facet);

Posted by FlamingMike on 8/8/2012 at 8:54 AM
In out case, the problem came from a custom facet used to output correct utf16 in binary files:

class UTF16Facet: public std::codecvt<char16_t, char, std::char_traits<char16_t>::state_type>
(...)

and was used like this:

std::locale unicodeLocale(std::locale(), new UTF16Facet);

and

std::u16ofstream myStream;
myStream.imbue(unicodeLocale);
...


As a workaround, I changed the facet class to increment refcount at creation:

     UTF16Facet() : std::codecvt<char16_t, char, std::char_traits<char16_t>::state_type>()
        {
            _Incref();
        }

This way, when _Locimp_dtor calls _DELETE_CRT(_This->_Facetvec[count]->_Decref()); it won't delete our custom facet and because of the refcount > 1.

Facet won't leak either because it will be destroyed on the stack later on.



Posted by FlamingMike on 8/8/2012 at 8:12 AM
We experience this issue under VC10 as well with our custom allocator.
Posted by Microsoft on 6/26/2012 at 11:39 PM
Thanks for your feedback.

We are rerouting this issue to the appropriate group within the Visual Studio Product Team for triage and resolution. These specialized experts will follow-up with your issue.
Posted by Microsoft on 6/26/2012 at 9:49 AM
Thank you for your feedback, we are currently reviewing the issue you have submitted. If this issue is urgent, please contact support directly(http://support.microsoft.com)