1 | /* Test for the close_range system call. |
2 | Copyright (C) 2021-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <dirent.h> |
20 | #include <errno.h> |
21 | #include <fcntl.h> |
22 | #include <limits.h> |
23 | #include <getopt.h> |
24 | #include <signal.h> |
25 | #include <stdbool.h> |
26 | #include <stdlib.h> |
27 | #include <stdint.h> |
28 | |
29 | #include <array_length.h> |
30 | #include <support/capture_subprocess.h> |
31 | #include <support/check.h> |
32 | #include <support/descriptors.h> |
33 | #include <support/support.h> |
34 | #include <support/xsched.h> |
35 | #include <support/xunistd.h> |
36 | |
37 | #define NFDS 100 |
38 | |
39 | static void |
40 | close_range_test_max_upper_limit (void) |
41 | { |
42 | struct support_descriptors *descrs = support_descriptors_list (); |
43 | |
44 | int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, mode: 0600); |
45 | |
46 | { |
47 | int r = close_range (fd: lowfd, max_fd: ~0U, flags: 0); |
48 | if (r == -1 && errno == ENOSYS) |
49 | FAIL_UNSUPPORTED ("close_range not supported" ); |
50 | TEST_COMPARE (r, 0); |
51 | } |
52 | |
53 | support_descriptors_check (descrs); |
54 | support_descriptors_free (descrs); |
55 | } |
56 | |
57 | static void |
58 | close_range_test_common (int lowfd, unsigned int flags) |
59 | { |
60 | const int maximum_fd = lowfd + NFDS - 1; |
61 | const int half_fd = lowfd + NFDS / 2; |
62 | const int gap_1 = maximum_fd - 8; |
63 | |
64 | /* Close half of the descriptors and check result. */ |
65 | TEST_COMPARE (close_range (lowfd, half_fd, flags), 0); |
66 | for (int i = lowfd; i <= half_fd; i++) |
67 | { |
68 | TEST_COMPARE (fcntl (i, F_GETFL), -1); |
69 | TEST_COMPARE (errno, EBADF); |
70 | } |
71 | for (int i = half_fd + 1; i < maximum_fd; i++) |
72 | TEST_VERIFY (fcntl (i, F_GETFL) > -1); |
73 | |
74 | /* Create some gaps, close up to a threshold, and check result. */ |
75 | xclose (lowfd + 57); |
76 | xclose (lowfd + 78); |
77 | xclose (lowfd + 81); |
78 | xclose (lowfd + 82); |
79 | xclose (lowfd + 84); |
80 | xclose (lowfd + 90); |
81 | |
82 | TEST_COMPARE (close_range (half_fd + 1, gap_1, flags), 0); |
83 | for (int i = half_fd + 1; i < gap_1; i++) |
84 | { |
85 | TEST_COMPARE (fcntl (i, F_GETFL), -1); |
86 | TEST_COMPARE (errno, EBADF); |
87 | } |
88 | for (int i = gap_1 + 1; i < maximum_fd; i++) |
89 | TEST_VERIFY (fcntl (i, F_GETFL) > -1); |
90 | |
91 | /* Close the remaining but the last one. */ |
92 | TEST_COMPARE (close_range (gap_1 + 1, maximum_fd - 1, flags), 0); |
93 | for (int i = gap_1 + 1; i < maximum_fd - 1; i++) |
94 | { |
95 | TEST_COMPARE (fcntl (i, F_GETFL), -1); |
96 | TEST_COMPARE (errno, EBADF); |
97 | } |
98 | TEST_VERIFY (fcntl (maximum_fd, F_GETFL) > -1); |
99 | |
100 | /* Close the last one. */ |
101 | TEST_COMPARE (close_range (maximum_fd, maximum_fd, flags), 0); |
102 | TEST_COMPARE (fcntl (maximum_fd, F_GETFL), -1); |
103 | TEST_COMPARE (errno, EBADF); |
104 | } |
105 | |
106 | /* Basic tests: check if the syscall close ranges with and without gaps. */ |
107 | static void |
108 | close_range_test (void) |
109 | { |
110 | struct support_descriptors *descrs = support_descriptors_list (); |
111 | |
112 | /* Check if the temporary file descriptor has no no gaps. */ |
113 | int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, mode: 0600); |
114 | |
115 | close_range_test_common (lowfd, flags: 0); |
116 | |
117 | /* Double check by check the /proc. */ |
118 | support_descriptors_check (descrs); |
119 | support_descriptors_free (descrs); |
120 | } |
121 | |
122 | #ifdef __linux__ |
123 | _Noreturn static int |
124 | close_range_test_fn (void *arg) |
125 | { |
126 | int lowfd = (int) ((uintptr_t) arg); |
127 | close_range_test_common (lowfd, flags: 0); |
128 | exit (EXIT_SUCCESS); |
129 | } |
130 | |
131 | /* Check if a clone_range on a subprocess created with CLONE_FILES close |
132 | the shared file descriptor table entries in the parent. */ |
133 | static void |
134 | close_range_test_subprocess (void) |
135 | { |
136 | struct support_descriptors *descrs = support_descriptors_list (); |
137 | |
138 | /* Check if the temporary file descriptor has no no gaps. */ |
139 | int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, mode: 0600); |
140 | |
141 | struct support_stack stack = support_stack_alloc (size: 4096); |
142 | |
143 | pid_t pid = xclone (fn: close_range_test_fn, arg: (void*) (uintptr_t) lowfd, |
144 | stack: stack.stack, stack_size: stack.size, CLONE_FILES | SIGCHLD); |
145 | TEST_VERIFY_EXIT (pid > 0); |
146 | int status; |
147 | xwaitpid (pid, status: &status, flags: 0); |
148 | TEST_VERIFY (WIFEXITED (status)); |
149 | TEST_COMPARE (WEXITSTATUS (status), 0); |
150 | |
151 | support_stack_free (stack: &stack); |
152 | |
153 | for (int i = lowfd; i < NFDS; i++) |
154 | TEST_VERIFY (fcntl (i, F_GETFL) < 0); |
155 | |
156 | support_descriptors_check (descrs); |
157 | support_descriptors_free (descrs); |
158 | } |
159 | #endif |
160 | |
161 | |
162 | #ifdef CLOSE_RANGE_UNSHARE |
163 | _Noreturn static int |
164 | close_range_unshare_test_fn (void *arg) |
165 | { |
166 | int lowfd = (int) ((uintptr_t) arg); |
167 | close_range_test_common (lowfd, CLOSE_RANGE_UNSHARE); |
168 | exit (EXIT_SUCCESS); |
169 | } |
170 | |
171 | /* Check if a close_range with CLOSE_RANGE_UNSHARE issued from a subprocess |
172 | created with CLONE_FILES does not close the parent file descriptor list. */ |
173 | static void |
174 | close_range_unshare_test (void) |
175 | { |
176 | struct support_descriptors *descrs1 = support_descriptors_list (); |
177 | |
178 | /* Check if the temporary file descriptor has no no gaps. */ |
179 | int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, mode: 0600); |
180 | |
181 | struct support_descriptors *descrs2 = support_descriptors_list (); |
182 | |
183 | struct support_stack stack = support_stack_alloc (size: 4096); |
184 | |
185 | pid_t pid = xclone (fn: close_range_unshare_test_fn, arg: (void*) (uintptr_t) lowfd, |
186 | stack: stack.stack, stack_size: stack.size, CLONE_FILES | SIGCHLD); |
187 | TEST_VERIFY_EXIT (pid > 0); |
188 | int status; |
189 | xwaitpid (pid, status: &status, flags: 0); |
190 | TEST_VERIFY (WIFEXITED (status)); |
191 | TEST_COMPARE (WEXITSTATUS (status), 0); |
192 | |
193 | support_stack_free (stack: &stack); |
194 | |
195 | for (int i = lowfd; i < lowfd + NFDS; i++) |
196 | TEST_VERIFY (fcntl (i, F_GETFL) > -1); |
197 | |
198 | support_descriptors_check (descrs2); |
199 | support_descriptors_free (descrs2); |
200 | |
201 | TEST_COMPARE (close_range (lowfd, lowfd + NFDS, 0), 0); |
202 | |
203 | support_descriptors_check (descrs1); |
204 | support_descriptors_free (descrs1); |
205 | } |
206 | #endif |
207 | |
208 | static bool |
209 | is_in_array (int *arr, size_t len, int fd) |
210 | { |
211 | bool r = false; |
212 | for (int i = 0; i < len; i++) |
213 | if (arr[i] == fd) |
214 | return true; |
215 | return r; |
216 | } |
217 | |
218 | static void |
219 | close_range_cloexec_test (void) |
220 | { |
221 | /* Check if the temporary file descriptor has no no gaps. */ |
222 | int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, mode: 0600); |
223 | |
224 | const int maximum_fd = lowfd + NFDS - 1; |
225 | const int half_fd = lowfd + NFDS / 2; |
226 | const int gap_1 = maximum_fd - 8; |
227 | |
228 | /* Close half of the descriptors and check result. */ |
229 | int r = close_range (fd: lowfd, max_fd: half_fd, CLOSE_RANGE_CLOEXEC); |
230 | if (r == -1 && errno == EINVAL) |
231 | { |
232 | printf (format: "%s: CLOSE_RANGE_CLOEXEC not supported\n" , __func__); |
233 | return; |
234 | } |
235 | for (int i = lowfd; i <= half_fd; i++) |
236 | { |
237 | int flags = fcntl (fd: i, F_GETFD); |
238 | TEST_VERIFY (flags > -1); |
239 | TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC); |
240 | } |
241 | for (int i = half_fd + 1; i < maximum_fd; i++) |
242 | TEST_VERIFY (fcntl (i, F_GETFL) > -1); |
243 | |
244 | /* Create some gaps, close up to a threshold, and check result. */ |
245 | static int gap_close[] = { 57, 78, 81, 82, 84, 90 }; |
246 | for (int i = 0; i < array_length (gap_close); i++) |
247 | xclose (lowfd + gap_close[i]); |
248 | |
249 | TEST_COMPARE (close_range (half_fd + 1, gap_1, CLOSE_RANGE_CLOEXEC), 0); |
250 | for (int i = half_fd + 1; i < gap_1; i++) |
251 | { |
252 | int flags = fcntl (fd: i, F_GETFD); |
253 | if (is_in_array (arr: gap_close, array_length (gap_close), fd: i - lowfd)) |
254 | TEST_COMPARE (flags, -1); |
255 | else |
256 | { |
257 | TEST_VERIFY (flags > -1); |
258 | TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC); |
259 | } |
260 | } |
261 | for (int i = gap_1 + 1; i < maximum_fd; i++) |
262 | TEST_VERIFY (fcntl (i, F_GETFL) > -1); |
263 | |
264 | /* Close the remaining but the last one. */ |
265 | TEST_COMPARE (close_range (gap_1 + 1, maximum_fd - 1, CLOSE_RANGE_CLOEXEC), |
266 | 0); |
267 | for (int i = gap_1 + 1; i < maximum_fd - 1; i++) |
268 | { |
269 | int flags = fcntl (fd: i, F_GETFD); |
270 | TEST_VERIFY (flags > -1); |
271 | TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC); |
272 | } |
273 | TEST_VERIFY (fcntl (maximum_fd, F_GETFL) > -1); |
274 | |
275 | /* Close the last one. */ |
276 | TEST_COMPARE (close_range (maximum_fd, maximum_fd, CLOSE_RANGE_CLOEXEC), 0); |
277 | { |
278 | int flags = fcntl (fd: maximum_fd, F_GETFD); |
279 | TEST_VERIFY (flags > -1); |
280 | TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC); |
281 | } |
282 | } |
283 | |
284 | static int |
285 | do_test (void) |
286 | { |
287 | close_range_test_max_upper_limit (); |
288 | close_range_test (); |
289 | #ifdef __linux__ |
290 | close_range_test_subprocess (); |
291 | #endif |
292 | #ifdef CLOSE_RANGE_UNSHARE |
293 | close_range_unshare_test (); |
294 | #endif |
295 | close_range_cloexec_test (); |
296 | |
297 | return 0; |
298 | } |
299 | |
300 | #include <support/test-driver.c> |
301 | |