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 15 FIXTURE(scm_rights) 16 { 17 int fd[32]; 18 }; 19 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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