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