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 <errno.h> 18 #include <linux/fs.h> 19 #include <stdint.h> 20 #include <string.h> 21 #include <sys/mman.h> 22 #include <sys/types.h> 23 #include <unistd.h> 24 25 #include <android-base/macros.h> 26 #include <android-base/unique_fd.h> 27 #include <cutils/ashmem.h> 28 #include <gtest/gtest.h> 29 30 using android::base::unique_fd; 31 32 void TestCreateRegion(size_t size, unique_fd &fd, int prot) { 33 fd = unique_fd(ashmem_create_region(nullptr, size)); 34 ASSERT_TRUE(fd >= 0); 35 ASSERT_TRUE(ashmem_valid(fd)); 36 ASSERT_EQ(size, static_cast<size_t>(ashmem_get_size_region(fd))); 37 ASSERT_EQ(0, ashmem_set_prot_region(fd, prot)); 38 } 39 40 void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) { 41 ASSERT_TRUE(fd >= 0); 42 ASSERT_TRUE(ashmem_valid(fd)); 43 *region = mmap(nullptr, size, prot, MAP_SHARED, fd, off); 44 ASSERT_NE(MAP_FAILED, *region); 45 } 46 47 void TestProtDenied(const unique_fd &fd, size_t size, int prot) { 48 ASSERT_TRUE(fd >= 0); 49 ASSERT_TRUE(ashmem_valid(fd)); 50 EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0)); 51 } 52 53 void TestProtIs(const unique_fd& fd, int prot) { 54 ASSERT_TRUE(fd >= 0); 55 ASSERT_TRUE(ashmem_valid(fd)); 56 EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK)); 57 } 58 59 void FillData(uint8_t* data, size_t dataLen) { 60 for (size_t i = 0; i < dataLen; i++) { 61 data[i] = i & 0xFF; 62 } 63 } 64 65 TEST(AshmemTest, BasicTest) { 66 constexpr size_t size = PAGE_SIZE; 67 uint8_t data[size]; 68 FillData(data, size); 69 70 unique_fd fd; 71 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE)); 72 73 void *region1; 74 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, ®ion1)); 75 76 memcpy(region1, &data, size); 77 ASSERT_EQ(0, memcmp(region1, &data, size)); 78 79 EXPECT_EQ(0, munmap(region1, size)); 80 81 void *region2; 82 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, ®ion2)); 83 ASSERT_EQ(0, memcmp(region2, &data, size)); 84 EXPECT_EQ(0, munmap(region2, size)); 85 } 86 87 TEST(AshmemTest, ForkTest) { 88 constexpr size_t size = PAGE_SIZE; 89 uint8_t data[size]; 90 FillData(data, size); 91 92 unique_fd fd; 93 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE)); 94 95 void *region1; 96 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, ®ion1)); 97 98 memcpy(region1, &data, size); 99 ASSERT_EQ(0, memcmp(region1, &data, size)); 100 EXPECT_EQ(0, munmap(region1, size)); 101 102 ASSERT_EXIT( 103 { 104 if (!ashmem_valid(fd)) { 105 _exit(3); 106 } 107 void* region2 = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 108 if (region2 == MAP_FAILED) { 109 _exit(1); 110 } 111 if (memcmp(region2, &data, size) != 0) { 112 _exit(2); 113 } 114 memset(region2, 0, size); 115 munmap(region2, size); 116 _exit(0); 117 }, 118 ::testing::ExitedWithCode(0), ""); 119 120 memset(&data, 0, size); 121 void *region2; 122 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, ®ion2)); 123 ASSERT_EQ(0, memcmp(region2, &data, size)); 124 EXPECT_EQ(0, munmap(region2, size)); 125 } 126 127 TEST(AshmemTest, FileOperationsTest) { 128 unique_fd fd; 129 void* region; 130 131 // Allocate a 4-page buffer, but leave page-sized holes on either side 132 constexpr size_t size = PAGE_SIZE * 4; 133 constexpr size_t dataSize = PAGE_SIZE * 2; 134 constexpr size_t holeSize = PAGE_SIZE; 135 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE)); 136 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, dataSize, PROT_READ | PROT_WRITE, ®ion, holeSize)); 137 138 uint8_t data[dataSize]; 139 FillData(data, dataSize); 140 memcpy(region, data, dataSize); 141 142 constexpr off_t dataStart = holeSize; 143 constexpr off_t dataEnd = dataStart + dataSize; 144 145 // The sequence of seeks below looks something like this: 146 // 147 // [ ][data][data][ ] 148 // --^ lseek(99, SEEK_SET) 149 // ------^ lseek(dataStart, SEEK_CUR) 150 // ------^ lseek(0, SEEK_DATA) 151 // ------------^ lseek(dataStart, SEEK_HOLE) 152 // ^-- lseek(-99, SEEK_END) 153 // ^------ lseek(-dataStart, SEEK_CUR) 154 const struct { 155 // lseek() parameters 156 off_t offset; 157 int whence; 158 // Expected lseek() return value 159 off_t ret; 160 } seeks[] = { 161 {99, SEEK_SET, 99}, {dataStart, SEEK_CUR, dataStart + 99}, 162 {0, SEEK_DATA, dataStart}, {dataStart, SEEK_HOLE, dataEnd}, 163 {-99, SEEK_END, size - 99}, {-dataStart, SEEK_CUR, dataEnd - 99}, 164 }; 165 for (const auto& cfg : seeks) { 166 errno = 0; 167 ASSERT_TRUE(ashmem_valid(fd)); 168 auto off = lseek(fd, cfg.offset, cfg.whence); 169 ASSERT_EQ(cfg.ret, off) << "lseek(" << cfg.offset << ", " << cfg.whence << ") failed" 170 << (errno ? ": " : "") << (errno ? strerror(errno) : ""); 171 172 if (off >= dataStart && off < dataEnd) { 173 off_t dataOff = off - dataStart; 174 ssize_t readSize = dataSize - dataOff; 175 uint8_t buf[readSize]; 176 177 ASSERT_EQ(readSize, TEMP_FAILURE_RETRY(read(fd, buf, readSize))); 178 EXPECT_EQ(0, memcmp(buf, data + dataOff, readSize)); 179 } 180 } 181 182 EXPECT_EQ(0, munmap(region, dataSize)); 183 } 184 185 TEST(AshmemTest, ProtTest) { 186 unique_fd fd; 187 constexpr size_t size = PAGE_SIZE; 188 void *region; 189 190 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ)); 191 TestProtDenied(fd, size, PROT_WRITE); 192 TestProtIs(fd, PROT_READ); 193 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, ®ion)); 194 EXPECT_EQ(0, munmap(region, size)); 195 196 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE)); 197 TestProtDenied(fd, size, PROT_READ); 198 TestProtIs(fd, PROT_WRITE); 199 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, ®ion)); 200 EXPECT_EQ(0, munmap(region, size)); 201 202 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE)); 203 TestProtIs(fd, PROT_READ | PROT_WRITE); 204 ASSERT_EQ(0, ashmem_set_prot_region(fd, PROT_READ)); 205 errno = 0; 206 ASSERT_EQ(-1, ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE)) 207 << "kernel shouldn't allow adding protection bits"; 208 EXPECT_EQ(EINVAL, errno); 209 TestProtIs(fd, PROT_READ); 210 TestProtDenied(fd, size, PROT_WRITE); 211 } 212 213 TEST(AshmemTest, ForkProtTest) { 214 unique_fd fd; 215 constexpr size_t size = PAGE_SIZE; 216 217 int protFlags[] = { PROT_READ, PROT_WRITE }; 218 for (size_t i = 0; i < arraysize(protFlags); i++) { 219 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE)); 220 ASSERT_EXIT( 221 { 222 if (!ashmem_valid(fd)) { 223 _exit(3); 224 } else if (ashmem_set_prot_region(fd, protFlags[i]) >= 0) { 225 _exit(0); 226 } else { 227 _exit(1); 228 } 229 }, 230 ::testing::ExitedWithCode(0), ""); 231 ASSERT_NO_FATAL_FAILURE(TestProtDenied(fd, size, protFlags[1-i])); 232 } 233 } 234 235 TEST(AshmemTest, ForkMultiRegionTest) { 236 constexpr size_t size = PAGE_SIZE; 237 uint8_t data[size]; 238 FillData(data, size); 239 240 constexpr int nRegions = 16; 241 unique_fd fd[nRegions]; 242 for (int i = 0; i < nRegions; i++) { 243 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd[i], PROT_READ | PROT_WRITE)); 244 void *region; 245 ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, ®ion)); 246 memcpy(region, &data, size); 247 ASSERT_EQ(0, memcmp(region, &data, size)); 248 EXPECT_EQ(0, munmap(region, size)); 249 } 250 251 ASSERT_EXIT({ 252 for (int i = 0; i < nRegions; i++) { 253 if (!ashmem_valid(fd[i])) { 254 _exit(3); 255 } 256 void *region = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd[i], 0); 257 if (region == MAP_FAILED) { 258 _exit(1); 259 } 260 if (memcmp(region, &data, size) != 0) { 261 munmap(region, size); 262 _exit(2); 263 } 264 memset(region, 0, size); 265 munmap(region, size); 266 } 267 _exit(0); 268 }, ::testing::ExitedWithCode(0), ""); 269 270 memset(&data, 0, size); 271 for (int i = 0; i < nRegions; i++) { 272 void *region; 273 ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, ®ion)); 274 ASSERT_EQ(0, memcmp(region, &data, size)); 275 EXPECT_EQ(0, munmap(region, size)); 276 } 277 } 278