xref: /freebsd/tests/sys/kern/sysctl_kern_proc.c (revision 7ef62cebc2f965b0f640263e179276928885e33d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021 The FreeBSD Foundation
5  *
6  * This software was developed by Mark Johnston under sponsorship from
7  * the FreeBSD Foundation.
8  */
9 
10 #include <sys/param.h>
11 #include <sys/stat.h>
12 #include <sys/sysctl.h>
13 #include <sys/user.h>
14 #include <sys/wait.h>
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 
21 #include <atf-c.h>
22 
23 /*
24  * These tests exercise the KERN_PROC_* sysctls.
25  */
26 
27 /*
28  * Loop through all valid PIDs and try to fetch info for each one.
29  */
30 static void
31 sysctl_kern_proc_all(int cmd)
32 {
33 	int mib[4], pid_max;
34 	void *buf;
35 	size_t sz;
36 
37 	sz = sizeof(pid_max);
38 	ATF_REQUIRE(sysctlbyname("kern.pid_max", &pid_max, &sz, NULL, 0) == 0);
39 
40 	mib[0] = CTL_KERN;
41 	mib[1] = KERN_PROC;
42 	mib[2] = cmd;
43 	for (int i = 1; i <= pid_max; i++) {
44 		mib[3] = i;
45 
46 		if (sysctl(mib, 4, NULL, &sz, NULL, 0) == 0) {
47 			buf = malloc(sz);
48 			ATF_REQUIRE(buf != NULL);
49 			(void)sysctl(mib, 4, buf, &sz, NULL, 0);
50 			free(buf);
51 		}
52 	}
53 
54 	mib[3] = -1;
55 	ATF_REQUIRE_ERRNO(ESRCH, sysctl(mib, 4, NULL, &sz, NULL, 0) != 0);
56 }
57 
58 /*
59  * Validate behaviour of the KERN_PROC_CWD sysctl.
60  */
61 ATF_TC_WITHOUT_HEAD(sysctl_kern_proc_cwd);
62 ATF_TC_BODY(sysctl_kern_proc_cwd, tc)
63 {
64 	struct kinfo_file kfile;
65 	char cwd[PATH_MAX];
66 	int cmd, mib[4];
67 	size_t sz;
68 	pid_t child;
69 	int status;
70 
71 	cmd = KERN_PROC_CWD;
72 
73 	mib[0] = CTL_KERN;
74 	mib[1] = KERN_PROC;
75 	mib[2] = cmd;
76 	mib[3] = getpid();
77 
78 	/* Try querying the kernel for the output buffer size. */
79 	sz = 0;
80 	ATF_REQUIRE(sysctl(mib, 4, NULL, &sz, NULL, 0) == 0);
81 	ATF_REQUIRE(sz <= sizeof(kfile));
82 
83 	sz = sizeof(kfile);
84 	memset(&kfile, 0, sz);
85 	ATF_REQUIRE(sysctl(mib, 4, &kfile, &sz, NULL, 0) == 0);
86 	ATF_REQUIRE(sz <= sizeof(kfile));
87 	ATF_REQUIRE(sz == (u_int)kfile.kf_structsize);
88 
89 	/* Make sure that we get the same result from getcwd(2). */
90 	ATF_REQUIRE(getcwd(cwd, sizeof(cwd)) == cwd);
91 	ATF_REQUIRE(strcmp(cwd, kfile.kf_path) == 0);
92 
93 	/* Spot-check some of the kinfo fields. */
94 	ATF_REQUIRE(kfile.kf_type == KF_TYPE_VNODE);
95 	ATF_REQUIRE(kfile.kf_fd == KF_FD_TYPE_CWD);
96 	ATF_REQUIRE(S_ISDIR(kfile.kf_un.kf_file.kf_file_mode));
97 	ATF_REQUIRE((kfile.kf_status & KF_ATTR_VALID) != 0);
98 
99 	/*
100 	 * Verify that a child process can get our CWD info, and that it
101 	 * matches the info we got above.
102 	 */
103 	child = fork();
104 	ATF_REQUIRE(child != -1);
105 	if (child == 0) {
106 		struct kinfo_file pkfile;
107 
108 		mib[0] = CTL_KERN;
109 		mib[1] = KERN_PROC;
110 		mib[2] = KERN_PROC_CWD;
111 		mib[3] = getppid();
112 
113 		sz = sizeof(pkfile);
114 		memset(&pkfile, 0, sz);
115 		if (sysctl(mib, 4, &pkfile, &sz, NULL, 0) != 0)
116 			_exit(1);
117 		if (memcmp(&kfile, &pkfile, sizeof(kfile)) != 0)
118 			_exit(2);
119 		_exit(0);
120 	}
121 	ATF_REQUIRE(waitpid(child, &status, 0) == child);
122 	ATF_REQUIRE(WIFEXITED(status));
123 	ATF_REQUIRE(WEXITSTATUS(status) == 0);
124 
125 	/*
126 	 * Truncate the output buffer ever so slightly and make sure that we get
127 	 * an error.
128 	 */
129 	sz--;
130 	ATF_REQUIRE_ERRNO(ENOMEM, sysctl(mib, 4, &kfile, &sz, NULL, 0) != 0);
131 
132 	sysctl_kern_proc_all(cmd);
133 }
134 
135 /*
136  * Validate behaviour of the KERN_PROC_FILEDESC sysctl.
137  */
138 ATF_TC_WITHOUT_HEAD(sysctl_kern_proc_filedesc);
139 ATF_TC_BODY(sysctl_kern_proc_filedesc, tc)
140 {
141 	int cmd, fd, mib[4];
142 	struct kinfo_file *kfile;
143 	char *buf, tmp[16];
144 	size_t sz, sz1;
145 
146 	cmd = KERN_PROC_FILEDESC;
147 
148 	mib[0] = CTL_KERN;
149 	mib[1] = KERN_PROC;
150 	mib[2] = cmd;
151 	mib[3] = getpid();
152 
153 	sz = 0;
154 	ATF_REQUIRE(sysctl(mib, 4, NULL, &sz, NULL, 0) == 0);
155 	ATF_REQUIRE(sz >= __offsetof(struct kinfo_file, kf_structsize) +
156 	    sizeof(kfile->kf_structsize));
157 
158 	buf = malloc(sz);
159 	ATF_REQUIRE(buf != NULL);
160 
161 	ATF_REQUIRE(sysctl(mib, 4, buf, &sz, NULL, 0) == 0);
162 
163 	/* Walk over the list of returned files. */
164 	for (sz1 = 0; sz1 < sz; sz1 += kfile->kf_structsize) {
165 		kfile = (void *)(buf + sz1);
166 
167 		ATF_REQUIRE((unsigned int)kfile->kf_structsize <= sz);
168 		ATF_REQUIRE((unsigned int)kfile->kf_structsize + sz1 <= sz);
169 
170 		ATF_REQUIRE((kfile->kf_status & KF_ATTR_VALID) != 0);
171 	}
172 	/* We shouldn't have any trailing bytes. */
173 	ATF_REQUIRE(sz1 == sz);
174 
175 	/*
176 	 * Open a file.  This increases the size of the output buffer, so an
177 	 * attempt to re-fetch the records without increasing the buffer size
178 	 * should fail with ENOMEM.
179 	 */
180 	snprintf(tmp, sizeof(tmp), "tmp.XXXXXX");
181 	fd = mkstemp(tmp);
182 	ATF_REQUIRE(fd >= 0);
183 	ATF_REQUIRE_ERRNO(ENOMEM, sysctl(mib, 4, buf, &sz, NULL, 0) != 0);
184 
185 	ATF_REQUIRE(unlink(tmp) == 0);
186 	ATF_REQUIRE(close(fd) == 0);
187 
188 	free(buf);
189 
190 	sysctl_kern_proc_all(cmd);
191 }
192 
193 ATF_TP_ADD_TCS(tp)
194 {
195 	ATF_TP_ADD_TC(tp, sysctl_kern_proc_cwd);
196 	ATF_TP_ADD_TC(tp, sysctl_kern_proc_filedesc);
197 
198 	return (atf_no_error());
199 }
200