C and C++ are members of the same family of languages. The evolutionary boldness of C++ removes some of the marketplace pressure on C; people who are continually pushing for innovation are naturally drawn to the C++ development process. Each language had a coherent original design (by Dennis Ritchie and Bjarne Stroustrup, respectively), followed by successive refinement in a very competitive marketplace of ideas. Both languages share an extreme concern for performance, with the slogan "don't leave space for a more-efficient systems-programming language underneath our language (C or C++)." However, it's unfair to complain that the original designs assigned too little importance to cybersecurity; both languages pre-date the beginnings of concern for security. But in recent years, as the marketplace has started to emphasize cybersecurity, C and C++ have been responding in several ways.

In early 2002, Bill Gates' famous "battleship-turning" memo made cybersecurity a top goal for Microsoft. About a year later, Microsoft proposed a new "bounds-checking" library to WG14, which eventually became Technical Report 24731-1. It now is part of C11 as the (optional) Annex K. (An almost-final draft of C11 is available here [PDF].)

The C11 Annex K Functions

I'll start my tour of Annex K with the fopen_s function. The main innovation is that files are opened with exclusive (also known as non-shared) access. Furthermore, if the mode string doesn't begin with u (such as with code being updated from the older fopen ), then to the extent that the underlying system supports it, the file gets a file permission that prevents other users on the system from accessing the file.

In this article, I'll sequentially enumerate the security benefits of these _s functions. The new semantics illustrate the pattern of "least privilege." This "exclusive" mode was previously available in the Posix open() function, but the ISO standard for C doesn't standardize system-dependent, low-level I/O. See Robert Seacord's book Secure Coding in C and C++ for detailed discussion of these various security benefits of the Annex K library.

fopen() safety

If a file is opened with x as the last character in the mode argument, and the requested filename is already in use, the fopen_s function fails (as opposed to truncating the existing file, which is presumably already being used by someone). If the application program had been required first to check whether the file was in use and then to create the new file, this would illustrate the "time-of-check versus time-of-use" vulnerability (TOCTOU). The Annex K document aims to minimize the TOCTOU.

The mode argument is passed to fopen_s as a const char* pointer, as is the filename argument. Requiring these pointers to be non-null is one of the "runtime-constraints" of the fopen_s function, to use the C11 terminology.

If any of the runtime-constraints is violated, the library function (here, fopen_s ) invokes the run time-constraint handler. (In Visual Studio, this handler is known as the invalid parameter handler  same concept, different name.)

This approach is a new pattern of response to security issues: Invoke the runtime-constraint handler if any runtime-constraint is violated. Previously, a runtime-constraint violation would have resulted in an undefined behavior if not caught.

If the runtime-constraints are not violated, then fopen_s returns the resulting FILE* pointer through an argument, rather than producing it as the returned value of the function. If fopen_s fails for any of several reasons, it returns a nonzero value according to the conventions encoded in <errno.h> . The various Annex K headers provide the typedef name errno_t for this returned int value. This reduces the inconsistency of return-value idioms to the greatest extent possible, by uniformly returning errno_t for erroneous conditions that did not violate a runtime-constraint.

This initial discussion about fopen_s has introduced the first four patterns of the Annex K library:

Provide least privilege;

Minimize TOCTOU vulnerability;

Use runtime-constraint handlers for logic errors;

Reduce the return value variability using errno_t returned values.

In the original C standard, and still in C++ today, most library functions specify something like "if copying takes place between objects that overlap, the behavior is undefined." In C99 and C11, there is a syntactic way to specify this restriction, the restrict keyword. As a result of all these various design decisions, the calling sequence for secure fopen_s calls looks like this:

errno_t fopen_s(FILE * restrict * restrict streamptr, const char * restrict filename, const char * restrict mode);

Designing the runtime-constraint handler provides the implementation and the project team a range of choices. The simplest handler simply invokes abort() . A somewhat more complex architecture gives the user a choice between aborting or debugging, potentially preserving the full state of the stack frames and global variables. Other handlers could be used: In an application that never terminates, the handler could reinitialize, flush the current transaction, start a new transaction, and so forth. In a specialized testing situation, the handler could log the failures.

The freopen_s function illustrates the same patterns as fopen_s , including the x and u mode flags.

Fixing tmpnam()

Continuing with the file-oriented functions, consider tmpnam_s :

errno_t tmpnam_s(char *s, rsize_t maxsize);

This function illustrates another security pattern in C11: "In the calling sequence of the function, every pointer through which the function might modify an array is immediately followed by the number of elements which the function is permitted to modify."

In the case of tmpnam_s , the second argument specifies a maximum for the number of characters that can be modified by tmpnam_s . The type of the second argument is rsize_t , designating a "restricted size_t " value. The intent is to prevent the common error of inadvertently passing a negative value, which after conversion to an unsigned type, becomes a huge number, and in this case, defeating the purpose of bounds-checking the string written into s . This common error is intended to be caught within tmpnam_s by comparing maxsize against RSIZE_MAX and invoking the runtime-constraint handler if it's larger. (I've said "intended" several times, because Annex K makes it optional whether RSIZE_MAX is any smaller than SIZE_MAX .) This manner of designating bounding values with the type rsize_t is another security best practice promulgated in the Annex K library.

Next, consider the tmpfile_s function:

errno_t tmpfile_s(FILE * restrict * restrict streamptr);

It could be invoked like this:

FILE *myTempFile = 0; errno_t err = tmpfile_s(&myTempFile);