xref: /freebsd/tests/sys/file/closefrom_test.c (revision 7ef62cebc2f965b0f640263e179276928885e33d)
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 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 /*
32  * Regression tests for the closefrom(2) system call.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/mman.h>
37 #include <sys/user.h>
38 #include <sys/wait.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <libutil.h>
42 #include <paths.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 
49 struct shared_info {
50 	int	failed;
51 	char	tag[64];
52 	char	message[0];
53 };
54 
55 static int test = 1;
56 
57 static void
58 ok(const char *descr)
59 {
60 
61 	printf("ok %d - %s\n", test, descr);
62 	test++;
63 }
64 
65 static void
66 fail(const char *descr, const char *fmt, ...)
67 {
68 	va_list ap;
69 
70 	printf("not ok %d - %s", test, descr);
71 	test++;
72 	if (fmt) {
73 		va_start(ap, fmt);
74 		printf(" # ");
75 		vprintf(fmt, ap);
76 		va_end(ap);
77 	}
78 	printf("\n");
79 	exit(1);
80 }
81 
82 #define	fail_err(descr)		fail((descr), "%s", strerror(errno))
83 
84 static void
85 cok(struct shared_info *info, const char *descr)
86 {
87 
88 	info->failed = 0;
89 	strlcpy(info->tag, descr, sizeof(info->tag));
90 	exit(0);
91 }
92 
93 static void
94 cfail(struct shared_info *info, const char *descr, const char *fmt, ...)
95 {
96 	va_list ap;
97 
98 	info->failed = 1;
99 	strlcpy(info->tag, descr, sizeof(info->tag));
100 	if (fmt) {
101 		va_start(ap, fmt);
102 		vsprintf(info->message, fmt, ap);
103 		va_end(ap);
104 	}
105 	exit(0);
106 }
107 
108 #define	cfail_err(info, descr)	cfail((info), (descr), "%s", strerror(errno))
109 
110 /*
111  * Use kinfo_getfile() to fetch the list of file descriptors and figure out
112  * the highest open file descriptor.
113  */
114 static int
115 highest_fd(void)
116 {
117 	struct kinfo_file *kif;
118 	int cnt, i, highest;
119 
120 	kif = kinfo_getfile(getpid(), &cnt);
121 	if (kif == NULL)
122 		fail_err("kinfo_getfile");
123 	highest = INT_MIN;
124 	for (i = 0; i < cnt; i++)
125 		if (kif[i].kf_fd > highest)
126 			highest = kif[i].kf_fd;
127 	free(kif);
128 	return (highest);
129 }
130 
131 static int
132 devnull(void)
133 {
134 	int fd;
135 
136 	fd = open(_PATH_DEVNULL, O_RDONLY);
137 	if (fd < 0)
138 		fail_err("open(\" "_PATH_DEVNULL" \")");
139 	return (fd);
140 }
141 
142 int
143 main(void)
144 {
145 	struct shared_info *info;
146 	pid_t pid;
147 	int fd, flags, i, start;
148 
149 	printf("1..21\n");
150 
151 	/* We better start up with fd's 0, 1, and 2 open. */
152 	start = devnull();
153 	if (start == -1)
154 		fail("open", "bad descriptor %d", start);
155 	ok("open");
156 
157 	/* Make sure highest_fd() works. */
158 	fd = highest_fd();
159 	if (start != fd)
160 		fail("highest_fd", "bad descriptor %d != %d", start, fd);
161 	ok("highest_fd");
162 
163 	/* Try to use closefrom() for just closing fd 3. */
164 	closefrom(start + 1);
165 	fd = highest_fd();
166 	if (fd != start)
167 		fail("closefrom", "highest fd %d", fd);
168 	ok("closefrom");
169 
170 	/* Eat up 16 descriptors. */
171 	for (i = 0; i < 16; i++)
172 		(void)devnull();
173 	fd = highest_fd();
174 	if (fd != start + 16)
175 		fail("open 16", "highest fd %d", fd);
176 	ok("open 16");
177 
178 	/* Close half of them. */
179 	closefrom(11);
180 	fd = highest_fd();
181 	if (fd != 10)
182 		fail("closefrom", "highest fd %d", fd);
183 	ok("closefrom");
184 
185 	/* Explicitly close descriptors 6 and 8 to create holes. */
186 	if (close(6) < 0 || close(8) < 0)
187 		fail_err("close2 ");
188 	ok("close 2");
189 
190 	/* Verify that close on 6 and 8 fails with EBADF. */
191 	if (close(6) == 0)
192 		fail("close(6)", "did not fail");
193 	if (errno != EBADF)
194 		fail_err("close(6)");
195 	ok("close(6)");
196 	if (close(8) == 0)
197 		fail("close(8)", "did not fail");
198 	if (errno != EBADF)
199 		fail_err("close(8)");
200 	ok("close(8)");
201 
202 	/* Close from 4 on. */
203 	closefrom(4);
204 	fd = highest_fd();
205 	if (fd != 3)
206 		fail("closefrom", "highest fd %d", fd);
207 	ok("closefrom");
208 
209 	/* Allocate a small SHM region for IPC with our child. */
210 	info = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON |
211 	    MAP_SHARED, -1, 0);
212 	if (info == MAP_FAILED)
213 		fail_err("mmap");
214 	ok("mmap");
215 
216 	/* Fork a child process to test closefrom(0). */
217 	pid = fork();
218 	if (pid < 0)
219 		fail_err("fork");
220 	if (pid == 0) {
221 		/* Child. */
222 		closefrom(0);
223 		fd = highest_fd();
224 		if (fd >= 0)
225 			cfail(info, "closefrom(0)", "highest fd %d", fd);
226 		cok(info, "closefrom(0)");
227 	}
228 	if (wait(NULL) < 0)
229 		fail_err("wait");
230 	if (info->failed)
231 		fail(info->tag, "%s", info->message);
232 	ok(info->tag);
233 
234 	/* Fork a child process to test closefrom(-1). */
235 	pid = fork();
236 	if (pid < 0)
237 		fail_err("fork");
238 	if (pid == 0) {
239 		/* Child. */
240 		closefrom(-1);
241 		fd = highest_fd();
242 		if (fd >= 0)
243 			cfail(info, "closefrom(-1)", "highest fd %d", fd);
244 		cok(info, "closefrom(-1)");
245 	}
246 	if (wait(NULL) < 0)
247 		fail_err("wait");
248 	if (info->failed)
249 		fail(info->tag, "%s", info->message);
250 	ok(info->tag);
251 
252 	/* Dup stdout to 6. */
253 	if (dup2(1, 6) < 0)
254 		fail_err("dup2");
255 	fd = highest_fd();
256 	if (fd != 6)
257 		fail("dup2", "highest fd %d", fd);
258 	ok("dup2");
259 
260 	/* Do a closefrom() starting in a hole. */
261 	closefrom(4);
262 	fd = highest_fd();
263 	if (fd != 3)
264 		fail("closefrom", "highest fd %d", fd);
265 	ok("closefrom");
266 
267 	/* Do a closefrom() beyond our highest open fd. */
268 	closefrom(32);
269 	fd = highest_fd();
270 	if (fd != 3)
271 		fail("closefrom", "highest fd %d", fd);
272 	ok("closefrom");
273 
274 	/* Chew up another 8 fd */
275 	for (i = 0; i < 8; i++)
276 		(void)devnull();
277 	fd = highest_fd();
278 	start = fd - 7;
279 
280 	/* close_range() a hole in the middle */
281 	close_range(start + 3, start + 5, 0);
282 	for (i = start + 3; i < start + 6; ++i) {
283 		if (close(i) == 0 || errno != EBADF) {
284 			--i;
285 			break;
286 		}
287 	}
288 	if (i != start + 6)
289 		fail("close_range", "failed to close at %d in %d - %d", i + 1,
290 		    start + 3, start + 6);
291 	ok("close_range");
292 
293 	/* close_range from the middle of the hole */
294 	close_range(start + 4, start + 6, 0);
295 	if ((i = highest_fd()) != fd)
296 		fail("close_range", "highest fd %d", i);
297 	ok("close_range");
298 
299 	/* close_range to the end; effectively closefrom(2) */
300 	close_range(start + 3, ~0L, 0);
301 	if ((i = highest_fd()) != start + 2)
302 		fail("close_range", "highest fd %d", i);
303 	ok("close_range");
304 
305 	/* Now close the rest */
306 	close_range(start, start + 4, 0);
307 	fd = highest_fd();
308 	if (fd != 3)
309 		fail("close_range", "highest fd %d", fd);
310 	ok("close_range");
311 
312 	/* Fork a child process to test closefrom(0) twice. */
313 	pid = fork();
314 	if (pid < 0)
315 		fail_err("fork");
316 	if (pid == 0) {
317 		/* Child. */
318 		closefrom(0);
319 		closefrom(0);
320 		cok(info, "closefrom(0)");
321 	}
322 	if (wait(NULL) < 0)
323 		fail_err("wait");
324 	if (info->failed)
325 		fail(info->tag, "%s", info->message);
326 	ok(info->tag);
327 
328 	/* test CLOSE_RANGE_CLOEXEC */
329 	for (i = 0; i < 8; i++)
330 		(void)devnull();
331 	fd = highest_fd();
332 	start = fd - 8;
333 	if (close_range(start + 1, start + 4, CLOSE_RANGE_CLOEXEC) < 0)
334 		fail_err("close_range(..., CLOSE_RANGE_CLOEXEC)");
335 	flags = fcntl(start, F_GETFD);
336 	if (flags < 0)
337 		fail_err("fcntl(.., F_GETFD)");
338 	if ((flags & FD_CLOEXEC) != 0)
339 		fail("close_range", "CLOSE_RANGE_CLOEXEC set close-on-exec "
340 		    "when it should not have on fd %d", start);
341 	for (i = start + 1; i <= start + 4; i++) {
342 		flags = fcntl(i, F_GETFD);
343 		if (flags < 0)
344 			fail_err("fcntl(.., F_GETFD)");
345 		if ((flags & FD_CLOEXEC) == 0)
346 			fail("close_range", "CLOSE_RANGE_CLOEXEC did not set "
347 			    "close-on-exec on fd %d", i);
348 	}
349 	for (; i < start + 8; i++) {
350 		flags = fcntl(i, F_GETFD);
351 		if (flags < 0)
352 			fail_err("fcntl(.., F_GETFD)");
353 		if ((flags & FD_CLOEXEC) != 0)
354 			fail("close_range", "CLOSE_RANGE_CLOEXEC set close-on-exec "
355 			    "when it should not have on fd %d", i);
356 	}
357 	if (close_range(start, start + 8, 0) < 0)
358 		fail_err("close_range");
359 	ok("close_range(..., CLOSE_RANGE_CLOEXEC)");
360 
361 	return (0);
362 }
363