unicodeio: Fix wrong result on FreeBSD.
[gnulib.git] / doc / xstdopen.texi
bloba01ea8ed4acebd1acf4641ae81fbcb65a929a958
1 @c GNU xstdopen and *-safer modules documentation
3 @c Copyright (C) 2019--2020 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 or
7 @c any later version published by the Free Software Foundation; with no
8 @c Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A
9 @c copy of the license is at <https://www.gnu.org/licenses/fdl-1.3.en.html>.
11 @c Written by Bruno Haible, based on ideas from Paul Eggert.
13 @node Closed standard fds
14 @section Handling closed standard file descriptors
16 @cindex xstdopen
17 @cindex stdopen
18 @cindex dirent-safer
19 @cindex fcntl-safer
20 @cindex fopen-safer
21 @cindex freopen-safer
22 @cindex openat-safer
23 @cindex pipe2-safer
24 @cindex popen-safer
25 @cindex stdlib-safer
26 @cindex tmpfile-safer
27 @cindex unistd-safer
29 Usually, when a program gets invoked, its file descriptors
30 0 (for standard input), 1 (for standard output), and 2 (for standard error)
31 are open.  But there are situations when some of these file descriptors are
32 closed.  These situations can arise when
33 @itemize @bullet
34 @item
35 The invoking process invokes @code{close()} on the file descriptor before
36 @code{exec}, or
37 @item
38 The invoking process invokes @code{posix_spawn_file_actions_addclose()} for
39 the file descriptor before @code{posix_spawn} or @code{posix_spawnp}, or
40 @item
41 The invoking process is a Bourne shell, and the shell script uses the
42 POSIX syntax for closing the file descriptor:
43 @code{<&-} for closing standard input,
44 @code{>&-} for closing standard output, or
45 @code{2>&-} for closing standard error.
46 @end itemize
48 When a closed file descriptor is accessed through a system call, such as
49 @code{fcntl()}, @code{fstat()}, @code{read()}, or @code{write()}, the
50 system calls fails with error @code{EBADF} ("Bad file descriptor").
52 When a new file descriptor is allocated, the operating system chooses the
53 smallest non-negative integer that does not yet correspond to an open file
54 descriptor.  So, when a given fd (0, 1, or 2) is closed, opening a new file
55 descriptor may assign the new file descriptor to this fd.  This can have
56 unintended effects, because now standard input/output/error of your process
57 is referring to a file that was not meant to be used in that role.
59 This situation is a security risk because the behaviour of the program
60 in this situation was surely never tested, therefore anything can happen
61 then -- from overwriting precious files of the user to endless loops.
63 To deal with this situation, you first need to determine whether your
64 program is affected by the problem.
65 @itemize @bullet
66 @item
67 Does your program invoke functions that allocate new file descriptors?
68 These are the system calls
69 @itemize @bullet
70 @item
71 @code{open()}, @code{openat()}, @code{creat()}
72 @item
73 @code{dup()}
74 @item
75 @code{fopen()}, @code{freopen()}
76 @item
77 @code{pipe()}, @code{pipe2()}, @code{popen()}
78 @item
79 @code{opendir()}
80 @item
81 @code{tmpfile()}, @code{mkstemp()}, @code{mkstemps()}, @code{mkostemp()},
82 @code{mkostemps()}
83 @end itemize
84 @noindent
85 Note that you also have to consider the libraries that your program uses.
86 @item
87 If your program may open two or more file descriptors or FILE streams for
88 reading at the same time, and some of them may reference standard input,
89 your program @emph{is affected}.
90 @item
91 If your program may open two or more file descriptors or FILE streams for
92 writing at the same time, and some of them may reference standard output
93 or standard error, your program @emph{is affected}.
94 @item
95 If your program does not open new file descriptors or FILE streams, it is
96 @emph{not affected}.
97 @item
98 If your program opens only one new file descriptor or FILE stream at a time,
99 it is @emph{not affected}.  This is often the case for programs that are
100 structured in simple phases: first a phase where input is read from a file
101 into memory, then a phase of processing in memory, finally a phase where
102 the result is written to a file.
103 @item
104 If your program opens only two new file descriptors or FILE streams at a
105 time, out of which one is for reading and the one is for writing, it is
106 @emph{not affected}.  This is because if the first file descriptor is
107 allocated and the second file descriptor is picked as 0, 1, or 2, and
108 both happen to be the same, writing to the one opened in @code{O_RDONLY}
109 mode will produce an error @code{EBADF}, as desired.
110 @end itemize
112 If your program is affected, what is the mitigation?
114 Some operating systems install open file descriptors in place of the
115 closed ones, either in the @code{exec} system call or during program
116 startup.  When such a file descriptor is accessed through a system call,
117 it behaves like an open file descriptor opened for the ``wrong'' direction:
118 the system calls @code{fcntl()} and @code{fstat()} succeed, whereas
119 @code{read()} from fd 0 and @code{write()} to fd 1 or 2 fail with error
120 @code{EBADF} ("Bad file descriptor").  The important point here is that
121 when your program allocates a new file descriptor, it will have a value
122 greater than 2.
124 This mitigation is enabled on HP-UX, for all programs, and on glibc,
125 FreeBSD, NetBSD, OpenBSD, but only for setuid or setgid programs.  Since
126 it is operating system dependent, it is not a complete mitigation.
128 For a complete mitigation, Gnulib provides two alternative sets of modules:
129 @itemize @bullet
130 @item
131 The @code{xstdopen} module.
132 @item
133 The @code{*-safer} modules:
134 @code{fcntl-safer},
135 @code{openat-safer},
136 @code{unistd-safer},
137 @code{fopen-safer},
138 @code{freopen-safer},
139 @code{pipe2-safer},
140 @code{popen-safer},
141 @code{dirent-safer},
142 @code{tmpfile-safer},
143 @code{stdlib-safer}.
144 @end itemize
146 The approach with the @code{xstdopen} module is simpler, but it adds three
147 system calls to program startup.  Whereas the approach with the @code{*-safer}
148 modules is more complex, but adds no overhead (no additional system calls)
149 in the normal case.
151 To use the approach with the @code{xstdopen} module:
152 @enumerate
153 @item
154 Import the module @code{xstdopen} from Gnulib.
155 @item
156 In the compilation unit that contains the @code{main} function, include
157 @code{"xstdopen.h"}.
158 @item
159 In the @code{main} function, near the beginning, namely right after
160 the i18n related initializations (@code{setlocale}, @code{bindtextdomain},
161 @code{textdomain} invocations, if any) and
162 the @code{closeout} initialization (if any), insert the invocation:
163 @smallexample
164 /* Ensure that stdin, stdout, stderr are open.  */
165 xstdopen ();
166 @end smallexample
167 @end enumerate
169 To use the approach with the @code{*-safer} modules:
170 @enumerate
171 @item
172 Import the relevant modules from Gnulib.
173 @item
174 In the compilation units that contain these function calls, include the
175 replacement header file.
176 @end enumerate
177 Do so according to this table:
178 @multitable @columnfractions .28 .32 .4
179 @headitem Function @tab Module @tab Header file
180 @item @code{open()}
181 @tab @code{fcntl-safer}
182 @tab @code{"fcntl--.h"}
183 @item @code{openat()}
184 @tab @code{openat-safer}
185 @tab @code{"fcntl--.h"}
186 @item @code{creat()}
187 @tab @code{fcntl-safer}
188 @tab @code{"fcntl--.h"}
189 @item @code{dup()}
190 @tab @code{unistd-safer}
191 @tab @code{"unistd--.h"}
192 @item @code{fopen()}
193 @tab @code{fopen-safer}
194 @tab @code{"stdio--.h"}
195 @item @code{freopen()}
196 @tab @code{freopen-safer}
197 @tab @code{"stdio--.h"}
198 @item @code{pipe()}
199 @tab @code{unistd-safer}
200 @tab @code{"unistd--.h"}
201 @item @code{pipe2()}
202 @tab @code{pipe2-safer}
203 @tab @code{"unistd--.h"}
204 @item @code{popen()}
205 @tab @code{popen-safer}
206 @tab @code{"stdio--.h"}
207 @item @code{opendir()}
208 @tab @code{dirent-safer}
209 @tab @code{"dirent--.h"}
210 @item @code{tmpfile()}
211 @tab @code{tmpfile-safer}
212 @tab @code{"stdio--.h"}
213 @item @code{mkstemp()}
214 @tab @code{stdlib-safer}
215 @tab @code{"stdlib--.h"}
216 @item @code{mkstemps()}
217 @tab @code{stdlib-safer}
218 @tab @code{"stdlib--.h"}
219 @item @code{mkostemp()}
220 @tab @code{stdlib-safer}
221 @tab @code{"stdlib--.h"}
222 @item @code{mkostemps()}
223 @tab @code{stdlib-safer}
224 @tab @code{"stdlib--.h"}
225 @end multitable