blob: 5493b2cd107492f0f48f5a1c72f32cbc34e5ba08 [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
42void FillData(uint8_t* data, size_t dataLen) {
43 for (size_t i = 0; i < dataLen; i++) {
44 data[i] = i & 0xFF;
45 }
46}
47
48TEST(AshmemTest, BasicTest) {
49 constexpr size_t size = PAGE_SIZE;
50 uint8_t data[size];
51 FillData(data, size);
52
53 unique_fd fd;
54 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
55
56 void *region1;
57 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region1));
58
59 memcpy(region1, &data, size);
60 ASSERT_EQ(0, memcmp(region1, &data, size));
61
62 EXPECT_EQ(0, munmap(region1, size));
63
64 void *region2;
65 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region2));
66 ASSERT_EQ(0, memcmp(region2, &data, size));
67 EXPECT_EQ(0, munmap(region2, size));
68}
69
70TEST(AshmemTest, ForkTest) {
71 constexpr size_t size = PAGE_SIZE;
72 uint8_t data[size];
73 FillData(data, size);
74
75 unique_fd fd;
76 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
77
78 void *region1;
79 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region1));
80
81 memcpy(region1, &data, size);
82 ASSERT_EQ(0, memcmp(region1, &data, size));
83 EXPECT_EQ(0, munmap(region1, size));
84
85 ASSERT_EXIT({
86 void *region2 = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
87 if (region2 == MAP_FAILED) {
88 _exit(1);
89 }
90 if (memcmp(region2, &data, size) != 0) {
91 _exit(2);
92 }
93 memset(region2, 0, size);
94 munmap(region2, size);
95 _exit(0);
96 }, ::testing::ExitedWithCode(0),"");
97
98 memset(&data, 0, size);
99 void *region2;
100 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region2));
101 ASSERT_EQ(0, memcmp(region2, &data, size));
102 EXPECT_EQ(0, munmap(region2, size));
103}
104
Greg Hackmannbe11d572017-04-05 10:03:10 -0700105TEST(AshmemTest, FileOperationsTest) {
106 unique_fd fd;
107 void* region;
108
109 // Allocate a 4-page buffer, but leave page-sized holes on either side
110 constexpr size_t size = PAGE_SIZE * 4;
111 constexpr size_t dataSize = PAGE_SIZE * 2;
112 constexpr size_t holeSize = PAGE_SIZE;
113 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
114 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, dataSize, PROT_READ | PROT_WRITE, &region, holeSize));
115
116 uint8_t data[dataSize];
117 FillData(data, dataSize);
118 memcpy(region, data, dataSize);
119
120 constexpr off_t dataStart = holeSize;
121 constexpr off_t dataEnd = dataStart + dataSize;
122
123 // The sequence of seeks below looks something like this:
124 //
125 // [ ][data][data][ ]
126 // --^ lseek(99, SEEK_SET)
127 // ------^ lseek(dataStart, SEEK_CUR)
128 // ------^ lseek(0, SEEK_DATA)
129 // ------------^ lseek(dataStart, SEEK_HOLE)
130 // ^-- lseek(-99, SEEK_END)
131 // ^------ lseek(-dataStart, SEEK_CUR)
132 const struct {
133 // lseek() parameters
134 off_t offset;
135 int whence;
136 // Expected lseek() return value
137 off_t ret;
138 } seeks[] = {
139 {99, SEEK_SET, 99}, {dataStart, SEEK_CUR, dataStart + 99},
140 {0, SEEK_DATA, dataStart}, {dataStart, SEEK_HOLE, dataEnd},
141 {-99, SEEK_END, size - 99}, {-dataStart, SEEK_CUR, dataEnd - 99},
142 };
143 for (const auto& cfg : seeks) {
144 errno = 0;
145 auto off = lseek(fd, cfg.offset, cfg.whence);
146 ASSERT_EQ(cfg.ret, off) << "lseek(" << cfg.offset << ", " << cfg.whence << ") failed"
147 << (errno ? ": " : "") << (errno ? strerror(errno) : "");
148
149 if (off >= dataStart && off < dataEnd) {
150 off_t dataOff = off - dataStart;
151 ssize_t readSize = dataSize - dataOff;
152 uint8_t buf[readSize];
153
154 ASSERT_EQ(readSize, TEMP_FAILURE_RETRY(read(fd, buf, readSize)));
155 EXPECT_EQ(0, memcmp(buf, data + dataOff, readSize));
156 }
157 }
158
159 EXPECT_EQ(0, munmap(region, dataSize));
160}
161
Connor O'Briena963ae82016-09-12 15:52:04 -0700162TEST(AshmemTest, ProtTest) {
163 unique_fd fd;
164 constexpr size_t size = PAGE_SIZE;
165 void *region;
166
167 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ));
168 TestProtDenied(fd, size, PROT_WRITE);
169 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region));
170 EXPECT_EQ(0, munmap(region, size));
171
172 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE));
173 TestProtDenied(fd, size, PROT_READ);
174 ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, &region));
175 EXPECT_EQ(0, munmap(region, size));
176}
177
178TEST(AshmemTest, ForkProtTest) {
179 unique_fd fd;
180 constexpr size_t size = PAGE_SIZE;
181
182 int protFlags[] = { PROT_READ, PROT_WRITE };
183 for (int i = 0; i < 2; i++) {
184 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
185 ASSERT_EXIT({
186 if (ashmem_set_prot_region(fd, protFlags[i]) >= 0) {
187 _exit(0);
188 } else {
189 _exit(1);
190 }
191 }, ::testing::ExitedWithCode(0), "");
192 ASSERT_NO_FATAL_FAILURE(TestProtDenied(fd, size, protFlags[1-i]));
193 }
194}
195
196TEST(AshmemTest, ForkMultiRegionTest) {
197 constexpr size_t size = PAGE_SIZE;
198 uint8_t data[size];
199 FillData(data, size);
200
201 constexpr int nRegions = 16;
202 unique_fd fd[nRegions];
203 for (int i = 0; i < nRegions; i++) {
204 ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd[i], PROT_READ | PROT_WRITE));
205 void *region;
206 ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, &region));
207 memcpy(region, &data, size);
208 ASSERT_EQ(0, memcmp(region, &data, size));
209 EXPECT_EQ(0, munmap(region, size));
210 }
211
212 ASSERT_EXIT({
213 for (int i = 0; i < nRegions; i++) {
214 void *region = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd[i], 0);
215 if (region == MAP_FAILED) {
216 _exit(1);
217 }
218 if (memcmp(region, &data, size) != 0) {
219 munmap(region, size);
220 _exit(2);
221 }
222 memset(region, 0, size);
223 munmap(region, size);
224 }
225 _exit(0);
226 }, ::testing::ExitedWithCode(0), "");
227
228 memset(&data, 0, size);
229 for (int i = 0; i < nRegions; i++) {
230 void *region;
231 ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, &region));
232 ASSERT_EQ(0, memcmp(region, &data, size));
233 EXPECT_EQ(0, munmap(region, size));
234 }
235}