xref: /freebsd/tests/sys/file/closefrom_test.c (revision df21a004be237a1dccd03c7b47254625eea62fa9)
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
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
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
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
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
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);
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);
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);
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);
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);
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);
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);
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
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);
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);
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 
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