2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2020 Rob Wing
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32 #include <sys/filedesc.h>
33 #include <sys/queue.h>
34 #include <sys/sysctl.h>
46 /* linked libraries */
49 #include <libprocstat.h>
56 * The following macros, struct freetable, struct fdescenttbl0
57 * and struct filedesc0 are copied from sys/kern/kern_descrip.c
60 #define NDSLOTSIZE sizeof(NDSLOTTYPE)
61 #define NDENTRIES (NDSLOTSIZE * __CHAR_BIT)
62 #define NDSLOT(x) ((x) / NDENTRIES)
63 #define NDBIT(x) ((NDSLOTTYPE)1 << ((x) % NDENTRIES))
64 #define NDSLOTS(x) (((x) + NDENTRIES - 1) / NDENTRIES)
67 struct fdescenttbl *ft_table;
68 SLIST_ENTRY(freetable) ft_next;
73 struct filedescent fdt_ofiles[NDFILE];
77 struct filedesc fd_fd;
78 SLIST_HEAD(, freetable) fd_free;
79 struct fdescenttbl0 fd_dfiles;
80 NDSLOTTYPE fd_dmap[NDSLOTS(NDFILE)];
88 ATF_REQUIRE((fd = open(AFILE, O_CREAT, 0644)) != -1);
90 for (i = 0; i < n; i++)
91 ATF_REQUIRE((fd = open(AFILE, O_RDONLY, 0644)) != -1);
95 * Get a count of the old file descriptor tables on the freelist.
98 old_tables(kvm_t *kd, struct kinfo_proc *kp)
100 struct filedesc0 fdp0;
101 struct freetable *ft, tft;
106 ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp0, sizeof(fdp0)) > 0);
108 SLIST_FOREACH(ft, &fdp0.fd_free, ft_next) {
109 ATF_REQUIRE(kvm_read(kd, (unsigned long) ft, &tft, sizeof(tft)) > 0 );
118 * The returning struct kinfo_proc stores kernel addresses that will be
119 * used by kvm_read to retrieve information for the current process.
121 static struct kinfo_proc *
122 read_kinfo(kvm_t *kd)
124 struct kinfo_proc *kp;
127 ATF_REQUIRE((kp = kvm_getprocs(kd, KERN_PROC_PID, (int) getpid(), &procs_found)) != NULL);
128 ATF_REQUIRE(procs_found == 1);
134 * Test a single threaded process that doesn't have a shared
135 * file descriptor table. The old tables should be freed.
137 ATF_TC(free_oldtables);
138 ATF_TC_HEAD(free_oldtables, tc)
140 atf_tc_set_md_var(tc, "require.user", "root");
143 ATF_TC_BODY(free_oldtables, tc)
146 struct kinfo_proc *kp;
148 ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
151 ATF_CHECK(old_tables(kd,kp) == 0);
155 exec_thread(void *args)
162 * Test a process with two threads that doesn't have a shared file
163 * descriptor table. The old tables should not be freed.
165 ATF_TC(oldtables_shared_via_threads);
166 ATF_TC_HEAD(oldtables_shared_via_threads, tc)
168 atf_tc_set_md_var(tc, "require.user", "root");
171 ATF_TC_BODY(oldtables_shared_via_threads, tc)
174 struct kinfo_proc *kp;
177 ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
178 ATF_REQUIRE(pthread_create(&thread, NULL, exec_thread, NULL) == 0);
183 ATF_CHECK(kp->ki_numthreads > 1);
184 ATF_CHECK(old_tables(kd,kp) > 1);
186 ATF_REQUIRE(pthread_cancel(thread) == 0);
187 ATF_REQUIRE(pthread_join(thread, NULL) == 0);
191 * Get the reference count of a file descriptor table.
194 filedesc_refcnt(kvm_t *kd, struct kinfo_proc *kp)
198 ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp, sizeof(fdp)) > 0);
200 return (fdp.fd_refcnt);
204 * Test a single threaded process that shares a file descriptor
205 * table with another process. The old tables should not be freed.
207 ATF_TC(oldtables_shared_via_process);
208 ATF_TC_HEAD(oldtables_shared_via_process, tc)
210 atf_tc_set_md_var(tc, "require.user", "root");
213 ATF_TC_BODY(oldtables_shared_via_process, tc)
216 struct kinfo_proc *kp;
220 ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
222 /* share the file descriptor table */
223 ATF_REQUIRE((child = rfork(RFPROC)) != -1);
231 /* let parent process open some files too */
234 /* get current status of child */
235 wpid = waitpid(child, &status, WUNTRACED);
237 /* child should be stopped */
238 ATF_REQUIRE(WIFSTOPPED(status));
241 * We want to read kernel data
242 * before the child exits
243 * otherwise we'll lose a reference count
244 * to the file descriptor table
249 ATF_CHECK(filedesc_refcnt(kd,kp) > 1);
250 ATF_CHECK(old_tables(kd,kp) > 1);
252 kill(child, SIGCONT);
255 /* child should have exited */
256 wpid = waitpid(child, &status, 0);
257 ATF_REQUIRE(WIFEXITED(status));
258 ATF_REQUIRE(WEXITSTATUS(status) == 127);
263 ATF_TP_ADD_TC(tp, free_oldtables);
264 ATF_TP_ADD_TC(tp, oldtables_shared_via_threads);
265 ATF_TP_ADD_TC(tp, oldtables_shared_via_process);
266 return (atf_no_error());