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