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