1 @c GNU xstdopen and *-safer modules documentation
3 @c Copyright (C) 2019 Free Software Foundation, Inc.
5 @c Permission is granted to copy, distribute and/or modify this document
6 @c under the terms of the GNU Free Documentation License, Version 1.3
7 @c or any later version published by the Free Software Foundation;
8 @c with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
9 @c Texts. A copy of the license is included in the ``GNU Free
10 @c Documentation License'' file as part of this distribution.
12 @c Written by Bruno Haible, based on ideas from Paul Eggert.
14 @node Closed standard fds
15 @section Handling closed standard file descriptors
30 Usually, when a program gets invoked, its file descriptors
31 0 (for standard input), 1 (for standard output), and 2 (for standard error)
32 are open. But there are situations when some of these file descriptors are
33 closed. These situations can arise when
36 The invoking process invokes @code{close()} on the file descriptor before
39 The invoking process invokes @code{posix_spawn_file_actions_addclose()} for
40 the file descriptor before @code{posix_spawn} or @code{posix_spawnp}, or
42 The invoking process is a Bourne shell, and the shell script uses the
43 POSIX syntax for closing the file descriptor:
44 @code{<&-} for closing standard input,
45 @code{>&-} for closing standard output, or
46 @code{2>&-} for closing standard error.
49 When a closed file descriptor is accessed through a system call, such as
50 @code{fcntl()}, @code{fstat()}, @code{read()}, or @code{write()}, the
51 system calls fails with error @code{EBADF} ("Bad file descriptor").
53 When a new file descriptor is allocated, the operating system chooses the
54 smallest non-negative integer that does not yet correspond to an open file
55 descriptor. So, when a given fd (0, 1, or 2) is closed, opening a new file
56 descriptor may assign the new file descriptor to this fd. This can have
57 unintended effects, because now standard input/output/error of your process
58 is referring to a file that was not meant to be used in that role.
60 This situation is a security risk because the behaviour of the program
61 in this situation was surely never tested, therefore anything can happen
62 then -- from overwriting precious files of the user to endless loops.
64 To deal with this situation, you first need to determine whether your
65 program is affected by the problem.
68 Does your program invoke functions that allocate new file descriptors?
69 These are the system calls
72 @code{open()}, @code{openat()}, @code{creat()}
76 @code{fopen()}, @code{freopen()}
78 @code{pipe()}, @code{pipe2()}, @code{popen()}
82 @code{tmpfile()}, @code{mkstemp()}, @code{mkstemps()}, @code{mkostemp()},
86 Note that you also have to consider the libraries that your program uses.
88 If your program may open two or more file descriptors or FILE streams for
89 reading at the same time, and some of them may reference standard input,
90 your program @emph{is affected}.
92 If your program may open two or more file descriptors or FILE streams for
93 writing at the same time, and some of them may reference standard output
94 or standard error, your program @emph{is affected}.
96 If your program does not open new file descriptors or FILE streams, it is
99 If your program opens only one new file descriptor or FILE stream at a time,
100 it is @emph{not affected}. This is often the case for programs that are
101 structured in simple phases: first a phase where input is read from a file
102 into memory, then a phase of processing in memory, finally a phase where
103 the result is written to a file.
105 If your program opens only two new file descriptors or FILE streams at a
106 time, out of which one is for reading and the one is for writing, it is
107 @emph{not affected}. This is because if the first file descriptor is
108 allocated and the second file descriptor is picked as 0, 1, or 2, and
109 both happen to be the same, writing to the one opened in @code{O_RDONLY}
110 mode will produce an error @code{EBADF}, as desired.
113 If your program is affected, what is the mitigation?
115 Some operating systems install open file descriptors in place of the
116 closed ones, either in the @code{exec} system call or during program
117 startup. When such a file descriptor is accessed through a system call,
118 it behaves like an open file descriptor opened for the ``wrong'' direction:
119 the system calls @code{fcntl()} and @code{fstat()} succeed, whereas
120 @code{read()} from fd 0 and @code{write()} to fd 1 or 2 fail with error
121 @code{EBADF} ("Bad file descriptor"). The important point here is that
122 when your program allocates a new file descriptor, it will have a value
125 This mitigation is enabled on HP-UX, for all programs, and on glibc,
126 FreeBSD, NetBSD, OpenBSD, but only for setuid or setgid programs. Since
127 it is operating system dependent, it is not a complete mitigation.
129 For a complete mitigation, Gnulib provides two alternative sets of modules:
132 The @code{xstdopen} module.
134 The @code{*-safer} modules:
139 @code{freopen-safer},
143 @code{tmpfile-safer},
147 The approach with the @code{xstdopen} module is simpler, but it adds three
148 system calls to program startup. Whereas the approach with the @code{*-safer}
149 modules is more complex, but adds no overhead (no additional system calls)
152 To use the approach with the @code{xstdopen} module:
155 Import the module @code{xstdopen} from Gnulib.
157 In the compilation unit that contains the @code{main} function, include
160 In the @code{main} function, near the beginning, namely right after
161 the i18n related initializations (@code{setlocale}, @code{bindtextdomain},
162 @code{textdomain} invocations, if any) and
163 the @code{closeout} initialization (if any), insert the invocation:
165 /* Ensure that stdin, stdout, stderr are open. */
170 To use the approach with the @code{*-safer} modules:
173 Import the relevant modules from Gnulib.
175 In the compilation units that contain these function calls, include the
176 replacement header file.
178 Do so according to this table:
179 @multitable @columnfractions .28 .32 .4
180 @headitem Function @tab Module @tab Header file
182 @tab @code{fcntl-safer}
183 @tab @code{"fcntl--.h"}
184 @item @code{openat()}
185 @tab @code{openat-safer}
186 @tab @code{"fcntl--.h"}
188 @tab @code{fcntl-safer}
189 @tab @code{"fcntl--.h"}
191 @tab @code{unistd-safer}
192 @tab @code{"unistd--.h"}
194 @tab @code{fopen-safer}
195 @tab @code{"stdio--.h"}
196 @item @code{freopen()}
197 @tab @code{freopen-safer}
198 @tab @code{"stdio--.h"}
200 @tab @code{unistd-safer}
201 @tab @code{"unistd--.h"}
203 @tab @code{pipe2-safer}
204 @tab @code{"unistd--.h"}
206 @tab @code{popen-safer}
207 @tab @code{"stdio--.h"}
208 @item @code{opendir()}
209 @tab @code{dirent-safer}
210 @tab @code{"dirent--.h"}
211 @item @code{tmpfile()}
212 @tab @code{tmpfile-safer}
213 @tab @code{"stdio--.h"}
214 @item @code{mkstemp()}
215 @tab @code{stdlib-safer}
216 @tab @code{"stdlib--.h"}
217 @item @code{mkstemps()}
218 @tab @code{stdlib-safer}
219 @tab @code{"stdlib--.h"}
220 @item @code{mkostemp()}
221 @tab @code{stdlib-safer}
222 @tab @code{"stdlib--.h"}
223 @item @code{mkostemps()}
224 @tab @code{stdlib-safer}
225 @tab @code{"stdlib--.h"}