MuldeR
MuldeR

Reputation: 577

Crash in _snprintf_s() in "ucrtbase.dll" with STATUS_ILLEGAL_INSTRUCTION

It was brought to my attention that my binary will crash on the CPU without SSE support, with exception code 0xC000001D (STATUS_ILLEGAL_INSTRUCTION), despite I'm compiling with option /arch:IA32. And I have been able to track down the exact place where it crashes: Wherever _snprintf_s() is called for the first time, it will crash. The crash is inside of ucrtbase.dll, not my own code.

Now, the interesting part is that, when I make a "fully static" build with compiler option /MT, so to avoid explicity dependency on ucrtbase.dll, the resulting binary works just fine! But, as soon as I compile the exactly some code as "shared" build, with option /MD, it will crash again in ucrtbase.dll.

So it would appear that "static" version of the UCRT still can work on the CPU without SSE support, but the "shared" (DLL) version can not. This inconsistency would clearly seem like a bug to me!

Any thoughts?


Build environment:

Test machine (used for compat-testing only):

Note: Redist DLLs (ucrtbase.dll +api-ms-win-*.dll) for the setup of the "shared" build have been copied straight from C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86 directory! To the best of my knowledge, this is the latest available version of these DLLs (v10.0.17134.12).


Even this minimal test program will reproduce the crash:

#include <stdio.h>

int main()
{
    char buffer[128];
    _snprintf_s(buffer, 128, _TRUNCATE, "Hello %s!\n", "World!");
    fputs(buffer, stdout);
    getc(stdin);
    return 0;
}

Upvotes: 4

Views: 2604

Answers (1)

MuldeR
MuldeR

Reputation: 577

Update:

After some more debugging and fiddling around, I made a very interesting observation: The UCRT "Redist" DLLs contained within the latest vcredist_x86.exe (Microsoft Visual C++ 2017 Redistributable installer), i.e. the version that ships with VS2017.8 (v14.15.26706), are actually quite different from those UCRT "Redist" DLLs that can be found in the Redist\ucrt\DLLs\x86 directory of the latest Windows SDK (v10.0.17134.12):

  • ucrtbase.dll from latest Windows SDK
    v10.0.17134.12

  • ucrtbase.dll from latest Visual C++ 2017 Redistributable installer:
    v10.0.10586.15

Indeed, I can confirm now that the application compiled with latest Visual C++ 2017 (v14.15.26706) and using the /MD option works correctly on the non-SSE CPU, as long as we use the "old" ucrtbase.dll version, as extracted from the latest vcredist_x86.exe installer.

I'm a bit concerned using such an old version of the UCRT, if the Windows SDK already provides a much newer version. But, apparently, that is what Microsoft is doing themselves with the Visual C++ 2017 Redistributable installer. So, I guess, it is what we are supposed to use...


Just in case anybody at Microsoft is reading this:

Things could have been way less confusing and error-prone for software developers, if, in the Redist\ucrt directory of the Windows SDK, there were separate sub-folders for each "incarnation" of the UCRT – the latest "cutting edge" version of the UCRT and the "compatible" version of the UCRT that we are actually supposed to redistribute. If then somebody at Microsoft spent three minutes for writing a little README file that tells us which "incarnation" of the CRT we should pick and put it at Redist\ucrt\README.txt, it would be almost impossible to do wrong...

Upvotes: 3

Related Questions