]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/file/closefrom_test.c
libarchive: merge security fix from vendor branch
[FreeBSD/FreeBSD.git] / tests / sys / file / closefrom_test.c
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 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 }