Function local statics generate faulty code for Windows XP

693 views Asked by At

I originally posted this on the ReverseEngineering StackExchange not knowing exactly where this belongs. I decided to post it here anyway.

Recently the Microsoft Visual Studio 2015 compiler finally complied with the C++ standards mandate to generate thread-safe code for function local statics. For the most part this works just fine but I ran into a situation on Windows XP where the following 3 instructions led to a blow up:

mov     eax,dword ptr fs:[0000002Ch]
mov     ecx,dword ptr [MyModule!_tls_index (102eea44)]
mov     ecx,dword ptr [eax+ecx*4]

Obviously the compiler seems to implement thread-safety by first poking into the TLS slot of the current thread. fs:2Ch is supposed to lead to the TLS array per documentation. However on Windows XP, fs:2Ch doesn't seem to be set. This returned 0 for me and so did the next instruction (_tls_index was also 0.) That led to the 3rd instruction blowing up as it was accessing invalid memory.

Does anybody know why fs:2Ch might not be set on Windows XP? Function local statics are used all over our code and I can't imagine no one else running into this.

UPDATE

I have carefully considered every tag I have applied to this question. Please DO NOT add or remove anything.

2

There are 2 answers

0
ForeverLearning On BEST ANSWER

This question was ably answered by Peter Ferrie over at the Reverse Engineering SE.

https://reverseengineering.stackexchange.com/a/14186/15780

1
DarrenFlexXu On

In the C++11 standard, block scope variables with static or thread storage duration must be zero-initialized before any other initialization takes place. Initialization occurs when control first passes through the declaration of the variable. If an exception is thrown during initialization, the variable is considered uninitialized, and initialization is re-attempted the next time control passes through the declaration. If control enters the declaration concurrently with initialization, the concurrent execution blocks while initialization is completed. The behavior is undefined if control re-enters the declaration recursively during initialization. By default, Visual Studio starting in Visual Studio 2015 implements this standard behavior. This behavior may be explicitly specified by setting the /Zc:threadSafeInit compiler option. + Thread-safe initialization of static local variables relies on code implemented in the Universal C run-time library (UCRT). To avoid taking a dependency on the UCRT, or to preserve the non-thread-safe initialization behavior of versions of Visual Studio prior to Visual Studio 2015, use the /Zc:threadSafeInit- option. If you know that thread-safety is not required, use this option to generate slightly smaller, faster code around static local declarations. Thread-safe static local variables use thread-local storage (TLS) internally to provide efficient execution when the static has already been initialized. The implementation of this feature relies on Windows operating system support functions in Windows Vista and later operating systems. Windows XP, Windows Server 2003, and older operating systems do not have this support, so they do not get the efficiency advantage. These operating systems also have a lower limit on the number of TLS sections that can be loaded. Exceeding the TLS section limit can cause a crash. If this is a problem in your code, especially in code that must run on older operating systems, use /Zc:threadSafeInit- to disable the thread-safe initialization code.