Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/file/closefrom_test.c
39536 views
1
/*-
2
* Copyright (c) 2009 Hudson River Trading LLC
3
* Written by: John H. Baldwin <[email protected]>
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..22\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
/* test CLOSE_RANGE_CLOFORK */
360
for (i = 0; i < 8; i++)
361
(void)devnull();
362
fd = highest_fd();
363
start = fd - 8;
364
if (close_range(start + 1, start + 4, CLOSE_RANGE_CLOFORK) < 0)
365
fail_err("close_range(..., CLOSE_RANGE_CLOFORK)");
366
flags = fcntl(start, F_GETFD);
367
if (flags < 0)
368
fail_err("fcntl(.., F_GETFD)");
369
if ((flags & FD_CLOFORK) != 0)
370
fail("close_range", "CLOSE_RANGE_CLOFORK set close-on-exec "
371
"when it should not have on fd %d", start);
372
for (i = start + 1; i <= start + 4; i++) {
373
flags = fcntl(i, F_GETFD);
374
if (flags < 0)
375
fail_err("fcntl(.., F_GETFD)");
376
if ((flags & FD_CLOFORK) == 0)
377
fail("close_range", "CLOSE_RANGE_CLOFORK did not set "
378
"close-on-exec on fd %d", i);
379
}
380
for (; i < start + 8; i++) {
381
flags = fcntl(i, F_GETFD);
382
if (flags < 0)
383
fail_err("fcntl(.., F_GETFD)");
384
if ((flags & FD_CLOFORK) != 0)
385
fail("close_range", "CLOSE_RANGE_CLOFORK set close-on-exec "
386
"when it should not have on fd %d", i);
387
}
388
if (close_range(start, start + 8, 0) < 0)
389
fail_err("close_range");
390
ok("close_range(..., CLOSE_RANGE_CLOFORK)");
391
392
return (0);
393
}
394
395