github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/verity_mmap.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 <stdint.h>
    16  #include <stdlib.h>
    17  #include <sys/mman.h>
    18  #include <sys/mount.h>
    19  
    20  #include <string>
    21  
    22  #include "gmock/gmock.h"
    23  #include "gtest/gtest.h"
    24  #include "test/util/capability_util.h"
    25  #include "test/util/fs_util.h"
    26  #include "test/util/memory_util.h"
    27  #include "test/util/temp_path.h"
    28  #include "test/util/test_util.h"
    29  #include "test/util/verity_util.h"
    30  
    31  namespace gvisor {
    32  namespace testing {
    33  
    34  namespace {
    35  
    36  class MmapTest : public ::testing::Test {
    37   protected:
    38    void SetUp() override {
    39      // Verity is implemented in VFS2.
    40      SKIP_IF(IsRunningWithVFS1());
    41  
    42      SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
    43      // Mount a tmpfs file system, to be wrapped by a verity fs.
    44      tmpfs_dir_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
    45      ASSERT_THAT(mount("", tmpfs_dir_.path().c_str(), "tmpfs", 0, ""),
    46                  SyscallSucceeds());
    47  
    48      // Create a new file in the tmpfs mount.
    49      file_ = ASSERT_NO_ERRNO_AND_VALUE(
    50          TempPath::CreateFileWith(tmpfs_dir_.path(), kContents, 0777));
    51      filename_ = Basename(file_.path());
    52    }
    53  
    54    TempPath tmpfs_dir_;
    55    TempPath file_;
    56    std::string filename_;
    57  };
    58  
    59  TEST_F(MmapTest, MmapRead) {
    60    std::string verity_dir =
    61        ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
    62  
    63    // Make sure the file can be open and mmapped in the mounted verity fs.
    64    auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE(
    65        Open(JoinPath(verity_dir, filename_), O_RDONLY, 0777));
    66  
    67    Mapping const m =
    68        ASSERT_NO_ERRNO_AND_VALUE(Mmap(nullptr, sizeof(kContents) - 1, PROT_READ,
    69                                       MAP_SHARED, verity_fd.get(), 0));
    70    EXPECT_THAT(std::string(m.view()), ::testing::StrEq(kContents));
    71  }
    72  
    73  TEST_F(MmapTest, ModifiedBeforeMmap) {
    74    std::string verity_dir =
    75        ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
    76  
    77    // Modify the file and check verification failure upon mmapping.
    78    auto const fd = ASSERT_NO_ERRNO_AND_VALUE(
    79        Open(JoinPath(tmpfs_dir_.path(), filename_), O_RDWR, 0777));
    80    ASSERT_NO_ERRNO(FlipRandomBit(fd.get(), sizeof(kContents) - 1));
    81  
    82    auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE(
    83        Open(JoinPath(verity_dir, filename_), O_RDONLY, 0777));
    84    Mapping const m =
    85        ASSERT_NO_ERRNO_AND_VALUE(Mmap(nullptr, sizeof(kContents) - 1, PROT_READ,
    86                                       MAP_SHARED, verity_fd.get(), 0));
    87  
    88    // Memory fault is expected when Translate fails.
    89    EXPECT_EXIT(std::string(m.view()), ::testing::KilledBySignal(SIGSEGV), "");
    90  }
    91  
    92  TEST_F(MmapTest, ModifiedAfterMmap) {
    93    std::string verity_dir =
    94        ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
    95  
    96    auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE(
    97        Open(JoinPath(verity_dir, filename_), O_RDONLY, 0777));
    98    Mapping const m =
    99        ASSERT_NO_ERRNO_AND_VALUE(Mmap(nullptr, sizeof(kContents) - 1, PROT_READ,
   100                                       MAP_SHARED, verity_fd.get(), 0));
   101  
   102    // Modify the file after mapping and check verification failure upon mmapping.
   103    auto const fd = ASSERT_NO_ERRNO_AND_VALUE(
   104        Open(JoinPath(tmpfs_dir_.path(), filename_), O_RDWR, 0777));
   105    ASSERT_NO_ERRNO(FlipRandomBit(fd.get(), sizeof(kContents) - 1));
   106  
   107    // Memory fault is expected when Translate fails.
   108    EXPECT_EXIT(std::string(m.view()), ::testing::KilledBySignal(SIGSEGV), "");
   109  }
   110  
   111  class MmapParamTest
   112      : public MmapTest,
   113        public ::testing::WithParamInterface<std::tuple<int, int>> {
   114   protected:
   115    int prot() const { return std::get<0>(GetParam()); }
   116    int flags() const { return std::get<1>(GetParam()); }
   117  };
   118  
   119  INSTANTIATE_TEST_SUITE_P(
   120      WriteExecNoneSharedPrivate, MmapParamTest,
   121      ::testing::Combine(::testing::ValuesIn({
   122                             PROT_WRITE,
   123                             PROT_EXEC,
   124                             PROT_NONE,
   125                         }),
   126                         ::testing::ValuesIn({MAP_SHARED, MAP_PRIVATE})));
   127  
   128  TEST_P(MmapParamTest, Mmap) {
   129    std::string verity_dir =
   130        ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
   131  
   132    // Make sure the file can be open and mmapped in the mounted verity fs.
   133    auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE(
   134        Open(JoinPath(verity_dir, filename_), O_RDONLY, 0777));
   135  
   136    if (prot() == PROT_WRITE && flags() == MAP_SHARED) {
   137      // Verity file system is read-only.
   138      EXPECT_THAT(
   139          reinterpret_cast<intptr_t>(mmap(nullptr, sizeof(kContents) - 1, prot(),
   140                                          flags(), verity_fd.get(), 0)),
   141          SyscallFailsWithErrno(EACCES));
   142    } else {
   143      Mapping const m = ASSERT_NO_ERRNO_AND_VALUE(Mmap(
   144          nullptr, sizeof(kContents) - 1, prot(), flags(), verity_fd.get(), 0));
   145      if (prot() == PROT_NONE) {
   146        // Memory mapped by MAP_NONE cannot be accessed.
   147        EXPECT_EXIT(std::string(m.view()), ::testing::KilledBySignal(SIGSEGV),
   148                    "");
   149      } else {
   150        EXPECT_THAT(std::string(m.view()), ::testing::StrEq(kContents));
   151      }
   152    }
   153  }
   154  
   155  }  // namespace
   156  
   157  }  // namespace testing
   158  }  // namespace gvisor