xref: /openbsd-src/regress/sys/kern/unixsockets/ungc.c (revision 6e6f23069b3cf1763997924bddd8a829f975f1af)
1 /* $OpenBSD: ungc.c,v 1.4 2022/02/03 17:22:01 bluhm 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 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/time.h>
22 #include <sys/un.h>
23 #include <stdio.h>
24 #include <err.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <string.h>
28 #include <time.h>
29 #include <unistd.h>
30 
31 union msg_control{
32 	struct cmsghdr cmsgh;
33 	char control[CMSG_SPACE(sizeof(int) * 2)];
34 };
35 
main(int argc,char * argv[])36 int main(int argc, char *argv[])
37 {
38 	struct timespec ts_start, ts_now, ts_time;
39 	union msg_control msg_control;
40 	int iov_buf;
41 	struct iovec iov;
42 	struct msghdr msgh;
43 	struct cmsghdr *cmsgh;
44 	int sp[2], sl[2], ts;
45 	int infinite = 0;
46 
47 	if (argc > 1 && !strcmp(argv[1], "--infinite"))
48 		infinite = 1;
49 
50 	if (!infinite)
51 		if (clock_gettime(CLOCK_BOOTTIME, &ts_start) <0)
52 			err(1, "clock_gettime");
53 
54 	while (1) {
55 		if (socketpair(AF_UNIX, SOCK_STREAM|O_NONBLOCK, 0, sp) < 0)
56 			err(1, "socketpair");
57 
58 		iov_buf = 0;
59 		iov.iov_base = &iov_buf;
60 		iov.iov_len = sizeof(iov_buf);
61 		msgh.msg_control = msg_control.control;
62 		msgh.msg_controllen = CMSG_SPACE(sizeof(int));
63 		msgh.msg_iov = &iov;
64 		msgh.msg_iovlen = 1;
65 		msgh.msg_name = NULL;
66 		msgh.msg_namelen = 0;
67 		cmsgh = CMSG_FIRSTHDR(&msgh);
68 		cmsgh->cmsg_len = CMSG_LEN(sizeof(int));
69 		cmsgh->cmsg_level = SOL_SOCKET;
70 		cmsgh->cmsg_type = SCM_RIGHTS;
71 
72 		*((int *)CMSG_DATA(cmsgh)) = sp[0];
73 
74 		if (sendmsg(sp[0], &msgh, 0) < 0) {
75 			if (errno == EMFILE) {
76 				/* Too may sockets in flight */
77 				close(sp[0]);
78 				goto skip;
79 			}
80 
81 			err(1, "sendmsg sp0");
82 		}
83 
84 		*((int *)CMSG_DATA(cmsgh)) = sp[1];
85 
86 		if (sendmsg(sp[1], &msgh, 0) < 0) {
87 			if (errno == EMFILE) {
88 				/* Too may sockets in flight */
89 				close(sp[0]);
90 				goto skip;
91 			}
92 
93 			err(1, "sendmsg sp1");
94 		}
95 
96 		/*
97 		 * After following close(2), the sp[0] socket has
98 		 * f_count equal to unp_msgcount. This socket is not
99 		 * in the loop and should not be killed by unp_gc().
100 		 * This sockets should be successfully received.
101 		 * The sp[1] socket is stored within sp[0] receive
102 		 * buffer. This socket should be also successfully
103 		 * received.
104 		 */
105 
106 		close(sp[0]);
107 
108 		if (socketpair(AF_UNIX, SOCK_STREAM|O_NONBLOCK, 0, sl) < 0)
109 			err(1, "socketpair");
110 
111 		iov_buf = 0;
112 		iov.iov_base = &iov_buf;
113 		iov.iov_len = sizeof(iov_buf);
114 		msgh.msg_control = msg_control.control;
115 		msgh.msg_controllen = CMSG_SPACE(sizeof(int) * 2);
116 		msgh.msg_iov = &iov;
117 		msgh.msg_iovlen = 1;
118 		msgh.msg_name = NULL;
119 		msgh.msg_namelen = 0;
120 		cmsgh = CMSG_FIRSTHDR(&msgh);
121 		cmsgh->cmsg_len = CMSG_LEN(sizeof(int) * 2);
122 		cmsgh->cmsg_level = SOL_SOCKET;
123 		cmsgh->cmsg_type = SCM_RIGHTS;
124 
125 		*((int *)CMSG_DATA(cmsgh) + 0) = sl[0];
126 		*((int *)CMSG_DATA(cmsgh) + 1) = sl[1];
127 
128 		if (sendmsg(sl[0], &msgh, 0) < 0) {
129 			if (errno != EMFILE)
130 				err(1, "sendmsg sl0");
131 		}
132 
133 		/*
134 		 * After following close(2), the sl[0] socket is not
135 		 * in the loop and should be disposed by sorflush().
136 		 * The sl[1] socket is in the loop and should be
137 		 * killed by unp_gc().
138 		 */
139 
140 		close(sl[0]);
141 		close(sl[1]);
142 
143 		if (recvmsg(sp[1], &msgh, 0) < 0) {
144 			if (errno == EMSGSIZE)
145 				goto skip;
146 			err(1, "recvmsg sp1");
147 		}
148 
149 		if (!(cmsgh = CMSG_FIRSTHDR(&msgh)))
150 			errx(1, "bad cmsg header");
151 		if (cmsgh->cmsg_level != SOL_SOCKET)
152 			errx(1, "bad cmsg level");
153 		if (cmsgh->cmsg_type != SCM_RIGHTS)
154 			errx(1, "bad cmsg type");
155 		if (cmsgh->cmsg_len != CMSG_LEN(sizeof(ts)))
156 			errx(1, "bad cmsg length");
157 
158 		ts = *((int *)CMSG_DATA(cmsgh));
159 
160 		if (recvmsg(ts, &msgh, 0) < 0) {
161 			if (errno == EMSGSIZE)
162 				goto skip;
163 			err(1, "recvmsg ts");
164 		}
165 
166 		close(ts);
167 
168 		if (!(cmsgh = CMSG_FIRSTHDR(&msgh)))
169 			errx(1, "bad cmsg header");
170 		if (cmsgh->cmsg_level != SOL_SOCKET)
171 			errx(1, "bad cmsg level");
172 		if (cmsgh->cmsg_type != SCM_RIGHTS)
173 			errx(1, "bad cmsg type");
174 		if (cmsgh->cmsg_len != CMSG_LEN(sizeof(ts)))
175 			errx(1, "bad cmsg length");
176 
177 		ts = *((int *)CMSG_DATA(cmsgh));
178 		close(ts);
179 
180 skip:
181 		close(sp[1]);
182 
183 		if (!infinite) {
184 			if (clock_gettime(CLOCK_BOOTTIME, &ts_now) <0)
185 				err(1, "clock_gettime");
186 
187 			timespecsub(&ts_now, &ts_start, &ts_time);
188 			if (ts_time.tv_sec >= 20)
189 				break;
190 		}
191 	}
192 
193 	return 0;
194 }
195