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
close_range_test_max_upper_limit(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, 0600);
45 
46   {
47     int r = close_range (lowfd, ~0U, 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
close_range_test_common(int lowfd,unsigned int flags)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
close_range_test(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, 0600);
114 
115   close_range_test_common (lowfd, 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
close_range_test_fn(void * arg)124 close_range_test_fn (void *arg)
125 {
126   int lowfd = (int) ((uintptr_t) arg);
127   close_range_test_common (lowfd, 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
close_range_test_subprocess(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, 0600);
140 
141   struct support_stack stack = support_stack_alloc (4096);
142 
143   pid_t pid = xclone (close_range_test_fn, (void*) (uintptr_t) lowfd,
144 		      stack.stack, stack.size, CLONE_FILES | SIGCHLD);
145   TEST_VERIFY_EXIT (pid > 0);
146   int status;
147   xwaitpid (pid, &status, 0);
148   TEST_VERIFY (WIFEXITED (status));
149   TEST_COMPARE (WEXITSTATUS (status), 0);
150 
151   support_stack_free (&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
close_range_unshare_test_fn(void * arg)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
close_range_unshare_test(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, 0600);
180 
181   struct support_descriptors *descrs2 = support_descriptors_list ();
182 
183   struct support_stack stack = support_stack_alloc (4096);
184 
185   pid_t pid = xclone (close_range_unshare_test_fn, (void*) (uintptr_t) lowfd,
186 		      stack.stack, stack.size, CLONE_FILES | SIGCHLD);
187   TEST_VERIFY_EXIT (pid > 0);
188   int status;
189   xwaitpid (pid, &status, 0);
190   TEST_VERIFY (WIFEXITED (status));
191   TEST_COMPARE (WEXITSTATUS (status), 0);
192 
193   support_stack_free (&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
is_in_array(int * arr,size_t len,int fd)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
close_range_cloexec_test(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, 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 (lowfd, half_fd, CLOSE_RANGE_CLOEXEC);
230   if (r == -1 && errno == EINVAL)
231     {
232       printf ("%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 (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 (i, F_GETFD);
253       if (is_in_array (gap_close, array_length (gap_close), 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 (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 (maximum_fd, F_GETFD);
279     TEST_VERIFY (flags > -1);
280     TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC);
281   }
282 }
283 
284 static int
do_test(void)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