Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/mount_setattr: add smoke tests for open_tree_attr(2) bug

There appear to be no other open_tree_attr(2) tests at the moment, but
as a minimal solution just add some additional checks in the existing
MOUNT_ATTR_IDMAP tests to make sure that open_tree_attr(2) cannot be
used to bypass the tested restrictions that apply to mount_setattr(2).

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
Link: https://lore.kernel.org/20250808-open_tree_attr-bugfix-idmap-v1-2-0ec7bc05646c@cyphar.com
Signed-off-by: Christian Brauner <brauner@kernel.org>

authored by

Aleksa Sarai and committed by
Christian Brauner
81e4b9cf 9308366f

+64 -13
+64 -13
tools/testing/selftests/mount_setattr/mount_setattr_test.c
··· 107 107 #endif 108 108 #endif 109 109 110 + #ifndef __NR_open_tree_attr 111 + #if defined __alpha__ 112 + #define __NR_open_tree_attr 577 113 + #elif defined _MIPS_SIM 114 + #if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */ 115 + #define __NR_open_tree_attr (467 + 4000) 116 + #endif 117 + #if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */ 118 + #define __NR_open_tree_attr (467 + 6000) 119 + #endif 120 + #if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */ 121 + #define __NR_open_tree_attr (467 + 5000) 122 + #endif 123 + #elif defined __ia64__ 124 + #define __NR_open_tree_attr (467 + 1024) 125 + #else 126 + #define __NR_open_tree_attr 467 127 + #endif 128 + #endif 129 + 110 130 #ifndef MOUNT_ATTR_IDMAP 111 131 #define MOUNT_ATTR_IDMAP 0x00100000 112 132 #endif ··· 139 119 struct mount_attr *attr, size_t size) 140 120 { 141 121 return syscall(__NR_mount_setattr, dfd, path, flags, attr, size); 122 + } 123 + 124 + static inline int sys_open_tree_attr(int dfd, const char *path, unsigned int flags, 125 + struct mount_attr *attr, size_t size) 126 + { 127 + return syscall(__NR_open_tree_attr, dfd, path, flags, attr, size); 142 128 } 143 129 144 130 static ssize_t write_nointr(int fd, const void *buf, size_t count) ··· 1248 1222 attr.userns_fd = get_userns_fd(0, 10000, 10000); 1249 1223 ASSERT_GE(attr.userns_fd, 0); 1250 1224 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0); 1225 + /* 1226 + * Make sure that open_tree_attr() without OPEN_TREE_CLONE is not a way 1227 + * to bypass this mount_setattr() restriction. 1228 + */ 1229 + ASSERT_LT(sys_open_tree_attr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0); 1230 + 1251 1231 ASSERT_EQ(close(attr.userns_fd), 0); 1252 1232 ASSERT_EQ(close(open_tree_fd), 0); 1253 1233 } ··· 1287 1255 ASSERT_GE(attr.userns_fd, 0); 1288 1256 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, 1289 1257 sizeof(attr)), 0); 1258 + /* 1259 + * Make sure that open_tree_attr() without OPEN_TREE_CLONE is not a way 1260 + * to bypass this mount_setattr() restriction. 1261 + */ 1262 + ASSERT_LT(sys_open_tree_attr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0); 1263 + 1290 1264 ASSERT_EQ(close(attr.userns_fd), 0); 1291 1265 ASSERT_EQ(close(open_tree_fd), 0); 1292 1266 } ··· 1359 1321 ASSERT_EQ(close(open_tree_fd), 0); 1360 1322 } 1361 1323 1324 + static bool expected_uid_gid(int dfd, const char *path, int flags, 1325 + uid_t expected_uid, gid_t expected_gid) 1326 + { 1327 + int ret; 1328 + struct stat st; 1329 + 1330 + ret = fstatat(dfd, path, &st, flags); 1331 + if (ret < 0) 1332 + return false; 1333 + 1334 + return st.st_uid == expected_uid && st.st_gid == expected_gid; 1335 + } 1336 + 1362 1337 /** 1363 1338 * Validate that currently changing the idmapping of an idmapped mount fails. 1364 1339 */ ··· 1381 1330 struct mount_attr attr = { 1382 1331 .attr_set = MOUNT_ATTR_IDMAP, 1383 1332 }; 1333 + 1334 + ASSERT_TRUE(expected_uid_gid(-EBADF, "/mnt/D", 0, 0, 0)); 1384 1335 1385 1336 if (!mount_setattr_supported()) 1386 1337 SKIP(return, "mount_setattr syscall not supported"); ··· 1401 1348 AT_EMPTY_PATH, &attr, sizeof(attr)), 0); 1402 1349 ASSERT_EQ(close(attr.userns_fd), 0); 1403 1350 1351 + EXPECT_FALSE(expected_uid_gid(open_tree_fd, ".", 0, 0, 0)); 1352 + EXPECT_TRUE(expected_uid_gid(open_tree_fd, ".", 0, 10000, 10000)); 1353 + 1404 1354 /* Change idmapping on a detached mount that is already idmapped. */ 1405 1355 attr.userns_fd = get_userns_fd(0, 20000, 10000); 1406 1356 ASSERT_GE(attr.userns_fd, 0); 1407 1357 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0); 1358 + /* 1359 + * Make sure that open_tree_attr() without OPEN_TREE_CLONE is not a way 1360 + * to bypass this mount_setattr() restriction. 1361 + */ 1362 + EXPECT_LT(sys_open_tree_attr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0); 1363 + EXPECT_FALSE(expected_uid_gid(open_tree_fd, ".", 0, 20000, 20000)); 1364 + EXPECT_TRUE(expected_uid_gid(open_tree_fd, ".", 0, 10000, 10000)); 1365 + 1408 1366 ASSERT_EQ(close(attr.userns_fd), 0); 1409 1367 ASSERT_EQ(close(open_tree_fd), 0); 1410 - } 1411 - 1412 - static bool expected_uid_gid(int dfd, const char *path, int flags, 1413 - uid_t expected_uid, gid_t expected_gid) 1414 - { 1415 - int ret; 1416 - struct stat st; 1417 - 1418 - ret = fstatat(dfd, path, &st, flags); 1419 - if (ret < 0) 1420 - return false; 1421 - 1422 - return st.st_uid == expected_uid && st.st_gid == expected_gid; 1423 1368 } 1424 1369 1425 1370 TEST_F(mount_setattr_idmapped, idmap_mount_tree_invalid)