1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright Amazon.com Inc. or its affiliates. */
3 #define _GNU_SOURCE
4 #include <sched.h>
5
6 #include <stdio.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <sys/un.h>
12
13 #include "../../kselftest_harness.h"
14
FIXTURE(scm_rights)15 FIXTURE(scm_rights)
16 {
17 int fd[32];
18 };
19
FIXTURE_VARIANT(scm_rights)20 FIXTURE_VARIANT(scm_rights)
21 {
22 char name[32];
23 int type;
24 int flags;
25 bool test_listener;
26 };
27
FIXTURE_VARIANT_ADD(scm_rights,dgram)28 FIXTURE_VARIANT_ADD(scm_rights, dgram)
29 {
30 .name = "UNIX ",
31 .type = SOCK_DGRAM,
32 .flags = 0,
33 .test_listener = false,
34 };
35
FIXTURE_VARIANT_ADD(scm_rights,stream)36 FIXTURE_VARIANT_ADD(scm_rights, stream)
37 {
38 .name = "UNIX-STREAM ",
39 .type = SOCK_STREAM,
40 .flags = 0,
41 .test_listener = false,
42 };
43
FIXTURE_VARIANT_ADD(scm_rights,stream_oob)44 FIXTURE_VARIANT_ADD(scm_rights, stream_oob)
45 {
46 .name = "UNIX-STREAM ",
47 .type = SOCK_STREAM,
48 .flags = MSG_OOB,
49 .test_listener = false,
50 };
51
FIXTURE_VARIANT_ADD(scm_rights,stream_listener)52 FIXTURE_VARIANT_ADD(scm_rights, stream_listener)
53 {
54 .name = "UNIX-STREAM ",
55 .type = SOCK_STREAM,
56 .flags = 0,
57 .test_listener = true,
58 };
59
FIXTURE_VARIANT_ADD(scm_rights,stream_listener_oob)60 FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob)
61 {
62 .name = "UNIX-STREAM ",
63 .type = SOCK_STREAM,
64 .flags = MSG_OOB,
65 .test_listener = true,
66 };
67
count_sockets(struct __test_metadata * _metadata,const FIXTURE_VARIANT (scm_rights)* variant)68 static int count_sockets(struct __test_metadata *_metadata,
69 const FIXTURE_VARIANT(scm_rights) *variant)
70 {
71 int sockets = -1, len, ret;
72 char *line = NULL;
73 size_t unused;
74 FILE *f;
75
76 f = fopen("/proc/net/protocols", "r");
77 ASSERT_NE(NULL, f);
78
79 len = strlen(variant->name);
80
81 while (getline(&line, &unused, f) != -1) {
82 int unused2;
83
84 if (strncmp(line, variant->name, len))
85 continue;
86
87 ret = sscanf(line + len, "%d %d", &unused2, &sockets);
88 ASSERT_EQ(2, ret);
89
90 break;
91 }
92
93 free(line);
94
95 ret = fclose(f);
96 ASSERT_EQ(0, ret);
97
98 return sockets;
99 }
100
FIXTURE_SETUP(scm_rights)101 FIXTURE_SETUP(scm_rights)
102 {
103 int ret;
104
105 ret = unshare(CLONE_NEWNET);
106 ASSERT_EQ(0, ret);
107
108 ret = count_sockets(_metadata, variant);
109 ASSERT_EQ(0, ret);
110 }
111
FIXTURE_TEARDOWN(scm_rights)112 FIXTURE_TEARDOWN(scm_rights)
113 {
114 int ret;
115
116 sleep(1);
117
118 ret = count_sockets(_metadata, variant);
119 ASSERT_EQ(0, ret);
120 }
121
create_listeners(struct __test_metadata * _metadata,FIXTURE_DATA (scm_rights)* self,int n)122 static void create_listeners(struct __test_metadata *_metadata,
123 FIXTURE_DATA(scm_rights) *self,
124 int n)
125 {
126 struct sockaddr_un addr = {
127 .sun_family = AF_UNIX,
128 };
129 socklen_t addrlen;
130 int i, ret;
131
132 for (i = 0; i < n * 2; i += 2) {
133 self->fd[i] = socket(AF_UNIX, SOCK_STREAM, 0);
134 ASSERT_LE(0, self->fd[i]);
135
136 addrlen = sizeof(addr.sun_family);
137 ret = bind(self->fd[i], (struct sockaddr *)&addr, addrlen);
138 ASSERT_EQ(0, ret);
139
140 ret = listen(self->fd[i], -1);
141 ASSERT_EQ(0, ret);
142
143 addrlen = sizeof(addr);
144 ret = getsockname(self->fd[i], (struct sockaddr *)&addr, &addrlen);
145 ASSERT_EQ(0, ret);
146
147 self->fd[i + 1] = socket(AF_UNIX, SOCK_STREAM, 0);
148 ASSERT_LE(0, self->fd[i + 1]);
149
150 ret = connect(self->fd[i + 1], (struct sockaddr *)&addr, addrlen);
151 ASSERT_EQ(0, ret);
152 }
153 }
154
create_socketpairs(struct __test_metadata * _metadata,FIXTURE_DATA (scm_rights)* self,const FIXTURE_VARIANT (scm_rights)* variant,int n)155 static void create_socketpairs(struct __test_metadata *_metadata,
156 FIXTURE_DATA(scm_rights) *self,
157 const FIXTURE_VARIANT(scm_rights) *variant,
158 int n)
159 {
160 int i, ret;
161
162 ASSERT_GE(sizeof(self->fd) / sizeof(int), n);
163
164 for (i = 0; i < n * 2; i += 2) {
165 ret = socketpair(AF_UNIX, variant->type, 0, self->fd + i);
166 ASSERT_EQ(0, ret);
167 }
168 }
169
__create_sockets(struct __test_metadata * _metadata,FIXTURE_DATA (scm_rights)* self,const FIXTURE_VARIANT (scm_rights)* variant,int n)170 static void __create_sockets(struct __test_metadata *_metadata,
171 FIXTURE_DATA(scm_rights) *self,
172 const FIXTURE_VARIANT(scm_rights) *variant,
173 int n)
174 {
175 ASSERT_LE(n * 2, sizeof(self->fd) / sizeof(self->fd[0]));
176
177 if (variant->test_listener)
178 create_listeners(_metadata, self, n);
179 else
180 create_socketpairs(_metadata, self, variant, n);
181 }
182
__close_sockets(struct __test_metadata * _metadata,FIXTURE_DATA (scm_rights)* self,int n)183 static void __close_sockets(struct __test_metadata *_metadata,
184 FIXTURE_DATA(scm_rights) *self,
185 int n)
186 {
187 int i, ret;
188
189 ASSERT_GE(sizeof(self->fd) / sizeof(int), n);
190
191 for (i = 0; i < n * 2; i++) {
192 ret = close(self->fd[i]);
193 ASSERT_EQ(0, ret);
194 }
195 }
196
__send_fd(struct __test_metadata * _metadata,const FIXTURE_DATA (scm_rights)* self,const FIXTURE_VARIANT (scm_rights)* variant,int inflight,int receiver)197 void __send_fd(struct __test_metadata *_metadata,
198 const FIXTURE_DATA(scm_rights) *self,
199 const FIXTURE_VARIANT(scm_rights) *variant,
200 int inflight, int receiver)
201 {
202 #define MSG "x"
203 #define MSGLEN 1
204 struct {
205 struct cmsghdr cmsghdr;
206 int fd[2];
207 } cmsg = {
208 .cmsghdr = {
209 .cmsg_len = CMSG_LEN(sizeof(cmsg.fd)),
210 .cmsg_level = SOL_SOCKET,
211 .cmsg_type = SCM_RIGHTS,
212 },
213 .fd = {
214 self->fd[inflight * 2],
215 self->fd[inflight * 2],
216 },
217 };
218 struct iovec iov = {
219 .iov_base = MSG,
220 .iov_len = MSGLEN,
221 };
222 struct msghdr msg = {
223 .msg_name = NULL,
224 .msg_namelen = 0,
225 .msg_iov = &iov,
226 .msg_iovlen = 1,
227 .msg_control = &cmsg,
228 .msg_controllen = CMSG_SPACE(sizeof(cmsg.fd)),
229 };
230 int ret;
231
232 ret = sendmsg(self->fd[receiver * 2 + 1], &msg, variant->flags);
233 ASSERT_EQ(MSGLEN, ret);
234 }
235
236 #define create_sockets(n) \
237 __create_sockets(_metadata, self, variant, n)
238 #define close_sockets(n) \
239 __close_sockets(_metadata, self, n)
240 #define send_fd(inflight, receiver) \
241 __send_fd(_metadata, self, variant, inflight, receiver)
242
TEST_F(scm_rights,self_ref)243 TEST_F(scm_rights, self_ref)
244 {
245 create_sockets(2);
246
247 send_fd(0, 0);
248
249 send_fd(1, 1);
250
251 close_sockets(2);
252 }
253
TEST_F(scm_rights,triangle)254 TEST_F(scm_rights, triangle)
255 {
256 create_sockets(6);
257
258 send_fd(0, 1);
259 send_fd(1, 2);
260 send_fd(2, 0);
261
262 send_fd(3, 4);
263 send_fd(4, 5);
264 send_fd(5, 3);
265
266 close_sockets(6);
267 }
268
TEST_F(scm_rights,cross_edge)269 TEST_F(scm_rights, cross_edge)
270 {
271 create_sockets(8);
272
273 send_fd(0, 1);
274 send_fd(1, 2);
275 send_fd(2, 0);
276 send_fd(1, 3);
277 send_fd(3, 2);
278
279 send_fd(4, 5);
280 send_fd(5, 6);
281 send_fd(6, 4);
282 send_fd(5, 7);
283 send_fd(7, 6);
284
285 close_sockets(8);
286 }
287
TEST_F(scm_rights,backtrack_from_scc)288 TEST_F(scm_rights, backtrack_from_scc)
289 {
290 create_sockets(10);
291
292 send_fd(0, 1);
293 send_fd(0, 4);
294 send_fd(1, 2);
295 send_fd(2, 3);
296 send_fd(3, 1);
297
298 send_fd(5, 6);
299 send_fd(5, 9);
300 send_fd(6, 7);
301 send_fd(7, 8);
302 send_fd(8, 6);
303
304 close_sockets(10);
305 }
306
307 TEST_HARNESS_MAIN
308