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