4

The declaration of strtok_s in C11, and its usage, look to be very different from the strtok_s in compilers like the latest bundled with Visual Studio 2022 (17.4.4) and also GCC 12.2.0 (looking at MinGW64 distribution).

I fear the different form has been developed as a safer and accepted alternative to strtok long before C11. What happens now if someone wants to use strtok_s and stay C11 compliant?

Are the compiler supplied libraries C11 compliant?

Maybe it's just that I've been fooled by something otherwise obvious, and someone can help me...


This is C11 (and similar is to C17 and early drafts of C23):

char *strtok_s(char * restrict s1,
    rsize_t * restrict s1max,
    const char * restrict s2,
    char ** restrict ptr);

the same can be found as a good reference in the safec library

While MSC/VC and GCC have the form

char* strtok_s(
    char* str,
    const char* delimiters,
    char** context
);
chqrlie
  • 131,814
  • 10
  • 121
  • 189
LuC
  • 347
  • 2
  • 10
  • 2
    In short: *you don't*, and *no*. Microsoft jumped the gun when implementing bounds checking functions. In general, Annex K is [a mess](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm). See also: [Is support of Annex K in C11 required for a conforming implementation?](https://stackoverflow.com/q/16700541/2505965) and [Why didn't gcc (or glibc) implement _s functions?](https://stackoverflow.com/q/50724726/2505965) – Oka Feb 01 '23 at 11:01
  • 2
    @Oka Microsoft did much more than jump the gun. Their misleading/deceptive "deprecation" of standard C functions in order to push a non-portable implementation reeks of vendor lock-in and ["embrace, extend, extinguish"](https://en.wikipedia.org/wiki/Embrace,_extend,_and_extinguish#Origin) – Andrew Henle Feb 01 '23 at 11:21
  • My take on it all is that the C11 committee was way too focused on getting MS to adhere to the standard, which in turn lead to Annex K (that MS ignored anyway, just as they always ignored technical standards) and the "optional VLA" debacle that will be fixed in C23. – Lundin Feb 01 '23 at 11:36
  • 1
    @Lundin https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2809.pdf seems relevant: "Annex K and ISO/IEC TR 24731-1 before it is based on the Microsoft implementation. Unfortunately, significant changes were made to the Microsoft implementation during the standardization process, making the existing Microsoft implementation nonconforming to the standard." Good old-fashioned finger pointing? – Andrew Henle Feb 01 '23 at 11:57
  • @AndrewHenle Microsoft has a long history of "hey we want to use your standard but change it". Like back in the day when I was learning Java and had to use Visual J++ in case I expected Microsoft JVM to be used. For quite a while there were two competing and somewhat non-compatible JVM on the Internet. "Using standard Java but changed..." – Lundin Feb 01 '23 at 12:38
  • It's not the only thing Microsoft jumped the gun on. Their implementations of the wide formatted I/O functions (e.g. `wscanf` and `wprintf`) noncomformantly define the standard `%c` and `%s` specifiers as operating on wide characters or strings when they should be operating on "narrow" characters or strings. – Ian Abbott Feb 01 '23 at 16:27
  • Didn't mention this thing to burst into flames, this is pointless. What is clear to me is that JTC1/SC22/WG14 and Microsoft just failed in communication; as a result, we have that Annex K which has a serious application issue. – LuC Feb 02 '23 at 08:46
  • 1
    @LuC It's **Microsoft** with the demonstrable history of case after case of "failed ... communication" in ways that always wind up with Microsoft pushing a proprietary implementation that does not meet standards but that do "coincidentally" promote vendor lock-in and marketplace fracturing. J++ was mentioned earlier - Microsoft was legally bound by Sun's Java standards but played EEE anyway. Sun forced Microsoft to pay $2 billion *and* Microsoft had to kill J++. It's really, really hard for JTC1/SC22/WG14 to "[fail] in communications" when all their meetings and draft standards are public. – Andrew Henle Feb 03 '23 at 09:41
  • @AndrewHenle the case for J++ was indeed really ridiculous, as all that has happened around Internet Explorer, although it's quite clear how that was a marketing issue. Disrupting the C library compatibility, however, doesn't look to me like a "successful" way to force the use of their products. My feeling is just that things evolved separately and everyone just wanted to have the "right way" to do things ¯\_(ツ)_/¯ – LuC Feb 03 '23 at 16:46

2 Answers2

6

The C11 "Annex K bounds checking interfaces" was received with a lot of scepticism and in practice nearly no standard lib implemented it. See for example Field Experience With Annex K — Bounds Checking Interfaces.

As for the MSVC compiler, it doesn't conform to any C standard and never made such claims - you can try this out to check if you are using such a compiler or not:

#if !defined(__STDC__) || (__STDC__==0)
  #error This compiler is non-conforming.
#endif

In particular, MSVC did not implement Annex K either, but already had non-standard library extensions in place prior to C11.

In practice _s means:

  • Possibly more safe or possibly less safe, depending on use and what the programmer expected.
  • Non-portable.
  • Possibly non-conforming.

If portability and standard conformance are important, then avoid _s functions.

In practice _s functions protect against two things: getting passed non-sanitized input or null pointers. So assuming that you do proper input sanitation and don't pass null pointers to library functions, the _s functions aren't giving you extra safety, just extra execution bloat and portability problems.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • MSVC doesn't default to C11 or C17, but they can be enforced: "__STDC__ Defined as 1 when compiled as C and if the /Za compiler option is specified. Starting in Visual Studio 2022 version 17.2, it's also defined as 1 when compiled as C and if the /std:c11 or /std:c17 compiler option is specified. Otherwise, undefined." – LuC Feb 01 '23 at 16:49
  • @LuC MSVC 19.33 "Command line error D8016 : '/std:c17' and '/Za' command-line options are incompatible", "Command line error D8016 : '/std:c11' and '/Za' command-line options are incompatible" or do you prefer "Command line warning D9002 : ignoring unknown option '/std:c99'" It ain't compliant to anything. – Lundin Feb 02 '23 at 07:34
  • @LuC No matter how I try, and I once tried really hard, I can't make MSVC to pass the "garbage test" as described here: https://stackoverflow.com/a/73457068/584518 – Lundin Feb 02 '23 at 07:36
  • that's clearly a bug in MSVC. Go figure, GCC has even an intentional "feature" that breaks compatibility with anonymous struct/union in their c99 implementation, until it's been removed since c11: even this is just questionable to me, maybe you would have called it "garbage" – LuC Feb 02 '23 at 08:26
  • @LuC Not sure what that has to do with anything, but there was some anonymous struct/union feature in GNU C before it was introduced in the C language with C11. It would be nice if GNU C deprecated all features that has been solved with standard C (zero-sized arrays is another example of a deprecated extension), but they love their extensions more than anything and providing a conforming C compiler always seems like a secondary priority for gcc. – Lundin Feb 02 '23 at 08:56
3

What happens now if someone wants to use strtok_s and stay C11 compliant?

You de facto can't.

And it's not limited to just strtok_s(). The entire C11 Annex K set of implementations is fractured, and because the major deviations from the standard are from Microsoft's implementation, there will probably never be a way to write portable, standard-conforming code using the Annex K functions.

Per N1967 Field Experience With Annex K — Bounds Checking Interface:

Available Implementations

Despite the specification of the APIs having been around for over a decade only a handful of implementations exist with varying degrees of completeness and conformance. The following is a survey of implementations that are known to exist and their status.

While two of the implementations below are available in portable source code form as Open Source projects, none of the popular Open Source distribution such as BSD or Linux has chosen to make either available to their users. At least one (GNU C Library) has repeatedly rejected proposals for inclusion for some of the same reasons as those noted by the Austin Group in their initial review of TR 24731-1 N1106]. It appears unlikely that the APIs will be provided by future versions of these distributions.

Microsoft Visual Studio

Microsoft Visual Studio implements an early version of the APIs. However, the implementation is incomplete and conforms neither to C11 nor to the original TR 24731-1. For example, it doesn't provide the set_constraint_handler_s function but instead defines a _invalid_parameter_handler _set_invalid_parameter_handler(_invalid_parameter_handler) function with similar behavior but a slightly different and incompatible signature. It also doesn't define the abort_handler_s and ignore_handler_s functions, the memset_s function (which isn't part of the TR), or the RSIZE_MAX macro. The Microsoft implementation also doesn't treat overlapping source and destination sequences as runtime-constraint violations and instead has undefined behavior in such cases.

As a result of the numerous deviations from the specification the Microsoft implementation cannot be considered conforming or portable.

...

Safe C Library

Safe C Library [SafeC] is a fairly efficient and portable but unfortunately very incomplete implementation of Annex K with support for the string manipulation subset of functions declared in <string.h>.

Due to its lack of support for Annex K facilities beyond the <string.h> functions the Safe C Library cannot be considered a conforming implementation.

Even the Safe C library is non-conforming.

Whether these functions are "safer" is debatable. Read the entire document.

Unnecessary Uses

A widespread fallacy originated by Microsoft's deprecation of the standard functions in an effort to increase the adoption of the APIs is that every call to the standard functions is necessarily unsafe and should be replaced by one to the "safer" API. As a result, security-minded teams sometimes naively embark on months-long projects rewriting their working code and dutifully replacing all instances of the "deprecated" functions with the corresponding APIs. This not only leads to unnecessary churn and raises the risk of injecting new bugs into correct code, it also makes the rewritten code less efficient.

Also, read the updated N1969 Updated Field Experience With Annex K — Bounds Checking Interfaces:

Despite more than a decade since the original proposal and nearly ten years since the ratification of ISO/IEC TR 24731-1:2007, and almost five years since the introduction of the Bounds checking interfaces into the C standard, no viable conforming implementations has emerged. The APIs continue to be controversial and requests for implementation continue to be rejected by implementers.

The design of the Bounds checking interfaces, though well-intentioned, suffers from far too many problems to correct. Using the APIs has been seen to lead to worse quality, less secure software than relying on established approaches or modern technologies. More effective and less intrusive approaches have become commonplace and are often preferred by users and security experts alike.

Therefore, we propose that Annex K be either removed from the next revision of the C standard, or deprecated and then removed.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56