STL std::string class causes crashes and memory corruption on multi-processor machines (813810)
The information in this article applies to:
- Microsoft Visual C++, 32-bit Editions 6.0
SYMPTOMSWhen you build applications in Microsoft Visual C++ 6.0 that use the supplied Standard Template Library
(STL), memory corruption may occur, or your computer may stop responding. These symptoms occur more frequently on multi-processor computers.
Previously, the same code may have worked without such issues on a
single-processor computer. When you examine the faulting thread in a debugger, you typically see the failure in a memory management function. Frequently you see the basic_string<char...> class methods in the stack trace. Because memory
corruption is also a symptom, failures may appear in areas that are unrelated to string
processing. The following are examples of stack traces where this
problem was the cause of a crash: 01 0012ebc4 77fb4014 0246ffd0 00000027 02531000 ntdll!RtlpDphReportCorruptedBlock+0x8c
02 0012ebec 77fb2cb1 02531000 01001002 0246ffd0 ntdll!RtlpDphNormalHeapFree+0x46
03 0012ec10 77fb5653 02530000 01001002 0246ffd0 ntdll!RtlpDebugPageHeapFree+0xa6
04 0012ec88 77fa760a 02530000 01001002 0246ffd0 ntdll!RtlDebugFreeHeap+0x203
05 0012ed28 77fcba9e 02530000 01001002 0246ffd0 ntdll!RtlFreeHeapSlowly+0x4d
06 0012edcc 004065a6 02530000 00000000 0246ffd0 ntdll!RtlFreeHeap+0x53
07 0012ee14 0041353a 0246ffd0 00404198 0246ffd0 main!free+0xda
08 0012ee1c 00404198 0246ffd0 0012eecc 004e9b70 main!operator delete+0x9 (FPO: [1,0,0]) (CONV: cdecl) [afxmem.cpp @ 349]
09 0012ee38 00402a71 02477fe0 00000011 004e9ce0 main!basic_string<char,char_traits_char,allocator<char> >::append_helper+0x68 (FPO: [EBP 0x0012eecc] [2,1,4]) (CONV: thiscall)
... NTDLL! 77f97710()
NTDLL! 77fb5721()
NTDLL! 77fa760a()
NTDLL! 77fcba9e()
MSVCRT! 78001d92()
operator delete(void * 0x00c266f8) line 6 + 10 bytes
std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Tidy(std::basic_string<char,std::char_traits<char>,std::allocator<char> > * const 0x0000000f {???}, unsigned char 1) line 591 + 6 bytes
... 00 0184fb9c 60f3abc3 main!__sbh_free_block+0x173
01 0184fbb4 60f2aa93 main!free+0x28
02 0184fbbc 60f2423c main!operator delete+0x9
03 0184fce8 60f244b0 main!function(std::basic_string<char,std::char_traits<char>,std::allocator<char> > var = std::basic_string<char,std::char_traits<char>,std::allocator<char> >)+0x79c
... ...
5ed 0198de20 77fac5f4 0198dec0 0198e3f8 0198dedc ntdll!ExecuteHandler+0x26
5ee 0198dea8 77f91a96 0198dec0 0198dedc 0198dec0 ntdll!RtlDispatchException+0x76
5ef 0198df14 77b22546 2cb01468 47ac0008 00000008 ntdll!KiUserExceptionDispatcher+0xe
5f0 0198e340 1001b22c 00ed0000 00000000 00000080 ole32!SyncStubInvoke+0x61
5f1 0198e37c 1001b123 00000080 1001a4ef 00000080 main!_heap_alloc+0xed
5f2 0198e384 1001a4ef 00000080 00000001 100022f1 main!_nh_malloc+0x10 (FPO: [2,0,0])
5f3 0198e390 100022f1 00000080 0000007c 0198f430 main!operator new+0xb (FPO: [1,0,0])
5f4 0198e3b0 10002207 0000003c 0000007d 0198f42c main!std::basic_stringbuf<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >::overflow+0x83 (CONV: thiscall) [C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE\sstream @ 60]
5f5 0198e3cc 10003194 00000000 0000006b 0198f6e0 main!std::basic_streambuf<unsigned short,std::char_traits<unsigned short> >::xsputn+0x6a (CONV: thiscall) [C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE\streambuf @ 166]
5f6 0198e404 10005621 0198f42c 010113b2 1003573c main!std::operator<<+0xb0 (CONV: cdecl) [C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE\ostream @ 305]
... CAUSEThe Standard Template Library (STL) that is included with Microsoft
Visual C++ 6.0 is not safe for multi-threaded applications. In particular, the
implementations of the std::string class depend on the basic_string<...> template class. The basic_string<...> template class reference
counts copies of a hidden character buffer. The basic_string<...> template class stores the count in an 8-bit
unsigned char. The following general issues occur after this implementation: - The basic_string<...> template class does not protect
the counting mechanism with the synchronization that is required for threads on multi-processor computers to run at the same time. Multi-threaded STL code that is running on
single-processor computers avoids this issue because only one thread runs at a
time, and memory reads or writes on integers are completed before another thread
can interrupt.
- Writing to an std::string class in one thread can corrupt the reading
of a copy of the std::string class, such as one created by assignment, in another thread. The
supposed copy shares the same hidden character buffer.
- String corruption may occur where a pointer or reference
to an std::string class is shared between threads. Typically, it is the responsibility of the programmer to avoid this situation.
RESOLUTIONYou must rebuild the application after you make the STL
thread-safe. The preferred method to obtain a thread-safe STL is to upgrade
the STL to a newer version that is based on the current Visual C++ standard. However, the STL that is based on
the current Visual C++ standard is not identical to the STL that was available at the time
that Microsoft Visual C++ 6.0 was released as a new product. However, upgrading to a new
version may be trivial depending on the STL functions that your application uses. To obtain new versions of the thread-safe STL, use one of the following methods: Method 1: Use Microsoft Visual C++ .NET (versions 7.0 and later)Open
each Visual C++ project in your application, allow the project to automatically
convert to the new project format, and then rebuild it. The std::string class
implementation in this version is thread-safe for the described problem. If you
use the DLL run-time library feature in your any one of the projects in your application, you must distribute the new Visual C++ run-time components
(such as Msvci7x.dll, Msvcp7x.dll, and Msvcr7x.dll) with your rebuilt application.
Note You do not have to distribute the Microsoft .NET Framework to client computers to use Microsoft Visual C++ .NET. Method 2: Use Microsoft Visual C++ 6.0 with a replacement STL from a
third partyThe details of integration vary by product, and the individual
vendors provide support. One source for a successor STL version is Dinkumware,
Ltd., the company where Microsoft licenses the Visual C++ 6.0 STL. It is
claimed that it can integrate with existing build processes. For more information, and for a list of known bugs and workarounds, visit the following Dinkumware Web site: Microsoft provides third-party
contact information to help you find technical support. This contact
information may change without notice. Microsoft does not guarantee the
accuracy of this third-party contact information.
The third-party products that this article discusses are
manufactured by companies that are independent of Microsoft. Microsoft makes no
warranty, implied or otherwise, regarding the performance or reliability of
these products.
WORKAROUNDWork around the std::string class issue in Microsoft Visual C++ 6.0 STLIf you do not upgrade to a new version of the STL, you can try to correct the
std::string class thread-safety issue in the standard Microsoft Visual C++ 6.0
installation. Although there are multi-threading issues with several of the
classes in the Microsoft Visual C++ 6.0 STL, by far the most common and
problematic class is the std::string class. The following steps and workarounds are stopgap measures to make sure that an application is working correctly, and the measures provide
time to investigate other alternatives. Consider that these instructions
will create new code paths and behavior perhaps throughout your whole
application. Thoroughly test the rebuilt application in accordance
with a company's or an individual's software policies before widespread
deployment. Disable string reference countingEach of the workarounds that is documented in this section require that you
first disable the reference-count mechanism. To disable reference counting, you
must modify the <xstring> header file and set the _FROZEN enumeration
constant to 0. In default installations, the <xstring> header file is in the following location: C:\Program files\Microsoft Visual Studio\VC98\Include Change the _FROZEN
enumeration constant to 0 in the <xstring> header file at line 62 so that it looks
similar to the following: enum _Mref {_FROZEN = 0}; // set to zero to disable sharing; original value 255 If you follow this recommendation, and you rebuild all software that uses
these header files, your std::string class code will be more thread-safe. There are some
caveats to that statement. Therefore, read the following workaround instructions
carefully. After you disable reference counting by setting the _FROZEN enumeration
constant to 0 in
the <xstring> header file, use one of the following methods to work around this problem.
Method 1: Use static CRT linkage onlyModify the project settings in all your projects
that use the std::string class to link to the static version of the Microsoft run-time
library (CRT). You cannot use this approach if your project also has the
Use
MFC in a Shared DLL setting enabled. For each project, follow these steps:
- Open the project.
- On the Project menu, click Settings.
- In the Configurations list, click Release.
- Click the C/C++ tab, and then click Code Generation in the Category list.
- In the Runtime library list, click Multi-thread (/MT).
- In the Configurations list, click Debug.
- In the Runtime library list, click Multi-thread debug (/MTd).
- If there are other configurations in the Configurations list, set the appropriate Runtime library option for them also.
- Click OK, and then rebuild the project.
This workaround makes sure that all your code uses
the modified version of the <xstring> file by static linking to the
whole multi-threaded run-time library, including MFC. One possible problem is that your final code size will be larger than a dynamically linked version,
perhaps enormously so. Method 2: Use dynamic CRT linkageIf your project code must link to the runtime library
(CRT) as a DLL, you must take a different approach. Dynamic CRT linkage is the
default setting for DLL projects. Dependencies on other components such as
MFC or third-party libraries that are licensed for use with your application, typically
require dynamic linkage to the CRT. If your only dependency is MFC, you can
use the Use MFC in a Static Library option, and apply Method 1. By default, when you create a new
project in Microsoft Visual C++ 6.0, the project uses the CRT from a
DLL. The dynamic CRT linkage project setting links your application
to implementations for some std::string class methods in the pre-built Microsoft CRT
DLL that is named Msvcp60.dll. Because Microsoft built that DLL by using the unmodified
<xstring> header file, the change to the _FROZEN constant that you made to the local copy
of <xstring> is not honored for functions that are called out of that
library. These include functions such as _Tidy(), and assign() that are supplied in the Msvcp60.dll file for the <char>
and <short> instantiations of the basic_string class. The basic_string class is the base for the std::string class.
To use static implementations of the std::string class in your modules
instead of the Microsoft-supplied implementations in the Msvcp60.dll file, follow these steps:
- In the <xstring> file, comment out the following code
that is found near the end of the file. To do this, you can enclose the code in a #if 0/#endif block:
#ifdef _DLL
#pragma warning(disable:4231) /* the extern before template is a non-standard extension */
extern template class _CRTIMP basic_string<char, char_traits<char>, allocator<char> >;
extern template class _CRTIMP basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >;
#pragma warning(default:4231) /* restore previous warning */
#endif // _DLL - Other string operators are defined in the <string> header file, and they are also included in the CRT through the Msvcp60.dll file. You notice at the end of the <string> file that there are a series of "extern
template _CRTIMP..." definitions that are protected by the #ifdef _DLL clause just like in the <xstring> file. Comment all these definitions out also:
#ifdef _DLL
#pragma warning(disable:4231) /* the extern before template is a non-standard extension */
extern template class _CRTIMP
basic_string<char, char_traits<char>, allocator<char> > __cdecl operator+(
const basic_string<char, char_traits<char>, allocator<char> >&,
const basic_string<char, char_traits<char>, allocator<char> >&);
extern template class _CRTIMP
basic_string<char, char_traits<char>, allocator<char> > __cdecl operator+(
...
extern template class _CRTIMP
basic_ostream<wchar_t, char_traits<wchar_t> >& __cdecl operator<<(
basic_ostream<wchar_t, char_traits<wchar_t> >&,
const basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >&);
#pragma warning(default:4231) /* restore previous warning */
#endif // _DLL - Save the modifications to these files, and then rebuild all
the projects in the application that use the STL. If your project declares
a class class __declspec(dllexport), and that class has members of the std::string type, you see
C4251 warnings. Because all your code is built with the std::string class now statically
linked, you can ignore these warnings. To explicitly disable those warnings, use the following notation:
#pragma warning(disable: 4251)
This approach balances the use of MFC and CRT functions
other than the std::string class from DLLs. There is a small increase in code size in each of
your modules that use the std::string class. Method 3: Using a clever hack to avoid linkage issuesCreate a typedef for unsigned char, and use that instead of the existing std::string class typedef. The typedef may take a form that is included in a header file in the source
files of the application that use the std::string class. The typedef may appear similar to the following: typedef std::basic_string<unsigned char> MyString;
#define string MyString Any string literals that are used
with this class must be cast or processed as unsigned char. There may be an increase in code size versus ease of implementation, and there are less linkage
side effects. Method 4: Using a custom std::string DLLThis option gains you the benefit of smallest code size
by putting the std::string class implementations in a single DLL. Create a DLL
project that exports the std::string class. Link to that DLL instead of to the
standard Msvcp60.dll file. You must redistribute this new DLL together with
your application. This is an advanced option. REFERENCESFor more information about Visual C++ language and
compiler issues, see the Is the STL included with VC++ thread-safe? topic at the following Microsoft Most Valuable Professional (MVP) Web site:
Modification Type: | Major | Last Reviewed: | 6/1/2004 |
---|
Keywords: | kbThreadSync kbprb KB813810 kbAudDeveloper kbAudITPRO |
---|
|