xref: /freebsd/tests/sys/kern/fdgrowtable_test.c (revision 3c85ca21d14d858c9518d94655a50082d28fc493)
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