| 1 | |
| 2 | #define _GNU_SOURCE
|
| 3 | #include <errno.h>
|
| 4 | #include <fcntl.h>
|
| 5 | #include <limits.h>
|
| 6 | #include <sched.h>
|
| 7 | #include <stdio.h>
|
| 8 | #include <stdlib.h>
|
| 9 | #include <string.h>
|
| 10 | #include <linux/nsfs.h>
|
| 11 | #include <poll.h>
|
| 12 | #include <sys/mount.h>
|
| 13 | #include <sys/socket.h>
|
| 14 | #include <sys/stat.h>
|
| 15 | #include <sys/types.h>
|
| 16 | #include <sys/wait.h>
|
| 17 | #include <sys/syscall.h>
|
| 18 | #include <unistd.h>
|
| 19 | #include <pthread.h>
|
| 20 | #include "../kselftest_harness.h"
|
| 21 | #include "../filesystems/utils.h"
|
| 22 | #include "wrappers.h"
|
| 23 |
|
| 24 | #ifndef PIDFD_THREAD
|
| 25 | #define PIDFD_THREAD O_EXCL
|
| 26 | #endif
|
| 27 |
|
| 28 | #ifndef FD_NSFS_ROOT
|
| 29 | #define FD_NSFS_ROOT -10003
|
| 30 | #endif
|
| 31 |
|
| 32 | #ifndef FILEID_NSFS
|
| 33 | #define FILEID_NSFS 0xf1
|
| 34 | #endif
|
| 35 |
|
| 36 | |
| 37 | * Test that initial namespaces can be reopened via file handle.
|
| 38 | * Initial namespaces should have active ref count of 1 from boot.
|
| 39 |
|
| 40 | TEST(init_ns_always_active)
|
| 41 | {
|
| 42 | struct file_handle *handle;
|
| 43 | int mount_id;
|
| 44 | int ret;
|
| 45 | int fd1, fd2;
|
| 46 | struct stat st1, st2;
|
| 47 |
|
| 48 | handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
|
| 49 | ASSERT_NE(handle, NULL);
|
| 50 |
|
| 51 |
|
| 52 | fd1 = open("/proc/1/ns/net", O_RDONLY);
|
| 53 | ASSERT_GE(fd1, 0);
|
| 54 |
|
| 55 |
|
| 56 | handle->handle_bytes = MAX_HANDLE_SZ;
|
| 57 | ret = name_to_handle_at(fd1, "", handle, &mount_id, AT_EMPTY_PATH);
|
| 58 | if (ret < 0 && errno == EOPNOTSUPP) {
|
| 59 | SKIP(free(handle); close(fd1);
|
| 60 | return, "nsfs doesn't support file handles");
|
| 61 | }
|
| 62 | ASSERT_EQ(ret, 0);
|
| 63 |
|
| 64 |
|
| 65 | close(fd1);
|
| 66 |
|
| 67 |
|
| 68 | fd2 = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 69 | if (fd2 < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
|
| 70 | SKIP(free(handle);
|
| 71 | return, "open_by_handle_at with FD_NSFS_ROOT not supported");
|
| 72 | }
|
| 73 | ASSERT_GE(fd2, 0);
|
| 74 |
|
| 75 |
|
| 76 | fd1 = open("/proc/1/ns/net", O_RDONLY);
|
| 77 | ASSERT_GE(fd1, 0);
|
| 78 | ASSERT_EQ(fstat(fd1, &st1), 0);
|
| 79 | ASSERT_EQ(fstat(fd2, &st2), 0);
|
| 80 | ASSERT_EQ(st1.st_ino, st2.st_ino);
|
| 81 |
|
| 82 | close(fd1);
|
| 83 | close(fd2);
|
| 84 | free(handle);
|
| 85 | }
|
| 86 |
|
| 87 | |
| 88 | * Test namespace lifecycle: create a namespace in a child process,
|
| 89 | * get a file handle while it's active, then try to reopen after
|
| 90 | * the process exits (namespace becomes inactive).
|
| 91 |
|
| 92 | TEST(ns_inactive_after_exit)
|
| 93 | {
|
| 94 | struct file_handle *handle;
|
| 95 | int mount_id;
|
| 96 | int ret;
|
| 97 | int fd;
|
| 98 | int pipefd[2];
|
| 99 | pid_t pid;
|
| 100 | int status;
|
| 101 | char buf[sizeof(*handle) + MAX_HANDLE_SZ];
|
| 102 |
|
| 103 |
|
| 104 | ASSERT_EQ(pipe(pipefd), 0);
|
| 105 |
|
| 106 | pid = fork();
|
| 107 | ASSERT_GE(pid, 0);
|
| 108 |
|
| 109 | if (pid == 0) {
|
| 110 |
|
| 111 | close(pipefd[0]);
|
| 112 |
|
| 113 |
|
| 114 | ret = unshare(CLONE_NEWNET);
|
| 115 | if (ret < 0) {
|
| 116 | close(pipefd[1]);
|
| 117 | exit(1);
|
| 118 | }
|
| 119 |
|
| 120 |
|
| 121 | fd = open("/proc/self/ns/net", O_RDONLY);
|
| 122 | if (fd < 0) {
|
| 123 | close(pipefd[1]);
|
| 124 | exit(1);
|
| 125 | }
|
| 126 |
|
| 127 |
|
| 128 | handle = (struct file_handle *)buf;
|
| 129 | handle->handle_bytes = MAX_HANDLE_SZ;
|
| 130 | ret = name_to_handle_at(fd, "", handle, &mount_id, AT_EMPTY_PATH);
|
| 131 | close(fd);
|
| 132 |
|
| 133 | if (ret < 0) {
|
| 134 | close(pipefd[1]);
|
| 135 | exit(1);
|
| 136 | }
|
| 137 |
|
| 138 |
|
| 139 | write(pipefd[1], buf, sizeof(*handle) + handle->handle_bytes);
|
| 140 | close(pipefd[1]);
|
| 141 |
|
| 142 |
|
| 143 | exit(0);
|
| 144 | }
|
| 145 |
|
| 146 |
|
| 147 | close(pipefd[1]);
|
| 148 |
|
| 149 |
|
| 150 | ret = read(pipefd[0], buf, sizeof(buf));
|
| 151 | close(pipefd[0]);
|
| 152 |
|
| 153 |
|
| 154 | waitpid(pid, &status, 0);
|
| 155 | ASSERT_TRUE(WIFEXITED(status));
|
| 156 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 157 |
|
| 158 | ASSERT_GT(ret, 0);
|
| 159 | handle = (struct file_handle *)buf;
|
| 160 |
|
| 161 |
|
| 162 | fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 163 | ASSERT_LT(fd, 0);
|
| 164 |
|
| 165 | ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
|
| 166 | }
|
| 167 |
|
| 168 | |
| 169 | * Test that a namespace remains active while a process is using it,
|
| 170 | * even after the creating process exits.
|
| 171 |
|
| 172 | TEST(ns_active_with_multiple_processes)
|
| 173 | {
|
| 174 | struct file_handle *handle;
|
| 175 | int mount_id;
|
| 176 | int ret;
|
| 177 | int fd;
|
| 178 | int pipefd[2];
|
| 179 | int syncpipe[2];
|
| 180 | pid_t pid1, pid2;
|
| 181 | int status;
|
| 182 | char buf[sizeof(*handle) + MAX_HANDLE_SZ];
|
| 183 | char sync_byte;
|
| 184 |
|
| 185 |
|
| 186 | ASSERT_EQ(pipe(pipefd), 0);
|
| 187 | ASSERT_EQ(pipe(syncpipe), 0);
|
| 188 |
|
| 189 | pid1 = fork();
|
| 190 | ASSERT_GE(pid1, 0);
|
| 191 |
|
| 192 | if (pid1 == 0) {
|
| 193 |
|
| 194 | close(pipefd[0]);
|
| 195 | close(syncpipe[1]);
|
| 196 |
|
| 197 |
|
| 198 | ret = unshare(CLONE_NEWNET);
|
| 199 | if (ret < 0) {
|
| 200 | close(pipefd[1]);
|
| 201 | close(syncpipe[0]);
|
| 202 | exit(1);
|
| 203 | }
|
| 204 |
|
| 205 |
|
| 206 | fd = open("/proc/self/ns/net", O_RDONLY);
|
| 207 | if (fd < 0) {
|
| 208 | close(pipefd[1]);
|
| 209 | close(syncpipe[0]);
|
| 210 | exit(1);
|
| 211 | }
|
| 212 |
|
| 213 | handle = (struct file_handle *)buf;
|
| 214 | handle->handle_bytes = MAX_HANDLE_SZ;
|
| 215 | ret = name_to_handle_at(fd, "", handle, &mount_id, AT_EMPTY_PATH);
|
| 216 | close(fd);
|
| 217 |
|
| 218 | if (ret < 0) {
|
| 219 | close(pipefd[1]);
|
| 220 | close(syncpipe[0]);
|
| 221 | exit(1);
|
| 222 | }
|
| 223 |
|
| 224 |
|
| 225 | write(pipefd[1], buf, sizeof(*handle) + handle->handle_bytes);
|
| 226 | close(pipefd[1]);
|
| 227 |
|
| 228 |
|
| 229 | read(syncpipe[0], &sync_byte, 1);
|
| 230 | close(syncpipe[0]);
|
| 231 | exit(0);
|
| 232 | }
|
| 233 |
|
| 234 |
|
| 235 | close(pipefd[1]);
|
| 236 | ret = read(pipefd[0], buf, sizeof(buf));
|
| 237 | close(pipefd[0]);
|
| 238 | ASSERT_GT(ret, 0);
|
| 239 |
|
| 240 | handle = (struct file_handle *)buf;
|
| 241 |
|
| 242 |
|
| 243 | pid2 = fork();
|
| 244 | ASSERT_GE(pid2, 0);
|
| 245 |
|
| 246 | if (pid2 == 0) {
|
| 247 |
|
| 248 | close(syncpipe[0]);
|
| 249 | close(syncpipe[1]);
|
| 250 |
|
| 251 |
|
| 252 | fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 253 | if (fd < 0) {
|
| 254 | exit(1);
|
| 255 | }
|
| 256 |
|
| 257 |
|
| 258 | ret = setns(fd, CLONE_NEWNET);
|
| 259 | close(fd);
|
| 260 | if (ret < 0) {
|
| 261 | exit(1);
|
| 262 | }
|
| 263 |
|
| 264 |
|
| 265 | sleep(1);
|
| 266 | exit(0);
|
| 267 | }
|
| 268 |
|
| 269 |
|
| 270 | usleep(100000);
|
| 271 |
|
| 272 |
|
| 273 | close(syncpipe[0]);
|
| 274 | sync_byte = 'X';
|
| 275 | write(syncpipe[1], &sync_byte, 1);
|
| 276 | close(syncpipe[1]);
|
| 277 |
|
| 278 |
|
| 279 | waitpid(pid1, &status, 0);
|
| 280 | ASSERT_TRUE(WIFEXITED(status));
|
| 281 |
|
| 282 |
|
| 283 | fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 284 | ASSERT_GE(fd, 0);
|
| 285 | close(fd);
|
| 286 |
|
| 287 |
|
| 288 | waitpid(pid2, &status, 0);
|
| 289 | ASSERT_TRUE(WIFEXITED(status));
|
| 290 | }
|
| 291 |
|
| 292 | |
| 293 | * Test user namespace active ref tracking via credential lifecycle
|
| 294 |
|
| 295 | TEST(userns_active_ref_lifecycle)
|
| 296 | {
|
| 297 | struct file_handle *handle;
|
| 298 | int mount_id;
|
| 299 | int ret;
|
| 300 | int fd;
|
| 301 | int pipefd[2];
|
| 302 | pid_t pid;
|
| 303 | int status;
|
| 304 | char buf[sizeof(*handle) + MAX_HANDLE_SZ];
|
| 305 |
|
| 306 | ASSERT_EQ(pipe(pipefd), 0);
|
| 307 |
|
| 308 | pid = fork();
|
| 309 | ASSERT_GE(pid, 0);
|
| 310 |
|
| 311 | if (pid == 0) {
|
| 312 |
|
| 313 | close(pipefd[0]);
|
| 314 |
|
| 315 |
|
| 316 | ret = unshare(CLONE_NEWUSER);
|
| 317 | if (ret < 0) {
|
| 318 | close(pipefd[1]);
|
| 319 | exit(1);
|
| 320 | }
|
| 321 |
|
| 322 |
|
| 323 | int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
|
| 324 | int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
|
| 325 | int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
|
| 326 |
|
| 327 | if (uid_map_fd >= 0 && gid_map_fd >= 0 && setgroups_fd >= 0) {
|
| 328 | write(setgroups_fd, "deny", 4);
|
| 329 | close(setgroups_fd);
|
| 330 |
|
| 331 | char mapping[64];
|
| 332 | snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
|
| 333 | write(uid_map_fd, mapping, strlen(mapping));
|
| 334 | close(uid_map_fd);
|
| 335 |
|
| 336 | snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
|
| 337 | write(gid_map_fd, mapping, strlen(mapping));
|
| 338 | close(gid_map_fd);
|
| 339 | }
|
| 340 |
|
| 341 |
|
| 342 | fd = open("/proc/self/ns/user", O_RDONLY);
|
| 343 | if (fd < 0) {
|
| 344 | close(pipefd[1]);
|
| 345 | exit(1);
|
| 346 | }
|
| 347 |
|
| 348 | handle = (struct file_handle *)buf;
|
| 349 | handle->handle_bytes = MAX_HANDLE_SZ;
|
| 350 | ret = name_to_handle_at(fd, "", handle, &mount_id, AT_EMPTY_PATH);
|
| 351 | close(fd);
|
| 352 |
|
| 353 | if (ret < 0) {
|
| 354 | close(pipefd[1]);
|
| 355 | exit(1);
|
| 356 | }
|
| 357 |
|
| 358 |
|
| 359 | write(pipefd[1], buf, sizeof(*handle) + handle->handle_bytes);
|
| 360 | close(pipefd[1]);
|
| 361 | exit(0);
|
| 362 | }
|
| 363 |
|
| 364 |
|
| 365 | close(pipefd[1]);
|
| 366 | ret = read(pipefd[0], buf, sizeof(buf));
|
| 367 | close(pipefd[0]);
|
| 368 |
|
| 369 | waitpid(pid, &status, 0);
|
| 370 | ASSERT_TRUE(WIFEXITED(status));
|
| 371 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 372 |
|
| 373 | ASSERT_GT(ret, 0);
|
| 374 | handle = (struct file_handle *)buf;
|
| 375 |
|
| 376 |
|
| 377 | fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 378 | ASSERT_LT(fd, 0);
|
| 379 | ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
|
| 380 | }
|
| 381 |
|
| 382 | |
| 383 | * Test PID namespace active ref tracking
|
| 384 |
|
| 385 | TEST(pidns_active_ref_lifecycle)
|
| 386 | {
|
| 387 | struct file_handle *handle;
|
| 388 | int mount_id;
|
| 389 | int ret;
|
| 390 | int fd;
|
| 391 | int pipefd[2];
|
| 392 | pid_t pid;
|
| 393 | int status;
|
| 394 | char buf[sizeof(*handle) + MAX_HANDLE_SZ];
|
| 395 |
|
| 396 | ASSERT_EQ(pipe(pipefd), 0);
|
| 397 |
|
| 398 | pid = fork();
|
| 399 | ASSERT_GE(pid, 0);
|
| 400 |
|
| 401 | if (pid == 0) {
|
| 402 |
|
| 403 | close(pipefd[0]);
|
| 404 |
|
| 405 |
|
| 406 | ret = unshare(CLONE_NEWPID);
|
| 407 | if (ret < 0) {
|
| 408 | close(pipefd[1]);
|
| 409 | exit(1);
|
| 410 | }
|
| 411 |
|
| 412 |
|
| 413 | pid_t child = fork();
|
| 414 | if (child < 0) {
|
| 415 | close(pipefd[1]);
|
| 416 | exit(1);
|
| 417 | }
|
| 418 |
|
| 419 | if (child == 0) {
|
| 420 |
|
| 421 | fd = open("/proc/self/ns/pid", O_RDONLY);
|
| 422 | if (fd < 0) {
|
| 423 | exit(1);
|
| 424 | }
|
| 425 |
|
| 426 | handle = (struct file_handle *)buf;
|
| 427 | handle->handle_bytes = MAX_HANDLE_SZ;
|
| 428 | ret = name_to_handle_at(fd, "", handle, &mount_id, AT_EMPTY_PATH);
|
| 429 | close(fd);
|
| 430 |
|
| 431 | if (ret < 0) {
|
| 432 | exit(1);
|
| 433 | }
|
| 434 |
|
| 435 |
|
| 436 | write(pipefd[1], buf, sizeof(*handle) + handle->handle_bytes);
|
| 437 | close(pipefd[1]);
|
| 438 | exit(0);
|
| 439 | }
|
| 440 |
|
| 441 |
|
| 442 | waitpid(child, NULL, 0);
|
| 443 | exit(0);
|
| 444 | }
|
| 445 |
|
| 446 |
|
| 447 | close(pipefd[1]);
|
| 448 | ret = read(pipefd[0], buf, sizeof(buf));
|
| 449 | close(pipefd[0]);
|
| 450 |
|
| 451 | waitpid(pid, &status, 0);
|
| 452 | ASSERT_TRUE(WIFEXITED(status));
|
| 453 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 454 |
|
| 455 | ASSERT_GT(ret, 0);
|
| 456 | handle = (struct file_handle *)buf;
|
| 457 |
|
| 458 |
|
| 459 | fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 460 | ASSERT_LT(fd, 0);
|
| 461 | ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
|
| 462 | }
|
| 463 |
|
| 464 | |
| 465 | * Test that an open file descriptor keeps a namespace active.
|
| 466 | * Even after the creating process exits, the namespace should remain
|
| 467 | * active as long as an fd is held open.
|
| 468 |
|
| 469 | TEST(ns_fd_keeps_active)
|
| 470 | {
|
| 471 | struct file_handle *handle;
|
| 472 | int mount_id;
|
| 473 | int ret;
|
| 474 | int nsfd;
|
| 475 | int pipe_child_ready[2];
|
| 476 | int pipe_parent_ready[2];
|
| 477 | pid_t pid;
|
| 478 | int status;
|
| 479 | char buf[sizeof(*handle) + MAX_HANDLE_SZ];
|
| 480 | char sync_byte;
|
| 481 | char proc_path[64];
|
| 482 |
|
| 483 | ASSERT_EQ(pipe(pipe_child_ready), 0);
|
| 484 | ASSERT_EQ(pipe(pipe_parent_ready), 0);
|
| 485 |
|
| 486 | pid = fork();
|
| 487 | ASSERT_GE(pid, 0);
|
| 488 |
|
| 489 | if (pid == 0) {
|
| 490 |
|
| 491 | close(pipe_child_ready[0]);
|
| 492 | close(pipe_parent_ready[1]);
|
| 493 |
|
| 494 | TH_LOG("Child: creating new network namespace");
|
| 495 |
|
| 496 |
|
| 497 | ret = unshare(CLONE_NEWNET);
|
| 498 | if (ret < 0) {
|
| 499 | TH_LOG("Child: unshare(CLONE_NEWNET) failed: %s", strerror(errno));
|
| 500 | close(pipe_child_ready[1]);
|
| 501 | close(pipe_parent_ready[0]);
|
| 502 | exit(1);
|
| 503 | }
|
| 504 |
|
| 505 | TH_LOG("Child: network namespace created successfully");
|
| 506 |
|
| 507 |
|
| 508 | nsfd = open("/proc/self/ns/net", O_RDONLY);
|
| 509 | if (nsfd < 0) {
|
| 510 | TH_LOG("Child: failed to open /proc/self/ns/net: %s", strerror(errno));
|
| 511 | close(pipe_child_ready[1]);
|
| 512 | close(pipe_parent_ready[0]);
|
| 513 | exit(1);
|
| 514 | }
|
| 515 |
|
| 516 | TH_LOG("Child: opened namespace fd %d", nsfd);
|
| 517 |
|
| 518 | handle = (struct file_handle *)buf;
|
| 519 | handle->handle_bytes = MAX_HANDLE_SZ;
|
| 520 | ret = name_to_handle_at(nsfd, "", handle, &mount_id, AT_EMPTY_PATH);
|
| 521 | close(nsfd);
|
| 522 |
|
| 523 | if (ret < 0) {
|
| 524 | TH_LOG("Child: name_to_handle_at failed: %s", strerror(errno));
|
| 525 | close(pipe_child_ready[1]);
|
| 526 | close(pipe_parent_ready[0]);
|
| 527 | exit(1);
|
| 528 | }
|
| 529 |
|
| 530 | TH_LOG("Child: got file handle (bytes=%u)", handle->handle_bytes);
|
| 531 |
|
| 532 |
|
| 533 | ret = write(pipe_child_ready[1], buf, sizeof(*handle) + handle->handle_bytes);
|
| 534 | TH_LOG("Child: sent %d bytes of file handle to parent", ret);
|
| 535 | close(pipe_child_ready[1]);
|
| 536 |
|
| 537 |
|
| 538 | TH_LOG("Child: waiting for parent to open fd");
|
| 539 | ret = read(pipe_parent_ready[0], &sync_byte, 1);
|
| 540 | close(pipe_parent_ready[0]);
|
| 541 |
|
| 542 | TH_LOG("Child: parent signaled (read %d bytes), exiting now", ret);
|
| 543 |
|
| 544 | exit(0);
|
| 545 | }
|
| 546 |
|
| 547 |
|
| 548 | close(pipe_child_ready[1]);
|
| 549 | close(pipe_parent_ready[0]);
|
| 550 |
|
| 551 | TH_LOG("Parent: reading file handle from child");
|
| 552 |
|
| 553 |
|
| 554 | ret = read(pipe_child_ready[0], buf, sizeof(buf));
|
| 555 | close(pipe_child_ready[0]);
|
| 556 | ASSERT_GT(ret, 0);
|
| 557 | handle = (struct file_handle *)buf;
|
| 558 |
|
| 559 | TH_LOG("Parent: received %d bytes, handle size=%u", ret, handle->handle_bytes);
|
| 560 |
|
| 561 |
|
| 562 | snprintf(proc_path, sizeof(proc_path), "/proc/%d/ns/net", pid);
|
| 563 | TH_LOG("Parent: opening child's namespace at %s", proc_path);
|
| 564 | nsfd = open(proc_path, O_RDONLY);
|
| 565 | if (nsfd < 0) {
|
| 566 | TH_LOG("Parent: failed to open %s: %s", proc_path, strerror(errno));
|
| 567 | close(pipe_parent_ready[1]);
|
| 568 | kill(pid, SIGKILL);
|
| 569 | waitpid(pid, NULL, 0);
|
| 570 | SKIP(return, "Failed to open child's namespace");
|
| 571 | }
|
| 572 |
|
| 573 | TH_LOG("Parent: opened child's namespace, got fd %d", nsfd);
|
| 574 |
|
| 575 |
|
| 576 | sync_byte = 'G';
|
| 577 | write(pipe_parent_ready[1], &sync_byte, 1);
|
| 578 | close(pipe_parent_ready[1]);
|
| 579 | TH_LOG("Parent: signaled child that we have the fd");
|
| 580 |
|
| 581 |
|
| 582 | waitpid(pid, &status, 0);
|
| 583 | ASSERT_TRUE(WIFEXITED(status));
|
| 584 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 585 |
|
| 586 | TH_LOG("Child exited, parent holds fd %d to namespace", nsfd);
|
| 587 |
|
| 588 | |
| 589 | * Namespace should still be ACTIVE because we hold an fd.
|
| 590 | * We should be able to reopen it via file handle.
|
| 591 |
|
| 592 | TH_LOG("Attempting to reopen namespace via file handle (should succeed - fd held)");
|
| 593 | int fd2 = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 594 | ASSERT_GE(fd2, 0);
|
| 595 |
|
| 596 | TH_LOG("Successfully reopened namespace via file handle, got fd %d", fd2);
|
| 597 |
|
| 598 |
|
| 599 | struct stat st1, st2;
|
| 600 | ASSERT_EQ(fstat(nsfd, &st1), 0);
|
| 601 | ASSERT_EQ(fstat(fd2, &st2), 0);
|
| 602 | TH_LOG("Namespace inodes: nsfd=%lu, fd2=%lu", st1.st_ino, st2.st_ino);
|
| 603 | ASSERT_EQ(st1.st_ino, st2.st_ino);
|
| 604 | close(fd2);
|
| 605 |
|
| 606 |
|
| 607 | TH_LOG("Closing fd %d - namespace should become inactive", nsfd);
|
| 608 | close(nsfd);
|
| 609 |
|
| 610 |
|
| 611 | TH_LOG("Attempting to reopen namespace via file handle (should fail - inactive)");
|
| 612 | fd2 = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 613 | ASSERT_LT(fd2, 0);
|
| 614 |
|
| 615 | TH_LOG("Reopen failed as expected: %s (errno=%d)", strerror(errno), errno);
|
| 616 | ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
|
| 617 | }
|
| 618 |
|
| 619 | |
| 620 | * Test hierarchical active reference propagation.
|
| 621 | * When a child namespace is active, its owning user namespace should also
|
| 622 | * be active automatically due to hierarchical active reference propagation.
|
| 623 | * This ensures parents are always reachable when children are active.
|
| 624 |
|
| 625 | TEST(ns_parent_always_reachable)
|
| 626 | {
|
| 627 | struct file_handle *parent_handle, *child_handle;
|
| 628 | int ret;
|
| 629 | int child_nsfd;
|
| 630 | int pipefd[2];
|
| 631 | pid_t pid;
|
| 632 | int status;
|
| 633 | __u64 parent_id, child_id;
|
| 634 | char parent_buf[sizeof(*parent_handle) + MAX_HANDLE_SZ];
|
| 635 | char child_buf[sizeof(*child_handle) + MAX_HANDLE_SZ];
|
| 636 |
|
| 637 | ASSERT_EQ(pipe(pipefd), 0);
|
| 638 |
|
| 639 | pid = fork();
|
| 640 | ASSERT_GE(pid, 0);
|
| 641 |
|
| 642 | if (pid == 0) {
|
| 643 |
|
| 644 | close(pipefd[0]);
|
| 645 |
|
| 646 | TH_LOG("Child: creating parent user namespace and setting up mappings");
|
| 647 |
|
| 648 |
|
| 649 | ret = setup_userns();
|
| 650 | if (ret < 0) {
|
| 651 | TH_LOG("Child: setup_userns() for parent failed: %s", strerror(errno));
|
| 652 | close(pipefd[1]);
|
| 653 | exit(1);
|
| 654 | }
|
| 655 |
|
| 656 | TH_LOG("Child: parent user namespace created, now uid=%d gid=%d", getuid(), getgid());
|
| 657 |
|
| 658 |
|
| 659 | int parent_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 660 | if (parent_fd < 0) {
|
| 661 | TH_LOG("Child: failed to open parent /proc/self/ns/user: %s", strerror(errno));
|
| 662 | close(pipefd[1]);
|
| 663 | exit(1);
|
| 664 | }
|
| 665 |
|
| 666 | TH_LOG("Child: opened parent userns fd %d", parent_fd);
|
| 667 |
|
| 668 | if (ioctl(parent_fd, NS_GET_ID, &parent_id) < 0) {
|
| 669 | TH_LOG("Child: NS_GET_ID for parent failed: %s", strerror(errno));
|
| 670 | close(parent_fd);
|
| 671 | close(pipefd[1]);
|
| 672 | exit(1);
|
| 673 | }
|
| 674 | close(parent_fd);
|
| 675 |
|
| 676 | TH_LOG("Child: got parent namespace ID %llu", (unsigned long long)parent_id);
|
| 677 |
|
| 678 |
|
| 679 | TH_LOG("Child: creating nested child user namespace");
|
| 680 | ret = setup_userns();
|
| 681 | if (ret < 0) {
|
| 682 | TH_LOG("Child: setup_userns() for child failed: %s", strerror(errno));
|
| 683 | close(pipefd[1]);
|
| 684 | exit(1);
|
| 685 | }
|
| 686 |
|
| 687 | TH_LOG("Child: nested child user namespace created, uid=%d gid=%d", getuid(), getgid());
|
| 688 |
|
| 689 |
|
| 690 | int child_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 691 | if (child_fd < 0) {
|
| 692 | TH_LOG("Child: failed to open child /proc/self/ns/user: %s", strerror(errno));
|
| 693 | close(pipefd[1]);
|
| 694 | exit(1);
|
| 695 | }
|
| 696 |
|
| 697 | TH_LOG("Child: opened child userns fd %d", child_fd);
|
| 698 |
|
| 699 | if (ioctl(child_fd, NS_GET_ID, &child_id) < 0) {
|
| 700 | TH_LOG("Child: NS_GET_ID for child failed: %s", strerror(errno));
|
| 701 | close(child_fd);
|
| 702 | close(pipefd[1]);
|
| 703 | exit(1);
|
| 704 | }
|
| 705 | close(child_fd);
|
| 706 |
|
| 707 | TH_LOG("Child: got child namespace ID %llu", (unsigned long long)child_id);
|
| 708 |
|
| 709 |
|
| 710 | TH_LOG("Child: sending both namespace IDs to parent");
|
| 711 | write(pipefd[1], &parent_id, sizeof(parent_id));
|
| 712 | write(pipefd[1], &child_id, sizeof(child_id));
|
| 713 | close(pipefd[1]);
|
| 714 |
|
| 715 | TH_LOG("Child: exiting - parent userns should become inactive");
|
| 716 |
|
| 717 | exit(0);
|
| 718 | }
|
| 719 |
|
| 720 |
|
| 721 | close(pipefd[1]);
|
| 722 |
|
| 723 | TH_LOG("Parent: reading both namespace IDs from child");
|
| 724 |
|
| 725 |
|
| 726 | ret = read(pipefd[0], &parent_id, sizeof(parent_id));
|
| 727 | if (ret != sizeof(parent_id)) {
|
| 728 | close(pipefd[0]);
|
| 729 | waitpid(pid, NULL, 0);
|
| 730 | SKIP(return, "Failed to read parent namespace ID from child");
|
| 731 | }
|
| 732 |
|
| 733 | ret = read(pipefd[0], &child_id, sizeof(child_id));
|
| 734 | close(pipefd[0]);
|
| 735 | if (ret != sizeof(child_id)) {
|
| 736 | waitpid(pid, NULL, 0);
|
| 737 | SKIP(return, "Failed to read child namespace ID from child");
|
| 738 | }
|
| 739 |
|
| 740 | TH_LOG("Parent: received parent_id=%llu, child_id=%llu",
|
| 741 | (unsigned long long)parent_id, (unsigned long long)child_id);
|
| 742 |
|
| 743 |
|
| 744 | parent_handle = (struct file_handle *)parent_buf;
|
| 745 | parent_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 746 | parent_handle->handle_type = FILEID_NSFS;
|
| 747 | struct nsfs_file_handle *parent_fh = (struct nsfs_file_handle *)parent_handle->f_handle;
|
| 748 | parent_fh->ns_id = parent_id;
|
| 749 | parent_fh->ns_type = 0;
|
| 750 | parent_fh->ns_inum = 0;
|
| 751 |
|
| 752 | child_handle = (struct file_handle *)child_buf;
|
| 753 | child_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 754 | child_handle->handle_type = FILEID_NSFS;
|
| 755 | struct nsfs_file_handle *child_fh = (struct nsfs_file_handle *)child_handle->f_handle;
|
| 756 | child_fh->ns_id = child_id;
|
| 757 | child_fh->ns_type = 0;
|
| 758 | child_fh->ns_inum = 0;
|
| 759 |
|
| 760 | TH_LOG("Parent: opening child namespace BEFORE child exits");
|
| 761 |
|
| 762 |
|
| 763 | child_nsfd = open_by_handle_at(FD_NSFS_ROOT, child_handle, O_RDONLY);
|
| 764 | if (child_nsfd < 0) {
|
| 765 | TH_LOG("Failed to open child namespace: %s (errno=%d)", strerror(errno), errno);
|
| 766 | waitpid(pid, NULL, 0);
|
| 767 | SKIP(return, "Failed to open child namespace");
|
| 768 | }
|
| 769 |
|
| 770 | TH_LOG("Opened child namespace fd %d", child_nsfd);
|
| 771 |
|
| 772 |
|
| 773 | TH_LOG("Parent: waiting for child to exit");
|
| 774 | waitpid(pid, &status, 0);
|
| 775 | ASSERT_TRUE(WIFEXITED(status));
|
| 776 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 777 |
|
| 778 | TH_LOG("Child process exited, parent holds fd to child namespace");
|
| 779 |
|
| 780 | |
| 781 | * With hierarchical active reference propagation:
|
| 782 | * Since the child namespace is active (parent process holds fd),
|
| 783 | * the parent user namespace should ALSO be active automatically.
|
| 784 | * This is because when we took an active reference on the child,
|
| 785 | * it propagated up to the owning user namespace.
|
| 786 |
|
| 787 | TH_LOG("Attempting to reopen parent namespace (should SUCCEED - hierarchical propagation)");
|
| 788 | int parent_fd = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY);
|
| 789 | ASSERT_GE(parent_fd, 0);
|
| 790 |
|
| 791 | TH_LOG("SUCCESS: Parent namespace is active (fd=%d) due to active child", parent_fd);
|
| 792 |
|
| 793 |
|
| 794 | TH_LOG("Verifying NS_GET_USERNS also works");
|
| 795 | int parent_fd2 = ioctl(child_nsfd, NS_GET_USERNS);
|
| 796 | if (parent_fd2 < 0) {
|
| 797 | close(parent_fd);
|
| 798 | close(child_nsfd);
|
| 799 | TH_LOG("NS_GET_USERNS failed: %s (errno=%d)", strerror(errno), errno);
|
| 800 | SKIP(return, "NS_GET_USERNS not supported or failed");
|
| 801 | }
|
| 802 |
|
| 803 | TH_LOG("NS_GET_USERNS succeeded, got parent fd %d", parent_fd2);
|
| 804 |
|
| 805 |
|
| 806 | struct stat st1, st2;
|
| 807 | ASSERT_EQ(fstat(parent_fd, &st1), 0);
|
| 808 | ASSERT_EQ(fstat(parent_fd2, &st2), 0);
|
| 809 | TH_LOG("Parent namespace inodes: parent_fd=%lu, parent_fd2=%lu", st1.st_ino, st2.st_ino);
|
| 810 | ASSERT_EQ(st1.st_ino, st2.st_ino);
|
| 811 |
|
| 812 | |
| 813 | * Close child fd - parent should remain active because we still
|
| 814 | * hold direct references to it (parent_fd and parent_fd2).
|
| 815 |
|
| 816 | TH_LOG("Closing child fd - parent should remain active (direct refs held)");
|
| 817 | close(child_nsfd);
|
| 818 |
|
| 819 |
|
| 820 | TH_LOG("Verifying parent still active via file handle");
|
| 821 | int parent_fd3 = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY);
|
| 822 | ASSERT_GE(parent_fd3, 0);
|
| 823 | close(parent_fd3);
|
| 824 |
|
| 825 | TH_LOG("Closing all fds to parent namespace");
|
| 826 | close(parent_fd);
|
| 827 | close(parent_fd2);
|
| 828 |
|
| 829 |
|
| 830 | TH_LOG("Attempting to reopen parent (should fail - inactive, no refs)");
|
| 831 | parent_fd = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY);
|
| 832 | ASSERT_LT(parent_fd, 0);
|
| 833 | TH_LOG("Parent inactive as expected: %s (errno=%d)", strerror(errno), errno);
|
| 834 | ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
|
| 835 | }
|
| 836 |
|
| 837 | |
| 838 | * Test that bind mounts keep namespaces in the tree even when inactive
|
| 839 |
|
| 840 | TEST(ns_bind_mount_keeps_in_tree)
|
| 841 | {
|
| 842 | struct file_handle *handle;
|
| 843 | int mount_id;
|
| 844 | int ret;
|
| 845 | int fd;
|
| 846 | int pipefd[2];
|
| 847 | pid_t pid;
|
| 848 | int status;
|
| 849 | char buf[sizeof(*handle) + MAX_HANDLE_SZ];
|
| 850 | char tmpfile[] = "/tmp/ns-test-XXXXXX";
|
| 851 | int tmpfd;
|
| 852 |
|
| 853 |
|
| 854 | tmpfd = mkstemp(tmpfile);
|
| 855 | if (tmpfd < 0) {
|
| 856 | SKIP(return, "Cannot create temporary file");
|
| 857 | }
|
| 858 | close(tmpfd);
|
| 859 |
|
| 860 | ASSERT_EQ(pipe(pipefd), 0);
|
| 861 |
|
| 862 | pid = fork();
|
| 863 | ASSERT_GE(pid, 0);
|
| 864 |
|
| 865 | if (pid == 0) {
|
| 866 |
|
| 867 | close(pipefd[0]);
|
| 868 |
|
| 869 |
|
| 870 | ret = unshare(CLONE_NEWNS);
|
| 871 | if (ret < 0) {
|
| 872 | close(pipefd[1]);
|
| 873 | unlink(tmpfile);
|
| 874 | exit(1);
|
| 875 | }
|
| 876 | ret = mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL);
|
| 877 | if (ret < 0) {
|
| 878 | close(pipefd[1]);
|
| 879 | unlink(tmpfile);
|
| 880 | exit(1);
|
| 881 | }
|
| 882 |
|
| 883 |
|
| 884 | ret = unshare(CLONE_NEWNET);
|
| 885 | if (ret < 0) {
|
| 886 | close(pipefd[1]);
|
| 887 | unlink(tmpfile);
|
| 888 | exit(1);
|
| 889 | }
|
| 890 |
|
| 891 |
|
| 892 | ret = mount("/proc/self/ns/net", tmpfile, NULL, MS_BIND, NULL);
|
| 893 | if (ret < 0) {
|
| 894 | close(pipefd[1]);
|
| 895 | unlink(tmpfile);
|
| 896 | exit(1);
|
| 897 | }
|
| 898 |
|
| 899 |
|
| 900 | fd = open("/proc/self/ns/net", O_RDONLY);
|
| 901 | if (fd < 0) {
|
| 902 | umount(tmpfile);
|
| 903 | close(pipefd[1]);
|
| 904 | unlink(tmpfile);
|
| 905 | exit(1);
|
| 906 | }
|
| 907 |
|
| 908 | handle = (struct file_handle *)buf;
|
| 909 | handle->handle_bytes = MAX_HANDLE_SZ;
|
| 910 | ret = name_to_handle_at(fd, "", handle, &mount_id, AT_EMPTY_PATH);
|
| 911 | close(fd);
|
| 912 |
|
| 913 | if (ret < 0) {
|
| 914 | umount(tmpfile);
|
| 915 | close(pipefd[1]);
|
| 916 | unlink(tmpfile);
|
| 917 | exit(1);
|
| 918 | }
|
| 919 |
|
| 920 |
|
| 921 | write(pipefd[1], buf, sizeof(*handle) + handle->handle_bytes);
|
| 922 | close(pipefd[1]);
|
| 923 | exit(0);
|
| 924 | }
|
| 925 |
|
| 926 |
|
| 927 | close(pipefd[1]);
|
| 928 | ret = read(pipefd[0], buf, sizeof(buf));
|
| 929 | close(pipefd[0]);
|
| 930 |
|
| 931 | waitpid(pid, &status, 0);
|
| 932 | ASSERT_TRUE(WIFEXITED(status));
|
| 933 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 934 |
|
| 935 | ASSERT_GT(ret, 0);
|
| 936 | handle = (struct file_handle *)buf;
|
| 937 |
|
| 938 | |
| 939 | * Namespace should be inactive but still in tree due to bind mount.
|
| 940 | * Reopening should fail with ENOENT (inactive) not ESTALE (not in tree).
|
| 941 |
|
| 942 | fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 943 | ASSERT_LT(fd, 0);
|
| 944 |
|
| 945 | if (errno != ENOENT && errno != ESTALE) {
|
| 946 | TH_LOG("Unexpected error: %d", errno);
|
| 947 | }
|
| 948 |
|
| 949 |
|
| 950 | umount(tmpfile);
|
| 951 | unlink(tmpfile);
|
| 952 | }
|
| 953 |
|
| 954 | |
| 955 | * Test multi-level hierarchy (3+ levels deep).
|
| 956 | * Grandparent → Parent → Child
|
| 957 | * When child is active, both parent AND grandparent should be active.
|
| 958 |
|
| 959 | TEST(ns_multilevel_hierarchy)
|
| 960 | {
|
| 961 | struct file_handle *gp_handle, *p_handle, *c_handle;
|
| 962 | int ret, pipefd[2];
|
| 963 | pid_t pid;
|
| 964 | int status;
|
| 965 | __u64 gp_id, p_id, c_id;
|
| 966 | char gp_buf[sizeof(*gp_handle) + MAX_HANDLE_SZ];
|
| 967 | char p_buf[sizeof(*p_handle) + MAX_HANDLE_SZ];
|
| 968 | char c_buf[sizeof(*c_handle) + MAX_HANDLE_SZ];
|
| 969 |
|
| 970 | ASSERT_EQ(pipe(pipefd), 0);
|
| 971 | pid = fork();
|
| 972 | ASSERT_GE(pid, 0);
|
| 973 |
|
| 974 | if (pid == 0) {
|
| 975 | close(pipefd[0]);
|
| 976 |
|
| 977 |
|
| 978 | if (setup_userns() < 0) {
|
| 979 | close(pipefd[1]);
|
| 980 | exit(1);
|
| 981 | }
|
| 982 |
|
| 983 | int gp_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 984 | if (gp_fd < 0) {
|
| 985 | close(pipefd[1]);
|
| 986 | exit(1);
|
| 987 | }
|
| 988 | if (ioctl(gp_fd, NS_GET_ID, &gp_id) < 0) {
|
| 989 | close(gp_fd);
|
| 990 | close(pipefd[1]);
|
| 991 | exit(1);
|
| 992 | }
|
| 993 | close(gp_fd);
|
| 994 |
|
| 995 |
|
| 996 | if (setup_userns() < 0) {
|
| 997 | close(pipefd[1]);
|
| 998 | exit(1);
|
| 999 | }
|
| 1000 |
|
| 1001 | int p_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 1002 | if (p_fd < 0) {
|
| 1003 | close(pipefd[1]);
|
| 1004 | exit(1);
|
| 1005 | }
|
| 1006 | if (ioctl(p_fd, NS_GET_ID, &p_id) < 0) {
|
| 1007 | close(p_fd);
|
| 1008 | close(pipefd[1]);
|
| 1009 | exit(1);
|
| 1010 | }
|
| 1011 | close(p_fd);
|
| 1012 |
|
| 1013 |
|
| 1014 | if (setup_userns() < 0) {
|
| 1015 | close(pipefd[1]);
|
| 1016 | exit(1);
|
| 1017 | }
|
| 1018 |
|
| 1019 | int c_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 1020 | if (c_fd < 0) {
|
| 1021 | close(pipefd[1]);
|
| 1022 | exit(1);
|
| 1023 | }
|
| 1024 | if (ioctl(c_fd, NS_GET_ID, &c_id) < 0) {
|
| 1025 | close(c_fd);
|
| 1026 | close(pipefd[1]);
|
| 1027 | exit(1);
|
| 1028 | }
|
| 1029 | close(c_fd);
|
| 1030 |
|
| 1031 |
|
| 1032 | write(pipefd[1], &gp_id, sizeof(gp_id));
|
| 1033 | write(pipefd[1], &p_id, sizeof(p_id));
|
| 1034 | write(pipefd[1], &c_id, sizeof(c_id));
|
| 1035 | close(pipefd[1]);
|
| 1036 | exit(0);
|
| 1037 | }
|
| 1038 |
|
| 1039 | close(pipefd[1]);
|
| 1040 |
|
| 1041 |
|
| 1042 | ret = read(pipefd[0], &gp_id, sizeof(gp_id));
|
| 1043 | if (ret != sizeof(gp_id)) {
|
| 1044 | close(pipefd[0]);
|
| 1045 | waitpid(pid, NULL, 0);
|
| 1046 | SKIP(return, "Failed to read grandparent namespace ID from child");
|
| 1047 | }
|
| 1048 |
|
| 1049 | ret = read(pipefd[0], &p_id, sizeof(p_id));
|
| 1050 | if (ret != sizeof(p_id)) {
|
| 1051 | close(pipefd[0]);
|
| 1052 | waitpid(pid, NULL, 0);
|
| 1053 | SKIP(return, "Failed to read parent namespace ID from child");
|
| 1054 | }
|
| 1055 |
|
| 1056 | ret = read(pipefd[0], &c_id, sizeof(c_id));
|
| 1057 | close(pipefd[0]);
|
| 1058 | if (ret != sizeof(c_id)) {
|
| 1059 | waitpid(pid, NULL, 0);
|
| 1060 | SKIP(return, "Failed to read child namespace ID from child");
|
| 1061 | }
|
| 1062 |
|
| 1063 |
|
| 1064 | gp_handle = (struct file_handle *)gp_buf;
|
| 1065 | gp_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1066 | gp_handle->handle_type = FILEID_NSFS;
|
| 1067 | struct nsfs_file_handle *gp_fh = (struct nsfs_file_handle *)gp_handle->f_handle;
|
| 1068 | gp_fh->ns_id = gp_id;
|
| 1069 | gp_fh->ns_type = 0;
|
| 1070 | gp_fh->ns_inum = 0;
|
| 1071 |
|
| 1072 | p_handle = (struct file_handle *)p_buf;
|
| 1073 | p_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1074 | p_handle->handle_type = FILEID_NSFS;
|
| 1075 | struct nsfs_file_handle *p_fh = (struct nsfs_file_handle *)p_handle->f_handle;
|
| 1076 | p_fh->ns_id = p_id;
|
| 1077 | p_fh->ns_type = 0;
|
| 1078 | p_fh->ns_inum = 0;
|
| 1079 |
|
| 1080 | c_handle = (struct file_handle *)c_buf;
|
| 1081 | c_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1082 | c_handle->handle_type = FILEID_NSFS;
|
| 1083 | struct nsfs_file_handle *c_fh = (struct nsfs_file_handle *)c_handle->f_handle;
|
| 1084 | c_fh->ns_id = c_id;
|
| 1085 | c_fh->ns_type = 0;
|
| 1086 | c_fh->ns_inum = 0;
|
| 1087 |
|
| 1088 |
|
| 1089 | int c_fd = open_by_handle_at(FD_NSFS_ROOT, c_handle, O_RDONLY);
|
| 1090 | if (c_fd < 0) {
|
| 1091 | waitpid(pid, NULL, 0);
|
| 1092 | SKIP(return, "Failed to open child namespace");
|
| 1093 | }
|
| 1094 |
|
| 1095 | waitpid(pid, &status, 0);
|
| 1096 | ASSERT_TRUE(WIFEXITED(status));
|
| 1097 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 1098 |
|
| 1099 | |
| 1100 | * With 3-level hierarchy and child active:
|
| 1101 | * - Child is active (we hold fd)
|
| 1102 | * - Parent should be active (propagated from child)
|
| 1103 | * - Grandparent should be active (propagated from parent)
|
| 1104 |
|
| 1105 | TH_LOG("Testing parent active when child is active");
|
| 1106 | int p_fd = open_by_handle_at(FD_NSFS_ROOT, p_handle, O_RDONLY);
|
| 1107 | ASSERT_GE(p_fd, 0);
|
| 1108 |
|
| 1109 | TH_LOG("Testing grandparent active when child is active");
|
| 1110 | int gp_fd = open_by_handle_at(FD_NSFS_ROOT, gp_handle, O_RDONLY);
|
| 1111 | ASSERT_GE(gp_fd, 0);
|
| 1112 |
|
| 1113 | close(c_fd);
|
| 1114 | close(p_fd);
|
| 1115 | close(gp_fd);
|
| 1116 | }
|
| 1117 |
|
| 1118 | |
| 1119 | * Test multiple children sharing same parent.
|
| 1120 | * Parent should stay active as long as ANY child is active.
|
| 1121 |
|
| 1122 | TEST(ns_multiple_children_same_parent)
|
| 1123 | {
|
| 1124 | struct file_handle *p_handle, *c1_handle, *c2_handle;
|
| 1125 | int ret, pipefd[2];
|
| 1126 | pid_t pid;
|
| 1127 | int status;
|
| 1128 | __u64 p_id, c1_id, c2_id;
|
| 1129 | char p_buf[sizeof(*p_handle) + MAX_HANDLE_SZ];
|
| 1130 | char c1_buf[sizeof(*c1_handle) + MAX_HANDLE_SZ];
|
| 1131 | char c2_buf[sizeof(*c2_handle) + MAX_HANDLE_SZ];
|
| 1132 |
|
| 1133 | ASSERT_EQ(pipe(pipefd), 0);
|
| 1134 | pid = fork();
|
| 1135 | ASSERT_GE(pid, 0);
|
| 1136 |
|
| 1137 | if (pid == 0) {
|
| 1138 | close(pipefd[0]);
|
| 1139 |
|
| 1140 |
|
| 1141 | if (setup_userns() < 0) {
|
| 1142 | close(pipefd[1]);
|
| 1143 | exit(1);
|
| 1144 | }
|
| 1145 |
|
| 1146 | int p_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 1147 | if (p_fd < 0) {
|
| 1148 | close(pipefd[1]);
|
| 1149 | exit(1);
|
| 1150 | }
|
| 1151 | if (ioctl(p_fd, NS_GET_ID, &p_id) < 0) {
|
| 1152 | close(p_fd);
|
| 1153 | close(pipefd[1]);
|
| 1154 | exit(1);
|
| 1155 | }
|
| 1156 | close(p_fd);
|
| 1157 |
|
| 1158 |
|
| 1159 | if (setup_userns() < 0) {
|
| 1160 | close(pipefd[1]);
|
| 1161 | exit(1);
|
| 1162 | }
|
| 1163 |
|
| 1164 | int c1_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 1165 | if (c1_fd < 0) {
|
| 1166 | close(pipefd[1]);
|
| 1167 | exit(1);
|
| 1168 | }
|
| 1169 | if (ioctl(c1_fd, NS_GET_ID, &c1_id) < 0) {
|
| 1170 | close(c1_fd);
|
| 1171 | close(pipefd[1]);
|
| 1172 | exit(1);
|
| 1173 | }
|
| 1174 | close(c1_fd);
|
| 1175 |
|
| 1176 |
|
| 1177 | |
| 1178 | * by creating a network namespace instead
|
| 1179 | if (unshare(CLONE_NEWNET) < 0) {
|
| 1180 | close(pipefd[1]);
|
| 1181 | exit(1);
|
| 1182 | }
|
| 1183 |
|
| 1184 | int c2_fd = open("/proc/self/ns/net", O_RDONLY);
|
| 1185 | if (c2_fd < 0) {
|
| 1186 | close(pipefd[1]);
|
| 1187 | exit(1);
|
| 1188 | }
|
| 1189 | if (ioctl(c2_fd, NS_GET_ID, &c2_id) < 0) {
|
| 1190 | close(c2_fd);
|
| 1191 | close(pipefd[1]);
|
| 1192 | exit(1);
|
| 1193 | }
|
| 1194 | close(c2_fd);
|
| 1195 |
|
| 1196 |
|
| 1197 | write(pipefd[1], &p_id, sizeof(p_id));
|
| 1198 | write(pipefd[1], &c1_id, sizeof(c1_id));
|
| 1199 | write(pipefd[1], &c2_id, sizeof(c2_id));
|
| 1200 | close(pipefd[1]);
|
| 1201 | exit(0);
|
| 1202 | }
|
| 1203 |
|
| 1204 | close(pipefd[1]);
|
| 1205 |
|
| 1206 |
|
| 1207 | ret = read(pipefd[0], &p_id, sizeof(p_id));
|
| 1208 | if (ret != sizeof(p_id)) {
|
| 1209 | close(pipefd[0]);
|
| 1210 | waitpid(pid, NULL, 0);
|
| 1211 | SKIP(return, "Failed to read parent namespace ID");
|
| 1212 | }
|
| 1213 |
|
| 1214 | ret = read(pipefd[0], &c1_id, sizeof(c1_id));
|
| 1215 | if (ret != sizeof(c1_id)) {
|
| 1216 | close(pipefd[0]);
|
| 1217 | waitpid(pid, NULL, 0);
|
| 1218 | SKIP(return, "Failed to read first child namespace ID");
|
| 1219 | }
|
| 1220 |
|
| 1221 | ret = read(pipefd[0], &c2_id, sizeof(c2_id));
|
| 1222 | close(pipefd[0]);
|
| 1223 | if (ret != sizeof(c2_id)) {
|
| 1224 | waitpid(pid, NULL, 0);
|
| 1225 | SKIP(return, "Failed to read second child namespace ID");
|
| 1226 | }
|
| 1227 |
|
| 1228 |
|
| 1229 | p_handle = (struct file_handle *)p_buf;
|
| 1230 | p_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1231 | p_handle->handle_type = FILEID_NSFS;
|
| 1232 | struct nsfs_file_handle *p_fh = (struct nsfs_file_handle *)p_handle->f_handle;
|
| 1233 | p_fh->ns_id = p_id;
|
| 1234 | p_fh->ns_type = 0;
|
| 1235 | p_fh->ns_inum = 0;
|
| 1236 |
|
| 1237 | c1_handle = (struct file_handle *)c1_buf;
|
| 1238 | c1_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1239 | c1_handle->handle_type = FILEID_NSFS;
|
| 1240 | struct nsfs_file_handle *c1_fh = (struct nsfs_file_handle *)c1_handle->f_handle;
|
| 1241 | c1_fh->ns_id = c1_id;
|
| 1242 | c1_fh->ns_type = 0;
|
| 1243 | c1_fh->ns_inum = 0;
|
| 1244 |
|
| 1245 | c2_handle = (struct file_handle *)c2_buf;
|
| 1246 | c2_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1247 | c2_handle->handle_type = FILEID_NSFS;
|
| 1248 | struct nsfs_file_handle *c2_fh = (struct nsfs_file_handle *)c2_handle->f_handle;
|
| 1249 | c2_fh->ns_id = c2_id;
|
| 1250 | c2_fh->ns_type = 0;
|
| 1251 | c2_fh->ns_inum = 0;
|
| 1252 |
|
| 1253 |
|
| 1254 | int c1_fd = open_by_handle_at(FD_NSFS_ROOT, c1_handle, O_RDONLY);
|
| 1255 | int c2_fd = open_by_handle_at(FD_NSFS_ROOT, c2_handle, O_RDONLY);
|
| 1256 |
|
| 1257 | if (c1_fd < 0 || c2_fd < 0) {
|
| 1258 | if (c1_fd >= 0) close(c1_fd);
|
| 1259 | if (c2_fd >= 0) close(c2_fd);
|
| 1260 | waitpid(pid, NULL, 0);
|
| 1261 | SKIP(return, "Failed to open child namespaces");
|
| 1262 | }
|
| 1263 |
|
| 1264 | waitpid(pid, &status, 0);
|
| 1265 | ASSERT_TRUE(WIFEXITED(status));
|
| 1266 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 1267 |
|
| 1268 |
|
| 1269 | TH_LOG("Both children active - parent should be active");
|
| 1270 | int p_fd = open_by_handle_at(FD_NSFS_ROOT, p_handle, O_RDONLY);
|
| 1271 | ASSERT_GE(p_fd, 0);
|
| 1272 | close(p_fd);
|
| 1273 |
|
| 1274 |
|
| 1275 | TH_LOG("Closing first child - parent should still be active");
|
| 1276 | close(c1_fd);
|
| 1277 | p_fd = open_by_handle_at(FD_NSFS_ROOT, p_handle, O_RDONLY);
|
| 1278 | ASSERT_GE(p_fd, 0);
|
| 1279 | close(p_fd);
|
| 1280 |
|
| 1281 |
|
| 1282 | TH_LOG("Closing second child - parent should become inactive");
|
| 1283 | close(c2_fd);
|
| 1284 | p_fd = open_by_handle_at(FD_NSFS_ROOT, p_handle, O_RDONLY);
|
| 1285 | ASSERT_LT(p_fd, 0);
|
| 1286 | }
|
| 1287 |
|
| 1288 | |
| 1289 | * Test that different namespace types with same owner all contribute
|
| 1290 | * active references to the owning user namespace.
|
| 1291 |
|
| 1292 | TEST(ns_different_types_same_owner)
|
| 1293 | {
|
| 1294 | struct file_handle *u_handle, *n_handle, *ut_handle;
|
| 1295 | int ret, pipefd[2];
|
| 1296 | pid_t pid;
|
| 1297 | int status;
|
| 1298 | __u64 u_id, n_id, ut_id;
|
| 1299 | char u_buf[sizeof(*u_handle) + MAX_HANDLE_SZ];
|
| 1300 | char n_buf[sizeof(*n_handle) + MAX_HANDLE_SZ];
|
| 1301 | char ut_buf[sizeof(*ut_handle) + MAX_HANDLE_SZ];
|
| 1302 |
|
| 1303 | ASSERT_EQ(pipe(pipefd), 0);
|
| 1304 | pid = fork();
|
| 1305 | ASSERT_GE(pid, 0);
|
| 1306 |
|
| 1307 | if (pid == 0) {
|
| 1308 | close(pipefd[0]);
|
| 1309 |
|
| 1310 |
|
| 1311 | if (setup_userns() < 0) {
|
| 1312 | close(pipefd[1]);
|
| 1313 | exit(1);
|
| 1314 | }
|
| 1315 |
|
| 1316 | int u_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 1317 | if (u_fd < 0) {
|
| 1318 | close(pipefd[1]);
|
| 1319 | exit(1);
|
| 1320 | }
|
| 1321 | if (ioctl(u_fd, NS_GET_ID, &u_id) < 0) {
|
| 1322 | close(u_fd);
|
| 1323 | close(pipefd[1]);
|
| 1324 | exit(1);
|
| 1325 | }
|
| 1326 | close(u_fd);
|
| 1327 |
|
| 1328 |
|
| 1329 | if (unshare(CLONE_NEWNET) < 0) {
|
| 1330 | close(pipefd[1]);
|
| 1331 | exit(1);
|
| 1332 | }
|
| 1333 |
|
| 1334 | int n_fd = open("/proc/self/ns/net", O_RDONLY);
|
| 1335 | if (n_fd < 0) {
|
| 1336 | close(pipefd[1]);
|
| 1337 | exit(1);
|
| 1338 | }
|
| 1339 | if (ioctl(n_fd, NS_GET_ID, &n_id) < 0) {
|
| 1340 | close(n_fd);
|
| 1341 | close(pipefd[1]);
|
| 1342 | exit(1);
|
| 1343 | }
|
| 1344 | close(n_fd);
|
| 1345 |
|
| 1346 |
|
| 1347 | if (unshare(CLONE_NEWUTS) < 0) {
|
| 1348 | close(pipefd[1]);
|
| 1349 | exit(1);
|
| 1350 | }
|
| 1351 |
|
| 1352 | int ut_fd = open("/proc/self/ns/uts", O_RDONLY);
|
| 1353 | if (ut_fd < 0) {
|
| 1354 | close(pipefd[1]);
|
| 1355 | exit(1);
|
| 1356 | }
|
| 1357 | if (ioctl(ut_fd, NS_GET_ID, &ut_id) < 0) {
|
| 1358 | close(ut_fd);
|
| 1359 | close(pipefd[1]);
|
| 1360 | exit(1);
|
| 1361 | }
|
| 1362 | close(ut_fd);
|
| 1363 |
|
| 1364 |
|
| 1365 | write(pipefd[1], &u_id, sizeof(u_id));
|
| 1366 | write(pipefd[1], &n_id, sizeof(n_id));
|
| 1367 | write(pipefd[1], &ut_id, sizeof(ut_id));
|
| 1368 | close(pipefd[1]);
|
| 1369 | exit(0);
|
| 1370 | }
|
| 1371 |
|
| 1372 | close(pipefd[1]);
|
| 1373 |
|
| 1374 |
|
| 1375 | ret = read(pipefd[0], &u_id, sizeof(u_id));
|
| 1376 | if (ret != sizeof(u_id)) {
|
| 1377 | close(pipefd[0]);
|
| 1378 | waitpid(pid, NULL, 0);
|
| 1379 | SKIP(return, "Failed to read user namespace ID");
|
| 1380 | }
|
| 1381 |
|
| 1382 | ret = read(pipefd[0], &n_id, sizeof(n_id));
|
| 1383 | if (ret != sizeof(n_id)) {
|
| 1384 | close(pipefd[0]);
|
| 1385 | waitpid(pid, NULL, 0);
|
| 1386 | SKIP(return, "Failed to read network namespace ID");
|
| 1387 | }
|
| 1388 |
|
| 1389 | ret = read(pipefd[0], &ut_id, sizeof(ut_id));
|
| 1390 | close(pipefd[0]);
|
| 1391 | if (ret != sizeof(ut_id)) {
|
| 1392 | waitpid(pid, NULL, 0);
|
| 1393 | SKIP(return, "Failed to read UTS namespace ID");
|
| 1394 | }
|
| 1395 |
|
| 1396 |
|
| 1397 | u_handle = (struct file_handle *)u_buf;
|
| 1398 | u_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1399 | u_handle->handle_type = FILEID_NSFS;
|
| 1400 | struct nsfs_file_handle *u_fh = (struct nsfs_file_handle *)u_handle->f_handle;
|
| 1401 | u_fh->ns_id = u_id;
|
| 1402 | u_fh->ns_type = 0;
|
| 1403 | u_fh->ns_inum = 0;
|
| 1404 |
|
| 1405 | n_handle = (struct file_handle *)n_buf;
|
| 1406 | n_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1407 | n_handle->handle_type = FILEID_NSFS;
|
| 1408 | struct nsfs_file_handle *n_fh = (struct nsfs_file_handle *)n_handle->f_handle;
|
| 1409 | n_fh->ns_id = n_id;
|
| 1410 | n_fh->ns_type = 0;
|
| 1411 | n_fh->ns_inum = 0;
|
| 1412 |
|
| 1413 | ut_handle = (struct file_handle *)ut_buf;
|
| 1414 | ut_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1415 | ut_handle->handle_type = FILEID_NSFS;
|
| 1416 | struct nsfs_file_handle *ut_fh = (struct nsfs_file_handle *)ut_handle->f_handle;
|
| 1417 | ut_fh->ns_id = ut_id;
|
| 1418 | ut_fh->ns_type = 0;
|
| 1419 | ut_fh->ns_inum = 0;
|
| 1420 |
|
| 1421 |
|
| 1422 | int n_fd = open_by_handle_at(FD_NSFS_ROOT, n_handle, O_RDONLY);
|
| 1423 | int ut_fd = open_by_handle_at(FD_NSFS_ROOT, ut_handle, O_RDONLY);
|
| 1424 |
|
| 1425 | if (n_fd < 0 || ut_fd < 0) {
|
| 1426 | if (n_fd >= 0) close(n_fd);
|
| 1427 | if (ut_fd >= 0) close(ut_fd);
|
| 1428 | waitpid(pid, NULL, 0);
|
| 1429 | SKIP(return, "Failed to open namespaces");
|
| 1430 | }
|
| 1431 |
|
| 1432 | waitpid(pid, &status, 0);
|
| 1433 | ASSERT_TRUE(WIFEXITED(status));
|
| 1434 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 1435 |
|
| 1436 | |
| 1437 | * Both network and UTS namespaces are active.
|
| 1438 | * User namespace should be active (gets 2 active refs).
|
| 1439 |
|
| 1440 | TH_LOG("Both net and uts active - user namespace should be active");
|
| 1441 | int u_fd = open_by_handle_at(FD_NSFS_ROOT, u_handle, O_RDONLY);
|
| 1442 | ASSERT_GE(u_fd, 0);
|
| 1443 | close(u_fd);
|
| 1444 |
|
| 1445 |
|
| 1446 | TH_LOG("Closing network ns - user ns should still be active (uts still active)");
|
| 1447 | close(n_fd);
|
| 1448 | u_fd = open_by_handle_at(FD_NSFS_ROOT, u_handle, O_RDONLY);
|
| 1449 | ASSERT_GE(u_fd, 0);
|
| 1450 | close(u_fd);
|
| 1451 |
|
| 1452 |
|
| 1453 | TH_LOG("Closing uts ns - user ns should become inactive");
|
| 1454 | close(ut_fd);
|
| 1455 | u_fd = open_by_handle_at(FD_NSFS_ROOT, u_handle, O_RDONLY);
|
| 1456 | ASSERT_LT(u_fd, 0);
|
| 1457 | }
|
| 1458 |
|
| 1459 | |
| 1460 | * Test hierarchical propagation with deep namespace hierarchy.
|
| 1461 | * Create: init_user_ns -> user_A -> user_B -> net_ns
|
| 1462 | * When net_ns is active, both user_A and user_B should be active.
|
| 1463 | * This verifies the conditional recursion in __ns_ref_active_put() works.
|
| 1464 |
|
| 1465 | TEST(ns_deep_hierarchy_propagation)
|
| 1466 | {
|
| 1467 | struct file_handle *ua_handle, *ub_handle, *net_handle;
|
| 1468 | int ret, pipefd[2];
|
| 1469 | pid_t pid;
|
| 1470 | int status;
|
| 1471 | __u64 ua_id, ub_id, net_id;
|
| 1472 | char ua_buf[sizeof(*ua_handle) + MAX_HANDLE_SZ];
|
| 1473 | char ub_buf[sizeof(*ub_handle) + MAX_HANDLE_SZ];
|
| 1474 | char net_buf[sizeof(*net_handle) + MAX_HANDLE_SZ];
|
| 1475 |
|
| 1476 | ASSERT_EQ(pipe(pipefd), 0);
|
| 1477 | pid = fork();
|
| 1478 | ASSERT_GE(pid, 0);
|
| 1479 |
|
| 1480 | if (pid == 0) {
|
| 1481 | close(pipefd[0]);
|
| 1482 |
|
| 1483 |
|
| 1484 | if (setup_userns() < 0) {
|
| 1485 | close(pipefd[1]);
|
| 1486 | exit(1);
|
| 1487 | }
|
| 1488 |
|
| 1489 | int ua_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 1490 | if (ua_fd < 0) {
|
| 1491 | close(pipefd[1]);
|
| 1492 | exit(1);
|
| 1493 | }
|
| 1494 | if (ioctl(ua_fd, NS_GET_ID, &ua_id) < 0) {
|
| 1495 | close(ua_fd);
|
| 1496 | close(pipefd[1]);
|
| 1497 | exit(1);
|
| 1498 | }
|
| 1499 | close(ua_fd);
|
| 1500 |
|
| 1501 | if (setup_userns() < 0) {
|
| 1502 | close(pipefd[1]);
|
| 1503 | exit(1);
|
| 1504 | }
|
| 1505 |
|
| 1506 | int ub_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 1507 | if (ub_fd < 0) {
|
| 1508 | close(pipefd[1]);
|
| 1509 | exit(1);
|
| 1510 | }
|
| 1511 | if (ioctl(ub_fd, NS_GET_ID, &ub_id) < 0) {
|
| 1512 | close(ub_fd);
|
| 1513 | close(pipefd[1]);
|
| 1514 | exit(1);
|
| 1515 | }
|
| 1516 | close(ub_fd);
|
| 1517 |
|
| 1518 | if (unshare(CLONE_NEWNET) < 0) {
|
| 1519 | close(pipefd[1]);
|
| 1520 | exit(1);
|
| 1521 | }
|
| 1522 |
|
| 1523 | int net_fd = open("/proc/self/ns/net", O_RDONLY);
|
| 1524 | if (net_fd < 0) {
|
| 1525 | close(pipefd[1]);
|
| 1526 | exit(1);
|
| 1527 | }
|
| 1528 | if (ioctl(net_fd, NS_GET_ID, &net_id) < 0) {
|
| 1529 | close(net_fd);
|
| 1530 | close(pipefd[1]);
|
| 1531 | exit(1);
|
| 1532 | }
|
| 1533 | close(net_fd);
|
| 1534 |
|
| 1535 |
|
| 1536 | write(pipefd[1], &ua_id, sizeof(ua_id));
|
| 1537 | write(pipefd[1], &ub_id, sizeof(ub_id));
|
| 1538 | write(pipefd[1], &net_id, sizeof(net_id));
|
| 1539 | close(pipefd[1]);
|
| 1540 | exit(0);
|
| 1541 | }
|
| 1542 |
|
| 1543 | close(pipefd[1]);
|
| 1544 |
|
| 1545 |
|
| 1546 | ret = read(pipefd[0], &ua_id, sizeof(ua_id));
|
| 1547 | if (ret != sizeof(ua_id)) {
|
| 1548 | close(pipefd[0]);
|
| 1549 | waitpid(pid, NULL, 0);
|
| 1550 | SKIP(return, "Failed to read user_A namespace ID");
|
| 1551 | }
|
| 1552 |
|
| 1553 | ret = read(pipefd[0], &ub_id, sizeof(ub_id));
|
| 1554 | if (ret != sizeof(ub_id)) {
|
| 1555 | close(pipefd[0]);
|
| 1556 | waitpid(pid, NULL, 0);
|
| 1557 | SKIP(return, "Failed to read user_B namespace ID");
|
| 1558 | }
|
| 1559 |
|
| 1560 | ret = read(pipefd[0], &net_id, sizeof(net_id));
|
| 1561 | close(pipefd[0]);
|
| 1562 | if (ret != sizeof(net_id)) {
|
| 1563 | waitpid(pid, NULL, 0);
|
| 1564 | SKIP(return, "Failed to read network namespace ID");
|
| 1565 | }
|
| 1566 |
|
| 1567 |
|
| 1568 | ua_handle = (struct file_handle *)ua_buf;
|
| 1569 | ua_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1570 | ua_handle->handle_type = FILEID_NSFS;
|
| 1571 | struct nsfs_file_handle *ua_fh = (struct nsfs_file_handle *)ua_handle->f_handle;
|
| 1572 | ua_fh->ns_id = ua_id;
|
| 1573 | ua_fh->ns_type = 0;
|
| 1574 | ua_fh->ns_inum = 0;
|
| 1575 |
|
| 1576 | ub_handle = (struct file_handle *)ub_buf;
|
| 1577 | ub_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1578 | ub_handle->handle_type = FILEID_NSFS;
|
| 1579 | struct nsfs_file_handle *ub_fh = (struct nsfs_file_handle *)ub_handle->f_handle;
|
| 1580 | ub_fh->ns_id = ub_id;
|
| 1581 | ub_fh->ns_type = 0;
|
| 1582 | ub_fh->ns_inum = 0;
|
| 1583 |
|
| 1584 | net_handle = (struct file_handle *)net_buf;
|
| 1585 | net_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1586 | net_handle->handle_type = FILEID_NSFS;
|
| 1587 | struct nsfs_file_handle *net_fh = (struct nsfs_file_handle *)net_handle->f_handle;
|
| 1588 | net_fh->ns_id = net_id;
|
| 1589 | net_fh->ns_type = 0;
|
| 1590 | net_fh->ns_inum = 0;
|
| 1591 |
|
| 1592 |
|
| 1593 | int net_fd = open_by_handle_at(FD_NSFS_ROOT, net_handle, O_RDONLY);
|
| 1594 | if (net_fd < 0) {
|
| 1595 | waitpid(pid, NULL, 0);
|
| 1596 | SKIP(return, "Failed to open network namespace");
|
| 1597 | }
|
| 1598 |
|
| 1599 | waitpid(pid, &status, 0);
|
| 1600 | ASSERT_TRUE(WIFEXITED(status));
|
| 1601 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 1602 |
|
| 1603 |
|
| 1604 | TH_LOG("Testing user_B active (net_ns active causes propagation)");
|
| 1605 | int ub_fd = open_by_handle_at(FD_NSFS_ROOT, ub_handle, O_RDONLY);
|
| 1606 | ASSERT_GE(ub_fd, 0);
|
| 1607 |
|
| 1608 | TH_LOG("Testing user_A active (propagated through user_B)");
|
| 1609 | int ua_fd = open_by_handle_at(FD_NSFS_ROOT, ua_handle, O_RDONLY);
|
| 1610 | ASSERT_GE(ua_fd, 0);
|
| 1611 |
|
| 1612 |
|
| 1613 | TH_LOG("Closing net_ns, user_B should remain active (direct ref held)");
|
| 1614 | close(net_fd);
|
| 1615 | int ub_fd2 = open_by_handle_at(FD_NSFS_ROOT, ub_handle, O_RDONLY);
|
| 1616 | ASSERT_GE(ub_fd2, 0);
|
| 1617 | close(ub_fd2);
|
| 1618 |
|
| 1619 |
|
| 1620 | TH_LOG("Closing user_B, user_A should remain active (direct ref held)");
|
| 1621 | close(ub_fd);
|
| 1622 | int ua_fd2 = open_by_handle_at(FD_NSFS_ROOT, ua_handle, O_RDONLY);
|
| 1623 | ASSERT_GE(ua_fd2, 0);
|
| 1624 | close(ua_fd2);
|
| 1625 |
|
| 1626 |
|
| 1627 | TH_LOG("Closing user_A, all should become inactive");
|
| 1628 | close(ua_fd);
|
| 1629 |
|
| 1630 |
|
| 1631 | ua_fd = open_by_handle_at(FD_NSFS_ROOT, ua_handle, O_RDONLY);
|
| 1632 | ASSERT_LT(ua_fd, 0);
|
| 1633 | }
|
| 1634 |
|
| 1635 | |
| 1636 | * Test that parent stays active as long as ANY child is active.
|
| 1637 | * Create parent user namespace with two child net namespaces.
|
| 1638 | * Parent should remain active until BOTH children are inactive.
|
| 1639 |
|
| 1640 | TEST(ns_parent_multiple_children_refcount)
|
| 1641 | {
|
| 1642 | struct file_handle *parent_handle, *net1_handle, *net2_handle;
|
| 1643 | int ret, pipefd[2], syncpipe[2];
|
| 1644 | pid_t pid;
|
| 1645 | int status;
|
| 1646 | __u64 p_id, n1_id, n2_id;
|
| 1647 | char p_buf[sizeof(*parent_handle) + MAX_HANDLE_SZ];
|
| 1648 | char n1_buf[sizeof(*net1_handle) + MAX_HANDLE_SZ];
|
| 1649 | char n2_buf[sizeof(*net2_handle) + MAX_HANDLE_SZ];
|
| 1650 | char sync_byte;
|
| 1651 |
|
| 1652 | ASSERT_EQ(pipe(pipefd), 0);
|
| 1653 | ASSERT_EQ(pipe(syncpipe), 0);
|
| 1654 | pid = fork();
|
| 1655 | ASSERT_GE(pid, 0);
|
| 1656 |
|
| 1657 | if (pid == 0) {
|
| 1658 | close(pipefd[0]);
|
| 1659 | close(syncpipe[1]);
|
| 1660 |
|
| 1661 |
|
| 1662 | if (setup_userns() < 0) {
|
| 1663 | close(pipefd[1]);
|
| 1664 | exit(1);
|
| 1665 | }
|
| 1666 |
|
| 1667 | int p_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 1668 | if (p_fd < 0) {
|
| 1669 | close(pipefd[1]);
|
| 1670 | exit(1);
|
| 1671 | }
|
| 1672 | if (ioctl(p_fd, NS_GET_ID, &p_id) < 0) {
|
| 1673 | close(p_fd);
|
| 1674 | close(pipefd[1]);
|
| 1675 | exit(1);
|
| 1676 | }
|
| 1677 | close(p_fd);
|
| 1678 |
|
| 1679 |
|
| 1680 | if (unshare(CLONE_NEWNET) < 0) {
|
| 1681 | close(pipefd[1]);
|
| 1682 | close(syncpipe[0]);
|
| 1683 | exit(1);
|
| 1684 | }
|
| 1685 |
|
| 1686 | int n1_fd = open("/proc/self/ns/net", O_RDONLY);
|
| 1687 | if (n1_fd < 0) {
|
| 1688 | close(pipefd[1]);
|
| 1689 | close(syncpipe[0]);
|
| 1690 | exit(1);
|
| 1691 | }
|
| 1692 | if (ioctl(n1_fd, NS_GET_ID, &n1_id) < 0) {
|
| 1693 | close(n1_fd);
|
| 1694 | close(pipefd[1]);
|
| 1695 | close(syncpipe[0]);
|
| 1696 | exit(1);
|
| 1697 | }
|
| 1698 |
|
| 1699 |
|
| 1700 |
|
| 1701 | if (unshare(CLONE_NEWNET) < 0) {
|
| 1702 | close(n1_fd);
|
| 1703 | close(pipefd[1]);
|
| 1704 | close(syncpipe[0]);
|
| 1705 | exit(1);
|
| 1706 | }
|
| 1707 |
|
| 1708 | int n2_fd = open("/proc/self/ns/net", O_RDONLY);
|
| 1709 | if (n2_fd < 0) {
|
| 1710 | close(n1_fd);
|
| 1711 | close(pipefd[1]);
|
| 1712 | close(syncpipe[0]);
|
| 1713 | exit(1);
|
| 1714 | }
|
| 1715 | if (ioctl(n2_fd, NS_GET_ID, &n2_id) < 0) {
|
| 1716 | close(n1_fd);
|
| 1717 | close(n2_fd);
|
| 1718 | close(pipefd[1]);
|
| 1719 | close(syncpipe[0]);
|
| 1720 | exit(1);
|
| 1721 | }
|
| 1722 |
|
| 1723 |
|
| 1724 |
|
| 1725 | write(pipefd[1], &p_id, sizeof(p_id));
|
| 1726 | write(pipefd[1], &n1_id, sizeof(n1_id));
|
| 1727 | write(pipefd[1], &n2_id, sizeof(n2_id));
|
| 1728 | close(pipefd[1]);
|
| 1729 |
|
| 1730 |
|
| 1731 | read(syncpipe[0], &sync_byte, 1);
|
| 1732 | close(syncpipe[0]);
|
| 1733 | exit(0);
|
| 1734 | }
|
| 1735 |
|
| 1736 | close(pipefd[1]);
|
| 1737 | close(syncpipe[0]);
|
| 1738 |
|
| 1739 |
|
| 1740 | ret = read(pipefd[0], &p_id, sizeof(p_id));
|
| 1741 | if (ret != sizeof(p_id)) {
|
| 1742 | close(pipefd[0]);
|
| 1743 | waitpid(pid, NULL, 0);
|
| 1744 | SKIP(return, "Failed to read parent namespace ID");
|
| 1745 | }
|
| 1746 |
|
| 1747 | ret = read(pipefd[0], &n1_id, sizeof(n1_id));
|
| 1748 | if (ret != sizeof(n1_id)) {
|
| 1749 | close(pipefd[0]);
|
| 1750 | waitpid(pid, NULL, 0);
|
| 1751 | SKIP(return, "Failed to read first network namespace ID");
|
| 1752 | }
|
| 1753 |
|
| 1754 | ret = read(pipefd[0], &n2_id, sizeof(n2_id));
|
| 1755 | close(pipefd[0]);
|
| 1756 | if (ret != sizeof(n2_id)) {
|
| 1757 | waitpid(pid, NULL, 0);
|
| 1758 | SKIP(return, "Failed to read second network namespace ID");
|
| 1759 | }
|
| 1760 |
|
| 1761 |
|
| 1762 | parent_handle = (struct file_handle *)p_buf;
|
| 1763 | parent_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1764 | parent_handle->handle_type = FILEID_NSFS;
|
| 1765 | struct nsfs_file_handle *p_fh = (struct nsfs_file_handle *)parent_handle->f_handle;
|
| 1766 | p_fh->ns_id = p_id;
|
| 1767 | p_fh->ns_type = 0;
|
| 1768 | p_fh->ns_inum = 0;
|
| 1769 |
|
| 1770 | net1_handle = (struct file_handle *)n1_buf;
|
| 1771 | net1_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1772 | net1_handle->handle_type = FILEID_NSFS;
|
| 1773 | struct nsfs_file_handle *n1_fh = (struct nsfs_file_handle *)net1_handle->f_handle;
|
| 1774 | n1_fh->ns_id = n1_id;
|
| 1775 | n1_fh->ns_type = 0;
|
| 1776 | n1_fh->ns_inum = 0;
|
| 1777 |
|
| 1778 | net2_handle = (struct file_handle *)n2_buf;
|
| 1779 | net2_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1780 | net2_handle->handle_type = FILEID_NSFS;
|
| 1781 | struct nsfs_file_handle *n2_fh = (struct nsfs_file_handle *)net2_handle->f_handle;
|
| 1782 | n2_fh->ns_id = n2_id;
|
| 1783 | n2_fh->ns_type = 0;
|
| 1784 | n2_fh->ns_inum = 0;
|
| 1785 |
|
| 1786 |
|
| 1787 | int n1_fd = open_by_handle_at(FD_NSFS_ROOT, net1_handle, O_RDONLY);
|
| 1788 | int n2_fd = open_by_handle_at(FD_NSFS_ROOT, net2_handle, O_RDONLY);
|
| 1789 | if (n1_fd < 0 || n2_fd < 0) {
|
| 1790 | if (n1_fd >= 0) close(n1_fd);
|
| 1791 | if (n2_fd >= 0) close(n2_fd);
|
| 1792 | sync_byte = 'G';
|
| 1793 | write(syncpipe[1], &sync_byte, 1);
|
| 1794 | close(syncpipe[1]);
|
| 1795 | waitpid(pid, NULL, 0);
|
| 1796 | SKIP(return, "Failed to open net namespaces");
|
| 1797 | }
|
| 1798 |
|
| 1799 |
|
| 1800 | sync_byte = 'G';
|
| 1801 | write(syncpipe[1], &sync_byte, 1);
|
| 1802 | close(syncpipe[1]);
|
| 1803 |
|
| 1804 |
|
| 1805 | waitpid(pid, &status, 0);
|
| 1806 | ASSERT_TRUE(WIFEXITED(status));
|
| 1807 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 1808 |
|
| 1809 |
|
| 1810 | TH_LOG("Both net namespaces active - parent should be active");
|
| 1811 | int p_fd = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY);
|
| 1812 | ASSERT_GE(p_fd, 0);
|
| 1813 | close(p_fd);
|
| 1814 |
|
| 1815 |
|
| 1816 | TH_LOG("Closing first net ns - parent should still be active");
|
| 1817 | close(n1_fd);
|
| 1818 | p_fd = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY);
|
| 1819 | ASSERT_GE(p_fd, 0);
|
| 1820 | close(p_fd);
|
| 1821 |
|
| 1822 |
|
| 1823 | TH_LOG("Closing second net ns - parent should become inactive");
|
| 1824 | close(n2_fd);
|
| 1825 | p_fd = open_by_handle_at(FD_NSFS_ROOT, parent_handle, O_RDONLY);
|
| 1826 | ASSERT_LT(p_fd, 0);
|
| 1827 | }
|
| 1828 |
|
| 1829 | |
| 1830 | * Test that user namespace as a child also propagates correctly.
|
| 1831 | * Create user_A -> user_B, verify when user_B is active that user_A
|
| 1832 | * is also active. This is different from non-user namespace children.
|
| 1833 |
|
| 1834 | TEST(ns_userns_child_propagation)
|
| 1835 | {
|
| 1836 | struct file_handle *ua_handle, *ub_handle;
|
| 1837 | int ret, pipefd[2];
|
| 1838 | pid_t pid;
|
| 1839 | int status;
|
| 1840 | __u64 ua_id, ub_id;
|
| 1841 | char ua_buf[sizeof(*ua_handle) + MAX_HANDLE_SZ];
|
| 1842 | char ub_buf[sizeof(*ub_handle) + MAX_HANDLE_SZ];
|
| 1843 |
|
| 1844 | ASSERT_EQ(pipe(pipefd), 0);
|
| 1845 | pid = fork();
|
| 1846 | ASSERT_GE(pid, 0);
|
| 1847 |
|
| 1848 | if (pid == 0) {
|
| 1849 | close(pipefd[0]);
|
| 1850 |
|
| 1851 |
|
| 1852 | if (setup_userns() < 0) {
|
| 1853 | close(pipefd[1]);
|
| 1854 | exit(1);
|
| 1855 | }
|
| 1856 |
|
| 1857 | int ua_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 1858 | if (ua_fd < 0) {
|
| 1859 | close(pipefd[1]);
|
| 1860 | exit(1);
|
| 1861 | }
|
| 1862 | if (ioctl(ua_fd, NS_GET_ID, &ua_id) < 0) {
|
| 1863 | close(ua_fd);
|
| 1864 | close(pipefd[1]);
|
| 1865 | exit(1);
|
| 1866 | }
|
| 1867 | close(ua_fd);
|
| 1868 |
|
| 1869 |
|
| 1870 | if (setup_userns() < 0) {
|
| 1871 | close(pipefd[1]);
|
| 1872 | exit(1);
|
| 1873 | }
|
| 1874 |
|
| 1875 | int ub_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 1876 | if (ub_fd < 0) {
|
| 1877 | close(pipefd[1]);
|
| 1878 | exit(1);
|
| 1879 | }
|
| 1880 | if (ioctl(ub_fd, NS_GET_ID, &ub_id) < 0) {
|
| 1881 | close(ub_fd);
|
| 1882 | close(pipefd[1]);
|
| 1883 | exit(1);
|
| 1884 | }
|
| 1885 | close(ub_fd);
|
| 1886 |
|
| 1887 |
|
| 1888 | write(pipefd[1], &ua_id, sizeof(ua_id));
|
| 1889 | write(pipefd[1], &ub_id, sizeof(ub_id));
|
| 1890 | close(pipefd[1]);
|
| 1891 | exit(0);
|
| 1892 | }
|
| 1893 |
|
| 1894 | close(pipefd[1]);
|
| 1895 |
|
| 1896 |
|
| 1897 | ret = read(pipefd[0], &ua_id, sizeof(ua_id));
|
| 1898 | if (ret != sizeof(ua_id)) {
|
| 1899 | close(pipefd[0]);
|
| 1900 | waitpid(pid, NULL, 0);
|
| 1901 | SKIP(return, "Failed to read user_A namespace ID");
|
| 1902 | }
|
| 1903 |
|
| 1904 | ret = read(pipefd[0], &ub_id, sizeof(ub_id));
|
| 1905 | close(pipefd[0]);
|
| 1906 | if (ret != sizeof(ub_id)) {
|
| 1907 | waitpid(pid, NULL, 0);
|
| 1908 | SKIP(return, "Failed to read user_B namespace ID");
|
| 1909 | }
|
| 1910 |
|
| 1911 |
|
| 1912 | ua_handle = (struct file_handle *)ua_buf;
|
| 1913 | ua_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1914 | ua_handle->handle_type = FILEID_NSFS;
|
| 1915 | struct nsfs_file_handle *ua_fh = (struct nsfs_file_handle *)ua_handle->f_handle;
|
| 1916 | ua_fh->ns_id = ua_id;
|
| 1917 | ua_fh->ns_type = 0;
|
| 1918 | ua_fh->ns_inum = 0;
|
| 1919 |
|
| 1920 | ub_handle = (struct file_handle *)ub_buf;
|
| 1921 | ub_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 1922 | ub_handle->handle_type = FILEID_NSFS;
|
| 1923 | struct nsfs_file_handle *ub_fh = (struct nsfs_file_handle *)ub_handle->f_handle;
|
| 1924 | ub_fh->ns_id = ub_id;
|
| 1925 | ub_fh->ns_type = 0;
|
| 1926 | ub_fh->ns_inum = 0;
|
| 1927 |
|
| 1928 |
|
| 1929 | int ub_fd = open_by_handle_at(FD_NSFS_ROOT, ub_handle, O_RDONLY);
|
| 1930 | if (ub_fd < 0) {
|
| 1931 | waitpid(pid, NULL, 0);
|
| 1932 | SKIP(return, "Failed to open user_B");
|
| 1933 | }
|
| 1934 |
|
| 1935 | waitpid(pid, &status, 0);
|
| 1936 | ASSERT_TRUE(WIFEXITED(status));
|
| 1937 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 1938 |
|
| 1939 |
|
| 1940 | TH_LOG("Testing user_A active when child user_B is active");
|
| 1941 | int ua_fd = open_by_handle_at(FD_NSFS_ROOT, ua_handle, O_RDONLY);
|
| 1942 | ASSERT_GE(ua_fd, 0);
|
| 1943 |
|
| 1944 |
|
| 1945 | TH_LOG("Closing user_B");
|
| 1946 | close(ub_fd);
|
| 1947 |
|
| 1948 |
|
| 1949 | int ua_fd2 = open_by_handle_at(FD_NSFS_ROOT, ua_handle, O_RDONLY);
|
| 1950 | ASSERT_GE(ua_fd2, 0);
|
| 1951 | close(ua_fd2);
|
| 1952 |
|
| 1953 |
|
| 1954 | TH_LOG("Closing user_A - should become inactive");
|
| 1955 | close(ua_fd);
|
| 1956 |
|
| 1957 | ua_fd = open_by_handle_at(FD_NSFS_ROOT, ua_handle, O_RDONLY);
|
| 1958 | ASSERT_LT(ua_fd, 0);
|
| 1959 | }
|
| 1960 |
|
| 1961 | |
| 1962 | * Test different namespace types (net, uts, ipc) all contributing
|
| 1963 | * active references to the same owning user namespace.
|
| 1964 |
|
| 1965 | TEST(ns_mixed_types_same_owner)
|
| 1966 | {
|
| 1967 | struct file_handle *user_handle, *net_handle, *uts_handle;
|
| 1968 | int ret, pipefd[2];
|
| 1969 | pid_t pid;
|
| 1970 | int status;
|
| 1971 | __u64 u_id, n_id, ut_id;
|
| 1972 | char u_buf[sizeof(*user_handle) + MAX_HANDLE_SZ];
|
| 1973 | char n_buf[sizeof(*net_handle) + MAX_HANDLE_SZ];
|
| 1974 | char ut_buf[sizeof(*uts_handle) + MAX_HANDLE_SZ];
|
| 1975 |
|
| 1976 | ASSERT_EQ(pipe(pipefd), 0);
|
| 1977 | pid = fork();
|
| 1978 | ASSERT_GE(pid, 0);
|
| 1979 |
|
| 1980 | if (pid == 0) {
|
| 1981 | close(pipefd[0]);
|
| 1982 |
|
| 1983 | if (setup_userns() < 0) {
|
| 1984 | close(pipefd[1]);
|
| 1985 | exit(1);
|
| 1986 | }
|
| 1987 |
|
| 1988 | int u_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 1989 | if (u_fd < 0) {
|
| 1990 | close(pipefd[1]);
|
| 1991 | exit(1);
|
| 1992 | }
|
| 1993 | if (ioctl(u_fd, NS_GET_ID, &u_id) < 0) {
|
| 1994 | close(u_fd);
|
| 1995 | close(pipefd[1]);
|
| 1996 | exit(1);
|
| 1997 | }
|
| 1998 | close(u_fd);
|
| 1999 |
|
| 2000 | if (unshare(CLONE_NEWNET) < 0) {
|
| 2001 | close(pipefd[1]);
|
| 2002 | exit(1);
|
| 2003 | }
|
| 2004 |
|
| 2005 | int n_fd = open("/proc/self/ns/net", O_RDONLY);
|
| 2006 | if (n_fd < 0) {
|
| 2007 | close(pipefd[1]);
|
| 2008 | exit(1);
|
| 2009 | }
|
| 2010 | if (ioctl(n_fd, NS_GET_ID, &n_id) < 0) {
|
| 2011 | close(n_fd);
|
| 2012 | close(pipefd[1]);
|
| 2013 | exit(1);
|
| 2014 | }
|
| 2015 | close(n_fd);
|
| 2016 |
|
| 2017 | if (unshare(CLONE_NEWUTS) < 0) {
|
| 2018 | close(pipefd[1]);
|
| 2019 | exit(1);
|
| 2020 | }
|
| 2021 |
|
| 2022 | int ut_fd = open("/proc/self/ns/uts", O_RDONLY);
|
| 2023 | if (ut_fd < 0) {
|
| 2024 | close(pipefd[1]);
|
| 2025 | exit(1);
|
| 2026 | }
|
| 2027 | if (ioctl(ut_fd, NS_GET_ID, &ut_id) < 0) {
|
| 2028 | close(ut_fd);
|
| 2029 | close(pipefd[1]);
|
| 2030 | exit(1);
|
| 2031 | }
|
| 2032 | close(ut_fd);
|
| 2033 |
|
| 2034 |
|
| 2035 | write(pipefd[1], &u_id, sizeof(u_id));
|
| 2036 | write(pipefd[1], &n_id, sizeof(n_id));
|
| 2037 | write(pipefd[1], &ut_id, sizeof(ut_id));
|
| 2038 | close(pipefd[1]);
|
| 2039 | exit(0);
|
| 2040 | }
|
| 2041 |
|
| 2042 | close(pipefd[1]);
|
| 2043 |
|
| 2044 |
|
| 2045 | ret = read(pipefd[0], &u_id, sizeof(u_id));
|
| 2046 | if (ret != sizeof(u_id)) {
|
| 2047 | close(pipefd[0]);
|
| 2048 | waitpid(pid, NULL, 0);
|
| 2049 | SKIP(return, "Failed to read user namespace ID");
|
| 2050 | }
|
| 2051 |
|
| 2052 | ret = read(pipefd[0], &n_id, sizeof(n_id));
|
| 2053 | if (ret != sizeof(n_id)) {
|
| 2054 | close(pipefd[0]);
|
| 2055 | waitpid(pid, NULL, 0);
|
| 2056 | SKIP(return, "Failed to read network namespace ID");
|
| 2057 | }
|
| 2058 |
|
| 2059 | ret = read(pipefd[0], &ut_id, sizeof(ut_id));
|
| 2060 | close(pipefd[0]);
|
| 2061 | if (ret != sizeof(ut_id)) {
|
| 2062 | waitpid(pid, NULL, 0);
|
| 2063 | SKIP(return, "Failed to read UTS namespace ID");
|
| 2064 | }
|
| 2065 |
|
| 2066 |
|
| 2067 | user_handle = (struct file_handle *)u_buf;
|
| 2068 | user_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 2069 | user_handle->handle_type = FILEID_NSFS;
|
| 2070 | struct nsfs_file_handle *u_fh = (struct nsfs_file_handle *)user_handle->f_handle;
|
| 2071 | u_fh->ns_id = u_id;
|
| 2072 | u_fh->ns_type = 0;
|
| 2073 | u_fh->ns_inum = 0;
|
| 2074 |
|
| 2075 | net_handle = (struct file_handle *)n_buf;
|
| 2076 | net_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 2077 | net_handle->handle_type = FILEID_NSFS;
|
| 2078 | struct nsfs_file_handle *n_fh = (struct nsfs_file_handle *)net_handle->f_handle;
|
| 2079 | n_fh->ns_id = n_id;
|
| 2080 | n_fh->ns_type = 0;
|
| 2081 | n_fh->ns_inum = 0;
|
| 2082 |
|
| 2083 | uts_handle = (struct file_handle *)ut_buf;
|
| 2084 | uts_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 2085 | uts_handle->handle_type = FILEID_NSFS;
|
| 2086 | struct nsfs_file_handle *ut_fh = (struct nsfs_file_handle *)uts_handle->f_handle;
|
| 2087 | ut_fh->ns_id = ut_id;
|
| 2088 | ut_fh->ns_type = 0;
|
| 2089 | ut_fh->ns_inum = 0;
|
| 2090 |
|
| 2091 |
|
| 2092 | int n_fd = open_by_handle_at(FD_NSFS_ROOT, net_handle, O_RDONLY);
|
| 2093 | int ut_fd = open_by_handle_at(FD_NSFS_ROOT, uts_handle, O_RDONLY);
|
| 2094 | if (n_fd < 0 || ut_fd < 0) {
|
| 2095 | if (n_fd >= 0) close(n_fd);
|
| 2096 | if (ut_fd >= 0) close(ut_fd);
|
| 2097 | waitpid(pid, NULL, 0);
|
| 2098 | SKIP(return, "Failed to open namespaces");
|
| 2099 | }
|
| 2100 |
|
| 2101 | waitpid(pid, &status, 0);
|
| 2102 | ASSERT_TRUE(WIFEXITED(status));
|
| 2103 | ASSERT_EQ(WEXITSTATUS(status), 0);
|
| 2104 |
|
| 2105 |
|
| 2106 | TH_LOG("Both net and uts active - user ns should be active");
|
| 2107 | int u_fd = open_by_handle_at(FD_NSFS_ROOT, user_handle, O_RDONLY);
|
| 2108 | ASSERT_GE(u_fd, 0);
|
| 2109 | close(u_fd);
|
| 2110 |
|
| 2111 |
|
| 2112 | TH_LOG("Closing net - user ns should still be active");
|
| 2113 | close(n_fd);
|
| 2114 | u_fd = open_by_handle_at(FD_NSFS_ROOT, user_handle, O_RDONLY);
|
| 2115 | ASSERT_GE(u_fd, 0);
|
| 2116 | close(u_fd);
|
| 2117 |
|
| 2118 |
|
| 2119 | TH_LOG("Closing uts - user ns should become inactive");
|
| 2120 | close(ut_fd);
|
| 2121 | u_fd = open_by_handle_at(FD_NSFS_ROOT, user_handle, O_RDONLY);
|
| 2122 | ASSERT_LT(u_fd, 0);
|
| 2123 | }
|
| 2124 |
|
| 2125 |
|
| 2126 | struct thread_ns_info {
|
| 2127 | __u64 ns_id;
|
| 2128 | int pidfd;
|
| 2129 | int pipefd;
|
| 2130 | int syncfd_read;
|
| 2131 | int syncfd_write;
|
| 2132 | int exit_code;
|
| 2133 | };
|
| 2134 |
|
| 2135 | static void *thread_create_namespace(void *arg)
|
| 2136 | {
|
| 2137 | struct thread_ns_info *info = (struct thread_ns_info *)arg;
|
| 2138 | int ret;
|
| 2139 |
|
| 2140 |
|
| 2141 | ret = unshare(CLONE_NEWNET);
|
| 2142 | if (ret < 0) {
|
| 2143 | info->exit_code = 1;
|
| 2144 | return NULL;
|
| 2145 | }
|
| 2146 |
|
| 2147 |
|
| 2148 | int fd = open("/proc/thread-self/ns/net", O_RDONLY);
|
| 2149 | if (fd < 0) {
|
| 2150 | info->exit_code = 2;
|
| 2151 | return NULL;
|
| 2152 | }
|
| 2153 |
|
| 2154 | ret = ioctl(fd, NS_GET_ID, &info->ns_id);
|
| 2155 | close(fd);
|
| 2156 | if (ret < 0) {
|
| 2157 | info->exit_code = 3;
|
| 2158 | return NULL;
|
| 2159 | }
|
| 2160 |
|
| 2161 |
|
| 2162 | info->pidfd = syscall(__NR_pidfd_open, gettid(), PIDFD_THREAD);
|
| 2163 | if (info->pidfd < 0) {
|
| 2164 | info->exit_code = 6;
|
| 2165 | return NULL;
|
| 2166 | }
|
| 2167 |
|
| 2168 |
|
| 2169 | if (write(info->pipefd, &info->ns_id, sizeof(info->ns_id)) != sizeof(info->ns_id)) {
|
| 2170 | info->exit_code = 4;
|
| 2171 | return NULL;
|
| 2172 | }
|
| 2173 |
|
| 2174 |
|
| 2175 | char sync_byte;
|
| 2176 | if (read(info->syncfd_read, &sync_byte, 1) != 1) {
|
| 2177 | info->exit_code = 5;
|
| 2178 | return NULL;
|
| 2179 | }
|
| 2180 |
|
| 2181 | info->exit_code = 0;
|
| 2182 | return NULL;
|
| 2183 | }
|
| 2184 |
|
| 2185 | |
| 2186 | * pthread_join() returns once exit_mm() wakes the CLEARTID futex, before
|
| 2187 | * exit_nsproxy_namespaces() drops the namespace reference. A pidfd becomes
|
| 2188 | * readable only from exit_notify(), after that point.
|
| 2189 |
|
| 2190 | static int wait_thread_exited(int pidfd)
|
| 2191 | {
|
| 2192 | struct pollfd pfd = { .fd = pidfd, .events = POLLIN };
|
| 2193 | int ret = poll(&pfd, 1, -1);
|
| 2194 |
|
| 2195 | close(pidfd);
|
| 2196 | return ret;
|
| 2197 | }
|
| 2198 |
|
| 2199 | |
| 2200 | * Test that namespace becomes inactive after thread exits.
|
| 2201 | * This verifies active reference counting works with threads, not just processes.
|
| 2202 |
|
| 2203 | TEST(thread_ns_inactive_after_exit)
|
| 2204 | {
|
| 2205 | pthread_t thread;
|
| 2206 | struct thread_ns_info info;
|
| 2207 | struct file_handle *handle;
|
| 2208 | int pipefd[2];
|
| 2209 | int syncpipe[2];
|
| 2210 | int ret;
|
| 2211 | char sync_byte;
|
| 2212 | char buf[sizeof(*handle) + MAX_HANDLE_SZ];
|
| 2213 |
|
| 2214 | ASSERT_EQ(pipe(pipefd), 0);
|
| 2215 | ASSERT_EQ(pipe(syncpipe), 0);
|
| 2216 |
|
| 2217 | info.pipefd = pipefd[1];
|
| 2218 | info.syncfd_read = syncpipe[0];
|
| 2219 | info.syncfd_write = -1;
|
| 2220 | info.exit_code = -1;
|
| 2221 |
|
| 2222 |
|
| 2223 | ret = pthread_create(&thread, NULL, thread_create_namespace, &info);
|
| 2224 | ASSERT_EQ(ret, 0);
|
| 2225 |
|
| 2226 |
|
| 2227 | __u64 ns_id;
|
| 2228 | ret = read(pipefd[0], &ns_id, sizeof(ns_id));
|
| 2229 | if (ret != sizeof(ns_id)) {
|
| 2230 | sync_byte = 'X';
|
| 2231 | write(syncpipe[1], &sync_byte, 1);
|
| 2232 | pthread_join(thread, NULL);
|
| 2233 | close(pipefd[0]);
|
| 2234 | close(pipefd[1]);
|
| 2235 | close(syncpipe[0]);
|
| 2236 | close(syncpipe[1]);
|
| 2237 | SKIP(return, "Failed to read namespace ID from thread");
|
| 2238 | }
|
| 2239 |
|
| 2240 | TH_LOG("Thread created namespace with ID %llu", (unsigned long long)ns_id);
|
| 2241 |
|
| 2242 |
|
| 2243 | handle = (struct file_handle *)buf;
|
| 2244 | handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 2245 | handle->handle_type = FILEID_NSFS;
|
| 2246 | struct nsfs_file_handle *fh = (struct nsfs_file_handle *)handle->f_handle;
|
| 2247 | fh->ns_id = ns_id;
|
| 2248 | fh->ns_type = 0;
|
| 2249 | fh->ns_inum = 0;
|
| 2250 |
|
| 2251 |
|
| 2252 | TH_LOG("Attempting to open namespace while thread is alive (should succeed)");
|
| 2253 | int nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 2254 | ASSERT_GE(nsfd, 0);
|
| 2255 | close(nsfd);
|
| 2256 |
|
| 2257 |
|
| 2258 | TH_LOG("Signaling thread to exit");
|
| 2259 | sync_byte = 'X';
|
| 2260 | ASSERT_EQ(write(syncpipe[1], &sync_byte, 1), 1);
|
| 2261 | close(syncpipe[1]);
|
| 2262 |
|
| 2263 |
|
| 2264 | ASSERT_EQ(wait_thread_exited(info.pidfd), 1);
|
| 2265 | ASSERT_EQ(pthread_join(thread, NULL), 0);
|
| 2266 | close(pipefd[0]);
|
| 2267 | close(pipefd[1]);
|
| 2268 | close(syncpipe[0]);
|
| 2269 |
|
| 2270 | if (info.exit_code != 0)
|
| 2271 | SKIP(return, "Thread failed to create namespace");
|
| 2272 |
|
| 2273 | TH_LOG("Thread exited, namespace should be inactive");
|
| 2274 |
|
| 2275 |
|
| 2276 | nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 2277 | ASSERT_LT(nsfd, 0);
|
| 2278 |
|
| 2279 | TH_LOG("Namespace inactive as expected: %s (errno=%d)", strerror(errno), errno);
|
| 2280 | ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
|
| 2281 | }
|
| 2282 |
|
| 2283 | |
| 2284 | * Test that a namespace remains active while a thread holds an fd to it.
|
| 2285 | * Even after the thread exits, the namespace should remain active as long as
|
| 2286 | * another thread holds a file descriptor to it.
|
| 2287 |
|
| 2288 | TEST(thread_ns_fd_keeps_active)
|
| 2289 | {
|
| 2290 | pthread_t thread;
|
| 2291 | struct thread_ns_info info;
|
| 2292 | struct file_handle *handle;
|
| 2293 | int pipefd[2];
|
| 2294 | int syncpipe[2];
|
| 2295 | int ret;
|
| 2296 | char sync_byte;
|
| 2297 | char buf[sizeof(*handle) + MAX_HANDLE_SZ];
|
| 2298 |
|
| 2299 | ASSERT_EQ(pipe(pipefd), 0);
|
| 2300 | ASSERT_EQ(pipe(syncpipe), 0);
|
| 2301 |
|
| 2302 | info.pipefd = pipefd[1];
|
| 2303 | info.syncfd_read = syncpipe[0];
|
| 2304 | info.syncfd_write = -1;
|
| 2305 | info.exit_code = -1;
|
| 2306 |
|
| 2307 |
|
| 2308 | ret = pthread_create(&thread, NULL, thread_create_namespace, &info);
|
| 2309 | ASSERT_EQ(ret, 0);
|
| 2310 |
|
| 2311 |
|
| 2312 | __u64 ns_id;
|
| 2313 | ret = read(pipefd[0], &ns_id, sizeof(ns_id));
|
| 2314 | if (ret != sizeof(ns_id)) {
|
| 2315 | sync_byte = 'X';
|
| 2316 | write(syncpipe[1], &sync_byte, 1);
|
| 2317 | pthread_join(thread, NULL);
|
| 2318 | close(pipefd[0]);
|
| 2319 | close(pipefd[1]);
|
| 2320 | close(syncpipe[0]);
|
| 2321 | close(syncpipe[1]);
|
| 2322 | SKIP(return, "Failed to read namespace ID from thread");
|
| 2323 | }
|
| 2324 |
|
| 2325 | TH_LOG("Thread created namespace with ID %llu", (unsigned long long)ns_id);
|
| 2326 |
|
| 2327 |
|
| 2328 | handle = (struct file_handle *)buf;
|
| 2329 | handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 2330 | handle->handle_type = FILEID_NSFS;
|
| 2331 | struct nsfs_file_handle *fh = (struct nsfs_file_handle *)handle->f_handle;
|
| 2332 | fh->ns_id = ns_id;
|
| 2333 | fh->ns_type = 0;
|
| 2334 | fh->ns_inum = 0;
|
| 2335 |
|
| 2336 |
|
| 2337 | TH_LOG("Opening namespace while thread is alive");
|
| 2338 | int nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 2339 | ASSERT_GE(nsfd, 0);
|
| 2340 |
|
| 2341 |
|
| 2342 | TH_LOG("Signaling thread to exit");
|
| 2343 | sync_byte = 'X';
|
| 2344 | write(syncpipe[1], &sync_byte, 1);
|
| 2345 | close(syncpipe[1]);
|
| 2346 |
|
| 2347 |
|
| 2348 | ASSERT_EQ(wait_thread_exited(info.pidfd), 1);
|
| 2349 | pthread_join(thread, NULL);
|
| 2350 | close(pipefd[0]);
|
| 2351 | close(pipefd[1]);
|
| 2352 | close(syncpipe[0]);
|
| 2353 |
|
| 2354 | if (info.exit_code != 0) {
|
| 2355 | close(nsfd);
|
| 2356 | SKIP(return, "Thread failed to create namespace");
|
| 2357 | }
|
| 2358 |
|
| 2359 | TH_LOG("Thread exited, but main thread holds fd - namespace should remain active");
|
| 2360 |
|
| 2361 |
|
| 2362 | int nsfd2 = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 2363 | ASSERT_GE(nsfd2, 0);
|
| 2364 |
|
| 2365 |
|
| 2366 | struct stat st1, st2;
|
| 2367 | ASSERT_EQ(fstat(nsfd, &st1), 0);
|
| 2368 | ASSERT_EQ(fstat(nsfd2, &st2), 0);
|
| 2369 | ASSERT_EQ(st1.st_ino, st2.st_ino);
|
| 2370 | close(nsfd2);
|
| 2371 |
|
| 2372 | TH_LOG("Closing fd - namespace should become inactive");
|
| 2373 | close(nsfd);
|
| 2374 |
|
| 2375 |
|
| 2376 | nsfd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
|
| 2377 | ASSERT_LT(nsfd, 0);
|
| 2378 |
|
| 2379 | TH_LOG("Namespace inactive as expected: %s (errno=%d)", strerror(errno), errno);
|
| 2380 | ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
|
| 2381 | }
|
| 2382 |
|
| 2383 |
|
| 2384 | struct thread_sleep_data {
|
| 2385 | int syncfd_read;
|
| 2386 | };
|
| 2387 |
|
| 2388 | static void *thread_sleep_and_wait(void *arg)
|
| 2389 | {
|
| 2390 | struct thread_sleep_data *data = (struct thread_sleep_data *)arg;
|
| 2391 | char sync_byte;
|
| 2392 |
|
| 2393 |
|
| 2394 | (void)read(data->syncfd_read, &sync_byte, 1);
|
| 2395 | return NULL;
|
| 2396 | }
|
| 2397 |
|
| 2398 | |
| 2399 | * Test that namespaces become inactive after subprocess with multiple threads exits.
|
| 2400 | * Create a subprocess that unshares user and network namespaces, then creates two
|
| 2401 | * threads that share those namespaces. Verify that after all threads and subprocess
|
| 2402 | * exit, the namespaces are no longer listed by listns() and cannot be opened by
|
| 2403 | * open_by_handle_at().
|
| 2404 |
|
| 2405 | TEST(thread_subprocess_ns_inactive_after_all_exit)
|
| 2406 | {
|
| 2407 | int pipefd[2];
|
| 2408 | int sv[2];
|
| 2409 | pid_t pid;
|
| 2410 | int status;
|
| 2411 | __u64 user_id, net_id;
|
| 2412 | struct file_handle *user_handle, *net_handle;
|
| 2413 | char user_buf[sizeof(*user_handle) + MAX_HANDLE_SZ];
|
| 2414 | char net_buf[sizeof(*net_handle) + MAX_HANDLE_SZ];
|
| 2415 | char sync_byte;
|
| 2416 | int ret;
|
| 2417 |
|
| 2418 | ASSERT_EQ(pipe(pipefd), 0);
|
| 2419 | ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0);
|
| 2420 |
|
| 2421 | pid = fork();
|
| 2422 | ASSERT_GE(pid, 0);
|
| 2423 |
|
| 2424 | if (pid == 0) {
|
| 2425 |
|
| 2426 | close(pipefd[0]);
|
| 2427 | close(sv[0]);
|
| 2428 |
|
| 2429 |
|
| 2430 | if (setup_userns() < 0) {
|
| 2431 | fprintf(stderr, "Child: setup_userns() failed: %s\n", strerror(errno));
|
| 2432 | close(pipefd[1]);
|
| 2433 | close(sv[1]);
|
| 2434 | exit(1);
|
| 2435 | }
|
| 2436 | fprintf(stderr, "Child: setup_userns() succeeded\n");
|
| 2437 |
|
| 2438 |
|
| 2439 | int user_fd = open("/proc/self/ns/user", O_RDONLY);
|
| 2440 | if (user_fd < 0) {
|
| 2441 | fprintf(stderr, "Child: open(/proc/self/ns/user) failed: %s\n", strerror(errno));
|
| 2442 | close(pipefd[1]);
|
| 2443 | close(sv[1]);
|
| 2444 | exit(1);
|
| 2445 | }
|
| 2446 |
|
| 2447 | if (ioctl(user_fd, NS_GET_ID, &user_id) < 0) {
|
| 2448 | fprintf(stderr, "Child: ioctl(NS_GET_ID) for user ns failed: %s\n", strerror(errno));
|
| 2449 | close(user_fd);
|
| 2450 | close(pipefd[1]);
|
| 2451 | close(sv[1]);
|
| 2452 | exit(1);
|
| 2453 | }
|
| 2454 | close(user_fd);
|
| 2455 | fprintf(stderr, "Child: user ns ID = %llu\n", (unsigned long long)user_id);
|
| 2456 |
|
| 2457 |
|
| 2458 | if (unshare(CLONE_NEWNET) < 0) {
|
| 2459 | fprintf(stderr, "Child: unshare(CLONE_NEWNET) failed: %s\n", strerror(errno));
|
| 2460 | close(pipefd[1]);
|
| 2461 | close(sv[1]);
|
| 2462 | exit(1);
|
| 2463 | }
|
| 2464 | fprintf(stderr, "Child: unshare(CLONE_NEWNET) succeeded\n");
|
| 2465 |
|
| 2466 |
|
| 2467 | int net_fd = open("/proc/self/ns/net", O_RDONLY);
|
| 2468 | if (net_fd < 0) {
|
| 2469 | fprintf(stderr, "Child: open(/proc/self/ns/net) failed: %s\n", strerror(errno));
|
| 2470 | close(pipefd[1]);
|
| 2471 | close(sv[1]);
|
| 2472 | exit(1);
|
| 2473 | }
|
| 2474 |
|
| 2475 | if (ioctl(net_fd, NS_GET_ID, &net_id) < 0) {
|
| 2476 | fprintf(stderr, "Child: ioctl(NS_GET_ID) for net ns failed: %s\n", strerror(errno));
|
| 2477 | close(net_fd);
|
| 2478 | close(pipefd[1]);
|
| 2479 | close(sv[1]);
|
| 2480 | exit(1);
|
| 2481 | }
|
| 2482 | close(net_fd);
|
| 2483 | fprintf(stderr, "Child: net ns ID = %llu\n", (unsigned long long)net_id);
|
| 2484 |
|
| 2485 |
|
| 2486 | if (write(pipefd[1], &user_id, sizeof(user_id)) != sizeof(user_id)) {
|
| 2487 | fprintf(stderr, "Child: write(user_id) failed: %s\n", strerror(errno));
|
| 2488 | exit(1);
|
| 2489 | }
|
| 2490 | if (write(pipefd[1], &net_id, sizeof(net_id)) != sizeof(net_id)) {
|
| 2491 | fprintf(stderr, "Child: write(net_id) failed: %s\n", strerror(errno));
|
| 2492 | exit(1);
|
| 2493 | }
|
| 2494 | close(pipefd[1]);
|
| 2495 | fprintf(stderr, "Child: sent namespace IDs to parent\n");
|
| 2496 |
|
| 2497 |
|
| 2498 | pthread_t thread1, thread2;
|
| 2499 | struct thread_sleep_data data;
|
| 2500 | data.syncfd_read = sv[1];
|
| 2501 |
|
| 2502 | int ret_thread = pthread_create(&thread1, NULL, thread_sleep_and_wait, &data);
|
| 2503 | if (ret_thread != 0) {
|
| 2504 | fprintf(stderr, "Child: pthread_create(thread1) failed: %s\n", strerror(ret_thread));
|
| 2505 | close(sv[1]);
|
| 2506 | exit(1);
|
| 2507 | }
|
| 2508 | fprintf(stderr, "Child: created thread1\n");
|
| 2509 |
|
| 2510 | ret_thread = pthread_create(&thread2, NULL, thread_sleep_and_wait, &data);
|
| 2511 | if (ret_thread != 0) {
|
| 2512 | fprintf(stderr, "Child: pthread_create(thread2) failed: %s\n", strerror(ret_thread));
|
| 2513 | close(sv[1]);
|
| 2514 | pthread_cancel(thread1);
|
| 2515 | exit(1);
|
| 2516 | }
|
| 2517 | fprintf(stderr, "Child: created thread2\n");
|
| 2518 |
|
| 2519 |
|
| 2520 | fprintf(stderr, "Child: waiting for threads to exit\n");
|
| 2521 | pthread_join(thread1, NULL);
|
| 2522 | fprintf(stderr, "Child: thread1 exited\n");
|
| 2523 | pthread_join(thread2, NULL);
|
| 2524 | fprintf(stderr, "Child: thread2 exited\n");
|
| 2525 |
|
| 2526 | close(sv[1]);
|
| 2527 |
|
| 2528 |
|
| 2529 | fprintf(stderr, "Child: all threads joined, exiting with success\n");
|
| 2530 | exit(0);
|
| 2531 | }
|
| 2532 |
|
| 2533 |
|
| 2534 | close(pipefd[1]);
|
| 2535 | close(sv[1]);
|
| 2536 |
|
| 2537 | TH_LOG("Parent: waiting to read namespace IDs from child");
|
| 2538 |
|
| 2539 |
|
| 2540 | ret = read(pipefd[0], &user_id, sizeof(user_id));
|
| 2541 | if (ret != sizeof(user_id)) {
|
| 2542 | TH_LOG("Parent: failed to read user_id, ret=%d, errno=%s", ret, strerror(errno));
|
| 2543 | close(pipefd[0]);
|
| 2544 | sync_byte = 'X';
|
| 2545 | (void)write(sv[0], &sync_byte, 1);
|
| 2546 | close(sv[0]);
|
| 2547 | waitpid(pid, NULL, 0);
|
| 2548 | SKIP(return, "Failed to read user namespace ID from child");
|
| 2549 | }
|
| 2550 |
|
| 2551 | ret = read(pipefd[0], &net_id, sizeof(net_id));
|
| 2552 | close(pipefd[0]);
|
| 2553 | if (ret != sizeof(net_id)) {
|
| 2554 | TH_LOG("Parent: failed to read net_id, ret=%d, errno=%s", ret, strerror(errno));
|
| 2555 | sync_byte = 'X';
|
| 2556 | (void)write(sv[0], &sync_byte, 1);
|
| 2557 | close(sv[0]);
|
| 2558 | waitpid(pid, NULL, 0);
|
| 2559 | SKIP(return, "Failed to read network namespace ID from child");
|
| 2560 | }
|
| 2561 |
|
| 2562 | TH_LOG("Child created user ns %llu and net ns %llu with 2 threads",
|
| 2563 | (unsigned long long)user_id, (unsigned long long)net_id);
|
| 2564 |
|
| 2565 |
|
| 2566 | user_handle = (struct file_handle *)user_buf;
|
| 2567 | user_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 2568 | user_handle->handle_type = FILEID_NSFS;
|
| 2569 | struct nsfs_file_handle *user_fh = (struct nsfs_file_handle *)user_handle->f_handle;
|
| 2570 | user_fh->ns_id = user_id;
|
| 2571 | user_fh->ns_type = 0;
|
| 2572 | user_fh->ns_inum = 0;
|
| 2573 |
|
| 2574 | net_handle = (struct file_handle *)net_buf;
|
| 2575 | net_handle->handle_bytes = sizeof(struct nsfs_file_handle);
|
| 2576 | net_handle->handle_type = FILEID_NSFS;
|
| 2577 | struct nsfs_file_handle *net_fh = (struct nsfs_file_handle *)net_handle->f_handle;
|
| 2578 | net_fh->ns_id = net_id;
|
| 2579 | net_fh->ns_type = 0;
|
| 2580 | net_fh->ns_inum = 0;
|
| 2581 |
|
| 2582 |
|
| 2583 | TH_LOG("Verifying namespaces are active while subprocess with threads is running");
|
| 2584 | int user_fd = open_by_handle_at(FD_NSFS_ROOT, user_handle, O_RDONLY);
|
| 2585 | ASSERT_GE(user_fd, 0);
|
| 2586 |
|
| 2587 | int net_fd = open_by_handle_at(FD_NSFS_ROOT, net_handle, O_RDONLY);
|
| 2588 | ASSERT_GE(net_fd, 0);
|
| 2589 |
|
| 2590 | close(user_fd);
|
| 2591 | close(net_fd);
|
| 2592 |
|
| 2593 |
|
| 2594 | TH_LOG("Verifying namespaces appear in listns() while active");
|
| 2595 | struct ns_id_req req = {
|
| 2596 | .size = sizeof(struct ns_id_req),
|
| 2597 | .spare = 0,
|
| 2598 | .ns_id = 0,
|
| 2599 | .ns_type = CLONE_NEWUSER,
|
| 2600 | .spare2 = 0,
|
| 2601 | .user_ns_id = 0,
|
| 2602 | };
|
| 2603 | __u64 ns_ids[256];
|
| 2604 | int nr_ids = sys_listns(&req, ns_ids, 256, 0);
|
| 2605 | if (nr_ids < 0) {
|
| 2606 | TH_LOG("listns() not available, skipping listns verification");
|
| 2607 | } else {
|
| 2608 |
|
| 2609 | int found_user = 0;
|
| 2610 | for (int i = 0; i < nr_ids; i++) {
|
| 2611 | if (ns_ids[i] == user_id) {
|
| 2612 | found_user = 1;
|
| 2613 | break;
|
| 2614 | }
|
| 2615 | }
|
| 2616 | ASSERT_TRUE(found_user);
|
| 2617 | TH_LOG("User namespace found in listns() as expected");
|
| 2618 |
|
| 2619 |
|
| 2620 | req.ns_type = CLONE_NEWNET;
|
| 2621 | nr_ids = sys_listns(&req, ns_ids, 256, 0);
|
| 2622 | if (nr_ids >= 0) {
|
| 2623 | int found_net = 0;
|
| 2624 | for (int i = 0; i < nr_ids; i++) {
|
| 2625 | if (ns_ids[i] == net_id) {
|
| 2626 | found_net = 1;
|
| 2627 | break;
|
| 2628 | }
|
| 2629 | }
|
| 2630 | ASSERT_TRUE(found_net);
|
| 2631 | TH_LOG("Network namespace found in listns() as expected");
|
| 2632 | }
|
| 2633 | }
|
| 2634 |
|
| 2635 |
|
| 2636 | TH_LOG("Signaling threads to exit");
|
| 2637 | sync_byte = 'X';
|
| 2638 |
|
| 2639 | ASSERT_EQ(write(sv[0], &sync_byte, 1), 1);
|
| 2640 | ASSERT_EQ(write(sv[0], &sync_byte, 1), 1);
|
| 2641 | close(sv[0]);
|
| 2642 |
|
| 2643 |
|
| 2644 | waitpid(pid, &status, 0);
|
| 2645 | ASSERT_TRUE(WIFEXITED(status));
|
| 2646 | if (WEXITSTATUS(status) != 0) {
|
| 2647 | TH_LOG("Child process failed with exit code %d", WEXITSTATUS(status));
|
| 2648 | SKIP(return, "Child process failed");
|
| 2649 | }
|
| 2650 |
|
| 2651 | TH_LOG("Subprocess and all threads have exited successfully");
|
| 2652 |
|
| 2653 |
|
| 2654 | TH_LOG("Verifying namespaces are inactive after subprocess and threads exit");
|
| 2655 | user_fd = open_by_handle_at(FD_NSFS_ROOT, user_handle, O_RDONLY);
|
| 2656 | ASSERT_LT(user_fd, 0);
|
| 2657 | TH_LOG("User namespace inactive as expected: %s (errno=%d)",
|
| 2658 | strerror(errno), errno);
|
| 2659 | ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
|
| 2660 |
|
| 2661 | net_fd = open_by_handle_at(FD_NSFS_ROOT, net_handle, O_RDONLY);
|
| 2662 | ASSERT_LT(net_fd, 0);
|
| 2663 | TH_LOG("Network namespace inactive as expected: %s (errno=%d)",
|
| 2664 | strerror(errno), errno);
|
| 2665 | ASSERT_TRUE(errno == ENOENT || errno == ESTALE);
|
| 2666 |
|
| 2667 |
|
| 2668 | TH_LOG("Verifying namespaces do NOT appear in listns() when inactive");
|
| 2669 | memset(&req, 0, sizeof(req));
|
| 2670 | req.size = sizeof(struct ns_id_req);
|
| 2671 | req.ns_type = CLONE_NEWUSER;
|
| 2672 | nr_ids = sys_listns(&req, ns_ids, 256, 0);
|
| 2673 | if (nr_ids >= 0) {
|
| 2674 | int found_user = 0;
|
| 2675 | for (int i = 0; i < nr_ids; i++) {
|
| 2676 | if (ns_ids[i] == user_id) {
|
| 2677 | found_user = 1;
|
| 2678 | break;
|
| 2679 | }
|
| 2680 | }
|
| 2681 | ASSERT_FALSE(found_user);
|
| 2682 | TH_LOG("User namespace correctly not listed in listns()");
|
| 2683 |
|
| 2684 |
|
| 2685 | req.ns_type = CLONE_NEWNET;
|
| 2686 | nr_ids = sys_listns(&req, ns_ids, 256, 0);
|
| 2687 | if (nr_ids >= 0) {
|
| 2688 | int found_net = 0;
|
| 2689 | for (int i = 0; i < nr_ids; i++) {
|
| 2690 | if (ns_ids[i] == net_id) {
|
| 2691 | found_net = 1;
|
| 2692 | break;
|
| 2693 | }
|
| 2694 | }
|
| 2695 | ASSERT_FALSE(found_net);
|
| 2696 | TH_LOG("Network namespace correctly not listed in listns()");
|
| 2697 | }
|
| 2698 | }
|
| 2699 | }
|
| 2700 |
|
| 2701 | TEST_HARNESS_MAIN
|