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