Direct answer to question
I like Robert's answer, but I also have some views on the questions I raised.
Do you use a library or compiler with support for the TR24731-1 functions?
No, I don't.
If so, which compiler or library and on which platform(s)?
I believe the functions are provided by MS Visual Studio (MS VC++ 2008 Edition, for example), and there are warnings to encourage you to use them.
Did you uncover any bugs as a result of fixing your code to use these functions?
Not yet. And I don't expect to uncover many in my code. Some of the other code I work with - maybe. But I've yet to be convinced.
Which functions provide the most value?
I like the fact that the printf_s() family of functions do not accept the '%n
' format specifier.
Are there any that provide no value or negative value?
The tmpfile_s()
and tmpnam_s()
functions are a horrible disappointment. They really needed to work more like mkstemp()
which both creates the file and opens it to ensure there is no TOCTOU (time-of-check, time-of-use) vulnerability. As it stands, those two provide very little value.
I also think that strerrorlen_s()
provides very little value.
Are you planning to use the library in the future?
I am in two minds about it. I started work on a library that would implement the capabilities of TR 24731 over a standard C library, but got caught by the amount of unit testing needed to demonstrate that it is working correctly. I'm not sure whether to continue that. I have some code that I want to port to Windows (mainly out of a perverse desire to provide support on all platforms - it's been working on Unix derivatives for a couple of decades now). Unfortunately, to get it to compile without warnings from the MSVC compilers, I have to plaster the code with stuff to prevent MSVC wittering about me using the perfectly reliable (when carefully used) standard C library functions. And that is not appetizing. It is bad enough that I have to deal with most of two decades worth of a system that has developed over that period; having to deal with someone's idea of fun (making people adopt TR 24731 when they don't need to) is annoying. That was partly why I started the library development - to allow me to use the same interfaces on Unix and Windows. But I'm not sure what I'll do from here.
Are you tracking the TR24731-2 work at all?
I'd not been tracking it until I went to the standards site while collecting the data for the question. The asprintf()
and vasprintf()
functions are probably valuable; I'd use those. I'm not certain about the memory stream I/O functions. Having strdup()
standardized at the C level would be a huge step forward. This seems less controversial to me than the part 1 (bounds checking) interfaces.
Overall, I'm not convinced by part 1 'Bounds-Checking Interfaces'. The material in the draft of part 2 'Dynamic Allocation Functions' is better.
If it were up to me, I'd move somewhat along the lines of part 1, but I'd also revised the interfaces in the C99 standard C library that return a char *
to the start of the string (e.g. strcpy()
and strcat()
) so that instead of returning a pointer to the start, they'd return a pointer to the null byte at the end of the new string. This would make some common idioms (such as repeatedly concatenating strings onto the end of another) more efficient because it would make it trivial to avoid the quadratic behaviour exhibited by code that repeatedly uses strcat()
. The replacements would all ensure null-termination of output strings, like the TR24731 versions do. I'm not wholly averse to the idea of the checking interface, nor to the exception handling functions. It's a tricky business.
Microsoft's implementation is not the same as the standard specification
Update (2011-05-08)
See also this question. Sadly, and fatally to the usefulness of the TR24731 functions, the definitions of some of the functions differs between the Microsoft implementation and the standard, rendering them useless (to me). My answer there cites vsnprintf_s()
.
For example, TR 24731-1 says the interface to vsnprintf_s()
is:
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdarg.h>
#include <stdio.h>
int vsnprintf_s(char * restrict s, rsize_t n,
const char * restrict format, va_list arg);
Unfortunately, MSDN says the interface to vsnprintf_s()
is:
int vsnprintf_s(
char *buffer,
size_t sizeOfBuffer,
size_t count,
const char *format,
va_list argptr
);
Parameters
- buffer - Storage location for output.
- sizeOfBuffer - The size of the buffer for output.
- count - Maximum number of characters to write (not including the terminating null), or _TRUNCATE.
- format - Format specification.
- argptr - Pointer to list of arguments.
Note that this is not simply a matter of type mapping: the number of fixed arguments is different, and therefore irreconcilable. It is also unclear to me (and presumably to the standards committee too) what benefit there is to having both 'sizeOfBuffer' and 'count'; it looks like the same information twice (or, at least, code will commonly be written with the same value for both parameters).
Similarly, there are also problems with scanf_s()
and its relatives. Microsoft says that the type of the buffer length parameter is unsigned
(explicitly stating 'The size parameter is of type unsigned
, not size_t
'). In contrast, in Annex K, the size parameter is of type rsize_t
, which is the restricted variant of size_t
(rsize_t
is another name for size_t
, but RSIZE_MAX
is smaller than SIZE_MAX
). So, again, the code calling scanf_s()
would have to be written differently for Microsoft C and Standard C.
Originally, I was planning to use the 'safe' functions as a way of getting some code to compile cleanly on Windows as well as Unix, without needing to write conditional code. Since this is defeated because the Microsoft and ISO functions are not always the same, it is pretty much time to give up.
Changes in Microsoft's vsnprintf()
in Visual Studio 2015
In the Visual Studio 2015 documentation for vsnprintf()
, it notes that the interface has changed:
Beginning with the UCRT in Visual Studio 2015 and Windows 10, vsnprintf
is no longer identical to _vsnprintf
. The vsnprintf
function complies with the C99 standard; _vnsprintf
is retained for backward compatibility.
However, the Microsoft interface for vsnprintf_s()
has not changed.
Other examples of differences between Microsoft and Annex K
The C11 standard variant of localtime_s()
is defined in ISO/IEC 9899:2011 Annex K.3.8.2.4 as:
struct tm *localtime_s(const time_t * restrict timer,
struct tm * restrict result);
compared with the MSDN variant of localtime_s()
defined as:
errno_t localtime_s(struct tm* _tm, const time_t *time);
and the POSIX variant localtime_r()
defined as:
struct tm *localtime_r(const time_t *restrict timer,
struct tm *restrict result);
The C11 standard and POSIX functions are equivalent apart from name. The Microsoft function is different in interface even though it shares a name with the C11 standard.
Another example of differences is Microsoft's strtok_s()
and Annex K's strtok_s()
:
char *strtok_s(char *strToken, const char *strDelimit, char **context);
vs:
char *strtok_s(char * restrict s1, rsize_t * restrict s1max, const char * restrict s2, char ** restrict ptr);
Note that the Microsoft variant has 3 arguments whereas the Annex K variant has 4. This means that the argument list to Microsoft's strtok_s()
is compatible with POSIX's strtok_r()
— so calls to these are effectively interchangeable if you change the function name (e.g. by a macro) — but the Standard C (Annex K) version is different from both with the extra argument.
The question Different declarations of qsort_r()
on Mac and Linux has an answer that also discusses qsort_s()
as defined by Microsoft and qsort_s()
as defined by TR24731-1 — again, the interfaces are different.
ISO/IEC 9899:2011 — C11 Standard
The C11 standard (December 2010 Draft; you could at one time obtain a PDF copy of the definitive standard, ISO/IEC 9899:2011, from the ANSI web store for 30 USD) does have the TR24731-1 functions in it as an optional part of the standard. They are defined in Annex K (Bounds-checking Interfaces), which is 'normative' rather than 'informational', but it is optional.
The C11 standard does not have the TR24731-2 functions in it — which is sad because the vasprintf()
function and its relatives could be really useful.
Quick summary:
- C11 contains TR24731-1
- C11 does not contain TR24731-2
- C18 is the same as C11 w.r.t TR24731.
Proposal to remove Annex K from the successor to C11
Deduplicator pointed out in a comment to another question that there is a proposal before the ISO C standard committee (ISO/IEC JTC1/SC22/WG14)
It contains references to some of the extant implementations of the Annex K functions — none of them widely used (but you can find them via the document if you are interested).
The document ends with the recommendation:
Therefore, we propose that Annex K be either removed from the next revision of the C standard, or deprecated and then removed.
I support that recommendation.
The C18 standard did not alter the status of Annex K. There is a paper N2336 advocating for making some changes to Annex K, repairing its defects rather than removing it altogether.
strlen()
to the code'. There are definitely times whenstrlen()
is not the right answer, such as when passing a buffer to an I/O function (such asgets_s()
). But maybe you can elaborate on what you're thinking of? – Swingeinggets()
— there's no requirement that the buffer be initialized with a maximum length string before it is used. Dittosprintf()
,strcpy()
. I don't think you could append anything withstrcat()
under the rules you're hypothesizing. It is a non-starter. – Swingeingrealloc()
because the functions that need protection don't allocate. Thestrcpy()
function, for example, doesn't do memory allocation; you can't sanely modify it to do memory allocation, even if you have garbage collection, because people don't generally use the return value but use the value passed as the first argument tostrcpy()
in further operations. Similar problems arise withgets()
andstrcat()
. Those at least return achar *
that might point to reallocated space (not that there's a guarantee that the arguments were allocated). […continued…] – Swingeingsprintf()
which don't return achar *
; there is no way for them to tell the calling code that they've 'reallocated' the memory where the result was placed. Note that one of the reasons why TR 24731-2 did not make it into C11 was that they would be the first functions to explicitly do memory allocation — other thanmalloc()
et al. Please take time to study what the functions do, what the Annex K / TR 24731-1 functions do, the rationales for why they do it, and so on. There are some sound reasons for the decisions made. – Swingeing