13c85ca21SRobert Wing /*- 23c85ca21SRobert Wing * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 33c85ca21SRobert Wing * 43c85ca21SRobert Wing * Copyright (c) 2020 Rob Wing 53c85ca21SRobert Wing * 63c85ca21SRobert Wing * Redistribution and use in source and binary forms, with or without 73c85ca21SRobert Wing * modification, are permitted provided that the following conditions 83c85ca21SRobert Wing * are met: 93c85ca21SRobert Wing * 1. Redistributions of source code must retain the above copyright 103c85ca21SRobert Wing * notice, this list of conditions and the following disclaimer. 113c85ca21SRobert Wing * 2. Redistributions in binary form must reproduce the above copyright 123c85ca21SRobert Wing * notice, this list of conditions and the following disclaimer in the 133c85ca21SRobert Wing * documentation and/or other materials provided with the distribution. 143c85ca21SRobert Wing * 153c85ca21SRobert Wing * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 163c85ca21SRobert Wing * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 173c85ca21SRobert Wing * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 183c85ca21SRobert Wing * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 193c85ca21SRobert Wing * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 203c85ca21SRobert Wing * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 213c85ca21SRobert Wing * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 223c85ca21SRobert Wing * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 233c85ca21SRobert Wing * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 243c85ca21SRobert Wing * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 253c85ca21SRobert Wing * SUCH DAMAGE. 263c85ca21SRobert Wing */ 273c85ca21SRobert Wing 283c85ca21SRobert Wing #include <sys/cdefs.h> 293c85ca21SRobert Wing __FBSDID("$FreeBSD$"); 303c85ca21SRobert Wing 313c85ca21SRobert Wing #include <sys/param.h> 323c85ca21SRobert Wing #include <sys/filedesc.h> 333c85ca21SRobert Wing #include <sys/queue.h> 343c85ca21SRobert Wing #include <sys/sysctl.h> 353c85ca21SRobert Wing #include <sys/user.h> 363c85ca21SRobert Wing #include <sys/wait.h> 373c85ca21SRobert Wing 383c85ca21SRobert Wing #include <atf-c.h> 393c85ca21SRobert Wing #include <fcntl.h> 403c85ca21SRobert Wing #include <signal.h> 413c85ca21SRobert Wing #include <stdio.h> 423c85ca21SRobert Wing #include <stdlib.h> 433c85ca21SRobert Wing #include <string.h> 443c85ca21SRobert Wing #include <unistd.h> 453c85ca21SRobert Wing 463c85ca21SRobert Wing /* linked libraries */ 473c85ca21SRobert Wing #include <kvm.h> 483c85ca21SRobert Wing #include <libutil.h> 493c85ca21SRobert Wing #include <libprocstat.h> 503c85ca21SRobert Wing #include <pthread.h> 513c85ca21SRobert Wing 523c85ca21SRobert Wing /* test-case macro */ 533c85ca21SRobert Wing #define AFILE "afile" 543c85ca21SRobert Wing 553c85ca21SRobert Wing /* 563c85ca21SRobert Wing * The following macros, struct freetable, struct fdescenttbl0 573c85ca21SRobert Wing * and struct filedesc0 are copied from sys/kern/kern_descrip.c 583c85ca21SRobert Wing */ 593c85ca21SRobert Wing #define NDFILE 20 603c85ca21SRobert Wing #define NDSLOTSIZE sizeof(NDSLOTTYPE) 613c85ca21SRobert Wing #define NDENTRIES (NDSLOTSIZE * __CHAR_BIT) 623c85ca21SRobert Wing #define NDSLOT(x) ((x) / NDENTRIES) 633c85ca21SRobert Wing #define NDBIT(x) ((NDSLOTTYPE)1 << ((x) % NDENTRIES)) 643c85ca21SRobert Wing #define NDSLOTS(x) (((x) + NDENTRIES - 1) / NDENTRIES) 653c85ca21SRobert Wing 663c85ca21SRobert Wing struct freetable { 673c85ca21SRobert Wing struct fdescenttbl *ft_table; 683c85ca21SRobert Wing SLIST_ENTRY(freetable) ft_next; 693c85ca21SRobert Wing }; 703c85ca21SRobert Wing 713c85ca21SRobert Wing struct fdescenttbl0 { 723c85ca21SRobert Wing int fdt_nfiles; 733c85ca21SRobert Wing struct filedescent fdt_ofiles[NDFILE]; 743c85ca21SRobert Wing }; 753c85ca21SRobert Wing 763c85ca21SRobert Wing struct filedesc0 { 773c85ca21SRobert Wing struct filedesc fd_fd; 783c85ca21SRobert Wing SLIST_HEAD(, freetable) fd_free; 793c85ca21SRobert Wing struct fdescenttbl0 fd_dfiles; 803c85ca21SRobert Wing NDSLOTTYPE fd_dmap[NDSLOTS(NDFILE)]; 813c85ca21SRobert Wing }; 823c85ca21SRobert Wing 833c85ca21SRobert Wing static void 843c85ca21SRobert Wing openfiles(int n) 853c85ca21SRobert Wing { 863c85ca21SRobert Wing int i, fd; 873c85ca21SRobert Wing 883c85ca21SRobert Wing ATF_REQUIRE((fd = open(AFILE, O_CREAT, 0644)) != -1); 893c85ca21SRobert Wing close(fd); 903c85ca21SRobert Wing for (i = 0; i < n; i++) 913c85ca21SRobert Wing ATF_REQUIRE((fd = open(AFILE, O_RDONLY, 0644)) != -1); 923c85ca21SRobert Wing } 933c85ca21SRobert Wing 943c85ca21SRobert Wing /* 953c85ca21SRobert Wing * Get a count of the old file descriptor tables on the freelist. 963c85ca21SRobert Wing */ 973c85ca21SRobert Wing static int 983c85ca21SRobert Wing old_tables(kvm_t *kd, struct kinfo_proc *kp) 993c85ca21SRobert Wing { 1003c85ca21SRobert Wing struct filedesc0 fdp0; 1013c85ca21SRobert Wing struct freetable *ft, tft; 1023c85ca21SRobert Wing int counter; 1033c85ca21SRobert Wing 1043c85ca21SRobert Wing counter = 0; 1053c85ca21SRobert Wing 1063c85ca21SRobert Wing ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp0, sizeof(fdp0)) > 0); 1073c85ca21SRobert Wing 1083c85ca21SRobert Wing SLIST_FOREACH(ft, &fdp0.fd_free, ft_next) { 1093c85ca21SRobert Wing ATF_REQUIRE(kvm_read(kd, (unsigned long) ft, &tft, sizeof(tft)) > 0 ); 1103c85ca21SRobert Wing ft = &tft; 1113c85ca21SRobert Wing counter++; 1123c85ca21SRobert Wing } 1133c85ca21SRobert Wing 1143c85ca21SRobert Wing return (counter); 1153c85ca21SRobert Wing } 1163c85ca21SRobert Wing 1173c85ca21SRobert Wing /* 1183c85ca21SRobert Wing * The returning struct kinfo_proc stores kernel addresses that will be 1193c85ca21SRobert Wing * used by kvm_read to retrieve information for the current process. 1203c85ca21SRobert Wing */ 1213c85ca21SRobert Wing static struct kinfo_proc * 1223c85ca21SRobert Wing read_kinfo(kvm_t *kd) 1233c85ca21SRobert Wing { 1243c85ca21SRobert Wing struct kinfo_proc *kp; 1253c85ca21SRobert Wing int procs_found; 1263c85ca21SRobert Wing 1273c85ca21SRobert Wing ATF_REQUIRE((kp = kvm_getprocs(kd, KERN_PROC_PID, (int) getpid(), &procs_found)) != NULL); 1283c85ca21SRobert Wing ATF_REQUIRE(procs_found == 1); 1293c85ca21SRobert Wing 1303c85ca21SRobert Wing return (kp); 1313c85ca21SRobert Wing } 1323c85ca21SRobert Wing 1333c85ca21SRobert Wing /* 1343c85ca21SRobert Wing * Test a single threaded process that doesn't have a shared 1353c85ca21SRobert Wing * file descriptor table. The old tables should be freed. 1363c85ca21SRobert Wing */ 1373c85ca21SRobert Wing ATF_TC(free_oldtables); 1383c85ca21SRobert Wing ATF_TC_HEAD(free_oldtables, tc) 1393c85ca21SRobert Wing { 1403c85ca21SRobert Wing atf_tc_set_md_var(tc, "require.user", "root"); 1413c85ca21SRobert Wing } 1423c85ca21SRobert Wing 1433c85ca21SRobert Wing ATF_TC_BODY(free_oldtables, tc) 1443c85ca21SRobert Wing { 1453c85ca21SRobert Wing kvm_t *kd; 1463c85ca21SRobert Wing struct kinfo_proc *kp; 1473c85ca21SRobert Wing 1483c85ca21SRobert Wing ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL); 1493c85ca21SRobert Wing openfiles(128); 1503c85ca21SRobert Wing kp = read_kinfo(kd); 1513c85ca21SRobert Wing ATF_CHECK(old_tables(kd,kp) == 0); 1523c85ca21SRobert Wing } 1533c85ca21SRobert Wing 1546d759051SRyan Libby static _Noreturn void * 1553c85ca21SRobert Wing exec_thread(void *args) 1563c85ca21SRobert Wing { 1573c85ca21SRobert Wing for (;;) 1583c85ca21SRobert Wing sleep(1); 1593c85ca21SRobert Wing } 1603c85ca21SRobert Wing 1613c85ca21SRobert Wing /* 1623c85ca21SRobert Wing * Test a process with two threads that doesn't have a shared file 1633c85ca21SRobert Wing * descriptor table. The old tables should not be freed. 1643c85ca21SRobert Wing */ 1653c85ca21SRobert Wing ATF_TC(oldtables_shared_via_threads); 1663c85ca21SRobert Wing ATF_TC_HEAD(oldtables_shared_via_threads, tc) 1673c85ca21SRobert Wing { 1683c85ca21SRobert Wing atf_tc_set_md_var(tc, "require.user", "root"); 1693c85ca21SRobert Wing } 1703c85ca21SRobert Wing 1713c85ca21SRobert Wing ATF_TC_BODY(oldtables_shared_via_threads, tc) 1723c85ca21SRobert Wing { 1733c85ca21SRobert Wing kvm_t *kd; 1743c85ca21SRobert Wing struct kinfo_proc *kp; 1753c85ca21SRobert Wing pthread_t thread; 1763c85ca21SRobert Wing 1773c85ca21SRobert Wing ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL); 1783c85ca21SRobert Wing ATF_REQUIRE(pthread_create(&thread, NULL, exec_thread, NULL) == 0); 1793c85ca21SRobert Wing 1803c85ca21SRobert Wing openfiles(128); 1813c85ca21SRobert Wing 1823c85ca21SRobert Wing kp = read_kinfo(kd); 1833c85ca21SRobert Wing ATF_CHECK(kp->ki_numthreads > 1); 1843c85ca21SRobert Wing ATF_CHECK(old_tables(kd,kp) > 1); 1853c85ca21SRobert Wing 1863c85ca21SRobert Wing ATF_REQUIRE(pthread_cancel(thread) == 0); 1873c85ca21SRobert Wing ATF_REQUIRE(pthread_join(thread, NULL) == 0); 1883c85ca21SRobert Wing } 1893c85ca21SRobert Wing 1903c85ca21SRobert Wing /* 1913c85ca21SRobert Wing * Get the reference count of a file descriptor table. 1923c85ca21SRobert Wing */ 1933c85ca21SRobert Wing static int 1943c85ca21SRobert Wing filedesc_refcnt(kvm_t *kd, struct kinfo_proc *kp) 1953c85ca21SRobert Wing { 1963c85ca21SRobert Wing struct filedesc fdp; 1973c85ca21SRobert Wing 1983c85ca21SRobert Wing ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp, sizeof(fdp)) > 0); 1993c85ca21SRobert Wing 2003c85ca21SRobert Wing return (fdp.fd_refcnt); 2013c85ca21SRobert Wing } 2023c85ca21SRobert Wing 2033c85ca21SRobert Wing /* 2043c85ca21SRobert Wing * Test a single threaded process that shares a file descriptor 2053c85ca21SRobert Wing * table with another process. The old tables should not be freed. 2063c85ca21SRobert Wing */ 2073c85ca21SRobert Wing ATF_TC(oldtables_shared_via_process); 2083c85ca21SRobert Wing ATF_TC_HEAD(oldtables_shared_via_process, tc) 2093c85ca21SRobert Wing { 2103c85ca21SRobert Wing atf_tc_set_md_var(tc, "require.user", "root"); 2113c85ca21SRobert Wing } 2123c85ca21SRobert Wing 2133c85ca21SRobert Wing ATF_TC_BODY(oldtables_shared_via_process, tc) 2143c85ca21SRobert Wing { 2153c85ca21SRobert Wing kvm_t *kd; 2163c85ca21SRobert Wing struct kinfo_proc *kp; 2173c85ca21SRobert Wing int status; 2183c85ca21SRobert Wing pid_t child, wpid; 2193c85ca21SRobert Wing 2203c85ca21SRobert Wing ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL); 2213c85ca21SRobert Wing 2223c85ca21SRobert Wing /* share the file descriptor table */ 2233c85ca21SRobert Wing ATF_REQUIRE((child = rfork(RFPROC)) != -1); 2243c85ca21SRobert Wing 2253c85ca21SRobert Wing if (child == 0) { 2263c85ca21SRobert Wing openfiles(128); 2273c85ca21SRobert Wing raise(SIGSTOP); 2283c85ca21SRobert Wing exit(127); 2293c85ca21SRobert Wing } 2303c85ca21SRobert Wing 2313c85ca21SRobert Wing /* let parent process open some files too */ 2323c85ca21SRobert Wing openfiles(128); 2333c85ca21SRobert Wing 2343c85ca21SRobert Wing /* get current status of child */ 2353c85ca21SRobert Wing wpid = waitpid(child, &status, WUNTRACED); 236*889b5662SMark Johnston ATF_REQUIRE(wpid == child); 2373c85ca21SRobert Wing 2383c85ca21SRobert Wing /* child should be stopped */ 2393c85ca21SRobert Wing ATF_REQUIRE(WIFSTOPPED(status)); 2403c85ca21SRobert Wing 2413c85ca21SRobert Wing /* 2423c85ca21SRobert Wing * We want to read kernel data 2433c85ca21SRobert Wing * before the child exits 2443c85ca21SRobert Wing * otherwise we'll lose a reference count 2453c85ca21SRobert Wing * to the file descriptor table 2463c85ca21SRobert Wing */ 2473c85ca21SRobert Wing if (child != 0) { 2483c85ca21SRobert Wing kp = read_kinfo(kd); 2493c85ca21SRobert Wing 2503c85ca21SRobert Wing ATF_CHECK(filedesc_refcnt(kd,kp) > 1); 2513c85ca21SRobert Wing ATF_CHECK(old_tables(kd,kp) > 1); 2523c85ca21SRobert Wing 2533c85ca21SRobert Wing kill(child, SIGCONT); 2543c85ca21SRobert Wing } 2553c85ca21SRobert Wing 2563c85ca21SRobert Wing /* child should have exited */ 2573c85ca21SRobert Wing wpid = waitpid(child, &status, 0); 258*889b5662SMark Johnston ATF_REQUIRE(wpid == child); 2593c85ca21SRobert Wing ATF_REQUIRE(WIFEXITED(status)); 2603c85ca21SRobert Wing ATF_REQUIRE(WEXITSTATUS(status) == 127); 2613c85ca21SRobert Wing } 2623c85ca21SRobert Wing 2633c85ca21SRobert Wing ATF_TP_ADD_TCS(tp) 2643c85ca21SRobert Wing { 2653c85ca21SRobert Wing ATF_TP_ADD_TC(tp, free_oldtables); 2663c85ca21SRobert Wing ATF_TP_ADD_TC(tp, oldtables_shared_via_threads); 2673c85ca21SRobert Wing ATF_TP_ADD_TC(tp, oldtables_shared_via_process); 2683c85ca21SRobert Wing return (atf_no_error()); 2693c85ca21SRobert Wing } 270