Next: Handling strings with NUL characters, Previous: Extern inline functions, Up: Particular Modules [Contents][Index]
Usually, when a program gets invoked, its file descriptors 0 (for standard input), 1 (for standard output), and 2 (for standard error) are open. But there are situations when some of these file descriptors are closed. These situations can arise when
close()
on the file descriptor before
exec
, or
posix_spawn_file_actions_addclose()
for
the file descriptor before posix_spawn
or posix_spawnp
, or
<&-
for closing standard input,
>&-
for closing standard output, or
2>&-
for closing standard error.
When a closed file descriptor is accessed through a system call, such as
fcntl()
, fstat()
, read()
, or write()
, the
system calls fails with error EBADF
("Bad file descriptor").
When a new file descriptor is allocated, the operating system chooses the smallest non-negative integer that does not yet correspond to an open file descriptor. So, when a given fd (0, 1, or 2) is closed, opening a new file descriptor may assign the new file descriptor to this fd. This can have unintended effects, because now standard input/output/error of your process is referring to a file that was not meant to be used in that role.
This situation is a security risk because the behaviour of the program in this situation was surely never tested, therefore anything can happen then – from overwriting precious files of the user to endless loops.
To deal with this situation, you first need to determine whether your program is affected by the problem.
open()
, openat()
, creat()
dup()
fopen()
, freopen()
pipe()
, pipe2()
, popen()
opendir()
tmpfile()
, mkstemp()
, mkstemps()
, mkostemp()
,
mkostemps()
Note that you also have to consider the libraries that your program uses.
O_RDONLY
mode will produce an error EBADF
, as desired.
If your program is affected, what is the mitigation?
Some operating systems install open file descriptors in place of the
closed ones, either in the exec
system call or during program
startup. When such a file descriptor is accessed through a system call,
it behaves like an open file descriptor opened for the “wrong” direction:
the system calls fcntl()
and fstat()
succeed, whereas
read()
from fd 0 and write()
to fd 1 or 2 fail with error
EBADF
("Bad file descriptor"). The important point here is that
when your program allocates a new file descriptor, it will have a value
greater than 2.
This mitigation is enabled on HP-UX, for all programs, and on glibc, FreeBSD, NetBSD, OpenBSD, but only for setuid or setgid programs. Since it is operating system dependent, it is not a complete mitigation.
For a complete mitigation, Gnulib provides two alternative sets of modules:
xstdopen
module.
*-safer
modules:
fcntl-safer
,
openat-safer
,
unistd-safer
,
fopen-safer
,
freopen-safer
,
pipe2-safer
,
popen-safer
,
dirent-safer
,
tmpfile-safer
,
stdlib-safer
.
The approach with the xstdopen
module is simpler, but it adds three
system calls to program startup. Whereas the approach with the *-safer
modules is more complex, but adds no overhead (no additional system calls)
in the normal case.
To use the approach with the xstdopen
module:
xstdopen
from Gnulib.
main
function, include
"xstdopen.h"
.
main
function, near the beginning, namely right after
the i18n related initializations (setlocale
, bindtextdomain
,
textdomain
invocations, if any) and
the closeout
initialization (if any), insert the invocation:
/* Ensure that stdin, stdout, stderr are open. */ xstdopen ();
To use the approach with the *-safer
modules:
Do so according to this table:
Function | Module | Header file |
---|---|---|
open() | fcntl-safer | "fcntl--.h" |
openat() | openat-safer | "fcntl--.h" |
creat() | fcntl-safer | "fcntl--.h" |
dup() | unistd-safer | "unistd--.h" |
fopen() | fopen-safer | "stdio--.h" |
freopen() | freopen-safer | "stdio--.h" |
pipe() | unistd-safer | "unistd--.h" |
pipe2() | pipe2-safer | "unistd--.h" |
popen() | popen-safer | "stdio--.h" |
opendir() | dirent-safer | "dirent--.h" |
tmpfile() | tmpfile-safer | "stdio--.h" |
mkstemp() | stdlib-safer | "stdlib--.h" |
mkstemps() | stdlib-safer | "stdlib--.h" |
mkostemp() | stdlib-safer | "stdlib--.h" |
mkostemps() | stdlib-safer | "stdlib--.h" |
Next: Handling strings with NUL characters, Previous: Extern inline functions, Up: Particular Modules [Contents][Index]