gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/pivot_root.cc (about) 1 // Copyright 2021 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <errno.h> 16 #include <fcntl.h> 17 #include <linux/capability.h> 18 #include <stddef.h> 19 #include <sys/mman.h> 20 #include <sys/mount.h> 21 #include <sys/stat.h> 22 #include <syscall.h> 23 #include <unistd.h> 24 25 #include <algorithm> 26 #include <functional> 27 #include <string> 28 #include <vector> 29 30 #include "gmock/gmock.h" 31 #include "gtest/gtest.h" 32 #include "absl/cleanup/cleanup.h" 33 #include "absl/strings/str_cat.h" 34 #include "absl/strings/str_split.h" 35 #include "absl/strings/string_view.h" 36 #include "test/util/capability_util.h" 37 #include "test/util/file_descriptor.h" 38 #include "test/util/fs_util.h" 39 #include "test/util/linux_capability_util.h" 40 #include "test/util/logging.h" 41 #include "test/util/mount_util.h" 42 #include "test/util/multiprocess_util.h" 43 #include "test/util/posix_error.h" 44 #include "test/util/temp_path.h" 45 #include "test/util/test_util.h" 46 47 namespace gvisor { 48 namespace testing { 49 50 namespace { 51 52 constexpr char kTmpfs[] = "tmpfs"; 53 54 TEST(PivotRootTest, Success) { 55 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 56 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 57 58 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 59 EXPECT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 60 SyscallSucceeds()); 61 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 62 EXPECT_THAT(mount("", new_root.path().c_str(), "tmpfs", 0, "mode=0700"), 63 SyscallSucceeds()); 64 const std::string new_root_path = 65 absl::StrCat("/", Basename(new_root.path())); 66 auto put_old = 67 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 68 const std::string put_old_path = 69 absl::StrCat(new_root_path, "/", Basename(put_old.path())); 70 71 const auto rest = [&] { 72 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 73 TEST_CHECK_SUCCESS( 74 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str())); 75 }; 76 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 77 } 78 79 TEST(PivotRootTest, CreatesNewRoot) { 80 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 81 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 82 83 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 84 EXPECT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 85 SyscallSucceeds()); 86 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 87 EXPECT_THAT(mount("", new_root.path().c_str(), "tmpfs", 0, "mode=0700"), 88 SyscallSucceeds()); 89 const std::string new_root_path = 90 absl::StrCat("/", Basename(new_root.path())); 91 auto put_old = 92 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 93 const std::string put_old_path = 94 absl::StrCat(new_root_path, "/", Basename(put_old.path())); 95 auto file_in_new_root = 96 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(new_root.path())); 97 const std::string file_in_new_root_path = file_in_new_root.path(); 98 const std::string file_in_new_root_new_path = 99 absl::StrCat("/", Basename(file_in_new_root_path)); 100 101 const auto rest = [&] { 102 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 103 // pivot_root and switch into new_root. 104 TEST_CHECK_SUCCESS( 105 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str())); 106 TEST_CHECK_SUCCESS(chdir("/")); 107 // Should not be able to stat file by its full path. 108 char buf[1024]; 109 struct stat statbuf; 110 TEST_CHECK_ERRNO(stat(file_in_new_root_path.c_str(), &statbuf), ENOENT); 111 // Should be able to stat file at new rooted path. 112 TEST_CHECK_SUCCESS(stat(file_in_new_root_new_path.c_str(), &statbuf)); 113 // getcwd should return "/". 114 TEST_CHECK_SUCCESS(syscall(__NR_getcwd, buf, sizeof(buf))); 115 TEST_PCHECK(strcmp(buf, "/") == 0); 116 // Statting '.', '..', '/', and '/..' all return the same dev and inode. 117 struct stat statbuf_dot; 118 TEST_CHECK_SUCCESS(stat(".", &statbuf_dot)); 119 struct stat statbuf_dotdot; 120 TEST_CHECK_SUCCESS(stat("..", &statbuf_dotdot)); 121 TEST_CHECK(statbuf_dot.st_dev == statbuf_dotdot.st_dev); 122 TEST_CHECK(statbuf_dot.st_ino == statbuf_dotdot.st_ino); 123 struct stat statbuf_slash; 124 TEST_CHECK_SUCCESS(stat("/", &statbuf_slash)); 125 TEST_CHECK(statbuf_dot.st_dev == statbuf_slash.st_dev); 126 TEST_CHECK(statbuf_dot.st_ino == statbuf_slash.st_ino); 127 struct stat statbuf_slashdotdot; 128 TEST_CHECK_SUCCESS(stat("/..", &statbuf_slashdotdot)); 129 TEST_CHECK(statbuf_dot.st_dev == statbuf_slashdotdot.st_dev); 130 TEST_CHECK(statbuf_dot.st_ino == statbuf_slashdotdot.st_ino); 131 }; 132 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 133 } 134 135 TEST(PivotRootTest, MovesOldRoot) { 136 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 137 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 138 139 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 140 EXPECT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 141 SyscallSucceeds()); 142 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 143 EXPECT_THAT(mount("", new_root.path().c_str(), "tmpfs", 0, "mode=0700"), 144 SyscallSucceeds()); 145 const std::string new_root_path = 146 absl::StrCat("/", Basename(new_root.path())); 147 auto put_old = 148 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 149 const std::string put_old_path = 150 absl::StrCat(new_root_path, "/", Basename(put_old.path())); 151 152 const std::string old_root_new_path = 153 absl::StrCat("/", Basename(put_old_path)); 154 155 const auto rest = [&] { 156 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 157 struct stat statbuf_oldroot; 158 TEST_CHECK_SUCCESS(stat("/", &statbuf_oldroot)); 159 // pivot_root and switch into new_root. 160 TEST_CHECK_SUCCESS( 161 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str())); 162 TEST_CHECK_SUCCESS(chdir("/")); 163 // Should not be able to stat file by its full path. 164 struct stat statbuf; 165 TEST_CHECK_ERRNO(stat(put_old_path.c_str(), &statbuf), ENOENT); 166 // Should be able to chdir to old root. 167 TEST_CHECK_SUCCESS(chdir(old_root_new_path.c_str())); 168 // Statting the root dir from before pivot_root and the put_old location 169 // should return the same inode and device. 170 struct stat statbuf_dot; 171 TEST_CHECK_SUCCESS(stat(".", &statbuf_dot)); 172 TEST_CHECK(statbuf_dot.st_ino == statbuf_oldroot.st_ino); 173 TEST_CHECK(statbuf_dot.st_dev == statbuf_oldroot.st_dev); 174 }; 175 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 176 } 177 178 TEST(PivotRootTest, ChangesCwdForAllProcesses) { 179 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 180 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 181 182 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 183 EXPECT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 184 SyscallSucceeds()); 185 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 186 EXPECT_THAT(mount("", new_root.path().c_str(), "tmpfs", 0, "mode=0700"), 187 SyscallSucceeds()); 188 const std::string new_root_path = 189 absl::StrCat("/", Basename(new_root.path())); 190 auto put_old = 191 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 192 const std::string put_old_path = 193 absl::StrCat(new_root_path, "/", Basename(put_old.path())); 194 const std::string old_root_new_path = 195 absl::StrCat("/", Basename(put_old_path)); 196 197 struct stat statbuf_newroot; 198 TEST_CHECK_SUCCESS(stat(new_root.path().c_str(), &statbuf_newroot)); 199 // Change cwd to the root path. 200 chdir(root.path().c_str()); 201 const auto rest = [&] { 202 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 203 TEST_CHECK_SUCCESS( 204 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str())); 205 }; 206 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 207 // pivot_root should change the cwd/root directory all threads and processes 208 // in the current mount namespace if they pointed the old root. 209 struct stat statbuf_cwd_after_syscall; 210 EXPECT_THAT(stat(".", &statbuf_cwd_after_syscall), SyscallSucceeds()); 211 EXPECT_EQ(statbuf_cwd_after_syscall.st_ino, statbuf_newroot.st_ino); 212 EXPECT_EQ(statbuf_cwd_after_syscall.st_dev, statbuf_newroot.st_dev); 213 } 214 215 TEST(PivotRootTest, DotDot) { 216 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 217 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 218 219 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 220 EXPECT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 221 SyscallSucceeds()); 222 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 223 EXPECT_THAT(mount("", new_root.path().c_str(), "tmpfs", 0, "mode=0700"), 224 SyscallSucceeds()); 225 const std::string new_root_path = 226 absl::StrCat("/", Basename(new_root.path())); 227 228 auto file_in_new_root = 229 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(new_root.path())); 230 const std::string file_in_new_root_path = 231 std::string(Basename(file_in_new_root.path())); 232 233 const auto rest = [&] { 234 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 235 TEST_CHECK_SUCCESS(chdir(new_root_path.c_str())); 236 // pivot_root should be able to stack put_old ontop of new_root. This allows 237 // users to pivot_root without creating a temp directory. 238 TEST_CHECK_SUCCESS(syscall(__NR_pivot_root, ".", ".")); 239 TEST_CHECK_SUCCESS(umount2(".", MNT_DETACH)); 240 struct stat statbuf; 241 TEST_CHECK_SUCCESS(stat(file_in_new_root_path.c_str(), &statbuf)); 242 }; 243 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 244 } 245 246 TEST(PivotRootTest, NotDir) { 247 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 248 249 auto file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 250 auto file2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 251 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 252 EXPECT_THAT( 253 syscall(__NR_pivot_root, file1.path().c_str(), file2.path().c_str()), 254 SyscallFailsWithErrno(ENOTDIR)); 255 EXPECT_THAT( 256 syscall(__NR_pivot_root, file1.path().c_str(), dir.path().c_str()), 257 SyscallFailsWithErrno(ENOTDIR)); 258 EXPECT_THAT( 259 syscall(__NR_pivot_root, dir.path().c_str(), file2.path().c_str()), 260 SyscallFailsWithErrno(ENOTDIR)); 261 } 262 263 TEST(PivotRootTest, NotExist) { 264 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 265 266 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 267 EXPECT_THAT(syscall(__NR_pivot_root, "/foo/bar", "/bar/baz"), 268 SyscallFailsWithErrno(ENOENT)); 269 EXPECT_THAT(syscall(__NR_pivot_root, dir.path().c_str(), "/bar/baz"), 270 SyscallFailsWithErrno(ENOENT)); 271 EXPECT_THAT(syscall(__NR_pivot_root, "/foo/bar", dir.path().c_str()), 272 SyscallFailsWithErrno(ENOENT)); 273 } 274 275 TEST(PivotRootTest, WithoutCapability) { 276 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETPCAP))); 277 278 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 279 const std::string new_root_path = new_root.path(); 280 EXPECT_THAT(mount("", new_root_path.c_str(), "tmpfs", 0, "mode=0700"), 281 SyscallSucceeds()); 282 auto put_old = 283 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root_path)); 284 const std::string put_old_path = put_old.path(); 285 286 AutoCapability cap(CAP_SYS_ADMIN, false); 287 EXPECT_THAT( 288 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str()), 289 SyscallFailsWithErrno(EPERM)); 290 } 291 292 TEST(PivotRootTest, NewRootOnRootMount) { 293 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 294 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 295 296 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 297 EXPECT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 298 SyscallSucceeds()); 299 auto new_root = 300 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path().c_str())); 301 const std::string new_root_path = 302 absl::StrCat("/", Basename(new_root.path())); 303 304 auto put_old = 305 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 306 const std::string put_old_path = 307 absl::StrCat(new_root_path, "/", Basename(put_old.path())); 308 309 const auto rest = [&] { 310 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 311 TEST_CHECK_ERRNO( 312 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str()), 313 EBUSY); 314 }; 315 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 316 } 317 318 TEST(PivotRootTest, NewRootNotAMountpoint) { 319 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 320 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 321 322 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 323 EXPECT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 324 SyscallSucceeds()); 325 // Make sure new_root is on a separate mount, otherwise this is the same 326 // as the NewRootOnRootMount test. 327 auto mountpoint = 328 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path().c_str())); 329 EXPECT_THAT(mount("", mountpoint.path().c_str(), "tmpfs", 0, "mode=0700"), 330 SyscallSucceeds()); 331 const std::string mountpoint_path = 332 absl::StrCat("/", Basename(mountpoint.path())); 333 auto new_root = 334 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(mountpoint.path())); 335 const std::string new_root_path = 336 absl::StrCat(mountpoint_path, "/", Basename(new_root.path())); 337 auto put_old = 338 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 339 const std::string put_old_path = 340 absl::StrCat(new_root_path, "/", Basename(put_old.path())); 341 342 const auto rest = [&] { 343 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 344 TEST_CHECK_ERRNO( 345 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str()), 346 EINVAL); 347 }; 348 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 349 } 350 351 TEST(PivotRootTest, PutOldNotUnderNewRoot) { 352 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 353 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 354 355 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 356 EXPECT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 357 SyscallSucceeds()); 358 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 359 const std::string new_root_path = 360 absl::StrCat("/", Basename(new_root.path())); 361 EXPECT_THAT(mount("", new_root.path().c_str(), "tmpfs", 0, "mode=0700"), 362 SyscallSucceeds()); 363 auto put_old = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 364 const std::string put_old_path = absl::StrCat("/", Basename(put_old.path())); 365 EXPECT_THAT(mount("", put_old.path().c_str(), "tmpfs", 0, "mode=0700"), 366 SyscallSucceeds()); 367 368 const auto rest = [&] { 369 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 370 TEST_CHECK_ERRNO( 371 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str()), 372 EINVAL); 373 }; 374 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 375 } 376 377 TEST(PivotRootTest, CurrentRootNotAMountPoint) { 378 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 379 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 380 381 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 382 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 383 EXPECT_THAT(mount("", new_root.path().c_str(), "tmpfs", 0, "mode=0700"), 384 SyscallSucceeds()); 385 const std::string new_root_path = 386 absl::StrCat("/", Basename(new_root.path())); 387 auto put_old = 388 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 389 const std::string put_old_path = 390 absl::StrCat(new_root_path, "/", Basename(put_old.path())); 391 392 const auto rest = [&] { 393 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 394 TEST_CHECK_ERRNO( 395 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str()), 396 EINVAL); 397 }; 398 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 399 } 400 401 TEST(PivotRootTest, OnRootFS) { 402 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 403 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 404 405 std::vector<ProcMountInfoEntry> mounts = 406 ASSERT_NO_ERRNO_AND_VALUE(ProcSelfMountInfoEntries()); 407 bool rootFSFound = false; 408 for (const auto& e : mounts) { 409 if (e.mount_point == "/" && e.id == e.parent_id) { 410 rootFSFound = true; 411 break; 412 } 413 } 414 415 SKIP_IF(!rootFSFound); 416 417 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 418 const std::string new_root_path = new_root.path(); 419 EXPECT_THAT(mount("", new_root_path.c_str(), "tmpfs", 0, "mode=0700"), 420 SyscallSucceeds()); 421 auto put_old = 422 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root_path)); 423 const std::string put_old_path = put_old.path(); 424 425 const auto rest = [&] { 426 TEST_CHECK_ERRNO( 427 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str()), 428 EINVAL); 429 }; 430 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 431 } 432 433 TEST(PivotRootTest, OnSharedNewRootParent) { 434 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 435 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 436 437 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 438 EXPECT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 439 SyscallSucceeds()); 440 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 441 EXPECT_THAT(mount("", new_root.path().c_str(), "tmpfs", 0, "mode=0700"), 442 SyscallSucceeds()); 443 const std::string new_root_path = JoinPath("/", Basename(new_root.path())); 444 auto put_old = 445 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 446 const std::string put_old_path = 447 JoinPath(new_root_path, "/", Basename(put_old.path())); 448 449 // Fails because parent has propagation type shared. 450 EXPECT_THAT(mount(nullptr, root.path().c_str(), nullptr, MS_SHARED, nullptr), 451 SyscallSucceeds()); 452 const auto rest = [&] { 453 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 454 TEST_CHECK_ERRNO( 455 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str()), 456 EINVAL); 457 }; 458 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 459 } 460 461 TEST(PivotRootTest, OnSharedNewRoot) { 462 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 463 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 464 465 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 466 EXPECT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 467 SyscallSucceeds()); 468 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 469 EXPECT_THAT(mount("", new_root.path().c_str(), "tmpfs", 0, "mode=0700"), 470 SyscallSucceeds()); 471 const std::string new_root_path = JoinPath("/", Basename(new_root.path())); 472 auto put_old = 473 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 474 const std::string put_old_path = 475 JoinPath(new_root_path, "/", Basename(put_old.path())); 476 477 // Fails because new_root has propagation type shared. 478 EXPECT_THAT( 479 mount(nullptr, new_root.path().c_str(), nullptr, MS_SHARED, nullptr), 480 SyscallSucceeds()); 481 const auto rest = [&] { 482 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 483 TEST_CHECK_ERRNO( 484 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str()), 485 EINVAL); 486 }; 487 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 488 } 489 490 TEST(PivotRootTest, OnSharedPutOldMountpoint) { 491 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 492 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 493 494 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 495 EXPECT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 496 SyscallSucceeds()); 497 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 498 EXPECT_THAT(mount("", new_root.path().c_str(), "tmpfs", 0, "mode=0700"), 499 SyscallSucceeds()); 500 const std::string new_root_path = JoinPath("/", Basename(new_root.path())); 501 auto put_old = 502 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 503 const std::string put_old_path = 504 JoinPath(new_root_path, "/", Basename(put_old.path())); 505 506 // Fails because put_old is a mountpoint and has propagation type shared. 507 EXPECT_THAT(mount("", put_old.path().c_str(), "tmpfs", 0, "mode=0700"), 508 SyscallSucceeds()); 509 EXPECT_THAT( 510 mount(nullptr, put_old.path().c_str(), nullptr, MS_SHARED, nullptr), 511 SyscallSucceeds()); 512 const auto rest = [&] { 513 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 514 TEST_CHECK_ERRNO( 515 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str()), 516 EINVAL); 517 }; 518 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 519 } 520 521 TEST(PivotRootTest, UnreachableNewRootFails) { 522 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 523 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 524 525 TempPath outside_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 526 ASSERT_THAT(mount("", outside_root.path().c_str(), kTmpfs, 0, ""), 527 SyscallSucceeds()); 528 TempPath root = 529 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(outside_root.path())); 530 ASSERT_THAT(mount("", root.path().c_str(), kTmpfs, 0, ""), SyscallSucceeds()); 531 TempPath new_root = 532 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 533 ASSERT_THAT(mount("", new_root.path().c_str(), kTmpfs, 0, ""), 534 SyscallSucceeds()); 535 const std::string new_root_path = 536 absl::StrCat("/", Basename(new_root.path())); 537 TempPath put_old = 538 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 539 const std::string put_old_path = 540 JoinPath(new_root_path, Basename(put_old.path())); 541 ASSERT_THAT(chdir(JoinPath(root.path(), "..").c_str()), SyscallSucceeds()); 542 543 const std::function<void()> rest = [&] { 544 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 545 // "." references the directory outside the chroot. 546 TEST_CHECK_ERRNO(syscall(__NR_pivot_root, ".", put_old_path.c_str()), 547 EINVAL); 548 }; 549 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 550 } 551 552 TEST(PivotRootTest, LockedNewRootFails) { 553 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 554 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 555 556 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 557 ASSERT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 558 SyscallSucceeds()); 559 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 560 ASSERT_THAT(mount("", new_root.path().c_str(), "tmpfs", 0, "mode=0700"), 561 SyscallSucceeds()); 562 const std::string new_root_path = JoinPath("/", Basename(new_root.path())); 563 auto put_old = 564 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path())); 565 const std::string put_old_path = 566 JoinPath(new_root_path, "/", Basename(put_old.path())); 567 ASSERT_THAT(mount("", put_old.path().c_str(), "tmpfs", 0, "mode=0700"), 568 SyscallSucceeds()); 569 570 const std::function<void()> rest = [&] { 571 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 572 TEST_CHECK_ERRNO( 573 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str()), 574 EINVAL); 575 }; 576 EXPECT_THAT(InForkedUserMountNamespace([] {}, rest), 577 IsPosixErrorOkAndHolds(0)); 578 } 579 580 TEST(PivotRootTest, OldRootUnlocked) { 581 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 582 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); 583 584 auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 585 ASSERT_THAT(mount("", root.path().c_str(), "tmpfs", 0, "mode=0700"), 586 SyscallSucceeds()); 587 auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 588 const std::string new_root_path = JoinPath("/", Basename(new_root.path())); 589 590 // The root mount will be locked when pivot_root is called but the new_root 591 // and put_old mounts won't be. 592 const std::function<void()> rest = [&] { 593 TEST_CHECK_SUCCESS(chroot(root.path().c_str())); 594 TEST_CHECK_SUCCESS(mount("", new_root_path.c_str(), "tmpfs", 0, "")); 595 std::string put_old_path = JoinPath(new_root_path, "put_old"); 596 TEST_CHECK_SUCCESS(mkdir(put_old_path.c_str(), 0700)); 597 TEST_CHECK_SUCCESS(mount("", put_old_path.c_str(), "tmpfs", 0, "")); 598 TEST_CHECK_SUCCESS( 599 syscall(__NR_pivot_root, new_root_path.c_str(), put_old_path.c_str())); 600 // The old root is no longer locked and can be unmounted. 601 TEST_CHECK_SUCCESS( 602 umount2(JoinPath("/", Basename(put_old_path)).c_str(), MNT_DETACH)); 603 }; 604 EXPECT_THAT(InForkedUserMountNamespace([] {}, rest), 605 IsPosixErrorOkAndHolds(0)); 606 } 607 608 } // namespace 609 610 } // namespace testing 611 } // namespace gvisor