blob: a87e23e300ec48d4796fd38c644c61a3121b253f [file] [log] [blame]
Connor O'Briena963ae82016-09-12 15:52:04 -07001/*
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
Greg Hackmannbe11d572017-04-05 10:03:10 -070017#include <android-base/unique_fd.h>
Connor O'Briena963ae82016-09-12 15:52:04 -070018#include <cutils/ashmem.h>
19#include <gtest/gtest.h>
Greg Hackmannbe11d572017-04-05 10:03:10 -070020#include <linux/fs.h>
21#include <sys/mman.h>
Connor O'Briena963ae82016-09-12 15:52:04 -070022
23using android::base::unique_fd;
24
25void 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
Greg Hackmannbe11d572017-04-05 10:03:10 -070033void 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);
Connor O'Briena963ae82016-09-12 15:52:04 -070035 ASSERT_NE(MAP_FAILED, *region);
36}
37
38void 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
Greg Hackmann4a9531d2017-04-11 15:08:36 -070042void TestProtIs(const unique_fd& fd, int prot) {
43 EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK));
44}
45
Connor O'Briena963ae82016-09-12 15:52:04 -070046void FillData(uint8_t* data, size_t dataLen) {
47 for (size_t i = 0; i < dataLen; i++) {
48 data[i] = i & 0xFF;
49 }
50}
51
52TEST(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, &region1));
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, &region2));
70 ASSERT_EQ(0, memcmp(region2, &data, size));
71 EXPECT_EQ(0, munmap(region2, size));
72}
73
74TEST(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, &region1));
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, &region2));
105 ASSERT_EQ(0, memcmp(region2, &data, size));
106 EXPECT_EQ(0, munmap(region2, size));
107}
108
Greg Hackmannbe11d572017-04-05 10:03:10 -0700109TEST(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, &region, 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
Connor O'Briena963ae82016-09-12 15:52:04 -0700166TEST(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);
Greg Hackmann4a9531d2017-04-11 15:08:36 -0700173 TestProtIs(fd, PROT_READ);
Connor O'Briena963ae82016-09-12 15:52:04 -0700174 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region));
175 EXPECT_EQ(0, munmap(region, size));
176
177 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE));
178 TestProtDenied(fd, size, PROT_READ);
Greg Hackmann4a9531d2017-04-11 15:08:36 -0700179 TestProtIs(fd, PROT_WRITE);
Connor O'Briena963ae82016-09-12 15:52:04 -0700180 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, &region));
181 EXPECT_EQ(0, munmap(region, size));
Greg Hackmann4a9531d2017-04-11 15:08:36 -0700182
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);
Connor O'Briena963ae82016-09-12 15:52:04 -0700192}
193
194TEST(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
212TEST(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, &region));
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, &region));
248 ASSERT_EQ(0, memcmp(region, &data, size));
249 EXPECT_EQ(0, munmap(region, size));
250 }
251}