1*3c85ca21SRobert Wing /*- 2*3c85ca21SRobert Wing * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*3c85ca21SRobert Wing * 4*3c85ca21SRobert Wing * Copyright (c) 2020 Rob Wing 5*3c85ca21SRobert Wing * 6*3c85ca21SRobert Wing * Redistribution and use in source and binary forms, with or without 7*3c85ca21SRobert Wing * modification, are permitted provided that the following conditions 8*3c85ca21SRobert Wing * are met: 9*3c85ca21SRobert Wing * 1. Redistributions of source code must retain the above copyright 10*3c85ca21SRobert Wing * notice, this list of conditions and the following disclaimer. 11*3c85ca21SRobert Wing * 2. Redistributions in binary form must reproduce the above copyright 12*3c85ca21SRobert Wing * notice, this list of conditions and the following disclaimer in the 13*3c85ca21SRobert Wing * documentation and/or other materials provided with the distribution. 14*3c85ca21SRobert Wing * 15*3c85ca21SRobert Wing * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*3c85ca21SRobert Wing * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*3c85ca21SRobert Wing * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*3c85ca21SRobert Wing * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19*3c85ca21SRobert Wing * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*3c85ca21SRobert Wing * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*3c85ca21SRobert Wing * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*3c85ca21SRobert Wing * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*3c85ca21SRobert Wing * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*3c85ca21SRobert Wing * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*3c85ca21SRobert Wing * SUCH DAMAGE. 26*3c85ca21SRobert Wing */ 27*3c85ca21SRobert Wing 28*3c85ca21SRobert Wing #include <sys/cdefs.h> 29*3c85ca21SRobert Wing __FBSDID("$FreeBSD$"); 30*3c85ca21SRobert Wing 31*3c85ca21SRobert Wing #include <sys/param.h> 32*3c85ca21SRobert Wing #include <sys/filedesc.h> 33*3c85ca21SRobert Wing #include <sys/queue.h> 34*3c85ca21SRobert Wing #include <sys/sysctl.h> 35*3c85ca21SRobert Wing #include <sys/user.h> 36*3c85ca21SRobert Wing #include <sys/wait.h> 37*3c85ca21SRobert Wing 38*3c85ca21SRobert Wing #include <atf-c.h> 39*3c85ca21SRobert Wing #include <fcntl.h> 40*3c85ca21SRobert Wing #include <signal.h> 41*3c85ca21SRobert Wing #include <stdio.h> 42*3c85ca21SRobert Wing #include <stdlib.h> 43*3c85ca21SRobert Wing #include <string.h> 44*3c85ca21SRobert Wing #include <unistd.h> 45*3c85ca21SRobert Wing 46*3c85ca21SRobert Wing /* linked libraries */ 47*3c85ca21SRobert Wing #include <kvm.h> 48*3c85ca21SRobert Wing #include <libutil.h> 49*3c85ca21SRobert Wing #include <libprocstat.h> 50*3c85ca21SRobert Wing #include <pthread.h> 51*3c85ca21SRobert Wing 52*3c85ca21SRobert Wing /* test-case macro */ 53*3c85ca21SRobert Wing #define AFILE "afile" 54*3c85ca21SRobert Wing 55*3c85ca21SRobert Wing /* 56*3c85ca21SRobert Wing * The following macros, struct freetable, struct fdescenttbl0 57*3c85ca21SRobert Wing * and struct filedesc0 are copied from sys/kern/kern_descrip.c 58*3c85ca21SRobert Wing */ 59*3c85ca21SRobert Wing #define NDFILE 20 60*3c85ca21SRobert Wing #define NDSLOTSIZE sizeof(NDSLOTTYPE) 61*3c85ca21SRobert Wing #define NDENTRIES (NDSLOTSIZE * __CHAR_BIT) 62*3c85ca21SRobert Wing #define NDSLOT(x) ((x) / NDENTRIES) 63*3c85ca21SRobert Wing #define NDBIT(x) ((NDSLOTTYPE)1 << ((x) % NDENTRIES)) 64*3c85ca21SRobert Wing #define NDSLOTS(x) (((x) + NDENTRIES - 1) / NDENTRIES) 65*3c85ca21SRobert Wing 66*3c85ca21SRobert Wing struct freetable { 67*3c85ca21SRobert Wing struct fdescenttbl *ft_table; 68*3c85ca21SRobert Wing SLIST_ENTRY(freetable) ft_next; 69*3c85ca21SRobert Wing }; 70*3c85ca21SRobert Wing 71*3c85ca21SRobert Wing struct fdescenttbl0 { 72*3c85ca21SRobert Wing int fdt_nfiles; 73*3c85ca21SRobert Wing struct filedescent fdt_ofiles[NDFILE]; 74*3c85ca21SRobert Wing }; 75*3c85ca21SRobert Wing 76*3c85ca21SRobert Wing struct filedesc0 { 77*3c85ca21SRobert Wing struct filedesc fd_fd; 78*3c85ca21SRobert Wing SLIST_HEAD(, freetable) fd_free; 79*3c85ca21SRobert Wing struct fdescenttbl0 fd_dfiles; 80*3c85ca21SRobert Wing NDSLOTTYPE fd_dmap[NDSLOTS(NDFILE)]; 81*3c85ca21SRobert Wing }; 82*3c85ca21SRobert Wing 83*3c85ca21SRobert Wing static void 84*3c85ca21SRobert Wing openfiles(int n) 85*3c85ca21SRobert Wing { 86*3c85ca21SRobert Wing int i, fd; 87*3c85ca21SRobert Wing 88*3c85ca21SRobert Wing ATF_REQUIRE((fd = open(AFILE, O_CREAT, 0644)) != -1); 89*3c85ca21SRobert Wing close(fd); 90*3c85ca21SRobert Wing for (i = 0; i < n; i++) 91*3c85ca21SRobert Wing ATF_REQUIRE((fd = open(AFILE, O_RDONLY, 0644)) != -1); 92*3c85ca21SRobert Wing } 93*3c85ca21SRobert Wing 94*3c85ca21SRobert Wing /* 95*3c85ca21SRobert Wing * Get a count of the old file descriptor tables on the freelist. 96*3c85ca21SRobert Wing */ 97*3c85ca21SRobert Wing static int 98*3c85ca21SRobert Wing old_tables(kvm_t *kd, struct kinfo_proc *kp) 99*3c85ca21SRobert Wing { 100*3c85ca21SRobert Wing struct filedesc0 fdp0; 101*3c85ca21SRobert Wing struct freetable *ft, tft; 102*3c85ca21SRobert Wing int counter; 103*3c85ca21SRobert Wing 104*3c85ca21SRobert Wing counter = 0; 105*3c85ca21SRobert Wing 106*3c85ca21SRobert Wing ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp0, sizeof(fdp0)) > 0); 107*3c85ca21SRobert Wing 108*3c85ca21SRobert Wing SLIST_FOREACH(ft, &fdp0.fd_free, ft_next) { 109*3c85ca21SRobert Wing ATF_REQUIRE(kvm_read(kd, (unsigned long) ft, &tft, sizeof(tft)) > 0 ); 110*3c85ca21SRobert Wing ft = &tft; 111*3c85ca21SRobert Wing counter++; 112*3c85ca21SRobert Wing } 113*3c85ca21SRobert Wing 114*3c85ca21SRobert Wing return (counter); 115*3c85ca21SRobert Wing } 116*3c85ca21SRobert Wing 117*3c85ca21SRobert Wing /* 118*3c85ca21SRobert Wing * The returning struct kinfo_proc stores kernel addresses that will be 119*3c85ca21SRobert Wing * used by kvm_read to retrieve information for the current process. 120*3c85ca21SRobert Wing */ 121*3c85ca21SRobert Wing static struct kinfo_proc * 122*3c85ca21SRobert Wing read_kinfo(kvm_t *kd) 123*3c85ca21SRobert Wing { 124*3c85ca21SRobert Wing struct kinfo_proc *kp; 125*3c85ca21SRobert Wing int procs_found; 126*3c85ca21SRobert Wing 127*3c85ca21SRobert Wing ATF_REQUIRE((kp = kvm_getprocs(kd, KERN_PROC_PID, (int) getpid(), &procs_found)) != NULL); 128*3c85ca21SRobert Wing ATF_REQUIRE(procs_found == 1); 129*3c85ca21SRobert Wing 130*3c85ca21SRobert Wing return (kp); 131*3c85ca21SRobert Wing } 132*3c85ca21SRobert Wing 133*3c85ca21SRobert Wing /* 134*3c85ca21SRobert Wing * Test a single threaded process that doesn't have a shared 135*3c85ca21SRobert Wing * file descriptor table. The old tables should be freed. 136*3c85ca21SRobert Wing */ 137*3c85ca21SRobert Wing ATF_TC(free_oldtables); 138*3c85ca21SRobert Wing ATF_TC_HEAD(free_oldtables, tc) 139*3c85ca21SRobert Wing { 140*3c85ca21SRobert Wing atf_tc_set_md_var(tc, "require.user", "root"); 141*3c85ca21SRobert Wing } 142*3c85ca21SRobert Wing 143*3c85ca21SRobert Wing ATF_TC_BODY(free_oldtables, tc) 144*3c85ca21SRobert Wing { 145*3c85ca21SRobert Wing kvm_t *kd; 146*3c85ca21SRobert Wing struct kinfo_proc *kp; 147*3c85ca21SRobert Wing 148*3c85ca21SRobert Wing ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL); 149*3c85ca21SRobert Wing openfiles(128); 150*3c85ca21SRobert Wing kp = read_kinfo(kd); 151*3c85ca21SRobert Wing ATF_CHECK(old_tables(kd,kp) == 0); 152*3c85ca21SRobert Wing } 153*3c85ca21SRobert Wing 154*3c85ca21SRobert Wing static void * 155*3c85ca21SRobert Wing exec_thread(void *args) 156*3c85ca21SRobert Wing { 157*3c85ca21SRobert Wing for (;;) 158*3c85ca21SRobert Wing sleep(1); 159*3c85ca21SRobert Wing } 160*3c85ca21SRobert Wing 161*3c85ca21SRobert Wing /* 162*3c85ca21SRobert Wing * Test a process with two threads that doesn't have a shared file 163*3c85ca21SRobert Wing * descriptor table. The old tables should not be freed. 164*3c85ca21SRobert Wing */ 165*3c85ca21SRobert Wing ATF_TC(oldtables_shared_via_threads); 166*3c85ca21SRobert Wing ATF_TC_HEAD(oldtables_shared_via_threads, tc) 167*3c85ca21SRobert Wing { 168*3c85ca21SRobert Wing atf_tc_set_md_var(tc, "require.user", "root"); 169*3c85ca21SRobert Wing } 170*3c85ca21SRobert Wing 171*3c85ca21SRobert Wing ATF_TC_BODY(oldtables_shared_via_threads, tc) 172*3c85ca21SRobert Wing { 173*3c85ca21SRobert Wing kvm_t *kd; 174*3c85ca21SRobert Wing struct kinfo_proc *kp; 175*3c85ca21SRobert Wing pthread_t thread; 176*3c85ca21SRobert Wing 177*3c85ca21SRobert Wing ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL); 178*3c85ca21SRobert Wing ATF_REQUIRE(pthread_create(&thread, NULL, exec_thread, NULL) == 0); 179*3c85ca21SRobert Wing 180*3c85ca21SRobert Wing openfiles(128); 181*3c85ca21SRobert Wing 182*3c85ca21SRobert Wing kp = read_kinfo(kd); 183*3c85ca21SRobert Wing ATF_CHECK(kp->ki_numthreads > 1); 184*3c85ca21SRobert Wing ATF_CHECK(old_tables(kd,kp) > 1); 185*3c85ca21SRobert Wing 186*3c85ca21SRobert Wing ATF_REQUIRE(pthread_cancel(thread) == 0); 187*3c85ca21SRobert Wing ATF_REQUIRE(pthread_join(thread, NULL) == 0); 188*3c85ca21SRobert Wing } 189*3c85ca21SRobert Wing 190*3c85ca21SRobert Wing /* 191*3c85ca21SRobert Wing * Get the reference count of a file descriptor table. 192*3c85ca21SRobert Wing */ 193*3c85ca21SRobert Wing static int 194*3c85ca21SRobert Wing filedesc_refcnt(kvm_t *kd, struct kinfo_proc *kp) 195*3c85ca21SRobert Wing { 196*3c85ca21SRobert Wing struct filedesc fdp; 197*3c85ca21SRobert Wing 198*3c85ca21SRobert Wing ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp, sizeof(fdp)) > 0); 199*3c85ca21SRobert Wing 200*3c85ca21SRobert Wing return (fdp.fd_refcnt); 201*3c85ca21SRobert Wing } 202*3c85ca21SRobert Wing 203*3c85ca21SRobert Wing /* 204*3c85ca21SRobert Wing * Test a single threaded process that shares a file descriptor 205*3c85ca21SRobert Wing * table with another process. The old tables should not be freed. 206*3c85ca21SRobert Wing */ 207*3c85ca21SRobert Wing ATF_TC(oldtables_shared_via_process); 208*3c85ca21SRobert Wing ATF_TC_HEAD(oldtables_shared_via_process, tc) 209*3c85ca21SRobert Wing { 210*3c85ca21SRobert Wing atf_tc_set_md_var(tc, "require.user", "root"); 211*3c85ca21SRobert Wing } 212*3c85ca21SRobert Wing 213*3c85ca21SRobert Wing ATF_TC_BODY(oldtables_shared_via_process, tc) 214*3c85ca21SRobert Wing { 215*3c85ca21SRobert Wing kvm_t *kd; 216*3c85ca21SRobert Wing struct kinfo_proc *kp; 217*3c85ca21SRobert Wing int status; 218*3c85ca21SRobert Wing pid_t child, wpid; 219*3c85ca21SRobert Wing 220*3c85ca21SRobert Wing ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL); 221*3c85ca21SRobert Wing 222*3c85ca21SRobert Wing /* share the file descriptor table */ 223*3c85ca21SRobert Wing ATF_REQUIRE((child = rfork(RFPROC)) != -1); 224*3c85ca21SRobert Wing 225*3c85ca21SRobert Wing if (child == 0) { 226*3c85ca21SRobert Wing openfiles(128); 227*3c85ca21SRobert Wing raise(SIGSTOP); 228*3c85ca21SRobert Wing exit(127); 229*3c85ca21SRobert Wing } 230*3c85ca21SRobert Wing 231*3c85ca21SRobert Wing /* let parent process open some files too */ 232*3c85ca21SRobert Wing openfiles(128); 233*3c85ca21SRobert Wing 234*3c85ca21SRobert Wing /* get current status of child */ 235*3c85ca21SRobert Wing wpid = waitpid(child, &status, WUNTRACED); 236*3c85ca21SRobert Wing 237*3c85ca21SRobert Wing /* child should be stopped */ 238*3c85ca21SRobert Wing ATF_REQUIRE(WIFSTOPPED(status)); 239*3c85ca21SRobert Wing 240*3c85ca21SRobert Wing /* 241*3c85ca21SRobert Wing * We want to read kernel data 242*3c85ca21SRobert Wing * before the child exits 243*3c85ca21SRobert Wing * otherwise we'll lose a reference count 244*3c85ca21SRobert Wing * to the file descriptor table 245*3c85ca21SRobert Wing */ 246*3c85ca21SRobert Wing if (child != 0) { 247*3c85ca21SRobert Wing kp = read_kinfo(kd); 248*3c85ca21SRobert Wing 249*3c85ca21SRobert Wing ATF_CHECK(filedesc_refcnt(kd,kp) > 1); 250*3c85ca21SRobert Wing ATF_CHECK(old_tables(kd,kp) > 1); 251*3c85ca21SRobert Wing 252*3c85ca21SRobert Wing kill(child, SIGCONT); 253*3c85ca21SRobert Wing } 254*3c85ca21SRobert Wing 255*3c85ca21SRobert Wing /* child should have exited */ 256*3c85ca21SRobert Wing wpid = waitpid(child, &status, 0); 257*3c85ca21SRobert Wing ATF_REQUIRE(WIFEXITED(status)); 258*3c85ca21SRobert Wing ATF_REQUIRE(WEXITSTATUS(status) == 127); 259*3c85ca21SRobert Wing } 260*3c85ca21SRobert Wing 261*3c85ca21SRobert Wing ATF_TP_ADD_TCS(tp) 262*3c85ca21SRobert Wing { 263*3c85ca21SRobert Wing ATF_TP_ADD_TC(tp, free_oldtables); 264*3c85ca21SRobert Wing ATF_TP_ADD_TC(tp, oldtables_shared_via_threads); 265*3c85ca21SRobert Wing ATF_TP_ADD_TC(tp, oldtables_shared_via_process); 266*3c85ca21SRobert Wing return (atf_no_error()); 267*3c85ca21SRobert Wing } 268