308 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright Amazon.com Inc. or its affiliates. */3#define _GNU_SOURCE4#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};27 28FIXTURE_VARIANT_ADD(scm_rights, dgram)29{30 .name = "UNIX ",31 .type = SOCK_DGRAM,32 .flags = 0,33 .test_listener = false,34};35 36FIXTURE_VARIANT_ADD(scm_rights, stream)37{38 .name = "UNIX-STREAM ",39 .type = SOCK_STREAM,40 .flags = 0,41 .test_listener = false,42};43 44FIXTURE_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 52FIXTURE_VARIANT_ADD(scm_rights, stream_listener)53{54 .name = "UNIX-STREAM ",55 .type = SOCK_STREAM,56 .flags = 0,57 .test_listener = true,58};59 60FIXTURE_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 68static 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 101FIXTURE_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 112FIXTURE_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 122static 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 155static 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 170static 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 else180 create_socketpairs(_metadata, self, variant, n);181}182 183static 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 197void __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 1204 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 243TEST_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 254TEST_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 269TEST_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 288TEST_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 307TEST_HARNESS_MAIN308