ISO C11 issues for glibc

Joseph S. Myers joseph@codesourcery.com
Wed Dec 21 23:13:00 GMT 2011


On Wed, 21 Dec 2011, Marek Polacek wrote:

> Which reminds me, what is your opinion on those *_s bounds-checking
> functions, e.g, vsnprintf_s etc, as specified in Annex K?
> I'd say we don't really need them since we have _FORTIFY_SOURCE
> and thus *_chk functions...

Apart from the reentrant functions (generally quite similar to existing 
POSIX functions, but under different names), the functions are generally 
designed for use in retrofitting large bodies of existing code to improve 
its security without needing a detailed understanding of that code.  
_FORTIFY_SOURCE is another way of retrofitting that requires no changes to 
the code at all, but I think it can be useful to have both.

There is certainly a case that, for example, it's better coding style to 
use snprintf rather than sprintf even when you *also* dynamically (or 
statically) size your buffers so that snprintf should never cause 
truncation.  Silent truncation is bad, and could be a security hole, but 
it's less likely to be a security hole than a buffer overrun, so if you 
write your code to avoid arbitrary limits and silent truncation, a coding 
style that always uses snprintf instead of sprintf can reduce the risk of 
a bug in the code turning into a security hole by changing a buffer 
overrun into truncation (if you passed the correct size; it can't solve 
all problems, but can at least encourage you to think about sizes 
everywhere).

Much the same applies to a few of the functions in Annex K, where the 
corresponding existing functions do not have buffer sizes passed 
explicitly; there is a use for coding styles that ensure that explicit 
buffer sizes are always passed (and personally I would have allowed in 
strlcpy/strlcat long ago for that reason; you don't want truncation ever 
to happen, but they still help reduce the risk of some bugs, and glibc has 
traditionally included functions that are widely used in libc on other 
systems, which by now applies to those functions).

Other bits are rather stranger, such as tmpfile_s (adding a runtime check 
that a pointer is not NULL) and tmpnam_s (which leaves a race condition 
still present, so certainly shouldn't be used in new code; presumably it 
was added on the basis that otherwise legacy code might not have tmpnam 
calls retrofitted at all, and some fixing was better than none).

Then there's rsize_t and RSIZE_MAX, intended to limit size_t parameters to 
less than SIZE_MAX (probably no more than SIZE_MAX >> 1).  There is a real 
issue here.  With normal implementations of C, it is impossible for 
objects half the address space or more in size to work reliably; pointer 
subtraction within such an object will produce values that cannot be 
represented in ptrdiff_t, so silently resulting in undefined behavior, and 
in practice even if the pointer target type is larger than one byte, the 
division happens after the subtraction and the results will be wrong (see 
<http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45779>).  And of course 
pointer subtraction might be generated internally even if not in the 
user's source code.

I don't think there's anything that can sensibly be done in the compiler 
about this issue; I think the only way to avoid security problems there is 
for malloc and other allocation functions to refuse to allocate objects 
using half or more of the address space (so it would no longer be possible 
to allocate 2GB or more in a single allocation on a 32-bit system; for 
64-bit systems there would be little practical effect).  (It's already the 
case that GCC will give an error if you attempts to declare an object half 
the address space or larger in (constant) size, whether statically 
allocated or on the stack.)  You don't need rsize_t for that, of course - 
you can and should change the existing allocation functions to put in this 
limit - and once you have such limits no objects can exceed RSIZE_MAX 
anyway so the checks (in various Annex K functions) for passing sizes 
bigger than RSIZE_MAX become just checks for some random possible bug in 
the program, much like the checks for NULL.

-- 
Joseph S. Myers
joseph@codesourcery.com



More information about the Libc-alpha mailing list