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
posix_spawn_file_actions_addclose()for the file descriptor before
<&-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
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.
Note that you also have to consider the libraries that your program uses.
O_RDONLYmode 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
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:
The approach with the
xstdopen module is simpler, but it adds three
system calls to program startup. Whereas the approach with the
modules is more complex, but adds no overhead (no additional system calls)
in the normal case.
To use the approach with the
mainfunction, near the beginning, namely right after the i18n related initializations (
textdomaininvocations, if any) and the
closeoutinitialization (if any), insert the invocation:
/* Ensure that stdin, stdout, stderr are open. */ xstdopen ();
To use the approach with the
Do so according to this table: