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
TestCreateRegion(size_t size,unique_fd & fd,int prot)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
TestMmap(const unique_fd & fd,size_t size,int prot,void ** region,off_t off=0)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
TestProtDenied(const unique_fd & fd,size_t size,int prot)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
TestProtIs(const unique_fd & fd,int prot)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
FillData(uint8_t * data,size_t dataLen)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
TEST(AshmemTest,BasicTest)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
TEST(AshmemTest,ForkTest)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
TEST(AshmemTest,FileOperationsTest)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
TEST(AshmemTest,ProtTest)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
TEST(AshmemTest,ForkProtTest)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
TEST(AshmemTest,ForkMultiRegionTest)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