defensive-coding-guide/modules/ROOT/pages/programming-languages/C-Libc.adoc
2018-09-20 11:51:33 +02:00

287 lines
8.2 KiB
Text

:experimental:
include::{partialsdir}/entities.adoc[]
[[sect-Defensive_Coding-C-Libc]]
== The C Standard Library
Parts of the C standard library (and the UNIX and GNU extensions)
are difficult to use, so you should avoid them.
Please check the applicable documentation before using the
recommended replacements. Many of these functions allocate
buffers using `malloc` which your code must
deallocate explicitly using `free`.
[[sect-Defensive_Coding-C-Absolutely-Banned]]
=== Absolutely Banned Interfaces
The functions listed below must not be used because they are
almost always unsafe. Use the indicated replacements instead.
* `gets`
⟶ `fgets`
* `getwd`
⟶ `getcwd`
or `get_current_dir_name`
* `readdir_r` ⟶ `readdir`
* `realpath` (with a non-NULL second parameter)
⟶ `realpath` with NULL as the second parameter,
or `canonicalize_file_name`
The constants listed below must not be used, either. Instead,
code must allocate memory dynamically and use interfaces with
length checking.
* `NAME_MAX` (limit not actually enforced by
the kernel)
* `PATH_MAX` (limit not actually enforced by
the kernel)
* `_PC_NAME_MAX` (This limit, returned by the
`pathconf` function, is not enforced by
the kernel.)
* `_PC_PATH_MAX` (This limit, returned by the
`pathconf` function, is not enforced by
the kernel.)
The following structure members must not be used.
* `f_namemax` in `struct
statvfs` (limit not actually enforced by the kernel,
see `_PC_NAME_MAX` above)
[[sect-Defensive_Coding-C-Avoid]]
=== Functions to Avoid
The following string manipulation functions can be used securely
in principle, but their use should be avoided because they are
difficult to use correctly. Calls to these functions can be
replaced with `asprintf` or
`vasprintf`. (For non-GNU targets, these
functions are available from Gnulib.) In some cases, the
`snprintf` function might be a suitable
replacement, see <<sect-Defensive_Coding-C-String-Functions-Length>>.
* `sprintf`
* `strcat`
* `strcpy`
* `vsprintf`
Use the indicated replacements for the functions below.
* `alloca` ⟶
`malloc` and `free`
(see <<sect-Defensive_Coding-C-Allocators-alloca>>)
* `putenv` ⟶
explicit `envp` argument in process creation
(see <<sect-Defensive_Coding-Tasks-Processes-environ>>)
* `setenv` ⟶
explicit `envp` argument in process creation
(see <<sect-Defensive_Coding-Tasks-Processes-environ>>)
* `strdupa` ⟶
`strdup` and `free`
(see <<sect-Defensive_Coding-C-Allocators-alloca>>)
* `strndupa` ⟶
`strndup` and `free`
(see <<sect-Defensive_Coding-C-Allocators-alloca>>)
* `system` ⟶
`posix_spawn`
or `fork`pass:attributes[{blank}]/pass:attributes[{blank}]`execve`pass:attributes[{blank}]/
(see <<sect-Defensive_Coding-Tasks-Processes-execve>>)
* `unsetenv` ⟶
explicit `envp` argument in process creation
(see <<sect-Defensive_Coding-Tasks-Processes-environ>>)
[[sect-Defensive_Coding-C-String-Functions-Length]]
=== String Functions with Explicit Length Arguments
The C run-time library provides string manipulation functions
which not just look for NUL characters for string termination,
but also honor explicit lengths provided by the caller.
However, these functions evolved over a long period of time, and
the lengths mean different things depending on the function.
[[sect-Defensive_Coding-C-Libc-snprintf]]
==== `snprintf`
The `snprintf` function provides a way to
construct a string in a statically-sized buffer. (If the buffer
size is allocated on the heap, consider use
`asprintf` instead.)
[source,c]
----
include::../snippets/C-String-Functions-snprintf.adoc[]
----
The second argument to the `snprintf` call
should always be the size of the buffer in the first argument
(which should be a character array). Elaborate pointer and
length arithmetic can introduce errors and nullify the
security benefits of `snprintf`.
In particular, `snprintf` is not well-suited
to constructing a string iteratively, by appending to an
existing buffer. `snprintf` returns one of
two values, `-1` on errors, or the number of
characters which *would have been written to the
buffer if the buffer were large enough*. This means
that adding the result of `snprintf` to the
buffer pointer to skip over the characters just written is
incorrect and risky. However, as long as the length argument
is not zero, the buffer will remain null-terminated. <<ex-Defensive_Coding-C-String-Functions-snprintf-incremental>>
works because `end -current > 0` is a loop
invariant. After the loop, the result string is in the
`buf` variable.
[[ex-Defensive_Coding-C-String-Functions-snprintf-incremental]]
.Repeatedly writing to a buffer using `snprintf`
====
[source,c]
----
include::../snippets/C-String-Functions-snprintf-incremental.adoc[]
----
====
If you want to avoid the call to `strlen`
for performance reasons, you have to check for a negative
return value from `snprintf` and also check
if the return value is equal to the specified buffer length or
larger. Only if neither condition applies, you may advance
the pointer to the start of the write buffer by the number
return by `snprintf`. However, this
optimization is rarely worthwhile.
Note that it is not permitted to use the same buffer both as
the destination and as a source argument.
[[sect-Defensive_Coding-C-Libc-vsnprintf]]
==== `vsnprintf` and Format Strings
If you use `vsnprintf` (or
`vasprintf` or even
`snprintf`) with a format string which is
not a constant, but a function argument, it is important to
annotate the function with a `format`
function attribute, so that GCC can warn about misuse of your
function (see <<ex-Defensive_Coding-C-String-Functions-format-Attribute>>).
[[ex-Defensive_Coding-C-String-Functions-format-Attribute]]
.The `format` function attribute
====
[source,c]
----
include::../snippets/C-String-Functions-format.adoc[]
----
====
[[sect-Defensive_Coding-C-Libc-strncpy]]
==== `strncpy`
The `strncpy` function does not ensure that
the target buffer is null-terminated. A common idiom for
ensuring NUL termination is:
[source,c]
----
include::../snippets/C-String-Functions-strncpy.adoc[]
----
Another approach uses the `strncat`
function for this purpose:
[source,c]
----
include::../snippets/C-String-Functions-strncat-as-strncpy.adoc[]
----
[[sect-Defensive_Coding-C-Libc-strncat]]
==== `strncat`
The length argument of the `strncat`
function specifies the maximum number of characters copied
from the source buffer, excluding the terminating NUL
character. This means that the required number of bytes in
the destination buffer is the length of the original string,
plus the length argument in the `strncat`
call, plus one. Consequently, this function is rarely
appropriate for performing a length-checked string operation,
with the notable exception of the `strcpy`
emulation described in <<sect-Defensive_Coding-C-Libc-strncpy>>.
To implement a length-checked string append, you can use an
approach similar to <<ex-Defensive_Coding-C-String-Functions-snprintf-incremental>>:
[source,c]
----
include::../snippets/C-String-Functions-strncat-emulation.adoc[]
----
In many cases, including this one, the string concatenation
can be avoided by combining everything into a single format
string:
[source,c]
----
include::../snippets/C-String-Functions-strncat-merged.adoc[]
----
But you should must not dynamically construct format strings
to avoid concatenation because this would prevent GCC from
type-checking the argument lists.
It is not possible to use format strings like
`"%s%s"` to implement concatenation, unless
you use separate buffers. `snprintf` does
not support overlapping source and target strings.
==== `strlcpy` and `strlcat`
Some systems support `strlcpy` and
`strlcat` functions which behave this way,
but these functions are not part of GNU libc.
`strlcpy` is often replaced with
`snprintf` with a `"%s"`
format string. See <<sect-Defensive_Coding-C-Libc-strncpy>> for a caveat
related to the `snprintf` return value.
To emulate `strlcat`, use the approach
described in <<sect-Defensive_Coding-C-Libc-strncat>>.
==== ISO C11 Annex K *pass:attributes[{blank}]`_s` functions
ISO C11 adds another set of length-checking functions, but GNU
libc currently does not implement them.
==== Other `strn*` and `stpn*` functions
GNU libc contains additional functions with different variants
of length checking. Consult the documentation before using
them to find out what the length actually means.