1510 lines · c
1// SPDX-License-Identifier: GPL-2.02#define _GNU_SOURCE3#include <sched.h>4#include <stdio.h>5#include <errno.h>6#include <pthread.h>7#include <string.h>8#include <sys/stat.h>9#include <sys/types.h>10#include <sys/mount.h>11#include <sys/wait.h>12#include <sys/vfs.h>13#include <sys/statvfs.h>14#include <sys/sysinfo.h>15#include <stdlib.h>16#include <unistd.h>17#include <fcntl.h>18#include <grp.h>19#include <stdbool.h>20#include <stdarg.h>21#include <linux/mount.h>22 23#include "../kselftest_harness.h"24 25#ifndef CLONE_NEWNS26#define CLONE_NEWNS 0x0002000027#endif28 29#ifndef CLONE_NEWUSER30#define CLONE_NEWUSER 0x1000000031#endif32 33#ifndef MS_REC34#define MS_REC 1638435#endif36 37#ifndef MS_RELATIME38#define MS_RELATIME (1 << 21)39#endif40 41#ifndef MS_STRICTATIME42#define MS_STRICTATIME (1 << 24)43#endif44 45#ifndef MOUNT_ATTR_RDONLY46#define MOUNT_ATTR_RDONLY 0x0000000147#endif48 49#ifndef MOUNT_ATTR_NOSUID50#define MOUNT_ATTR_NOSUID 0x0000000251#endif52 53#ifndef MOUNT_ATTR_NOEXEC54#define MOUNT_ATTR_NOEXEC 0x0000000855#endif56 57#ifndef MOUNT_ATTR_NODIRATIME58#define MOUNT_ATTR_NODIRATIME 0x0000008059#endif60 61#ifndef MOUNT_ATTR__ATIME62#define MOUNT_ATTR__ATIME 0x0000007063#endif64 65#ifndef MOUNT_ATTR_RELATIME66#define MOUNT_ATTR_RELATIME 0x0000000067#endif68 69#ifndef MOUNT_ATTR_NOATIME70#define MOUNT_ATTR_NOATIME 0x0000001071#endif72 73#ifndef MOUNT_ATTR_STRICTATIME74#define MOUNT_ATTR_STRICTATIME 0x0000002075#endif76 77#ifndef AT_RECURSIVE78#define AT_RECURSIVE 0x800079#endif80 81#ifndef MS_SHARED82#define MS_SHARED (1 << 20)83#endif84 85#define DEFAULT_THREADS 486#define ptr_to_int(p) ((int)((intptr_t)(p)))87#define int_to_ptr(u) ((void *)((intptr_t)(u)))88 89#ifndef __NR_mount_setattr90 #if defined __alpha__91 #define __NR_mount_setattr 55292 #elif defined _MIPS_SIM93 #if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */94 #define __NR_mount_setattr (442 + 4000)95 #endif96 #if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */97 #define __NR_mount_setattr (442 + 6000)98 #endif99 #if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */100 #define __NR_mount_setattr (442 + 5000)101 #endif102 #elif defined __ia64__103 #define __NR_mount_setattr (442 + 1024)104 #else105 #define __NR_mount_setattr 442106 #endif107#endif108 109#ifndef __NR_open_tree110 #if defined __alpha__111 #define __NR_open_tree 538112 #elif defined _MIPS_SIM113 #if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */114 #define __NR_open_tree 4428115 #endif116 #if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */117 #define __NR_open_tree 6428118 #endif119 #if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */120 #define __NR_open_tree 5428121 #endif122 #elif defined __ia64__123 #define __NR_open_tree (428 + 1024)124 #else125 #define __NR_open_tree 428126 #endif127#endif128 129#ifndef MOUNT_ATTR_IDMAP130#define MOUNT_ATTR_IDMAP 0x00100000131#endif132 133#ifndef MOUNT_ATTR_NOSYMFOLLOW134#define MOUNT_ATTR_NOSYMFOLLOW 0x00200000135#endif136 137static inline int sys_mount_setattr(int dfd, const char *path, unsigned int flags,138 struct mount_attr *attr, size_t size)139{140 return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);141}142 143#ifndef OPEN_TREE_CLONE144#define OPEN_TREE_CLONE 1145#endif146 147#ifndef OPEN_TREE_CLOEXEC148#define OPEN_TREE_CLOEXEC O_CLOEXEC149#endif150 151#ifndef AT_RECURSIVE152#define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */153#endif154 155static inline int sys_open_tree(int dfd, const char *filename, unsigned int flags)156{157 return syscall(__NR_open_tree, dfd, filename, flags);158}159 160static ssize_t write_nointr(int fd, const void *buf, size_t count)161{162 ssize_t ret;163 164 do {165 ret = write(fd, buf, count);166 } while (ret < 0 && errno == EINTR);167 168 return ret;169}170 171static int write_file(const char *path, const void *buf, size_t count)172{173 int fd;174 ssize_t ret;175 176 fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);177 if (fd < 0)178 return -1;179 180 ret = write_nointr(fd, buf, count);181 close(fd);182 if (ret < 0 || (size_t)ret != count)183 return -1;184 185 return 0;186}187 188static int create_and_enter_userns(void)189{190 uid_t uid;191 gid_t gid;192 char map[100];193 194 uid = getuid();195 gid = getgid();196 197 if (unshare(CLONE_NEWUSER))198 return -1;199 200 if (write_file("/proc/self/setgroups", "deny", sizeof("deny") - 1) &&201 errno != ENOENT)202 return -1;203 204 snprintf(map, sizeof(map), "0 %d 1", uid);205 if (write_file("/proc/self/uid_map", map, strlen(map)))206 return -1;207 208 209 snprintf(map, sizeof(map), "0 %d 1", gid);210 if (write_file("/proc/self/gid_map", map, strlen(map)))211 return -1;212 213 if (setgid(0))214 return -1;215 216 if (setuid(0))217 return -1;218 219 return 0;220}221 222static int prepare_unpriv_mountns(void)223{224 if (create_and_enter_userns())225 return -1;226 227 if (unshare(CLONE_NEWNS))228 return -1;229 230 if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0))231 return -1;232 233 return 0;234}235 236#ifndef ST_NOSYMFOLLOW237#define ST_NOSYMFOLLOW 0x2000 /* do not follow symlinks */238#endif239 240static int read_mnt_flags(const char *path)241{242 int ret;243 struct statvfs stat;244 unsigned int mnt_flags;245 246 ret = statvfs(path, &stat);247 if (ret != 0)248 return -EINVAL;249 250 if (stat.f_flag & ~(ST_RDONLY | ST_NOSUID | ST_NODEV | ST_NOEXEC |251 ST_NOATIME | ST_NODIRATIME | ST_RELATIME |252 ST_SYNCHRONOUS | ST_MANDLOCK | ST_NOSYMFOLLOW))253 return -EINVAL;254 255 mnt_flags = 0;256 if (stat.f_flag & ST_RDONLY)257 mnt_flags |= MS_RDONLY;258 if (stat.f_flag & ST_NOSUID)259 mnt_flags |= MS_NOSUID;260 if (stat.f_flag & ST_NODEV)261 mnt_flags |= MS_NODEV;262 if (stat.f_flag & ST_NOEXEC)263 mnt_flags |= MS_NOEXEC;264 if (stat.f_flag & ST_NOATIME)265 mnt_flags |= MS_NOATIME;266 if (stat.f_flag & ST_NODIRATIME)267 mnt_flags |= MS_NODIRATIME;268 if (stat.f_flag & ST_RELATIME)269 mnt_flags |= MS_RELATIME;270 if (stat.f_flag & ST_SYNCHRONOUS)271 mnt_flags |= MS_SYNCHRONOUS;272 if (stat.f_flag & ST_MANDLOCK)273 mnt_flags |= ST_MANDLOCK;274 if (stat.f_flag & ST_NOSYMFOLLOW)275 mnt_flags |= ST_NOSYMFOLLOW;276 277 return mnt_flags;278}279 280static char *get_field(char *src, int nfields)281{282 int i;283 char *p = src;284 285 for (i = 0; i < nfields; i++) {286 while (*p && *p != ' ' && *p != '\t')287 p++;288 289 if (!*p)290 break;291 292 p++;293 }294 295 return p;296}297 298static void null_endofword(char *word)299{300 while (*word && *word != ' ' && *word != '\t')301 word++;302 *word = '\0';303}304 305static bool is_shared_mount(const char *path)306{307 size_t len = 0;308 char *line = NULL;309 FILE *f = NULL;310 311 f = fopen("/proc/self/mountinfo", "re");312 if (!f)313 return false;314 315 while (getline(&line, &len, f) != -1) {316 char *opts, *target;317 318 target = get_field(line, 4);319 if (!target)320 continue;321 322 opts = get_field(target, 2);323 if (!opts)324 continue;325 326 null_endofword(target);327 328 if (strcmp(target, path) != 0)329 continue;330 331 null_endofword(opts);332 if (strstr(opts, "shared:"))333 return true;334 }335 336 free(line);337 fclose(f);338 339 return false;340}341 342static void *mount_setattr_thread(void *data)343{344 struct mount_attr attr = {345 .attr_set = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID,346 .attr_clr = 0,347 .propagation = MS_SHARED,348 };349 350 if (sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)))351 pthread_exit(int_to_ptr(-1));352 353 pthread_exit(int_to_ptr(0));354}355 356/* Attempt to de-conflict with the selftests tree. */357#ifndef SKIP358#define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__)359#endif360 361static bool mount_setattr_supported(void)362{363 int ret;364 365 ret = sys_mount_setattr(-EBADF, "", AT_EMPTY_PATH, NULL, 0);366 if (ret < 0 && errno == ENOSYS)367 return false;368 369 return true;370}371 372FIXTURE(mount_setattr) {373};374 375#define NOSYMFOLLOW_TARGET "/mnt/A/AA/data"376#define NOSYMFOLLOW_SYMLINK "/mnt/A/AA/symlink"377 378FIXTURE_SETUP(mount_setattr)379{380 int fd = -EBADF;381 382 if (!mount_setattr_supported())383 SKIP(return, "mount_setattr syscall not supported");384 385 ASSERT_EQ(prepare_unpriv_mountns(), 0);386 387 (void)umount2("/mnt", MNT_DETACH);388 (void)umount2("/tmp", MNT_DETACH);389 390 ASSERT_EQ(mount("testing", "/tmp", "tmpfs", MS_NOATIME | MS_NODEV,391 "size=100000,mode=700"), 0);392 393 ASSERT_EQ(mkdir("/tmp/B", 0777), 0);394 395 ASSERT_EQ(mount("testing", "/tmp/B", "tmpfs", MS_NOATIME | MS_NODEV,396 "size=100000,mode=700"), 0);397 398 ASSERT_EQ(mkdir("/tmp/B/BB", 0777), 0);399 400 ASSERT_EQ(mount("testing", "/tmp/B/BB", "tmpfs", MS_NOATIME | MS_NODEV,401 "size=100000,mode=700"), 0);402 403 ASSERT_EQ(mount("testing", "/mnt", "tmpfs", MS_NOATIME | MS_NODEV,404 "size=100000,mode=700"), 0);405 406 ASSERT_EQ(mkdir("/mnt/A", 0777), 0);407 408 ASSERT_EQ(mount("testing", "/mnt/A", "tmpfs", MS_NOATIME | MS_NODEV,409 "size=100000,mode=700"), 0);410 411 ASSERT_EQ(mkdir("/mnt/A/AA", 0777), 0);412 413 ASSERT_EQ(mount("/tmp", "/mnt/A/AA", NULL, MS_BIND | MS_REC, NULL), 0);414 415 ASSERT_EQ(mkdir("/mnt/B", 0777), 0);416 417 ASSERT_EQ(mount("testing", "/mnt/B", "ramfs",418 MS_NOATIME | MS_NODEV | MS_NOSUID, 0), 0);419 420 ASSERT_EQ(mkdir("/mnt/B/BB", 0777), 0);421 422 ASSERT_EQ(mount("testing", "/tmp/B/BB", "devpts",423 MS_RELATIME | MS_NOEXEC | MS_RDONLY, 0), 0);424 425 fd = creat(NOSYMFOLLOW_TARGET, O_RDWR | O_CLOEXEC);426 ASSERT_GT(fd, 0);427 ASSERT_EQ(symlink(NOSYMFOLLOW_TARGET, NOSYMFOLLOW_SYMLINK), 0);428 ASSERT_EQ(close(fd), 0);429}430 431FIXTURE_TEARDOWN(mount_setattr)432{433 if (!mount_setattr_supported())434 SKIP(return, "mount_setattr syscall not supported");435 436 (void)umount2("/mnt/A", MNT_DETACH);437 (void)umount2("/tmp", MNT_DETACH);438}439 440TEST_F(mount_setattr, invalid_attributes)441{442 struct mount_attr invalid_attr = {443 .attr_set = (1U << 31),444 };445 446 if (!mount_setattr_supported())447 SKIP(return, "mount_setattr syscall not supported");448 449 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,450 sizeof(invalid_attr)), 0);451 452 invalid_attr.attr_set = 0;453 invalid_attr.attr_clr = (1U << 31);454 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,455 sizeof(invalid_attr)), 0);456 457 invalid_attr.attr_clr = 0;458 invalid_attr.propagation = (1U << 31);459 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,460 sizeof(invalid_attr)), 0);461 462 invalid_attr.attr_set = (1U << 31);463 invalid_attr.attr_clr = (1U << 31);464 invalid_attr.propagation = (1U << 31);465 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,466 sizeof(invalid_attr)), 0);467 468 ASSERT_NE(sys_mount_setattr(-1, "mnt/A", AT_RECURSIVE, &invalid_attr,469 sizeof(invalid_attr)), 0);470}471 472TEST_F(mount_setattr, extensibility)473{474 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;475 char *s = "dummy";476 struct mount_attr invalid_attr = {};477 struct mount_attr_large {478 struct mount_attr attr1;479 struct mount_attr attr2;480 struct mount_attr attr3;481 } large_attr = {};482 483 if (!mount_setattr_supported())484 SKIP(return, "mount_setattr syscall not supported");485 486 old_flags = read_mnt_flags("/mnt/A");487 ASSERT_GT(old_flags, 0);488 489 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, NULL,490 sizeof(invalid_attr)), 0);491 ASSERT_EQ(errno, EFAULT);492 493 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, (void *)s,494 sizeof(invalid_attr)), 0);495 ASSERT_EQ(errno, EINVAL);496 497 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr, 0), 0);498 ASSERT_EQ(errno, EINVAL);499 500 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,501 sizeof(invalid_attr) / 2), 0);502 ASSERT_EQ(errno, EINVAL);503 504 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,505 sizeof(invalid_attr) / 2), 0);506 ASSERT_EQ(errno, EINVAL);507 508 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE,509 (void *)&large_attr, sizeof(large_attr)), 0);510 511 large_attr.attr3.attr_set = MOUNT_ATTR_RDONLY;512 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE,513 (void *)&large_attr, sizeof(large_attr)), 0);514 515 large_attr.attr3.attr_set = 0;516 large_attr.attr1.attr_set = MOUNT_ATTR_RDONLY;517 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE,518 (void *)&large_attr, sizeof(large_attr)), 0);519 520 expected_flags = old_flags;521 expected_flags |= MS_RDONLY;522 523 new_flags = read_mnt_flags("/mnt/A");524 ASSERT_EQ(new_flags, expected_flags);525 526 new_flags = read_mnt_flags("/mnt/A/AA");527 ASSERT_EQ(new_flags, expected_flags);528 529 new_flags = read_mnt_flags("/mnt/A/AA/B");530 ASSERT_EQ(new_flags, expected_flags);531 532 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");533 ASSERT_EQ(new_flags, expected_flags);534}535 536TEST_F(mount_setattr, basic)537{538 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;539 struct mount_attr attr = {540 .attr_set = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_RELATIME,541 .attr_clr = MOUNT_ATTR__ATIME,542 };543 544 if (!mount_setattr_supported())545 SKIP(return, "mount_setattr syscall not supported");546 547 old_flags = read_mnt_flags("/mnt/A");548 ASSERT_GT(old_flags, 0);549 550 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", 0, &attr, sizeof(attr)), 0);551 552 expected_flags = old_flags;553 expected_flags |= MS_RDONLY;554 expected_flags |= MS_NOEXEC;555 expected_flags &= ~MS_NOATIME;556 expected_flags |= MS_RELATIME;557 558 new_flags = read_mnt_flags("/mnt/A");559 ASSERT_EQ(new_flags, expected_flags);560 561 new_flags = read_mnt_flags("/mnt/A/AA");562 ASSERT_EQ(new_flags, old_flags);563 564 new_flags = read_mnt_flags("/mnt/A/AA/B");565 ASSERT_EQ(new_flags, old_flags);566 567 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");568 ASSERT_EQ(new_flags, old_flags);569}570 571TEST_F(mount_setattr, basic_recursive)572{573 int fd;574 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;575 struct mount_attr attr = {576 .attr_set = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_RELATIME,577 .attr_clr = MOUNT_ATTR__ATIME,578 };579 580 if (!mount_setattr_supported())581 SKIP(return, "mount_setattr syscall not supported");582 583 old_flags = read_mnt_flags("/mnt/A");584 ASSERT_GT(old_flags, 0);585 586 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);587 588 expected_flags = old_flags;589 expected_flags |= MS_RDONLY;590 expected_flags |= MS_NOEXEC;591 expected_flags &= ~MS_NOATIME;592 expected_flags |= MS_RELATIME;593 594 new_flags = read_mnt_flags("/mnt/A");595 ASSERT_EQ(new_flags, expected_flags);596 597 new_flags = read_mnt_flags("/mnt/A/AA");598 ASSERT_EQ(new_flags, expected_flags);599 600 new_flags = read_mnt_flags("/mnt/A/AA/B");601 ASSERT_EQ(new_flags, expected_flags);602 603 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");604 ASSERT_EQ(new_flags, expected_flags);605 606 memset(&attr, 0, sizeof(attr));607 attr.attr_clr = MOUNT_ATTR_RDONLY;608 attr.propagation = MS_SHARED;609 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);610 611 expected_flags &= ~MS_RDONLY;612 new_flags = read_mnt_flags("/mnt/A");613 ASSERT_EQ(new_flags, expected_flags);614 615 ASSERT_EQ(is_shared_mount("/mnt/A"), true);616 617 new_flags = read_mnt_flags("/mnt/A/AA");618 ASSERT_EQ(new_flags, expected_flags);619 620 ASSERT_EQ(is_shared_mount("/mnt/A/AA"), true);621 622 new_flags = read_mnt_flags("/mnt/A/AA/B");623 ASSERT_EQ(new_flags, expected_flags);624 625 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B"), true);626 627 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");628 ASSERT_EQ(new_flags, expected_flags);629 630 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B/BB"), true);631 632 fd = open("/mnt/A/AA/B/b", O_RDWR | O_CLOEXEC | O_CREAT | O_EXCL, 0777);633 ASSERT_GE(fd, 0);634 635 /*636 * We're holding a fd open for writing so this needs to fail somewhere637 * in the middle and the mount options need to be unchanged.638 */639 attr.attr_set = MOUNT_ATTR_RDONLY;640 ASSERT_LT(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);641 642 new_flags = read_mnt_flags("/mnt/A");643 ASSERT_EQ(new_flags, expected_flags);644 645 ASSERT_EQ(is_shared_mount("/mnt/A"), true);646 647 new_flags = read_mnt_flags("/mnt/A/AA");648 ASSERT_EQ(new_flags, expected_flags);649 650 ASSERT_EQ(is_shared_mount("/mnt/A/AA"), true);651 652 new_flags = read_mnt_flags("/mnt/A/AA/B");653 ASSERT_EQ(new_flags, expected_flags);654 655 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B"), true);656 657 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");658 ASSERT_EQ(new_flags, expected_flags);659 660 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B/BB"), true);661 662 EXPECT_EQ(close(fd), 0);663}664 665TEST_F(mount_setattr, mount_has_writers)666{667 int fd, dfd;668 unsigned int old_flags = 0, new_flags = 0;669 struct mount_attr attr = {670 .attr_set = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_RELATIME,671 .attr_clr = MOUNT_ATTR__ATIME,672 .propagation = MS_SHARED,673 };674 675 if (!mount_setattr_supported())676 SKIP(return, "mount_setattr syscall not supported");677 678 old_flags = read_mnt_flags("/mnt/A");679 ASSERT_GT(old_flags, 0);680 681 fd = open("/mnt/A/AA/B/b", O_RDWR | O_CLOEXEC | O_CREAT | O_EXCL, 0777);682 ASSERT_GE(fd, 0);683 684 /*685 * We're holding a fd open to a mount somwhere in the middle so this686 * needs to fail somewhere in the middle. After this the mount options687 * need to be unchanged.688 */689 ASSERT_LT(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);690 691 new_flags = read_mnt_flags("/mnt/A");692 ASSERT_EQ(new_flags, old_flags);693 694 ASSERT_EQ(is_shared_mount("/mnt/A"), false);695 696 new_flags = read_mnt_flags("/mnt/A/AA");697 ASSERT_EQ(new_flags, old_flags);698 699 ASSERT_EQ(is_shared_mount("/mnt/A/AA"), false);700 701 new_flags = read_mnt_flags("/mnt/A/AA/B");702 ASSERT_EQ(new_flags, old_flags);703 704 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B"), false);705 706 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");707 ASSERT_EQ(new_flags, old_flags);708 709 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B/BB"), false);710 711 dfd = open("/mnt/A/AA/B", O_DIRECTORY | O_CLOEXEC);712 ASSERT_GE(dfd, 0);713 EXPECT_EQ(fsync(dfd), 0);714 EXPECT_EQ(close(dfd), 0);715 716 EXPECT_EQ(fsync(fd), 0);717 EXPECT_EQ(close(fd), 0);718 719 /* All writers are gone so this should succeed. */720 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);721}722 723TEST_F(mount_setattr, mixed_mount_options)724{725 unsigned int old_flags1 = 0, old_flags2 = 0, new_flags = 0, expected_flags = 0;726 struct mount_attr attr = {727 .attr_clr = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NOEXEC | MOUNT_ATTR__ATIME,728 .attr_set = MOUNT_ATTR_RELATIME,729 };730 731 if (!mount_setattr_supported())732 SKIP(return, "mount_setattr syscall not supported");733 734 old_flags1 = read_mnt_flags("/mnt/B");735 ASSERT_GT(old_flags1, 0);736 737 old_flags2 = read_mnt_flags("/mnt/B/BB");738 ASSERT_GT(old_flags2, 0);739 740 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/B", AT_RECURSIVE, &attr, sizeof(attr)), 0);741 742 expected_flags = old_flags2;743 expected_flags &= ~(MS_RDONLY | MS_NOEXEC | MS_NOATIME | MS_NOSUID);744 expected_flags |= MS_RELATIME;745 746 new_flags = read_mnt_flags("/mnt/B");747 ASSERT_EQ(new_flags, expected_flags);748 749 expected_flags = old_flags2;750 expected_flags &= ~(MS_RDONLY | MS_NOEXEC | MS_NOATIME | MS_NOSUID);751 expected_flags |= MS_RELATIME;752 753 new_flags = read_mnt_flags("/mnt/B/BB");754 ASSERT_EQ(new_flags, expected_flags);755}756 757TEST_F(mount_setattr, time_changes)758{759 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;760 struct mount_attr attr = {761 .attr_set = MOUNT_ATTR_NODIRATIME | MOUNT_ATTR_NOATIME,762 };763 764 if (!mount_setattr_supported())765 SKIP(return, "mount_setattr syscall not supported");766 767 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);768 769 attr.attr_set = MOUNT_ATTR_STRICTATIME;770 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);771 772 attr.attr_set = MOUNT_ATTR_STRICTATIME | MOUNT_ATTR_NOATIME;773 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);774 775 attr.attr_set = MOUNT_ATTR_STRICTATIME | MOUNT_ATTR_NOATIME;776 attr.attr_clr = MOUNT_ATTR__ATIME;777 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);778 779 attr.attr_set = 0;780 attr.attr_clr = MOUNT_ATTR_STRICTATIME;781 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);782 783 attr.attr_clr = MOUNT_ATTR_NOATIME;784 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);785 786 old_flags = read_mnt_flags("/mnt/A");787 ASSERT_GT(old_flags, 0);788 789 attr.attr_set = MOUNT_ATTR_NODIRATIME | MOUNT_ATTR_NOATIME;790 attr.attr_clr = MOUNT_ATTR__ATIME;791 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);792 793 expected_flags = old_flags;794 expected_flags |= MS_NOATIME;795 expected_flags |= MS_NODIRATIME;796 797 new_flags = read_mnt_flags("/mnt/A");798 ASSERT_EQ(new_flags, expected_flags);799 800 new_flags = read_mnt_flags("/mnt/A/AA");801 ASSERT_EQ(new_flags, expected_flags);802 803 new_flags = read_mnt_flags("/mnt/A/AA/B");804 ASSERT_EQ(new_flags, expected_flags);805 806 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");807 ASSERT_EQ(new_flags, expected_flags);808 809 memset(&attr, 0, sizeof(attr));810 attr.attr_set &= ~MOUNT_ATTR_NOATIME;811 attr.attr_set |= MOUNT_ATTR_RELATIME;812 attr.attr_clr |= MOUNT_ATTR__ATIME;813 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);814 815 expected_flags &= ~MS_NOATIME;816 expected_flags |= MS_RELATIME;817 818 new_flags = read_mnt_flags("/mnt/A");819 ASSERT_EQ(new_flags, expected_flags);820 821 new_flags = read_mnt_flags("/mnt/A/AA");822 ASSERT_EQ(new_flags, expected_flags);823 824 new_flags = read_mnt_flags("/mnt/A/AA/B");825 ASSERT_EQ(new_flags, expected_flags);826 827 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");828 ASSERT_EQ(new_flags, expected_flags);829 830 memset(&attr, 0, sizeof(attr));831 attr.attr_set &= ~MOUNT_ATTR_RELATIME;832 attr.attr_set |= MOUNT_ATTR_STRICTATIME;833 attr.attr_clr |= MOUNT_ATTR__ATIME;834 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);835 836 expected_flags &= ~MS_RELATIME;837 838 new_flags = read_mnt_flags("/mnt/A");839 ASSERT_EQ(new_flags, expected_flags);840 841 new_flags = read_mnt_flags("/mnt/A/AA");842 ASSERT_EQ(new_flags, expected_flags);843 844 new_flags = read_mnt_flags("/mnt/A/AA/B");845 ASSERT_EQ(new_flags, expected_flags);846 847 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");848 ASSERT_EQ(new_flags, expected_flags);849 850 memset(&attr, 0, sizeof(attr));851 attr.attr_set &= ~MOUNT_ATTR_STRICTATIME;852 attr.attr_set |= MOUNT_ATTR_NOATIME;853 attr.attr_clr |= MOUNT_ATTR__ATIME;854 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);855 856 expected_flags |= MS_NOATIME;857 new_flags = read_mnt_flags("/mnt/A");858 ASSERT_EQ(new_flags, expected_flags);859 860 new_flags = read_mnt_flags("/mnt/A/AA");861 ASSERT_EQ(new_flags, expected_flags);862 863 new_flags = read_mnt_flags("/mnt/A/AA/B");864 ASSERT_EQ(new_flags, expected_flags);865 866 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");867 ASSERT_EQ(new_flags, expected_flags);868 869 memset(&attr, 0, sizeof(attr));870 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);871 872 new_flags = read_mnt_flags("/mnt/A");873 ASSERT_EQ(new_flags, expected_flags);874 875 new_flags = read_mnt_flags("/mnt/A/AA");876 ASSERT_EQ(new_flags, expected_flags);877 878 new_flags = read_mnt_flags("/mnt/A/AA/B");879 ASSERT_EQ(new_flags, expected_flags);880 881 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");882 ASSERT_EQ(new_flags, expected_flags);883 884 memset(&attr, 0, sizeof(attr));885 attr.attr_clr = MOUNT_ATTR_NODIRATIME;886 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);887 888 expected_flags &= ~MS_NODIRATIME;889 890 new_flags = read_mnt_flags("/mnt/A");891 ASSERT_EQ(new_flags, expected_flags);892 893 new_flags = read_mnt_flags("/mnt/A/AA");894 ASSERT_EQ(new_flags, expected_flags);895 896 new_flags = read_mnt_flags("/mnt/A/AA/B");897 ASSERT_EQ(new_flags, expected_flags);898 899 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");900 ASSERT_EQ(new_flags, expected_flags);901}902 903TEST_F(mount_setattr, multi_threaded)904{905 int i, j, nthreads, ret = 0;906 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;907 pthread_attr_t pattr;908 pthread_t threads[DEFAULT_THREADS];909 910 if (!mount_setattr_supported())911 SKIP(return, "mount_setattr syscall not supported");912 913 old_flags = read_mnt_flags("/mnt/A");914 ASSERT_GT(old_flags, 0);915 916 /* Try to change mount options from multiple threads. */917 nthreads = get_nprocs_conf();918 if (nthreads > DEFAULT_THREADS)919 nthreads = DEFAULT_THREADS;920 921 pthread_attr_init(&pattr);922 for (i = 0; i < nthreads; i++)923 ASSERT_EQ(pthread_create(&threads[i], &pattr, mount_setattr_thread, NULL), 0);924 925 for (j = 0; j < i; j++) {926 void *retptr = NULL;927 928 EXPECT_EQ(pthread_join(threads[j], &retptr), 0);929 930 ret += ptr_to_int(retptr);931 EXPECT_EQ(ret, 0);932 }933 pthread_attr_destroy(&pattr);934 935 ASSERT_EQ(ret, 0);936 937 expected_flags = old_flags;938 expected_flags |= MS_RDONLY;939 expected_flags |= MS_NOSUID;940 new_flags = read_mnt_flags("/mnt/A");941 ASSERT_EQ(new_flags, expected_flags);942 943 ASSERT_EQ(is_shared_mount("/mnt/A"), true);944 945 new_flags = read_mnt_flags("/mnt/A/AA");946 ASSERT_EQ(new_flags, expected_flags);947 948 ASSERT_EQ(is_shared_mount("/mnt/A/AA"), true);949 950 new_flags = read_mnt_flags("/mnt/A/AA/B");951 ASSERT_EQ(new_flags, expected_flags);952 953 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B"), true);954 955 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");956 ASSERT_EQ(new_flags, expected_flags);957 958 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B/BB"), true);959}960 961TEST_F(mount_setattr, wrong_user_namespace)962{963 int ret;964 struct mount_attr attr = {965 .attr_set = MOUNT_ATTR_RDONLY,966 };967 968 if (!mount_setattr_supported())969 SKIP(return, "mount_setattr syscall not supported");970 971 EXPECT_EQ(create_and_enter_userns(), 0);972 ret = sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr));973 ASSERT_LT(ret, 0);974 ASSERT_EQ(errno, EPERM);975}976 977TEST_F(mount_setattr, wrong_mount_namespace)978{979 int fd, ret;980 struct mount_attr attr = {981 .attr_set = MOUNT_ATTR_RDONLY,982 };983 984 if (!mount_setattr_supported())985 SKIP(return, "mount_setattr syscall not supported");986 987 fd = open("/mnt/A", O_DIRECTORY | O_CLOEXEC);988 ASSERT_GE(fd, 0);989 990 ASSERT_EQ(unshare(CLONE_NEWNS), 0);991 992 ret = sys_mount_setattr(fd, "", AT_EMPTY_PATH | AT_RECURSIVE, &attr, sizeof(attr));993 ASSERT_LT(ret, 0);994 ASSERT_EQ(errno, EINVAL);995}996 997FIXTURE(mount_setattr_idmapped) {998};999 1000FIXTURE_SETUP(mount_setattr_idmapped)1001{1002 int img_fd = -EBADF;1003 1004 ASSERT_EQ(unshare(CLONE_NEWNS), 0);1005 1006 ASSERT_EQ(mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0), 0);1007 1008 (void)umount2("/mnt", MNT_DETACH);1009 (void)umount2("/tmp", MNT_DETACH);1010 1011 ASSERT_EQ(mount("testing", "/tmp", "tmpfs", MS_NOATIME | MS_NODEV,1012 "size=100000,mode=700"), 0);1013 1014 ASSERT_EQ(mkdir("/tmp/B", 0777), 0);1015 ASSERT_EQ(mknodat(-EBADF, "/tmp/B/b", S_IFREG | 0644, 0), 0);1016 ASSERT_EQ(chown("/tmp/B/b", 0, 0), 0);1017 1018 ASSERT_EQ(mount("testing", "/tmp/B", "tmpfs", MS_NOATIME | MS_NODEV,1019 "size=100000,mode=700"), 0);1020 1021 ASSERT_EQ(mkdir("/tmp/B/BB", 0777), 0);1022 ASSERT_EQ(mknodat(-EBADF, "/tmp/B/BB/b", S_IFREG | 0644, 0), 0);1023 ASSERT_EQ(chown("/tmp/B/BB/b", 0, 0), 0);1024 1025 ASSERT_EQ(mount("testing", "/tmp/B/BB", "tmpfs", MS_NOATIME | MS_NODEV,1026 "size=100000,mode=700"), 0);1027 1028 ASSERT_EQ(mount("testing", "/mnt", "tmpfs", MS_NOATIME | MS_NODEV,1029 "size=100000,mode=700"), 0);1030 1031 ASSERT_EQ(mkdir("/mnt/A", 0777), 0);1032 1033 ASSERT_EQ(mount("testing", "/mnt/A", "tmpfs", MS_NOATIME | MS_NODEV,1034 "size=100000,mode=700"), 0);1035 1036 ASSERT_EQ(mkdir("/mnt/A/AA", 0777), 0);1037 1038 ASSERT_EQ(mount("/tmp", "/mnt/A/AA", NULL, MS_BIND | MS_REC, NULL), 0);1039 1040 ASSERT_EQ(mkdir("/mnt/B", 0777), 0);1041 1042 ASSERT_EQ(mount("testing", "/mnt/B", "ramfs",1043 MS_NOATIME | MS_NODEV | MS_NOSUID, 0), 0);1044 1045 ASSERT_EQ(mkdir("/mnt/B/BB", 0777), 0);1046 1047 ASSERT_EQ(mount("testing", "/tmp/B/BB", "devpts",1048 MS_RELATIME | MS_NOEXEC | MS_RDONLY, 0), 0);1049 1050 ASSERT_EQ(mkdir("/mnt/C", 0777), 0);1051 ASSERT_EQ(mkdir("/mnt/D", 0777), 0);1052 img_fd = openat(-EBADF, "/mnt/C/ext4.img", O_CREAT | O_WRONLY, 0600);1053 ASSERT_GE(img_fd, 0);1054 ASSERT_EQ(ftruncate(img_fd, 1024 * 2048), 0);1055 ASSERT_EQ(system("mkfs.ext4 -q /mnt/C/ext4.img"), 0);1056 ASSERT_EQ(system("mount -o loop -t ext4 /mnt/C/ext4.img /mnt/D/"), 0);1057 ASSERT_EQ(close(img_fd), 0);1058}1059 1060FIXTURE_TEARDOWN(mount_setattr_idmapped)1061{1062 (void)umount2("/mnt/A", MNT_DETACH);1063 (void)umount2("/tmp", MNT_DETACH);1064}1065 1066/**1067 * Validate that negative fd values are rejected.1068 */1069TEST_F(mount_setattr_idmapped, invalid_fd_negative)1070{1071 struct mount_attr attr = {1072 .attr_set = MOUNT_ATTR_IDMAP,1073 .userns_fd = -EBADF,1074 };1075 1076 if (!mount_setattr_supported())1077 SKIP(return, "mount_setattr syscall not supported");1078 1079 ASSERT_NE(sys_mount_setattr(-1, "/", 0, &attr, sizeof(attr)), 0) {1080 TH_LOG("failure: created idmapped mount with negative fd");1081 }1082}1083 1084/**1085 * Validate that excessively large fd values are rejected.1086 */1087TEST_F(mount_setattr_idmapped, invalid_fd_large)1088{1089 struct mount_attr attr = {1090 .attr_set = MOUNT_ATTR_IDMAP,1091 .userns_fd = INT64_MAX,1092 };1093 1094 if (!mount_setattr_supported())1095 SKIP(return, "mount_setattr syscall not supported");1096 1097 ASSERT_NE(sys_mount_setattr(-1, "/", 0, &attr, sizeof(attr)), 0) {1098 TH_LOG("failure: created idmapped mount with too large fd value");1099 }1100}1101 1102/**1103 * Validate that closed fd values are rejected.1104 */1105TEST_F(mount_setattr_idmapped, invalid_fd_closed)1106{1107 int fd;1108 struct mount_attr attr = {1109 .attr_set = MOUNT_ATTR_IDMAP,1110 };1111 1112 if (!mount_setattr_supported())1113 SKIP(return, "mount_setattr syscall not supported");1114 1115 fd = open("/dev/null", O_RDONLY | O_CLOEXEC);1116 ASSERT_GE(fd, 0);1117 ASSERT_GE(close(fd), 0);1118 1119 attr.userns_fd = fd;1120 ASSERT_NE(sys_mount_setattr(-1, "/", 0, &attr, sizeof(attr)), 0) {1121 TH_LOG("failure: created idmapped mount with closed fd");1122 }1123}1124 1125/**1126 * Validate that the initial user namespace is rejected.1127 */1128TEST_F(mount_setattr_idmapped, invalid_fd_initial_userns)1129{1130 int open_tree_fd = -EBADF;1131 struct mount_attr attr = {1132 .attr_set = MOUNT_ATTR_IDMAP,1133 };1134 1135 if (!mount_setattr_supported())1136 SKIP(return, "mount_setattr syscall not supported");1137 1138 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",1139 AT_NO_AUTOMOUNT |1140 AT_SYMLINK_NOFOLLOW |1141 OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE);1142 ASSERT_GE(open_tree_fd, 0);1143 1144 attr.userns_fd = open("/proc/1/ns/user", O_RDONLY | O_CLOEXEC);1145 ASSERT_GE(attr.userns_fd, 0);1146 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0);1147 ASSERT_EQ(errno, EPERM);1148 ASSERT_EQ(close(attr.userns_fd), 0);1149 ASSERT_EQ(close(open_tree_fd), 0);1150}1151 1152static int map_ids(pid_t pid, unsigned long nsid, unsigned long hostid,1153 unsigned long range)1154{1155 char map[100], procfile[256];1156 1157 snprintf(procfile, sizeof(procfile), "/proc/%d/uid_map", pid);1158 snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range);1159 if (write_file(procfile, map, strlen(map)))1160 return -1;1161 1162 1163 snprintf(procfile, sizeof(procfile), "/proc/%d/gid_map", pid);1164 snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range);1165 if (write_file(procfile, map, strlen(map)))1166 return -1;1167 1168 return 0;1169}1170 1171#define __STACK_SIZE (8 * 1024 * 1024)1172static pid_t do_clone(int (*fn)(void *), void *arg, int flags)1173{1174 void *stack;1175 1176 stack = malloc(__STACK_SIZE);1177 if (!stack)1178 return -ENOMEM;1179 1180#ifdef __ia64__1181 return __clone2(fn, stack, __STACK_SIZE, flags | SIGCHLD, arg, NULL);1182#else1183 return clone(fn, stack + __STACK_SIZE, flags | SIGCHLD, arg, NULL);1184#endif1185}1186 1187static int get_userns_fd_cb(void *data)1188{1189 return kill(getpid(), SIGSTOP);1190}1191 1192static int wait_for_pid(pid_t pid)1193{1194 int status, ret;1195 1196again:1197 ret = waitpid(pid, &status, 0);1198 if (ret == -1) {1199 if (errno == EINTR)1200 goto again;1201 1202 return -1;1203 }1204 1205 if (!WIFEXITED(status))1206 return -1;1207 1208 return WEXITSTATUS(status);1209}1210 1211static int get_userns_fd(unsigned long nsid, unsigned long hostid, unsigned long range)1212{1213 int ret;1214 pid_t pid;1215 char path[256];1216 1217 pid = do_clone(get_userns_fd_cb, NULL, CLONE_NEWUSER);1218 if (pid < 0)1219 return -errno;1220 1221 ret = map_ids(pid, nsid, hostid, range);1222 if (ret < 0)1223 return ret;1224 1225 snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);1226 ret = open(path, O_RDONLY | O_CLOEXEC);1227 kill(pid, SIGKILL);1228 wait_for_pid(pid);1229 return ret;1230}1231 1232/**1233 * Validate that an attached mount in our mount namespace cannot be idmapped.1234 * (The kernel enforces that the mount's mount namespace and the caller's mount1235 * namespace match.)1236 */1237TEST_F(mount_setattr_idmapped, attached_mount_inside_current_mount_namespace)1238{1239 int open_tree_fd = -EBADF;1240 struct mount_attr attr = {1241 .attr_set = MOUNT_ATTR_IDMAP,1242 };1243 1244 if (!mount_setattr_supported())1245 SKIP(return, "mount_setattr syscall not supported");1246 1247 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",1248 AT_EMPTY_PATH |1249 AT_NO_AUTOMOUNT |1250 AT_SYMLINK_NOFOLLOW |1251 OPEN_TREE_CLOEXEC);1252 ASSERT_GE(open_tree_fd, 0);1253 1254 attr.userns_fd = get_userns_fd(0, 10000, 10000);1255 ASSERT_GE(attr.userns_fd, 0);1256 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0);1257 ASSERT_EQ(close(attr.userns_fd), 0);1258 ASSERT_EQ(close(open_tree_fd), 0);1259}1260 1261/**1262 * Validate that idmapping a mount is rejected if the mount's mount namespace1263 * and our mount namespace don't match.1264 * (The kernel enforces that the mount's mount namespace and the caller's mount1265 * namespace match.)1266 */1267TEST_F(mount_setattr_idmapped, attached_mount_outside_current_mount_namespace)1268{1269 int open_tree_fd = -EBADF;1270 struct mount_attr attr = {1271 .attr_set = MOUNT_ATTR_IDMAP,1272 };1273 1274 if (!mount_setattr_supported())1275 SKIP(return, "mount_setattr syscall not supported");1276 1277 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",1278 AT_EMPTY_PATH |1279 AT_NO_AUTOMOUNT |1280 AT_SYMLINK_NOFOLLOW |1281 OPEN_TREE_CLOEXEC);1282 ASSERT_GE(open_tree_fd, 0);1283 1284 ASSERT_EQ(unshare(CLONE_NEWNS), 0);1285 1286 attr.userns_fd = get_userns_fd(0, 10000, 10000);1287 ASSERT_GE(attr.userns_fd, 0);1288 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr,1289 sizeof(attr)), 0);1290 ASSERT_EQ(close(attr.userns_fd), 0);1291 ASSERT_EQ(close(open_tree_fd), 0);1292}1293 1294/**1295 * Validate that an attached mount in our mount namespace can be idmapped.1296 */1297TEST_F(mount_setattr_idmapped, detached_mount_inside_current_mount_namespace)1298{1299 int open_tree_fd = -EBADF;1300 struct mount_attr attr = {1301 .attr_set = MOUNT_ATTR_IDMAP,1302 };1303 1304 if (!mount_setattr_supported())1305 SKIP(return, "mount_setattr syscall not supported");1306 1307 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",1308 AT_EMPTY_PATH |1309 AT_NO_AUTOMOUNT |1310 AT_SYMLINK_NOFOLLOW |1311 OPEN_TREE_CLOEXEC |1312 OPEN_TREE_CLONE);1313 ASSERT_GE(open_tree_fd, 0);1314 1315 /* Changing mount properties on a detached mount. */1316 attr.userns_fd = get_userns_fd(0, 10000, 10000);1317 ASSERT_GE(attr.userns_fd, 0);1318 ASSERT_EQ(sys_mount_setattr(open_tree_fd, "",1319 AT_EMPTY_PATH, &attr, sizeof(attr)), 0);1320 ASSERT_EQ(close(attr.userns_fd), 0);1321 ASSERT_EQ(close(open_tree_fd), 0);1322}1323 1324/**1325 * Validate that a detached mount not in our mount namespace can be idmapped.1326 */1327TEST_F(mount_setattr_idmapped, detached_mount_outside_current_mount_namespace)1328{1329 int open_tree_fd = -EBADF;1330 struct mount_attr attr = {1331 .attr_set = MOUNT_ATTR_IDMAP,1332 };1333 1334 if (!mount_setattr_supported())1335 SKIP(return, "mount_setattr syscall not supported");1336 1337 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",1338 AT_EMPTY_PATH |1339 AT_NO_AUTOMOUNT |1340 AT_SYMLINK_NOFOLLOW |1341 OPEN_TREE_CLOEXEC |1342 OPEN_TREE_CLONE);1343 ASSERT_GE(open_tree_fd, 0);1344 1345 ASSERT_EQ(unshare(CLONE_NEWNS), 0);1346 1347 /* Changing mount properties on a detached mount. */1348 attr.userns_fd = get_userns_fd(0, 10000, 10000);1349 ASSERT_GE(attr.userns_fd, 0);1350 ASSERT_EQ(sys_mount_setattr(open_tree_fd, "",1351 AT_EMPTY_PATH, &attr, sizeof(attr)), 0);1352 ASSERT_EQ(close(attr.userns_fd), 0);1353 ASSERT_EQ(close(open_tree_fd), 0);1354}1355 1356/**1357 * Validate that currently changing the idmapping of an idmapped mount fails.1358 */1359TEST_F(mount_setattr_idmapped, change_idmapping)1360{1361 int open_tree_fd = -EBADF;1362 struct mount_attr attr = {1363 .attr_set = MOUNT_ATTR_IDMAP,1364 };1365 1366 if (!mount_setattr_supported())1367 SKIP(return, "mount_setattr syscall not supported");1368 1369 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",1370 AT_EMPTY_PATH |1371 AT_NO_AUTOMOUNT |1372 AT_SYMLINK_NOFOLLOW |1373 OPEN_TREE_CLOEXEC |1374 OPEN_TREE_CLONE);1375 ASSERT_GE(open_tree_fd, 0);1376 1377 attr.userns_fd = get_userns_fd(0, 10000, 10000);1378 ASSERT_GE(attr.userns_fd, 0);1379 ASSERT_EQ(sys_mount_setattr(open_tree_fd, "",1380 AT_EMPTY_PATH, &attr, sizeof(attr)), 0);1381 ASSERT_EQ(close(attr.userns_fd), 0);1382 1383 /* Change idmapping on a detached mount that is already idmapped. */1384 attr.userns_fd = get_userns_fd(0, 20000, 10000);1385 ASSERT_GE(attr.userns_fd, 0);1386 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0);1387 ASSERT_EQ(close(attr.userns_fd), 0);1388 ASSERT_EQ(close(open_tree_fd), 0);1389}1390 1391static bool expected_uid_gid(int dfd, const char *path, int flags,1392 uid_t expected_uid, gid_t expected_gid)1393{1394 int ret;1395 struct stat st;1396 1397 ret = fstatat(dfd, path, &st, flags);1398 if (ret < 0)1399 return false;1400 1401 return st.st_uid == expected_uid && st.st_gid == expected_gid;1402}1403 1404TEST_F(mount_setattr_idmapped, idmap_mount_tree_invalid)1405{1406 int open_tree_fd = -EBADF;1407 struct mount_attr attr = {1408 .attr_set = MOUNT_ATTR_IDMAP,1409 };1410 1411 if (!mount_setattr_supported())1412 SKIP(return, "mount_setattr syscall not supported");1413 1414 ASSERT_EQ(expected_uid_gid(-EBADF, "/tmp/B/b", 0, 0, 0), 0);1415 ASSERT_EQ(expected_uid_gid(-EBADF, "/tmp/B/BB/b", 0, 0, 0), 0);1416 1417 ASSERT_EQ(mount("testing", "/mnt/A", "ramfs", MS_NOATIME | MS_NODEV,1418 "size=100000,mode=700"), 0);1419 1420 ASSERT_EQ(mkdir("/mnt/A/AA", 0777), 0);1421 1422 ASSERT_EQ(mount("/tmp", "/mnt/A/AA", NULL, MS_BIND | MS_REC, NULL), 0);1423 1424 open_tree_fd = sys_open_tree(-EBADF, "/mnt/A",1425 AT_RECURSIVE |1426 AT_EMPTY_PATH |1427 AT_NO_AUTOMOUNT |1428 AT_SYMLINK_NOFOLLOW |1429 OPEN_TREE_CLOEXEC |1430 OPEN_TREE_CLONE);1431 ASSERT_GE(open_tree_fd, 0);1432 1433 attr.userns_fd = get_userns_fd(0, 10000, 10000);1434 ASSERT_GE(attr.userns_fd, 0);1435 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0);1436 ASSERT_EQ(close(attr.userns_fd), 0);1437 ASSERT_EQ(close(open_tree_fd), 0);1438 1439 ASSERT_EQ(expected_uid_gid(-EBADF, "/tmp/B/b", 0, 0, 0), 0);1440 ASSERT_EQ(expected_uid_gid(-EBADF, "/tmp/B/BB/b", 0, 0, 0), 0);1441 ASSERT_EQ(expected_uid_gid(open_tree_fd, "B/b", 0, 0, 0), 0);1442 ASSERT_EQ(expected_uid_gid(open_tree_fd, "B/BB/b", 0, 0, 0), 0);1443 1444 (void)umount2("/mnt/A", MNT_DETACH);1445}1446 1447TEST_F(mount_setattr, mount_attr_nosymfollow)1448{1449 int fd;1450 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;1451 struct mount_attr attr = {1452 .attr_set = MOUNT_ATTR_NOSYMFOLLOW,1453 };1454 1455 if (!mount_setattr_supported())1456 SKIP(return, "mount_setattr syscall not supported");1457 1458 fd = open(NOSYMFOLLOW_SYMLINK, O_RDWR | O_CLOEXEC);1459 ASSERT_GT(fd, 0);1460 ASSERT_EQ(close(fd), 0);1461 1462 old_flags = read_mnt_flags("/mnt/A");1463 ASSERT_GT(old_flags, 0);1464 1465 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);1466 1467 expected_flags = old_flags;1468 expected_flags |= ST_NOSYMFOLLOW;1469 1470 new_flags = read_mnt_flags("/mnt/A");1471 ASSERT_EQ(new_flags, expected_flags);1472 1473 new_flags = read_mnt_flags("/mnt/A/AA");1474 ASSERT_EQ(new_flags, expected_flags);1475 1476 new_flags = read_mnt_flags("/mnt/A/AA/B");1477 ASSERT_EQ(new_flags, expected_flags);1478 1479 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");1480 ASSERT_EQ(new_flags, expected_flags);1481 1482 fd = open(NOSYMFOLLOW_SYMLINK, O_RDWR | O_CLOEXEC);1483 ASSERT_LT(fd, 0);1484 ASSERT_EQ(errno, ELOOP);1485 1486 attr.attr_set &= ~MOUNT_ATTR_NOSYMFOLLOW;1487 attr.attr_clr |= MOUNT_ATTR_NOSYMFOLLOW;1488 1489 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);1490 1491 expected_flags &= ~ST_NOSYMFOLLOW;1492 new_flags = read_mnt_flags("/mnt/A");1493 ASSERT_EQ(new_flags, expected_flags);1494 1495 new_flags = read_mnt_flags("/mnt/A/AA");1496 ASSERT_EQ(new_flags, expected_flags);1497 1498 new_flags = read_mnt_flags("/mnt/A/AA/B");1499 ASSERT_EQ(new_flags, expected_flags);1500 1501 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");1502 ASSERT_EQ(new_flags, expected_flags);1503 1504 fd = open(NOSYMFOLLOW_SYMLINK, O_RDWR | O_CLOEXEC);1505 ASSERT_GT(fd, 0);1506 ASSERT_EQ(close(fd), 0);1507}1508 1509TEST_HARNESS_MAIN1510