Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
15FIXTURE(scm_rights)
16{
17 int fd[32];
18};
19
20FIXTURE_VARIANT(scm_rights)
21{
22 char name[32];
23 int type;
24 int flags;
25 bool test_listener;
26 bool disabled;
27};
28
29FIXTURE_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
38FIXTURE_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
47FIXTURE_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
56FIXTURE_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
65FIXTURE_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
74FIXTURE_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
83FIXTURE_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
92FIXTURE_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
101FIXTURE_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
110FIXTURE_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
119static 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
152FIXTURE_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
166FIXTURE_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
179static 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
219static 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
240static 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
253static 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
267void __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
319TEST_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
330TEST_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
345TEST_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
364TEST_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
383TEST_HARNESS_MAIN