log10l: Work around inaccurate implementation on NetBSD.
[gnulib.git] / doc / xstdopen.texi
blobf68f85b5c82d71ede94b1d7c4e9f653e25330c98
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
17 @cindex xstdopen
18 @cindex stdopen
19 @cindex dirent-safer
20 @cindex fcntl-safer
21 @cindex fopen-safer
22 @cindex freopen-safer
23 @cindex openat-safer
24 @cindex pipe2-safer
25 @cindex popen-safer
26 @cindex stdlib-safer
27 @cindex tmpfile-safer
28 @cindex unistd-safer
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
34 @itemize @bullet
35 @item
36 The invoking process invokes @code{close()} on the file descriptor before
37 @code{exec}, or
38 @item
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
41 @item
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.
47 @end itemize
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.
66 @itemize @bullet
67 @item
68 Does your program invoke functions that allocate new file descriptors?
69 These are the system calls
70 @itemize @bullet
71 @item
72 @code{open()}, @code{openat()}, @code{creat()}
73 @item
74 @code{dup()}
75 @item
76 @code{fopen()}, @code{freopen()}
77 @item
78 @code{pipe()}, @code{pipe2()}, @code{popen()}
79 @item
80 @code{opendir()}
81 @item
82 @code{tmpfile()}, @code{mkstemp()}, @code{mkstemps()}, @code{mkostemp()},
83 @code{mkostemps()}
84 @end itemize
85 @noindent
86 Note that you also have to consider the libraries that your program uses.
87 @item
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}.
91 @item
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}.
95 @item
96 If your program does not open new file descriptors or FILE streams, it is
97 @emph{not affected}.
98 @item
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.
104 @item
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.
111 @end itemize
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
123 greater than 2.
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:
130 @itemize @bullet
131 @item
132 The @code{xstdopen} module.
133 @item
134 The @code{*-safer} modules:
135 @code{fcntl-safer},
136 @code{openat-safer},
137 @code{unistd-safer},
138 @code{fopen-safer},
139 @code{freopen-safer},
140 @code{pipe2-safer},
141 @code{popen-safer},
142 @code{dirent-safer},
143 @code{tmpfile-safer},
144 @code{stdlib-safer}.
145 @end itemize
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)
150 in the normal case.
152 To use the approach with the @code{xstdopen} module:
153 @enumerate
154 @item
155 Import the module @code{xstdopen} from Gnulib.
156 @item
157 In the compilation unit that contains the @code{main} function, include
158 @code{"xstdopen.h"}.
159 @item
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:
164 @smallexample
165 /* Ensure that stdin, stdout, stderr are open.  */
166 xstdopen ();
167 @end smallexample
168 @end enumerate
170 To use the approach with the @code{*-safer} modules:
171 @enumerate
172 @item
173 Import the relevant modules from Gnulib.
174 @item
175 In the compilation units that contain these function calls, include the
176 replacement header file.
177 @end enumerate
178 Do so according to this table:
179 @multitable @columnfractions .28 .32 .4
180 @headitem Function @tab Module @tab Header file
181 @item @code{open()}
182 @tab @code{fcntl-safer}
183 @tab @code{"fcntl--.h"}
184 @item @code{openat()}
185 @tab @code{openat-safer}
186 @tab @code{"fcntl--.h"}
187 @item @code{creat()}
188 @tab @code{fcntl-safer}
189 @tab @code{"fcntl--.h"}
190 @item @code{dup()}
191 @tab @code{unistd-safer}
192 @tab @code{"unistd--.h"}
193 @item @code{fopen()}
194 @tab @code{fopen-safer}
195 @tab @code{"stdio--.h"}
196 @item @code{freopen()}
197 @tab @code{freopen-safer}
198 @tab @code{"stdio--.h"}
199 @item @code{pipe()}
200 @tab @code{unistd-safer}
201 @tab @code{"unistd--.h"}
202 @item @code{pipe2()}
203 @tab @code{pipe2-safer}
204 @tab @code{"unistd--.h"}
205 @item @code{popen()}
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"}
226 @end multitable