xref: /openbsd-src/regress/sys/kern/unixsockets/unsopassgc.c (revision d0f299081ecdda98dc7a87b149b17b038a84b9fa)
1*d0f29908Smvs /* $OpenBSD: unsopassgc.c,v 1.4 2021/12/29 00:04:35 mvs Exp $ */
26767823dSmvs 
36767823dSmvs /*
46767823dSmvs  * Copyright (c) 2021 Vitaliy Makkoveev <mvs@openbsd.org>
56767823dSmvs  *
66767823dSmvs  * Permission to use, copy, modify, and distribute this software for any
76767823dSmvs  * purpose with or without fee is hereby granted, provided that the above
86767823dSmvs  * copyright notice and this permission notice appear in all copies.
96767823dSmvs  *
106767823dSmvs  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
116767823dSmvs  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
126767823dSmvs  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
136767823dSmvs  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
146767823dSmvs  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
156767823dSmvs  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
166767823dSmvs  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176767823dSmvs  */
186767823dSmvs 
196767823dSmvs /*
206767823dSmvs  * Try to beak unix(4) sockets garbage collector and make it to clean
216767823dSmvs  * `so_rcv' buffer of alive socket. Successful breakage should produce
226767823dSmvs  * kernel panic.
236767823dSmvs  */
246767823dSmvs 
256767823dSmvs #include <sys/types.h>
266767823dSmvs #include <sys/socket.h>
276767823dSmvs #include <sys/stat.h>
286767823dSmvs #include <sys/sysctl.h>
296767823dSmvs #include <sys/un.h>
306767823dSmvs #include <stdarg.h>
316767823dSmvs #include <stdio.h>
326767823dSmvs #include <stdlib.h>
336767823dSmvs #include <err.h>
346767823dSmvs #include <errno.h>
356767823dSmvs #include <pthread.h>
366767823dSmvs #include <string.h>
376767823dSmvs #include <time.h>
386767823dSmvs #include <unistd.h>
396767823dSmvs 
406767823dSmvs static pthread_mutex_t therr_mtx = PTHREAD_MUTEX_INITIALIZER;
416767823dSmvs 
426767823dSmvs static void
therr(int eval,const char * fmt,...)436767823dSmvs therr(int eval, const char *fmt, ...)
446767823dSmvs {
456767823dSmvs 	va_list ap;
466767823dSmvs 
476767823dSmvs 	pthread_mutex_lock(&therr_mtx);
486767823dSmvs 
496767823dSmvs 	va_start(ap, fmt);
506767823dSmvs 	verr(eval, fmt, ap);
516767823dSmvs 	va_end(ap);
526767823dSmvs }
536767823dSmvs 
546767823dSmvs static void
therrx(int eval,const char * fmt,...)556767823dSmvs therrx(int eval, const char *fmt, ...)
566767823dSmvs {
576767823dSmvs 	va_list ap;
586767823dSmvs 
596767823dSmvs 	pthread_mutex_lock(&therr_mtx);
606767823dSmvs 
616767823dSmvs 	va_start(ap, fmt);
626767823dSmvs 	verrx(eval, fmt, ap);
636767823dSmvs 	va_end(ap);
646767823dSmvs }
656767823dSmvs 
666767823dSmvs static void
therrc(int eval,int code,const char * fmt,...)676767823dSmvs therrc(int eval, int code, const char *fmt, ...)
686767823dSmvs {
696767823dSmvs 	va_list ap;
706767823dSmvs 
716767823dSmvs 	pthread_mutex_lock(&therr_mtx);
726767823dSmvs 
736767823dSmvs 	va_start(ap, fmt);
746767823dSmvs 	verrc(eval, code, fmt, ap);
756767823dSmvs 	va_end(ap);
766767823dSmvs }
776767823dSmvs 
786767823dSmvs #define PASSFD_NUM (4)
796767823dSmvs 
806767823dSmvs union msg_control {
816767823dSmvs 	struct cmsghdr cmsgh;
826767823dSmvs 	char control[CMSG_SPACE(sizeof(int) * PASSFD_NUM)];
836767823dSmvs };
846767823dSmvs 
856767823dSmvs static struct thr_pass_arg {
866767823dSmvs 	int s[2];
876767823dSmvs 	int passfd;
886767823dSmvs } *thr_pass_args;
896767823dSmvs 
906767823dSmvs static struct thr_gc_arg {
916767823dSmvs 	int passfd;
926767823dSmvs } *thr_gc_arg;
936767823dSmvs 
946767823dSmvs static void *
thr_send(void * arg)956767823dSmvs thr_send(void *arg)
966767823dSmvs {
976767823dSmvs 	union msg_control msg_control;
986767823dSmvs 	int iov_buf;
996767823dSmvs 	struct iovec iov;
1006767823dSmvs 	struct msghdr msgh;
1016767823dSmvs 	struct cmsghdr *cmsgh;
1026767823dSmvs 	int *s = ((struct thr_pass_arg *)arg)->s;
1036767823dSmvs 	int passfd = ((struct thr_pass_arg *)arg)->passfd;
1046767823dSmvs 
1056767823dSmvs 	while (1) {
1066767823dSmvs 		iov_buf = 0;
1076767823dSmvs 		iov.iov_base = &iov_buf;
1086767823dSmvs 		iov.iov_len = sizeof(iov_buf);
1096767823dSmvs 		msgh.msg_control = msg_control.control;
1106767823dSmvs 		msgh.msg_controllen = sizeof(msg_control.control);
1116767823dSmvs 		msgh.msg_iov = &iov;
1126767823dSmvs 		msgh.msg_iovlen = 1;
1136767823dSmvs 		msgh.msg_name = NULL;
1146767823dSmvs 		msgh.msg_namelen = 0;
1156767823dSmvs 		cmsgh = CMSG_FIRSTHDR(&msgh);
1166767823dSmvs 		cmsgh->cmsg_len = CMSG_LEN(sizeof(int) * PASSFD_NUM);
1176767823dSmvs 		cmsgh->cmsg_level = SOL_SOCKET;
1186767823dSmvs 		cmsgh->cmsg_type = SCM_RIGHTS;
1196767823dSmvs 		*((int *)CMSG_DATA(cmsgh) + 0) = s[0];
1206767823dSmvs 		*((int *)CMSG_DATA(cmsgh) + 1) = s[1];
1216767823dSmvs 		*((int *)CMSG_DATA(cmsgh) + 2) = passfd;
1226767823dSmvs 		*((int *)CMSG_DATA(cmsgh) + 3) = passfd;
1236767823dSmvs 
1246767823dSmvs 		if (sendmsg(s[0], &msgh, 0) < 0) {
1256767823dSmvs 			switch (errno) {
1266767823dSmvs 			case EMFILE:
1276767823dSmvs 			case ENOBUFS:
1286767823dSmvs 				break;
1296767823dSmvs 			default:
1306767823dSmvs 				therr(1, "sendmsg");
1316767823dSmvs 			}
1326767823dSmvs 		}
1336767823dSmvs 	}
1346767823dSmvs 
1356767823dSmvs 	return NULL;
1366767823dSmvs }
1376767823dSmvs 
1386767823dSmvs static void *
thr_recv(void * arg)1396767823dSmvs thr_recv(void *arg)
1406767823dSmvs {
1416767823dSmvs 	union msg_control msg_control;
1426767823dSmvs 	int iov_buf;
1436767823dSmvs 	struct iovec iov;
1446767823dSmvs 	struct msghdr msgh;
1456767823dSmvs 	struct cmsghdr *cmsgh;
1466767823dSmvs 	int i, fd;
1476767823dSmvs 	int *s = ((struct thr_pass_arg *)arg)->s;
1486767823dSmvs 
1496767823dSmvs 	while (1) {
1506767823dSmvs 		msg_control.cmsgh.cmsg_level = SOL_SOCKET;
1516767823dSmvs 		msg_control.cmsgh.cmsg_type = SCM_RIGHTS;
1526767823dSmvs 		msg_control.cmsgh.cmsg_len =
1536767823dSmvs 		    CMSG_LEN(sizeof(int) * PASSFD_NUM);
1546767823dSmvs 
1556767823dSmvs 		iov.iov_base = &iov_buf;
1566767823dSmvs 		iov.iov_len = sizeof(iov_buf);
1576767823dSmvs 
1586767823dSmvs 		msgh.msg_control = msg_control.control;
1596767823dSmvs 		msgh.msg_controllen = sizeof(msg_control.control);
1606767823dSmvs 		msgh.msg_iov = &iov;
1616767823dSmvs 		msgh.msg_iovlen = 1;
1626767823dSmvs 		msgh.msg_name = NULL;
1636767823dSmvs 		msgh.msg_namelen = 0;
1646767823dSmvs 
1656767823dSmvs 		if(recvmsg(s[1], &msgh, 0) < 0)
1666767823dSmvs 			therr(1, "recvmsg");
1676767823dSmvs 
1686767823dSmvs 		if(!(cmsgh = CMSG_FIRSTHDR(&msgh)))
1696767823dSmvs 			therrx(1, "bad cmsg header");
1706767823dSmvs 		if(cmsgh->cmsg_level != SOL_SOCKET)
1716767823dSmvs 			therrx(1, "bad cmsg level");
1726767823dSmvs 		if(cmsgh->cmsg_type != SCM_RIGHTS)
1736767823dSmvs 			therrx(1, "bad cmsg type");
1746767823dSmvs 		if(cmsgh->cmsg_len != CMSG_LEN(sizeof(fd) * PASSFD_NUM))
1756767823dSmvs 			therrx(1, "bad cmsg length");
1766767823dSmvs 
1776767823dSmvs 		for (i = 0; i < PASSFD_NUM; ++i) {
1786767823dSmvs 			fd = *((int *)CMSG_DATA(cmsgh) + i);
1796767823dSmvs 			close(fd);
1806767823dSmvs 		}
1816767823dSmvs 	}
1826767823dSmvs 
1836767823dSmvs 	return NULL;
1846767823dSmvs }
1856767823dSmvs 
1866767823dSmvs static void *
thr_dispose(void * arg)187*d0f29908Smvs thr_dispose(void *arg)
188*d0f29908Smvs {
189*d0f29908Smvs 	uint8_t buf[sizeof(union msg_control)];
190*d0f29908Smvs 	int *s = ((struct thr_pass_arg *)arg)->s;
191*d0f29908Smvs 
192*d0f29908Smvs 	while (1) {
193*d0f29908Smvs 		if (read(s[1], buf, sizeof(buf)) < 0)
194*d0f29908Smvs 			therr(1, "read");
195*d0f29908Smvs 	}
196*d0f29908Smvs 
197*d0f29908Smvs 	return NULL;
198*d0f29908Smvs }
199*d0f29908Smvs 
200*d0f29908Smvs static void *
thr_gc(void * arg)2016767823dSmvs thr_gc(void *arg)
2026767823dSmvs {
2036767823dSmvs 	union msg_control msg_control;
2046767823dSmvs 	int iov_buf;
2056767823dSmvs 	struct iovec iov;
2066767823dSmvs 	struct msghdr msgh;
2076767823dSmvs 	struct cmsghdr *cmsgh;
2086767823dSmvs 	int s[2], passfd = ((struct thr_gc_arg *)arg)->passfd;
2096767823dSmvs 
2106767823dSmvs 	while (1) {
2116767823dSmvs 		if (socketpair(AF_UNIX, SOCK_DGRAM, 0, s) < 0)
2126767823dSmvs 			therr(1, "socketpair");
2136767823dSmvs 
2146767823dSmvs 		iov_buf = 0;
2156767823dSmvs 		iov.iov_base = &iov_buf;
2166767823dSmvs 		iov.iov_len = sizeof(iov_buf);
2176767823dSmvs 		msgh.msg_control = msg_control.control;
2186767823dSmvs 		msgh.msg_controllen = sizeof(msg_control.control);
2196767823dSmvs 		msgh.msg_iov = &iov;
2206767823dSmvs 		msgh.msg_iovlen = 1;
2216767823dSmvs 		msgh.msg_name = NULL;
2226767823dSmvs 		msgh.msg_namelen = 0;
2236767823dSmvs 		cmsgh = CMSG_FIRSTHDR(&msgh);
2246767823dSmvs 		cmsgh->cmsg_len = CMSG_LEN(sizeof(int) * PASSFD_NUM);
2256767823dSmvs 		cmsgh->cmsg_level = SOL_SOCKET;
2266767823dSmvs 		cmsgh->cmsg_type = SCM_RIGHTS;
2276767823dSmvs 		*((int *)CMSG_DATA(cmsgh) + 0) = s[0];
2286767823dSmvs 		*((int *)CMSG_DATA(cmsgh) + 1) = s[1];
2296767823dSmvs 		*((int *)CMSG_DATA(cmsgh) + 2) = passfd;
2306767823dSmvs 		*((int *)CMSG_DATA(cmsgh) + 3) = passfd;
2316767823dSmvs 
2326767823dSmvs 		if (sendmsg(s[0], &msgh, 0) < 0) {
2336767823dSmvs 			switch (errno) {
2346767823dSmvs 			case EMFILE:
2356767823dSmvs 			case ENOBUFS:
2366767823dSmvs 				break;
2376767823dSmvs 			default:
2386767823dSmvs 				therr(1, "sendmsg");
2396767823dSmvs 			}
2406767823dSmvs 		}
2416767823dSmvs 
2426767823dSmvs 		close(s[0]);
2436767823dSmvs 		close(s[1]);
2446767823dSmvs 	}
2456767823dSmvs 
2466767823dSmvs 	return NULL;
2476767823dSmvs }
2486767823dSmvs 
2496767823dSmvs int
main(int argc,char * argv[])2506767823dSmvs main(int argc, char *argv[])
2516767823dSmvs {
2526767823dSmvs 	struct timespec testtime = {
2536767823dSmvs 		.tv_sec = 60,
2546767823dSmvs 		.tv_nsec = 0,
2556767823dSmvs 	};
2566767823dSmvs 
2576767823dSmvs 	int mib[2], ncpu;
2586767823dSmvs 	size_t len;
2596767823dSmvs 
2606767823dSmvs 	pthread_t thr;
2616767823dSmvs 	int i, error;
2626767823dSmvs 
2636767823dSmvs 	if (argc == 2 && !strcmp(argv[1], "--infinite"))
2646767823dSmvs 		testtime.tv_sec = (10 * 365 * 86400);
2656767823dSmvs 
2666767823dSmvs 	mib[0] = CTL_HW;
2676767823dSmvs 	mib[1] = HW_NCPUONLINE;
2686767823dSmvs 	len = sizeof(ncpu);
2696767823dSmvs 
2706767823dSmvs 	if (sysctl(mib, 2, &ncpu, &len, NULL, 0) < 0)
2716767823dSmvs 		err(1, "sysctl");
2726767823dSmvs 	if (ncpu <= 0)
2736767823dSmvs 		errx(1, "Wrong number of CPUs online: %d", ncpu);
2746767823dSmvs 
2756767823dSmvs 	if (!(thr_pass_args = calloc(ncpu, sizeof(*thr_pass_args))))
2766767823dSmvs 		err(1, "malloc");
2776767823dSmvs 	if (!(thr_gc_arg = malloc(sizeof(*thr_gc_arg))))
2786767823dSmvs 		err(1, "malloc");
2796767823dSmvs 
2806767823dSmvs 	for (i = 0; i < ncpu; ++i) {
2816767823dSmvs 		if (socketpair(AF_UNIX, SOCK_DGRAM, 0, thr_pass_args[i].s) < 0)
2826767823dSmvs 			err(1, "socketpair");
2836767823dSmvs 		thr_pass_args[i].passfd = thr_pass_args[i].s[0];
2846767823dSmvs 	}
2856767823dSmvs 
28632e9891fSmvs 	thr_gc_arg->passfd = thr_pass_args[0].s[0];
2876767823dSmvs 
2886767823dSmvs 	for (i = 0; i < ncpu; ++i) {
2896767823dSmvs 		error = pthread_create(&thr, NULL,
2906767823dSmvs 		    thr_send, &thr_pass_args[i]);
2916767823dSmvs 		if (error)
2926767823dSmvs 			therrc(1, error, "pthread_create");
2936767823dSmvs 		error = pthread_create(&thr, NULL,
2946767823dSmvs 		    thr_recv, &thr_pass_args[i]);
2956767823dSmvs 		if (error)
2966767823dSmvs 			therrc(1, error, "pthread_create");
297*d0f29908Smvs 		error = pthread_create(&thr, NULL,
298*d0f29908Smvs 		    thr_dispose, &thr_pass_args[i]);
299*d0f29908Smvs 		if (error)
300*d0f29908Smvs 			therrc(1, error, "pthread_create");
3016767823dSmvs 	}
3026767823dSmvs 
3036767823dSmvs 	if ((error = pthread_create(&thr, NULL, thr_gc, thr_gc_arg)))
3046767823dSmvs 		therrc(1, error, "pthread_create");
3056767823dSmvs 
3066767823dSmvs 	nanosleep(&testtime, NULL);
3076767823dSmvs 
3086767823dSmvs 	return 0;
3096767823dSmvs }
310