:experimental: include::partial$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 <>. * `sprintf` * `strcat` * `strcpy` * `vsprintf` Use the indicated replacements for the functions below. * `alloca` ⟶ `malloc` and `free` (see <>) * `putenv` ⟶ explicit `envp` argument in process creation (see xref:../tasks/Tasks-Processes.adoc#sect-Defensive_Coding-Tasks-Processes-environ[Specifying the Process Environment]) * `setenv` ⟶ explicit `envp` argument in process creation (see xref:../tasks/Tasks-Processes.adoc#sect-Defensive_Coding-Tasks-Processes-environ[Specifying the Process Environment]) * `strdupa` ⟶ `strdup` and `free` (see <>) * `strndupa` ⟶ `strndup` and `free` (see <>) * `system` ⟶ `posix_spawn` or `fork`pass:attributes[{blank}]/pass:attributes[{blank}]`execve`pass:attributes[{blank}]/ (see xref:../tasks/Tasks-Processes.adoc#sect-Defensive_Coding-Tasks-Processes-execve[Bypassing the Shell]) * `unsetenv` ⟶ explicit `envp` argument in process creation (see xref:../tasks/Tasks-Processes.adoc#sect-Defensive_Coding-Tasks-Processes-environ[Specifying the Process Environment]) [[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::example$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. <> 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::example$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]] .The `format` function attribute ==== [source,c] ---- include::example$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::example$C-String-Functions-strncpy.adoc[] ---- Another approach uses the `strncat` function for this purpose: [source,c] ---- include::example$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 <>. To implement a length-checked string append, you can use an approach similar to <>: [source,c] ---- include::example$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::example$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 <> for a caveat related to the `snprintf` return value. To emulate `strlcat`, use the approach described in <>. ==== 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. === Using tricky syscalls or library functions ==== `readlink` This is the hardest system call to use correctly because of everything you have to do * The buf should be of PATH_MAX length, that includes space for the terminating NUL character. * The bufsize should be `sizeof(buf) - 1` * `readlink` return value should be caught as a signed integer (ideally type `ssize_t`). * It should be checked for < 0 for indication of errors. * The caller needs to '\0' -terminate the buffer using the returned value as an index. ==== `chroot` * Target dir should be writable only by root (this implies owned by). * Must call `chdir` immediately after chroot or you are not really in the changed root. ==== `stat`, `lstat`, `fstatat` * These functions have an inherent race in that you operate on the path name which could change in the mean time. Using fstat is recommended when stat is used. * If `S_ISLNK` macro is used, the stat buffer MUST come from lstat or from fstatat with `AT_SYMLINK_NOFOLLOW` * If you are doing something really important, call fstat after opening and compare the before and after stat buffers before trusting them. ==== `setgid`, `setuid`: * Call these in the right order: groups and then uid. * Always check the return code. * If `setgid` & `setuid` are used, supplemental groups are not reset. This must be done with setgroups or initgroups before the uid change.