4

So here's a fun one...

we have a few C libraries which should be platform independent, even though they were developed on linux, because they only rely on the c standard library as defined in ISO/IEC 9899:1999. When we compiled those libraries with MinGW everything seemed to work fine at first, but today we found out that the snprintf() implementation of msvcrt is braindea... sorry, i meant "incompatible" with the definition in the C99 standard.

I would have expected a warning from MinGW, telling me that -std=c99 actually isn't fully supported. Because otherwise, how am i supposed to know?

Is there any alternative c standard library for windows, and most importantly: can MinGW somehow be told to link against it instead of msvcrt?

If not, we would at the very least need a list or something where we can check for other potential portability problems concerning msvcrt and c99.

PS: I know about Cygwin and MSYS2, but i'd rather have native windows binaries (in part because we also use our libraries in Matlab).

Edit: Sorry, i should have explained more clearly what exactly my problem with msvcrt's snprintf() is. According to the standard, snprintf() is required to output a '\0' as the last character, if the output doesn't fit. However, msvcrt just doesn't do that, so the resulting string is no longer properly terminated. I have no idea why anyone would choose to implement snprintf() that way, because to me just omitting the '\0' doesn't make any sense at all.

We have also tried the suggested __USE_MINGW_ANSI_STDIO, but i guess that just fixes the missing format specifiers? At least it didn't seem to make a difference for our specific problem.

Lundin
  • 195,001
  • 40
  • 254
  • 396
Felix G
  • 674
  • 1
  • 7
  • 17

2 Answers2

4

The standard enforces snprintf to behave like this:

Otherwise, output characters beyond the n-1st are discarded rather than being written to the array, and a null character is written at the end of the characters actually written into the array.

snprintf in msvcrt is indeed not the standard one, but a Microsoft version as explained here:
Is snprintf() ALWAYS null terminating?

The following code gives non-compliant results:

#include <stdio.h>

int main (void)
{
  char dst[3];
  snprintf(dst, 3, "%c%c%c", 'A', 'B', 'C');

  for(size_t i=0; i<3; i++)
  {
    printf("%.2X ", dst[i]);
  }
}

I get output 41 42 43 which is not standard compliant. To get standard C, you have to add this before the stdio.h include:

#define __USE_MINGW_ANSI_STDIO 1

And now you get 41 42 00 which is compliant.

The root of all these program security problems is Microsoft, who have been using non-compliant C libs in their products for the past 20 years.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 1
    (To reproduce the problem, I purposely tried this with a very old version of Mingw, < gcc 5. Microsoft have lately made some attempts to update their products to year 1999 standards, so it is quite possible that the latest Microsoft products nowadays even live up to the (withdrawn) ISO 9899:1999 standard. Otherwise, give them more time.) – Lundin Aug 22 '19 at 11:54
  • Thanks for the information. So far we've tried -D__USE_MINGW_ANSI_STDIO as well as the #define, but for some reason our MinGW (5.4) still doesn't use the standard compliant version. We will now test an isolated example like yours, just to be sure. – Felix G Aug 22 '19 at 12:26
  • @FelixG Might be some linking order hiccup, if several files include stdio.h. – Lundin Aug 22 '19 at 12:28
  • @Lundin nope, no VLAs whatsoever. – Antti Haapala -- Слава Україні Aug 22 '19 at 12:32
  • @Lundin It's working now, apparently that there were still some old .o files in there when we first tested it ^^. Thank you very much. – Felix G Aug 22 '19 at 12:50
2

If you need C99 stdio from MinGW-w64, you can define __USE_MINGW_ANSI_STDIO so that you bypass the msvcrt implementation. It's best to define this through a compiler argument

-D__USE_MINGW_ANSI_STDIO

Alternatively, you could try to use a MinGW-w64 build that is set up to link with the new ucrt, but I don't know of any pre-existing easy-to-use stable builds that are set up that way.

rubenvb
  • 74,642
  • 33
  • 187
  • 332