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 bool disabled;
27 };
28
FIXTURE_VARIANT_ADD(scm_rights,dgram)29 FIXTURE_VARIANT_ADD(scm_rights, dgram)
30 {
31 .name = "UNIX ",
32 .type = SOCK_DGRAM,
33 .flags = 0,
34 .test_listener = false,
35 .disabled = false,
36 };
37
FIXTURE_VARIANT_ADD(scm_rights,dgram_disabled)38 FIXTURE_VARIANT_ADD(scm_rights, dgram_disabled)
39 {
40 .name = "UNIX ",
41 .type = SOCK_DGRAM,
42 .flags = 0,
43 .test_listener = false,
44 .disabled = true,
45 };
46
FIXTURE_VARIANT_ADD(scm_rights,stream)47 FIXTURE_VARIANT_ADD(scm_rights, stream)
48 {
49 .name = "UNIX-STREAM ",
50 .type = SOCK_STREAM,
51 .flags = 0,
52 .test_listener = false,
53 .disabled = false,
54 };
55
FIXTURE_VARIANT_ADD(scm_rights,stream_disabled)56 FIXTURE_VARIANT_ADD(scm_rights, stream_disabled)
57 {
58 .name = "UNIX-STREAM ",
59 .type = SOCK_STREAM,
60 .flags = 0,
61 .test_listener = false,
62 .disabled = true,
63 };
64
FIXTURE_VARIANT_ADD(scm_rights,stream_oob)65 FIXTURE_VARIANT_ADD(scm_rights, stream_oob)
66 {
67 .name = "UNIX-STREAM ",
68 .type = SOCK_STREAM,
69 .flags = MSG_OOB,
70 .test_listener = false,
71 .disabled = false,
72 };
73
FIXTURE_VARIANT_ADD(scm_rights,stream_oob_disabled)74 FIXTURE_VARIANT_ADD(scm_rights, stream_oob_disabled)
75 {
76 .name = "UNIX-STREAM ",
77 .type = SOCK_STREAM,
78 .flags = MSG_OOB,
79 .test_listener = false,
80 .disabled = true,
81 };
82
FIXTURE_VARIANT_ADD(scm_rights,stream_listener)83 FIXTURE_VARIANT_ADD(scm_rights, stream_listener)
84 {
85 .name = "UNIX-STREAM ",
86 .type = SOCK_STREAM,
87 .flags = 0,
88 .test_listener = true,
89 .disabled = false,
90 };
91
FIXTURE_VARIANT_ADD(scm_rights,stream_listener_disabled)92 FIXTURE_VARIANT_ADD(scm_rights, stream_listener_disabled)
93 {
94 .name = "UNIX-STREAM ",
95 .type = SOCK_STREAM,
96 .flags = 0,
97 .test_listener = true,
98 .disabled = true,
99 };
100
FIXTURE_VARIANT_ADD(scm_rights,stream_listener_oob)101 FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob)
102 {
103 .name = "UNIX-STREAM ",
104 .type = SOCK_STREAM,
105 .flags = MSG_OOB,
106 .test_listener = true,
107 .disabled = false,
108 };
109
FIXTURE_VARIANT_ADD(scm_rights,stream_listener_oob_disabled)110 FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob_disabled)
111 {
112 .name = "UNIX-STREAM ",
113 .type = SOCK_STREAM,
114 .flags = MSG_OOB,
115 .test_listener = true,
116 .disabled = true,
117 };
118
count_sockets(struct __test_metadata * _metadata,const FIXTURE_VARIANT (scm_rights)* variant)119 static int count_sockets(struct __test_metadata *_metadata,
120 const FIXTURE_VARIANT(scm_rights) *variant)
121 {
122 int sockets = -1, len, ret;
123 char *line = NULL;
124 size_t unused;
125 FILE *f;
126
127 f = fopen("/proc/net/protocols", "r");
128 ASSERT_NE(NULL, f);
129
130 len = strlen(variant->name);
131
132 while (getline(&line, &unused, f) != -1) {
133 int unused2;
134
135 if (strncmp(line, variant->name, len))
136 continue;
137
138 ret = sscanf(line + len, "%d %d", &unused2, &sockets);
139 ASSERT_EQ(2, ret);
140
141 break;
142 }
143
144 free(line);
145
146 ret = fclose(f);
147 ASSERT_EQ(0, ret);
148
149 return sockets;
150 }
151
FIXTURE_SETUP(scm_rights)152 FIXTURE_SETUP(scm_rights)
153 {
154 int ret;
155
156 ret = unshare(CLONE_NEWNET);
157 ASSERT_EQ(0, ret);
158
159 if (variant->disabled)
160 return;
161
162 ret = count_sockets(_metadata, variant);
163 ASSERT_EQ(0, ret);
164 }
165
FIXTURE_TEARDOWN(scm_rights)166 FIXTURE_TEARDOWN(scm_rights)
167 {
168 int ret;
169
170 if (variant->disabled)
171 return;
172
173 sleep(1);
174
175 ret = count_sockets(_metadata, variant);
176 ASSERT_EQ(0, ret);
177 }
178
create_listeners(struct __test_metadata * _metadata,FIXTURE_DATA (scm_rights)* self,const FIXTURE_VARIANT (scm_rights)* variant,int n)179 static void create_listeners(struct __test_metadata *_metadata,
180 FIXTURE_DATA(scm_rights) *self,
181 const FIXTURE_VARIANT(scm_rights) *variant,
182 int n)
183 {
184 struct sockaddr_un addr = {
185 .sun_family = AF_UNIX,
186 };
187 socklen_t addrlen;
188 int i, ret;
189
190 for (i = 0; i < n * 2; i += 2) {
191 self->fd[i] = socket(AF_UNIX, SOCK_STREAM, 0);
192 ASSERT_LE(0, self->fd[i]);
193
194 addrlen = sizeof(addr.sun_family);
195 ret = bind(self->fd[i], (struct sockaddr *)&addr, addrlen);
196 ASSERT_EQ(0, ret);
197
198 ret = listen(self->fd[i], -1);
199 ASSERT_EQ(0, ret);
200
201 if (variant->disabled) {
202 ret = setsockopt(self->fd[i], SOL_SOCKET, SO_PASSRIGHTS,
203 &(int){0}, sizeof(int));
204 ASSERT_EQ(0, ret);
205 }
206
207 addrlen = sizeof(addr);
208 ret = getsockname(self->fd[i], (struct sockaddr *)&addr, &addrlen);
209 ASSERT_EQ(0, ret);
210
211 self->fd[i + 1] = socket(AF_UNIX, SOCK_STREAM, 0);
212 ASSERT_LE(0, self->fd[i + 1]);
213
214 ret = connect(self->fd[i + 1], (struct sockaddr *)&addr, addrlen);
215 ASSERT_EQ(0, ret);
216 }
217 }
218
create_socketpairs(struct __test_metadata * _metadata,FIXTURE_DATA (scm_rights)* self,const FIXTURE_VARIANT (scm_rights)* variant,int n)219 static void create_socketpairs(struct __test_metadata *_metadata,
220 FIXTURE_DATA(scm_rights) *self,
221 const FIXTURE_VARIANT(scm_rights) *variant,
222 int n)
223 {
224 int i, ret;
225
226 ASSERT_GE(sizeof(self->fd) / sizeof(int), n);
227
228 for (i = 0; i < n * 2; i += 2) {
229 ret = socketpair(AF_UNIX, variant->type, 0, self->fd + i);
230 ASSERT_EQ(0, ret);
231
232 if (variant->disabled) {
233 ret = setsockopt(self->fd[i], SOL_SOCKET, SO_PASSRIGHTS,
234 &(int){0}, sizeof(int));
235 ASSERT_EQ(0, ret);
236 }
237 }
238 }
239
__create_sockets(struct __test_metadata * _metadata,FIXTURE_DATA (scm_rights)* self,const FIXTURE_VARIANT (scm_rights)* variant,int n)240 static void __create_sockets(struct __test_metadata *_metadata,
241 FIXTURE_DATA(scm_rights) *self,
242 const FIXTURE_VARIANT(scm_rights) *variant,
243 int n)
244 {
245 ASSERT_LE(n * 2, sizeof(self->fd) / sizeof(self->fd[0]));
246
247 if (variant->test_listener)
248 create_listeners(_metadata, self, variant, n);
249 else
250 create_socketpairs(_metadata, self, variant, n);
251 }
252
__close_sockets(struct __test_metadata * _metadata,FIXTURE_DATA (scm_rights)* self,int n)253 static void __close_sockets(struct __test_metadata *_metadata,
254 FIXTURE_DATA(scm_rights) *self,
255 int n)
256 {
257 int i, ret;
258
259 ASSERT_GE(sizeof(self->fd) / sizeof(int), n);
260
261 for (i = 0; i < n * 2; i++) {
262 ret = close(self->fd[i]);
263 ASSERT_EQ(0, ret);
264 }
265 }
266
__send_fd(struct __test_metadata * _metadata,const FIXTURE_DATA (scm_rights)* self,const FIXTURE_VARIANT (scm_rights)* variant,int inflight,int receiver)267 void __send_fd(struct __test_metadata *_metadata,
268 const FIXTURE_DATA(scm_rights) *self,
269 const FIXTURE_VARIANT(scm_rights) *variant,
270 int inflight, int receiver)
271 {
272 #define MSG "x"
273 #define MSGLEN 1
274 struct {
275 struct cmsghdr cmsghdr;
276 int fd[2];
277 } cmsg = {
278 .cmsghdr = {
279 .cmsg_len = CMSG_LEN(sizeof(cmsg.fd)),
280 .cmsg_level = SOL_SOCKET,
281 .cmsg_type = SCM_RIGHTS,
282 },
283 .fd = {
284 self->fd[inflight * 2],
285 self->fd[inflight * 2],
286 },
287 };
288 struct iovec iov = {
289 .iov_base = MSG,
290 .iov_len = MSGLEN,
291 };
292 struct msghdr msg = {
293 .msg_name = NULL,
294 .msg_namelen = 0,
295 .msg_iov = &iov,
296 .msg_iovlen = 1,
297 .msg_control = &cmsg,
298 .msg_controllen = CMSG_SPACE(sizeof(cmsg.fd)),
299 };
300 int ret;
301
302 ret = sendmsg(self->fd[receiver * 2 + 1], &msg, variant->flags);
303
304 if (variant->disabled) {
305 ASSERT_EQ(-1, ret);
306 ASSERT_EQ(-EPERM, -errno);
307 } else {
308 ASSERT_EQ(MSGLEN, ret);
309 }
310 }
311
312 #define create_sockets(n) \
313 __create_sockets(_metadata, self, variant, n)
314 #define close_sockets(n) \
315 __close_sockets(_metadata, self, n)
316 #define send_fd(inflight, receiver) \
317 __send_fd(_metadata, self, variant, inflight, receiver)
318
TEST_F(scm_rights,self_ref)319 TEST_F(scm_rights, self_ref)
320 {
321 create_sockets(2);
322
323 send_fd(0, 0);
324
325 send_fd(1, 1);
326
327 close_sockets(2);
328 }
329
TEST_F(scm_rights,triangle)330 TEST_F(scm_rights, triangle)
331 {
332 create_sockets(6);
333
334 send_fd(0, 1);
335 send_fd(1, 2);
336 send_fd(2, 0);
337
338 send_fd(3, 4);
339 send_fd(4, 5);
340 send_fd(5, 3);
341
342 close_sockets(6);
343 }
344
TEST_F(scm_rights,cross_edge)345 TEST_F(scm_rights, cross_edge)
346 {
347 create_sockets(8);
348
349 send_fd(0, 1);
350 send_fd(1, 2);
351 send_fd(2, 0);
352 send_fd(1, 3);
353 send_fd(3, 2);
354
355 send_fd(4, 5);
356 send_fd(5, 6);
357 send_fd(6, 4);
358 send_fd(5, 7);
359 send_fd(7, 6);
360
361 close_sockets(8);
362 }
363
TEST_F(scm_rights,backtrack_from_scc)364 TEST_F(scm_rights, backtrack_from_scc)
365 {
366 create_sockets(10);
367
368 send_fd(0, 1);
369 send_fd(0, 4);
370 send_fd(1, 2);
371 send_fd(2, 3);
372 send_fd(3, 1);
373
374 send_fd(5, 6);
375 send_fd(5, 9);
376 send_fd(6, 7);
377 send_fd(7, 8);
378 send_fd(8, 6);
379
380 close_sockets(10);
381 }
382
383 TEST_HARNESS_MAIN
384