1 /* $OpenBSD: unsopassgc.c,v 1.4 2021/12/29 00:04:35 mvs Exp $ */
2 
3 /*
4  * Copyright (c) 2021 Vitaliy Makkoveev <mvs@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Try to beak unix(4) sockets garbage collector and make it to clean
21  * `so_rcv' buffer of alive socket. Successful breakage should produce
22  * kernel panic.
23  */
24 
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <sys/sysctl.h>
29 #include <sys/un.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <pthread.h>
36 #include <string.h>
37 #include <time.h>
38 #include <unistd.h>
39 
40 static pthread_mutex_t therr_mtx = PTHREAD_MUTEX_INITIALIZER;
41 
42 static void
therr(int eval,const char * fmt,...)43 therr(int eval, const char *fmt, ...)
44 {
45 	va_list ap;
46 
47 	pthread_mutex_lock(&therr_mtx);
48 
49 	va_start(ap, fmt);
50 	verr(eval, fmt, ap);
51 	va_end(ap);
52 }
53 
54 static void
therrx(int eval,const char * fmt,...)55 therrx(int eval, const char *fmt, ...)
56 {
57 	va_list ap;
58 
59 	pthread_mutex_lock(&therr_mtx);
60 
61 	va_start(ap, fmt);
62 	verrx(eval, fmt, ap);
63 	va_end(ap);
64 }
65 
66 static void
therrc(int eval,int code,const char * fmt,...)67 therrc(int eval, int code, const char *fmt, ...)
68 {
69 	va_list ap;
70 
71 	pthread_mutex_lock(&therr_mtx);
72 
73 	va_start(ap, fmt);
74 	verrc(eval, code, fmt, ap);
75 	va_end(ap);
76 }
77 
78 #define PASSFD_NUM (4)
79 
80 union msg_control {
81 	struct cmsghdr cmsgh;
82 	char control[CMSG_SPACE(sizeof(int) * PASSFD_NUM)];
83 };
84 
85 static struct thr_pass_arg {
86 	int s[2];
87 	int passfd;
88 } *thr_pass_args;
89 
90 static struct thr_gc_arg {
91 	int passfd;
92 } *thr_gc_arg;
93 
94 static void *
thr_send(void * arg)95 thr_send(void *arg)
96 {
97 	union msg_control msg_control;
98 	int iov_buf;
99 	struct iovec iov;
100 	struct msghdr msgh;
101 	struct cmsghdr *cmsgh;
102 	int *s = ((struct thr_pass_arg *)arg)->s;
103 	int passfd = ((struct thr_pass_arg *)arg)->passfd;
104 
105 	while (1) {
106 		iov_buf = 0;
107 		iov.iov_base = &iov_buf;
108 		iov.iov_len = sizeof(iov_buf);
109 		msgh.msg_control = msg_control.control;
110 		msgh.msg_controllen = sizeof(msg_control.control);
111 		msgh.msg_iov = &iov;
112 		msgh.msg_iovlen = 1;
113 		msgh.msg_name = NULL;
114 		msgh.msg_namelen = 0;
115 		cmsgh = CMSG_FIRSTHDR(&msgh);
116 		cmsgh->cmsg_len = CMSG_LEN(sizeof(int) * PASSFD_NUM);
117 		cmsgh->cmsg_level = SOL_SOCKET;
118 		cmsgh->cmsg_type = SCM_RIGHTS;
119 		*((int *)CMSG_DATA(cmsgh) + 0) = s[0];
120 		*((int *)CMSG_DATA(cmsgh) + 1) = s[1];
121 		*((int *)CMSG_DATA(cmsgh) + 2) = passfd;
122 		*((int *)CMSG_DATA(cmsgh) + 3) = passfd;
123 
124 		if (sendmsg(s[0], &msgh, 0) < 0) {
125 			switch (errno) {
126 			case EMFILE:
127 			case ENOBUFS:
128 				break;
129 			default:
130 				therr(1, "sendmsg");
131 			}
132 		}
133 	}
134 
135 	return NULL;
136 }
137 
138 static void *
thr_recv(void * arg)139 thr_recv(void *arg)
140 {
141 	union msg_control msg_control;
142 	int iov_buf;
143 	struct iovec iov;
144 	struct msghdr msgh;
145 	struct cmsghdr *cmsgh;
146 	int i, fd;
147 	int *s = ((struct thr_pass_arg *)arg)->s;
148 
149 	while (1) {
150 		msg_control.cmsgh.cmsg_level = SOL_SOCKET;
151 		msg_control.cmsgh.cmsg_type = SCM_RIGHTS;
152 		msg_control.cmsgh.cmsg_len =
153 		    CMSG_LEN(sizeof(int) * PASSFD_NUM);
154 
155 		iov.iov_base = &iov_buf;
156 		iov.iov_len = sizeof(iov_buf);
157 
158 		msgh.msg_control = msg_control.control;
159 		msgh.msg_controllen = sizeof(msg_control.control);
160 		msgh.msg_iov = &iov;
161 		msgh.msg_iovlen = 1;
162 		msgh.msg_name = NULL;
163 		msgh.msg_namelen = 0;
164 
165 		if(recvmsg(s[1], &msgh, 0) < 0)
166 			therr(1, "recvmsg");
167 
168 		if(!(cmsgh = CMSG_FIRSTHDR(&msgh)))
169 			therrx(1, "bad cmsg header");
170 		if(cmsgh->cmsg_level != SOL_SOCKET)
171 			therrx(1, "bad cmsg level");
172 		if(cmsgh->cmsg_type != SCM_RIGHTS)
173 			therrx(1, "bad cmsg type");
174 		if(cmsgh->cmsg_len != CMSG_LEN(sizeof(fd) * PASSFD_NUM))
175 			therrx(1, "bad cmsg length");
176 
177 		for (i = 0; i < PASSFD_NUM; ++i) {
178 			fd = *((int *)CMSG_DATA(cmsgh) + i);
179 			close(fd);
180 		}
181 	}
182 
183 	return NULL;
184 }
185 
186 static void *
thr_dispose(void * arg)187 thr_dispose(void *arg)
188 {
189 	uint8_t buf[sizeof(union msg_control)];
190 	int *s = ((struct thr_pass_arg *)arg)->s;
191 
192 	while (1) {
193 		if (read(s[1], buf, sizeof(buf)) < 0)
194 			therr(1, "read");
195 	}
196 
197 	return NULL;
198 }
199 
200 static void *
thr_gc(void * arg)201 thr_gc(void *arg)
202 {
203 	union msg_control msg_control;
204 	int iov_buf;
205 	struct iovec iov;
206 	struct msghdr msgh;
207 	struct cmsghdr *cmsgh;
208 	int s[2], passfd = ((struct thr_gc_arg *)arg)->passfd;
209 
210 	while (1) {
211 		if (socketpair(AF_UNIX, SOCK_DGRAM, 0, s) < 0)
212 			therr(1, "socketpair");
213 
214 		iov_buf = 0;
215 		iov.iov_base = &iov_buf;
216 		iov.iov_len = sizeof(iov_buf);
217 		msgh.msg_control = msg_control.control;
218 		msgh.msg_controllen = sizeof(msg_control.control);
219 		msgh.msg_iov = &iov;
220 		msgh.msg_iovlen = 1;
221 		msgh.msg_name = NULL;
222 		msgh.msg_namelen = 0;
223 		cmsgh = CMSG_FIRSTHDR(&msgh);
224 		cmsgh->cmsg_len = CMSG_LEN(sizeof(int) * PASSFD_NUM);
225 		cmsgh->cmsg_level = SOL_SOCKET;
226 		cmsgh->cmsg_type = SCM_RIGHTS;
227 		*((int *)CMSG_DATA(cmsgh) + 0) = s[0];
228 		*((int *)CMSG_DATA(cmsgh) + 1) = s[1];
229 		*((int *)CMSG_DATA(cmsgh) + 2) = passfd;
230 		*((int *)CMSG_DATA(cmsgh) + 3) = passfd;
231 
232 		if (sendmsg(s[0], &msgh, 0) < 0) {
233 			switch (errno) {
234 			case EMFILE:
235 			case ENOBUFS:
236 				break;
237 			default:
238 				therr(1, "sendmsg");
239 			}
240 		}
241 
242 		close(s[0]);
243 		close(s[1]);
244 	}
245 
246 	return NULL;
247 }
248 
249 int
main(int argc,char * argv[])250 main(int argc, char *argv[])
251 {
252 	struct timespec testtime = {
253 		.tv_sec = 60,
254 		.tv_nsec = 0,
255 	};
256 
257 	int mib[2], ncpu;
258 	size_t len;
259 
260 	pthread_t thr;
261 	int i, error;
262 
263 	if (argc == 2 && !strcmp(argv[1], "--infinite"))
264 		testtime.tv_sec = (10 * 365 * 86400);
265 
266 	mib[0] = CTL_HW;
267 	mib[1] = HW_NCPUONLINE;
268 	len = sizeof(ncpu);
269 
270 	if (sysctl(mib, 2, &ncpu, &len, NULL, 0) < 0)
271 		err(1, "sysctl");
272 	if (ncpu <= 0)
273 		errx(1, "Wrong number of CPUs online: %d", ncpu);
274 
275 	if (!(thr_pass_args = calloc(ncpu, sizeof(*thr_pass_args))))
276 		err(1, "malloc");
277 	if (!(thr_gc_arg = malloc(sizeof(*thr_gc_arg))))
278 		err(1, "malloc");
279 
280 	for (i = 0; i < ncpu; ++i) {
281 		if (socketpair(AF_UNIX, SOCK_DGRAM, 0, thr_pass_args[i].s) < 0)
282 			err(1, "socketpair");
283 		thr_pass_args[i].passfd = thr_pass_args[i].s[0];
284 	}
285 
286 	thr_gc_arg->passfd = thr_pass_args[0].s[0];
287 
288 	for (i = 0; i < ncpu; ++i) {
289 		error = pthread_create(&thr, NULL,
290 		    thr_send, &thr_pass_args[i]);
291 		if (error)
292 			therrc(1, error, "pthread_create");
293 		error = pthread_create(&thr, NULL,
294 		    thr_recv, &thr_pass_args[i]);
295 		if (error)
296 			therrc(1, error, "pthread_create");
297 		error = pthread_create(&thr, NULL,
298 		    thr_dispose, &thr_pass_args[i]);
299 		if (error)
300 			therrc(1, error, "pthread_create");
301 	}
302 
303 	if ((error = pthread_create(&thr, NULL, thr_gc, thr_gc_arg)))
304 		therrc(1, error, "pthread_create");
305 
306 	nanosleep(&testtime, NULL);
307 
308 	return 0;
309 }
310