1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <android-base/unique_fd.h> 18 #include <cutils/ashmem.h> 19 #include <gtest/gtest.h> 20 #include <linux/fs.h> 21 #include <sys/mman.h> 22 23 using android::base::unique_fd; 24 25 void TestCreateRegion(size_t size, unique_fd &fd, int prot) { 26 fd = unique_fd(ashmem_create_region(nullptr, size)); 27 ASSERT_TRUE(fd >= 0); 28 ASSERT_TRUE(ashmem_valid(fd)); 29 ASSERT_EQ(size, static_cast<size_t>(ashmem_get_size_region(fd))); 30 ASSERT_EQ(0, ashmem_set_prot_region(fd, prot)); 31 } 32 33 void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) { 34 *region = mmap(nullptr, size, prot, MAP_SHARED, fd, off); 35 ASSERT_NE(MAP_FAILED, *region); 36 } 37 38 void TestProtDenied(const unique_fd &fd, size_t size, int prot) { 39 EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0)); 40 } 41 42 void TestProtIs(const unique_fd& fd, int prot) { 43 EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK)); 44 } 45 46 void FillData(uint8_t* data, size_t dataLen) { 47 for (size_t i = 0; i < dataLen; i++) { 48 data[i] = i & 0xFF; 49 } 50 } 51 52 TEST(AshmemTest, BasicTest) { 53 constexpr size_t size = PAGE_SIZE; 54 uint8_t data[size]; 55 FillData(data, size); 56 57 unique_fd fd; 58 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE)); 59 60 void *region1; 61 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, ®ion1)); 62 63 memcpy(region1, &data, size); 64 ASSERT_EQ(0, memcmp(region1, &data, size)); 65 66 EXPECT_EQ(0, munmap(region1, size)); 67 68 void *region2; 69 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, ®ion2)); 70 ASSERT_EQ(0, memcmp(region2, &data, size)); 71 EXPECT_EQ(0, munmap(region2, size)); 72 } 73 74 TEST(AshmemTest, ForkTest) { 75 constexpr size_t size = PAGE_SIZE; 76 uint8_t data[size]; 77 FillData(data, size); 78 79 unique_fd fd; 80 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE)); 81 82 void *region1; 83 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, ®ion1)); 84 85 memcpy(region1, &data, size); 86 ASSERT_EQ(0, memcmp(region1, &data, size)); 87 EXPECT_EQ(0, munmap(region1, size)); 88 89 ASSERT_EXIT({ 90 void *region2 = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 91 if (region2 == MAP_FAILED) { 92 _exit(1); 93 } 94 if (memcmp(region2, &data, size) != 0) { 95 _exit(2); 96 } 97 memset(region2, 0, size); 98 munmap(region2, size); 99 _exit(0); 100 }, ::testing::ExitedWithCode(0),""); 101 102 memset(&data, 0, size); 103 void *region2; 104 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, ®ion2)); 105 ASSERT_EQ(0, memcmp(region2, &data, size)); 106 EXPECT_EQ(0, munmap(region2, size)); 107 } 108 109 TEST(AshmemTest, FileOperationsTest) { 110 unique_fd fd; 111 void* region; 112 113 // Allocate a 4-page buffer, but leave page-sized holes on either side 114 constexpr size_t size = PAGE_SIZE * 4; 115 constexpr size_t dataSize = PAGE_SIZE * 2; 116 constexpr size_t holeSize = PAGE_SIZE; 117 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE)); 118 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, dataSize, PROT_READ | PROT_WRITE, ®ion, holeSize)); 119 120 uint8_t data[dataSize]; 121 FillData(data, dataSize); 122 memcpy(region, data, dataSize); 123 124 constexpr off_t dataStart = holeSize; 125 constexpr off_t dataEnd = dataStart + dataSize; 126 127 // The sequence of seeks below looks something like this: 128 // 129 // [ ][data][data][ ] 130 // --^ lseek(99, SEEK_SET) 131 // ------^ lseek(dataStart, SEEK_CUR) 132 // ------^ lseek(0, SEEK_DATA) 133 // ------------^ lseek(dataStart, SEEK_HOLE) 134 // ^-- lseek(-99, SEEK_END) 135 // ^------ lseek(-dataStart, SEEK_CUR) 136 const struct { 137 // lseek() parameters 138 off_t offset; 139 int whence; 140 // Expected lseek() return value 141 off_t ret; 142 } seeks[] = { 143 {99, SEEK_SET, 99}, {dataStart, SEEK_CUR, dataStart + 99}, 144 {0, SEEK_DATA, dataStart}, {dataStart, SEEK_HOLE, dataEnd}, 145 {-99, SEEK_END, size - 99}, {-dataStart, SEEK_CUR, dataEnd - 99}, 146 }; 147 for (const auto& cfg : seeks) { 148 errno = 0; 149 auto off = lseek(fd, cfg.offset, cfg.whence); 150 ASSERT_EQ(cfg.ret, off) << "lseek(" << cfg.offset << ", " << cfg.whence << ") failed" 151 << (errno ? ": " : "") << (errno ? strerror(errno) : ""); 152 153 if (off >= dataStart && off < dataEnd) { 154 off_t dataOff = off - dataStart; 155 ssize_t readSize = dataSize - dataOff; 156 uint8_t buf[readSize]; 157 158 ASSERT_EQ(readSize, TEMP_FAILURE_RETRY(read(fd, buf, readSize))); 159 EXPECT_EQ(0, memcmp(buf, data + dataOff, readSize)); 160 } 161 } 162 163 EXPECT_EQ(0, munmap(region, dataSize)); 164 } 165 166 TEST(AshmemTest, ProtTest) { 167 unique_fd fd; 168 constexpr size_t size = PAGE_SIZE; 169 void *region; 170 171 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ)); 172 TestProtDenied(fd, size, PROT_WRITE); 173 TestProtIs(fd, PROT_READ); 174 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, ®ion)); 175 EXPECT_EQ(0, munmap(region, size)); 176 177 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE)); 178 TestProtDenied(fd, size, PROT_READ); 179 TestProtIs(fd, PROT_WRITE); 180 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, ®ion)); 181 EXPECT_EQ(0, munmap(region, size)); 182 183 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE)); 184 TestProtIs(fd, PROT_READ | PROT_WRITE); 185 ASSERT_EQ(0, ashmem_set_prot_region(fd, PROT_READ)); 186 errno = 0; 187 ASSERT_EQ(-1, ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE)) 188 << "kernel shouldn't allow adding protection bits"; 189 EXPECT_EQ(EINVAL, errno); 190 TestProtIs(fd, PROT_READ); 191 TestProtDenied(fd, size, PROT_WRITE); 192 } 193 194 TEST(AshmemTest, ForkProtTest) { 195 unique_fd fd; 196 constexpr size_t size = PAGE_SIZE; 197 198 int protFlags[] = { PROT_READ, PROT_WRITE }; 199 for (int i = 0; i < 2; i++) { 200 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE)); 201 ASSERT_EXIT({ 202 if (ashmem_set_prot_region(fd, protFlags[i]) >= 0) { 203 _exit(0); 204 } else { 205 _exit(1); 206 } 207 }, ::testing::ExitedWithCode(0), ""); 208 ASSERT_NO_FATAL_FAILURE(TestProtDenied(fd, size, protFlags[1-i])); 209 } 210 } 211 212 TEST(AshmemTest, ForkMultiRegionTest) { 213 constexpr size_t size = PAGE_SIZE; 214 uint8_t data[size]; 215 FillData(data, size); 216 217 constexpr int nRegions = 16; 218 unique_fd fd[nRegions]; 219 for (int i = 0; i < nRegions; i++) { 220 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd[i], PROT_READ | PROT_WRITE)); 221 void *region; 222 ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, ®ion)); 223 memcpy(region, &data, size); 224 ASSERT_EQ(0, memcmp(region, &data, size)); 225 EXPECT_EQ(0, munmap(region, size)); 226 } 227 228 ASSERT_EXIT({ 229 for (int i = 0; i < nRegions; i++) { 230 void *region = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd[i], 0); 231 if (region == MAP_FAILED) { 232 _exit(1); 233 } 234 if (memcmp(region, &data, size) != 0) { 235 munmap(region, size); 236 _exit(2); 237 } 238 memset(region, 0, size); 239 munmap(region, size); 240 } 241 _exit(0); 242 }, ::testing::ExitedWithCode(0), ""); 243 244 memset(&data, 0, size); 245 for (int i = 0; i < nRegions; i++) { 246 void *region; 247 ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, ®ion)); 248 ASSERT_EQ(0, memcmp(region, &data, size)); 249 EXPECT_EQ(0, munmap(region, size)); 250 } 251 } 252