xref: /freebsd/tests/sys/kern/fdgrowtable_test.c (revision 4c6ceca9cc44fc9b02dc39e80713f2ad3ab8eeb6)
13c85ca21SRobert Wing /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
293c85ca21SRobert Wing #include <sys/filedesc.h>
303c85ca21SRobert Wing #include <sys/queue.h>
313c85ca21SRobert Wing #include <sys/sysctl.h>
323c85ca21SRobert Wing #include <sys/user.h>
333c85ca21SRobert Wing #include <sys/wait.h>
343c85ca21SRobert Wing 
353c85ca21SRobert Wing #include <atf-c.h>
363c85ca21SRobert Wing #include <fcntl.h>
373c85ca21SRobert Wing #include <signal.h>
383c85ca21SRobert Wing #include <stdio.h>
393c85ca21SRobert Wing #include <stdlib.h>
403c85ca21SRobert Wing #include <string.h>
413c85ca21SRobert Wing #include <unistd.h>
423c85ca21SRobert Wing 
433c85ca21SRobert Wing /* linked libraries */
443c85ca21SRobert Wing #include <kvm.h>
453c85ca21SRobert Wing #include <libutil.h>
463c85ca21SRobert Wing #include <libprocstat.h>
473c85ca21SRobert Wing #include <pthread.h>
483c85ca21SRobert Wing 
493c85ca21SRobert Wing /* test-case macro */
503c85ca21SRobert Wing #define AFILE "afile"
513c85ca21SRobert Wing 
523c85ca21SRobert Wing /*
533c85ca21SRobert Wing  * The following macros, struct freetable, struct fdescenttbl0
543c85ca21SRobert Wing  * and struct filedesc0 are copied from sys/kern/kern_descrip.c
553c85ca21SRobert Wing  */
563c85ca21SRobert Wing #define NDFILE		20
573c85ca21SRobert Wing #define NDSLOTSIZE	sizeof(NDSLOTTYPE)
583c85ca21SRobert Wing #define	NDENTRIES	(NDSLOTSIZE * __CHAR_BIT)
593c85ca21SRobert Wing #define NDSLOT(x)	((x) / NDENTRIES)
603c85ca21SRobert Wing #define NDBIT(x)	((NDSLOTTYPE)1 << ((x) % NDENTRIES))
613c85ca21SRobert Wing #define	NDSLOTS(x)	(((x) + NDENTRIES - 1) / NDENTRIES)
623c85ca21SRobert Wing 
633c85ca21SRobert Wing struct freetable {
643c85ca21SRobert Wing 	struct fdescenttbl *ft_table;
653c85ca21SRobert Wing 	SLIST_ENTRY(freetable) ft_next;
663c85ca21SRobert Wing };
673c85ca21SRobert Wing 
683c85ca21SRobert Wing struct fdescenttbl0 {
693c85ca21SRobert Wing 	int	fdt_nfiles;
703c85ca21SRobert Wing 	struct	filedescent fdt_ofiles[NDFILE];
713c85ca21SRobert Wing };
723c85ca21SRobert Wing 
733c85ca21SRobert Wing struct filedesc0 {
743c85ca21SRobert Wing 	struct filedesc fd_fd;
753c85ca21SRobert Wing 	SLIST_HEAD(, freetable) fd_free;
763c85ca21SRobert Wing 	struct	fdescenttbl0 fd_dfiles;
773c85ca21SRobert Wing 	NDSLOTTYPE fd_dmap[NDSLOTS(NDFILE)];
783c85ca21SRobert Wing };
793c85ca21SRobert Wing 
803c85ca21SRobert Wing static void
openfiles(int n)813c85ca21SRobert Wing openfiles(int n)
823c85ca21SRobert Wing {
833c85ca21SRobert Wing 	int i, fd;
843c85ca21SRobert Wing 
853c85ca21SRobert Wing 	ATF_REQUIRE((fd = open(AFILE, O_CREAT, 0644)) != -1);
863c85ca21SRobert Wing 	close(fd);
873c85ca21SRobert Wing 	for (i = 0; i < n; i++)
883c85ca21SRobert Wing 		ATF_REQUIRE((fd = open(AFILE, O_RDONLY, 0644)) != -1);
893c85ca21SRobert Wing }
903c85ca21SRobert Wing 
913c85ca21SRobert Wing /*
923c85ca21SRobert Wing  * Get a count of the old file descriptor tables on the freelist.
933c85ca21SRobert Wing  */
943c85ca21SRobert Wing static int
old_tables(kvm_t * kd,struct kinfo_proc * kp)953c85ca21SRobert Wing old_tables(kvm_t *kd, struct kinfo_proc *kp)
963c85ca21SRobert Wing {
973c85ca21SRobert Wing 	struct filedesc0 fdp0;
983c85ca21SRobert Wing 	struct freetable *ft, tft;
993c85ca21SRobert Wing 	int counter;
1003c85ca21SRobert Wing 
1013c85ca21SRobert Wing 	counter = 0;
1023c85ca21SRobert Wing 
1033c85ca21SRobert Wing 	ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp0, sizeof(fdp0)) > 0);
1043c85ca21SRobert Wing 
1053c85ca21SRobert Wing 	SLIST_FOREACH(ft, &fdp0.fd_free, ft_next) {
1063c85ca21SRobert Wing 		ATF_REQUIRE(kvm_read(kd, (unsigned long) ft, &tft, sizeof(tft)) > 0 );
1073c85ca21SRobert Wing 		ft = &tft;
1083c85ca21SRobert Wing 		counter++;
1093c85ca21SRobert Wing 	}
1103c85ca21SRobert Wing 
1113c85ca21SRobert Wing 	return (counter);
1123c85ca21SRobert Wing }
1133c85ca21SRobert Wing 
1143c85ca21SRobert Wing /*
1153c85ca21SRobert Wing  *  The returning struct kinfo_proc stores kernel addresses that will be
1163c85ca21SRobert Wing  *  used by kvm_read to retrieve information for the current process.
1173c85ca21SRobert Wing  */
1183c85ca21SRobert Wing static struct kinfo_proc *
read_kinfo(kvm_t * kd)1193c85ca21SRobert Wing read_kinfo(kvm_t *kd)
1203c85ca21SRobert Wing {
1213c85ca21SRobert Wing 	struct kinfo_proc *kp;
1223c85ca21SRobert Wing 	int procs_found;
1233c85ca21SRobert Wing 
1243c85ca21SRobert Wing 	ATF_REQUIRE((kp = kvm_getprocs(kd, KERN_PROC_PID, (int) getpid(), &procs_found)) != NULL);
1253c85ca21SRobert Wing 	ATF_REQUIRE(procs_found == 1);
1263c85ca21SRobert Wing 
1273c85ca21SRobert Wing 	return (kp);
1283c85ca21SRobert Wing }
1293c85ca21SRobert Wing 
1303c85ca21SRobert Wing /*
1313c85ca21SRobert Wing  * Test a single threaded process that doesn't have a shared
1323c85ca21SRobert Wing  * file descriptor table. The old tables should be freed.
1333c85ca21SRobert Wing  */
1343c85ca21SRobert Wing ATF_TC(free_oldtables);
ATF_TC_HEAD(free_oldtables,tc)1353c85ca21SRobert Wing ATF_TC_HEAD(free_oldtables, tc)
1363c85ca21SRobert Wing {
1373c85ca21SRobert Wing 	atf_tc_set_md_var(tc, "require.user", "root");
1383c85ca21SRobert Wing }
1393c85ca21SRobert Wing 
ATF_TC_BODY(free_oldtables,tc)1403c85ca21SRobert Wing ATF_TC_BODY(free_oldtables, tc)
1413c85ca21SRobert Wing {
1423c85ca21SRobert Wing 	kvm_t *kd;
1433c85ca21SRobert Wing 	struct kinfo_proc *kp;
1443c85ca21SRobert Wing 
1453c85ca21SRobert Wing 	ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
1463c85ca21SRobert Wing 	openfiles(128);
1473c85ca21SRobert Wing 	kp = read_kinfo(kd);
1483c85ca21SRobert Wing 	ATF_CHECK(old_tables(kd,kp) == 0);
1493c85ca21SRobert Wing }
1503c85ca21SRobert Wing 
1516d759051SRyan Libby static _Noreturn void *
exec_thread(void * args)1523c85ca21SRobert Wing exec_thread(void *args)
1533c85ca21SRobert Wing {
1543c85ca21SRobert Wing 	for (;;)
1553c85ca21SRobert Wing 		sleep(1);
1563c85ca21SRobert Wing }
1573c85ca21SRobert Wing 
1583c85ca21SRobert Wing /*
1593c85ca21SRobert Wing  * Test a process with two threads that doesn't have a shared file
1603c85ca21SRobert Wing  * descriptor table. The old tables should not be freed.
1613c85ca21SRobert Wing  */
1623c85ca21SRobert Wing ATF_TC(oldtables_shared_via_threads);
ATF_TC_HEAD(oldtables_shared_via_threads,tc)1633c85ca21SRobert Wing ATF_TC_HEAD(oldtables_shared_via_threads, tc)
1643c85ca21SRobert Wing {
1653c85ca21SRobert Wing 	atf_tc_set_md_var(tc, "require.user", "root");
1663c85ca21SRobert Wing }
1673c85ca21SRobert Wing 
ATF_TC_BODY(oldtables_shared_via_threads,tc)1683c85ca21SRobert Wing ATF_TC_BODY(oldtables_shared_via_threads, tc)
1693c85ca21SRobert Wing {
170*4c6ceca9SGleb Smirnoff 	pid_t child;
1713c85ca21SRobert Wing 	kvm_t *kd;
1723c85ca21SRobert Wing 	struct kinfo_proc *kp;
1733c85ca21SRobert Wing 	pthread_t thread;
1743c85ca21SRobert Wing 
175*4c6ceca9SGleb Smirnoff 	if ((child = rfork(RFPROC | RFCFDG)) > 0) {
176*4c6ceca9SGleb Smirnoff 		pid_t wpid;
177*4c6ceca9SGleb Smirnoff 		int status;
178*4c6ceca9SGleb Smirnoff 
179*4c6ceca9SGleb Smirnoff 		wpid = waitpid(child, &status, 0);
180*4c6ceca9SGleb Smirnoff 		ATF_REQUIRE(wpid == child);
181*4c6ceca9SGleb Smirnoff 		ATF_REQUIRE(WIFEXITED(status));
182*4c6ceca9SGleb Smirnoff 		ATF_REQUIRE(WEXITSTATUS(status) == EXIT_SUCCESS);
183*4c6ceca9SGleb Smirnoff 		return;
184*4c6ceca9SGleb Smirnoff 	}
185*4c6ceca9SGleb Smirnoff 
186*4c6ceca9SGleb Smirnoff #define	REQUIRE(expression)	do {					\
187*4c6ceca9SGleb Smirnoff 		if (!(expression))					\
188*4c6ceca9SGleb Smirnoff 			exit(EXIT_FAILURE);				\
189*4c6ceca9SGleb Smirnoff 	} while (0)
190*4c6ceca9SGleb Smirnoff 
191*4c6ceca9SGleb Smirnoff 	REQUIRE(child == 0);
192*4c6ceca9SGleb Smirnoff 
193*4c6ceca9SGleb Smirnoff 	REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
194*4c6ceca9SGleb Smirnoff 	REQUIRE(pthread_create(&thread, NULL, exec_thread, NULL) == 0);
1953c85ca21SRobert Wing 
1963c85ca21SRobert Wing 	openfiles(128);
1973c85ca21SRobert Wing 
1983c85ca21SRobert Wing 	kp = read_kinfo(kd);
199*4c6ceca9SGleb Smirnoff 	REQUIRE(kp->ki_numthreads > 1);
200*4c6ceca9SGleb Smirnoff 	REQUIRE(old_tables(kd,kp) > 1);
2013c85ca21SRobert Wing 
202*4c6ceca9SGleb Smirnoff 	REQUIRE(pthread_cancel(thread) == 0);
203*4c6ceca9SGleb Smirnoff 	REQUIRE(pthread_join(thread, NULL) == 0);
204*4c6ceca9SGleb Smirnoff #undef REQUIRE
205*4c6ceca9SGleb Smirnoff 
206*4c6ceca9SGleb Smirnoff 	exit(EXIT_SUCCESS);
2073c85ca21SRobert Wing }
2083c85ca21SRobert Wing 
2093c85ca21SRobert Wing /*
2103c85ca21SRobert Wing  * Get the reference count of a file descriptor table.
2113c85ca21SRobert Wing  */
2123c85ca21SRobert Wing static int
filedesc_refcnt(kvm_t * kd,struct kinfo_proc * kp)2133c85ca21SRobert Wing filedesc_refcnt(kvm_t *kd, struct kinfo_proc *kp)
2143c85ca21SRobert Wing {
2153c85ca21SRobert Wing 	struct filedesc fdp;
2163c85ca21SRobert Wing 
2173c85ca21SRobert Wing 	ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp, sizeof(fdp)) > 0);
2183c85ca21SRobert Wing 
2193c85ca21SRobert Wing 	return (fdp.fd_refcnt);
2203c85ca21SRobert Wing }
2213c85ca21SRobert Wing 
2223c85ca21SRobert Wing /*
2233c85ca21SRobert Wing  * Test a single threaded process that shares a file descriptor
2243c85ca21SRobert Wing  * table with another process. The old tables should not be freed.
2253c85ca21SRobert Wing  */
2263c85ca21SRobert Wing ATF_TC(oldtables_shared_via_process);
ATF_TC_HEAD(oldtables_shared_via_process,tc)2273c85ca21SRobert Wing ATF_TC_HEAD(oldtables_shared_via_process, tc)
2283c85ca21SRobert Wing {
2293c85ca21SRobert Wing 	atf_tc_set_md_var(tc, "require.user", "root");
2303c85ca21SRobert Wing }
2313c85ca21SRobert Wing 
ATF_TC_BODY(oldtables_shared_via_process,tc)2323c85ca21SRobert Wing ATF_TC_BODY(oldtables_shared_via_process, tc)
2333c85ca21SRobert Wing {
2343c85ca21SRobert Wing 	kvm_t *kd;
2353c85ca21SRobert Wing 	struct kinfo_proc *kp;
2363c85ca21SRobert Wing 	int status;
2373c85ca21SRobert Wing 	pid_t child, wpid;
2383c85ca21SRobert Wing 
2393c85ca21SRobert Wing 	ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
2403c85ca21SRobert Wing 
2413c85ca21SRobert Wing 	/* share the file descriptor table */
2423c85ca21SRobert Wing 	ATF_REQUIRE((child = rfork(RFPROC)) != -1);
2433c85ca21SRobert Wing 
2443c85ca21SRobert Wing 	if (child == 0) {
2453c85ca21SRobert Wing 		openfiles(128);
2463c85ca21SRobert Wing 		raise(SIGSTOP);
2473c85ca21SRobert Wing 		exit(127);
2483c85ca21SRobert Wing 	}
2493c85ca21SRobert Wing 
2503c85ca21SRobert Wing 	/* let parent process open some files too */
2513c85ca21SRobert Wing 	openfiles(128);
2523c85ca21SRobert Wing 
2533c85ca21SRobert Wing 	/* get current status of child */
2543c85ca21SRobert Wing 	wpid = waitpid(child, &status, WUNTRACED);
255889b5662SMark Johnston 	ATF_REQUIRE(wpid == child);
2563c85ca21SRobert Wing 
2573c85ca21SRobert Wing 	/* child should be stopped */
2583c85ca21SRobert Wing 	ATF_REQUIRE(WIFSTOPPED(status));
2593c85ca21SRobert Wing 
2603c85ca21SRobert Wing 	/*
2613c85ca21SRobert Wing 	 * We want to read kernel data
2623c85ca21SRobert Wing 	 * before the child exits
2633c85ca21SRobert Wing 	 * otherwise we'll lose a reference count
2643c85ca21SRobert Wing 	 * to the file descriptor table
2653c85ca21SRobert Wing 	 */
2663c85ca21SRobert Wing 	kp = read_kinfo(kd);
2673c85ca21SRobert Wing 
2683c85ca21SRobert Wing 	ATF_CHECK(filedesc_refcnt(kd,kp) > 1);
2693c85ca21SRobert Wing 	ATF_CHECK(old_tables(kd,kp) > 1);
2703c85ca21SRobert Wing 
2713c85ca21SRobert Wing 	kill(child, SIGCONT);
2723c85ca21SRobert Wing 
2733c85ca21SRobert Wing 	/* child should have exited */
2743c85ca21SRobert Wing 	wpid = waitpid(child, &status, 0);
275889b5662SMark Johnston 	ATF_REQUIRE(wpid == child);
2763c85ca21SRobert Wing 	ATF_REQUIRE(WIFEXITED(status));
2773c85ca21SRobert Wing 	ATF_REQUIRE(WEXITSTATUS(status) == 127);
2783c85ca21SRobert Wing }
2793c85ca21SRobert Wing 
ATF_TP_ADD_TCS(tp)2803c85ca21SRobert Wing ATF_TP_ADD_TCS(tp)
2813c85ca21SRobert Wing {
2823c85ca21SRobert Wing 	ATF_TP_ADD_TC(tp, free_oldtables);
2833c85ca21SRobert Wing 	ATF_TP_ADD_TC(tp, oldtables_shared_via_threads);
2843c85ca21SRobert Wing 	ATF_TP_ADD_TC(tp, oldtables_shared_via_process);
2853c85ca21SRobert Wing 	return (atf_no_error());
2863c85ca21SRobert Wing }
287