1 /*-
2 * Copyright (c) 2009 Hudson River Trading LLC
3 * Written by: John H. Baldwin <jhb@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 /*
29 * Regression tests for the closefrom(2) system call.
30 */
31
32 #include <sys/param.h>
33 #include <sys/mman.h>
34 #include <sys/stat.h>
35 #include <sys/user.h>
36 #include <sys/wait.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <libutil.h>
40 #include <paths.h>
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include <atf-c.h>
48
49 static char *shared_page;
50
51 /*
52 * A variant of ATF_REQUIRE that is suitable for use in child
53 * processes. Since these tests close stderr, errors are reported to
54 * a shared page of memory checked by the parent process.
55 */
56 #define CHILD_REQUIRE(exp) do { \
57 if (!(exp)) \
58 child_fail_require(__FILE__, __LINE__, \
59 #exp " not met"); \
60 } while (0)
61
62 static __dead2 __printflike(3, 4) void
child_fail_require(const char * file,int line,const char * fmt,...)63 child_fail_require(const char *file, int line, const char *fmt, ...)
64 {
65 FILE *fp;
66 va_list ap;
67
68 fp = fmemopen(shared_page, PAGE_SIZE - 1, "w");
69 if (fp == NULL)
70 exit(1);
71
72 fprintf(fp, "%s:%d: ", file, line);
73 va_start(ap, fmt);
74 vfprintf(fp, fmt, ap);
75 va_end(ap);
76 fclose(fp);
77
78 exit(0);
79 }
80
81 static pid_t
child_fork(void)82 child_fork(void)
83 {
84 shared_page = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON |
85 MAP_SHARED, -1, 0);
86 ATF_REQUIRE_MSG(shared_page != MAP_FAILED, "mmap: %s", strerror(errno));
87 return (atf_utils_fork());
88 }
89
90 static void
child_wait(pid_t pid)91 child_wait(pid_t pid)
92 {
93 atf_utils_wait(pid, 0, "", "");
94 if (shared_page[0] != '\0')
95 atf_tc_fail("%s", shared_page);
96 }
97
98 /*
99 * Use kinfo_getfile() to fetch the list of file descriptors and figure out
100 * the highest open file descriptor.
101 */
102 static int
highest_fd(void)103 highest_fd(void)
104 {
105 struct kinfo_file *kif;
106 int cnt, i, highest;
107
108 kif = kinfo_getfile(getpid(), &cnt);
109 ATF_REQUIRE_MSG(kif != NULL, "kinfo_getfile: %s", strerror(errno));
110 highest = -1;
111 for (i = 0; i < cnt; i++)
112 if (kif[i].kf_fd > highest)
113 highest = kif[i].kf_fd;
114 free(kif);
115 return (highest);
116 }
117
118 static int
devnull(void)119 devnull(void)
120 {
121 int fd;
122
123 fd = open(_PATH_DEVNULL, O_RDONLY);
124 ATF_REQUIRE_MSG(fd != -1, "open(\" "_PATH_DEVNULL" \"): %s",
125 strerror(errno));
126 return (fd);
127 }
128
129 ATF_TC_WITHOUT_HEAD(closefrom_simple);
ATF_TC_BODY(closefrom_simple,tc)130 ATF_TC_BODY(closefrom_simple, tc)
131 {
132 int fd, start;
133
134 /* We'd better start up with fd's 0, 1, and 2 open. */
135 start = highest_fd();
136 ATF_REQUIRE(start >= 2);
137
138 fd = devnull();
139 ATF_REQUIRE(fd > start);
140
141 /* Make sure highest_fd() works. */
142 ATF_REQUIRE_INTEQ(fd, highest_fd());
143
144 /* Try to use closefrom() to close just the new fd. */
145 closefrom(fd);
146 ATF_REQUIRE_INTEQ(start, highest_fd());
147 }
148
149 ATF_TC_WITHOUT_HEAD(closefrom_with_holes);
ATF_TC_BODY(closefrom_with_holes,tc)150 ATF_TC_BODY(closefrom_with_holes, tc)
151 {
152 int i, start;
153
154 start = highest_fd();
155
156 /* Eat up 16 descriptors. */
157 for (i = 0; i < 16; i++)
158 (void)devnull();
159
160 ATF_REQUIRE_INTEQ(start + 16, highest_fd());
161
162 /* Close half of them. */
163 closefrom(start + 9);
164 ATF_REQUIRE_INTEQ(start + 8, highest_fd());
165
166 /* Explicitly close two descriptors to create holes. */
167 ATF_REQUIRE_MSG(close(start + 3) == 0, "close(start + 3): %s",
168 strerror(errno));
169 ATF_REQUIRE_MSG(close(start + 5) == 0, "close(start + 5): %s",
170 strerror(errno));
171
172 /* Verify that close on the closed descriptors fails with EBADF. */
173 ATF_REQUIRE_ERRNO(EBADF, close(start + 3) == -1);
174 ATF_REQUIRE_ERRNO(EBADF, close(start + 5) == -1);
175
176 /* Close most remaining descriptors. */
177 closefrom(start + 2);
178 ATF_REQUIRE_INTEQ(start + 1, highest_fd());
179 }
180
181 ATF_TC_WITHOUT_HEAD(closefrom_zero);
ATF_TC_BODY(closefrom_zero,tc)182 ATF_TC_BODY(closefrom_zero, tc)
183 {
184 pid_t pid;
185 int fd;
186
187 /* Ensure standard descriptors are open. */
188 ATF_REQUIRE(highest_fd() >= 2);
189
190 pid = child_fork();
191 if (pid == 0) {
192 /* Child. */
193 closefrom(0);
194 fd = highest_fd();
195 CHILD_REQUIRE(fd == -1);
196 exit(0);
197 }
198
199 child_wait(pid);
200 }
201
202 ATF_TC_WITHOUT_HEAD(closefrom_negative_one);
ATF_TC_BODY(closefrom_negative_one,tc)203 ATF_TC_BODY(closefrom_negative_one, tc)
204 {
205 pid_t pid;
206 int fd;
207
208 /* Ensure standard descriptors are open. */
209 ATF_REQUIRE(highest_fd() >= 2);
210
211 pid = child_fork();
212 if (pid == 0) {
213 /* Child. */
214 closefrom(-1);
215 fd = highest_fd();
216 CHILD_REQUIRE(fd == -1);
217 exit(0);
218 }
219
220 child_wait(pid);
221 }
222
223 ATF_TC_WITHOUT_HEAD(closefrom_in_holes);
ATF_TC_BODY(closefrom_in_holes,tc)224 ATF_TC_BODY(closefrom_in_holes, tc)
225 {
226 int start;
227
228 start = highest_fd();
229 ATF_REQUIRE(start >= 2);
230
231 /* Dup stdout to a higher fd. */
232 ATF_REQUIRE_INTEQ(start + 4, dup2(1, start + 4));
233 ATF_REQUIRE_INTEQ(start + 4, highest_fd());
234
235 /* Do a closefrom() starting in a hole. */
236 closefrom(start + 2);
237 ATF_REQUIRE_INTEQ(start, highest_fd());
238
239 /* Do a closefrom() beyond our highest open fd. */
240 closefrom(start + 32);
241 ATF_REQUIRE_INTEQ(start, highest_fd());
242 }
243
244 ATF_TC_WITHOUT_HEAD(closerange_basic);
ATF_TC_BODY(closerange_basic,tc)245 ATF_TC_BODY(closerange_basic, tc)
246 {
247 struct stat sb;
248 int i, start;
249
250 start = highest_fd();
251
252 /* Open 8 file descriptors */
253 for (i = 0; i < 8; i++)
254 (void)devnull();
255 ATF_REQUIRE_INTEQ(start + 8, highest_fd());
256
257 /* close_range() a hole in the middle */
258 ATF_REQUIRE_INTEQ(0, close_range(start + 3, start + 5, 0));
259 for (i = start + 3; i < start + 6; ++i)
260 ATF_REQUIRE_ERRNO(EBADF, fstat(i, &sb) == -1);
261
262 /* close_range from the middle of the hole */
263 ATF_REQUIRE_INTEQ(0, close_range(start + 4, start + 6, 0));
264 ATF_REQUIRE_INTEQ(start + 8, highest_fd());
265
266 /* close_range to the end; effectively closefrom(2) */
267 ATF_REQUIRE_INTEQ(0, close_range(start + 3, ~0L, 0));
268 ATF_REQUIRE_INTEQ(start + 2, highest_fd());
269
270 /* Now close the rest */
271 ATF_REQUIRE_INTEQ(0, close_range(start + 1, start + 4, 0));
272 ATF_REQUIRE_INTEQ(start, highest_fd());
273 }
274
275 ATF_TC_WITHOUT_HEAD(closefrom_zero_twice);
ATF_TC_BODY(closefrom_zero_twice,tc)276 ATF_TC_BODY(closefrom_zero_twice, tc)
277 {
278 pid_t pid;
279 int fd;
280
281 /* Ensure standard descriptors are open. */
282 ATF_REQUIRE(highest_fd() >= 2);
283
284 pid = child_fork();
285 if (pid == 0) {
286 /* Child. */
287 closefrom(0);
288 fd = highest_fd();
289 CHILD_REQUIRE(fd == -1);
290 closefrom(0);
291 fd = highest_fd();
292 CHILD_REQUIRE(fd == -1);
293 exit(0);
294 }
295
296 child_wait(pid);
297 }
298
299 static void
require_fd_flag(int fd,const char * descr,const char * descr2,int flag,bool set)300 require_fd_flag(int fd, const char *descr, const char *descr2, int flag,
301 bool set)
302 {
303 int flags;
304
305 flags = fcntl(fd, F_GETFD);
306 ATF_REQUIRE_MSG(flags >= 0, "fcntl(.., F_GETFD): %s", strerror(errno));
307
308 if (set) {
309 ATF_REQUIRE_MSG((flags & flag) == flag,
310 "%s did not set %s on fd %d", descr, descr2, fd);
311 } else {
312 ATF_REQUIRE_MSG((flags & flag) == 0,
313 "%s set %s when it should not have on fd %d", descr, descr2,
314 fd);
315 }
316 }
317
318 ATF_TC_WITHOUT_HEAD(closerange_CLOEXEC);
ATF_TC_BODY(closerange_CLOEXEC,tc)319 ATF_TC_BODY(closerange_CLOEXEC, tc)
320 {
321 int i, start;
322
323 start = highest_fd();
324 ATF_REQUIRE(start >= 2);
325
326 for (i = 0; i < 8; i++)
327 (void)devnull();
328 ATF_REQUIRE_INTEQ(start + 8, highest_fd());
329
330 ATF_REQUIRE_INTEQ(0, close_range(start + 2, start + 5,
331 CLOSE_RANGE_CLOEXEC));
332 for (i = 1; i < 9; i++) {
333 require_fd_flag(start + i, "CLOSE_RANGE_CLOEXEC",
334 "close-on-exec", FD_CLOEXEC, i >= 2 && i <= 5);
335 }
336 ATF_REQUIRE_INTEQ(0, close_range(start + 1, start + 8, 0));
337 }
338
339 ATF_TC_WITHOUT_HEAD(closerange_CLOFORK);
ATF_TC_BODY(closerange_CLOFORK,tc)340 ATF_TC_BODY(closerange_CLOFORK, tc)
341 {
342 int i, start;
343
344 start = highest_fd();
345 ATF_REQUIRE(start >= 2);
346
347 for (i = 0; i < 8; i++)
348 (void)devnull();
349 ATF_REQUIRE_INTEQ(start + 8, highest_fd());
350
351 ATF_REQUIRE_INTEQ(0, close_range(start + 2, start + 5,
352 CLOSE_RANGE_CLOFORK));
353 for (i = 1; i < 9; i++) {
354 require_fd_flag(start + i, "CLOSE_RANGE_CLOFORK",
355 "close-on-fork", FD_CLOFORK, i >= 2 && i <= 5);
356 }
357 ATF_REQUIRE_INTEQ(0, close_range(start + 1, start + 8, 0));
358 }
359
ATF_TP_ADD_TCS(tp)360 ATF_TP_ADD_TCS(tp)
361 {
362 ATF_TP_ADD_TC(tp, closefrom_simple);
363 ATF_TP_ADD_TC(tp, closefrom_with_holes);
364 ATF_TP_ADD_TC(tp, closefrom_zero);
365 ATF_TP_ADD_TC(tp, closefrom_negative_one);
366 ATF_TP_ADD_TC(tp, closefrom_in_holes);
367 ATF_TP_ADD_TC(tp, closerange_basic);
368 ATF_TP_ADD_TC(tp, closefrom_zero_twice);
369 ATF_TP_ADD_TC(tp, closerange_CLOEXEC);
370 ATF_TP_ADD_TC(tp, closerange_CLOFORK);
371
372 return (atf_no_error());
373 }
374