• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     // We've been inconsistent historically about whether or not these file
40     // descriptors were CLOEXEC. Make sure we're consistent going forward.
41     // https://issuetracker.google.com/165667331
42     ASSERT_EQ(FD_CLOEXEC, (fcntl(fd, F_GETFD) & FD_CLOEXEC));
43 }
44 
TestMmap(const unique_fd & fd,size_t size,int prot,void ** region,off_t off=0)45 void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {
46     ASSERT_TRUE(fd >= 0);
47     ASSERT_TRUE(ashmem_valid(fd));
48     *region = mmap(nullptr, size, prot, MAP_SHARED, fd, off);
49     ASSERT_NE(MAP_FAILED, *region);
50 }
51 
TestProtDenied(const unique_fd & fd,size_t size,int prot)52 void TestProtDenied(const unique_fd &fd, size_t size, int prot) {
53     ASSERT_TRUE(fd >= 0);
54     ASSERT_TRUE(ashmem_valid(fd));
55     EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
56 }
57 
TestProtIs(const unique_fd & fd,int prot)58 void TestProtIs(const unique_fd& fd, int prot) {
59     ASSERT_TRUE(fd >= 0);
60     ASSERT_TRUE(ashmem_valid(fd));
61     EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK));
62 }
63 
FillData(uint8_t * data,size_t dataLen)64 void FillData(uint8_t* data, size_t dataLen) {
65     for (size_t i = 0; i < dataLen; i++) {
66         data[i] = i & 0xFF;
67     }
68 }
69 
TEST(AshmemTest,BasicTest)70 TEST(AshmemTest, BasicTest) {
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 = nullptr;
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 
84     EXPECT_EQ(0, munmap(region1, size));
85 
86     void *region2;
87     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region2));
88     ASSERT_EQ(0, memcmp(region2, &data, size));
89     EXPECT_EQ(0, munmap(region2, size));
90 }
91 
TEST(AshmemTest,ForkTest)92 TEST(AshmemTest, ForkTest) {
93     constexpr size_t size = PAGE_SIZE;
94     uint8_t data[size];
95     FillData(data, size);
96 
97     unique_fd fd;
98     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
99 
100     void* region1 = nullptr;
101     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region1));
102 
103     memcpy(region1, &data, size);
104     ASSERT_EQ(0, memcmp(region1, &data, size));
105     EXPECT_EQ(0, munmap(region1, size));
106 
107     ASSERT_EXIT(
108         {
109             if (!ashmem_valid(fd)) {
110                 _exit(3);
111             }
112             void* region2 = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
113             if (region2 == MAP_FAILED) {
114                 _exit(1);
115             }
116             if (memcmp(region2, &data, size) != 0) {
117                 _exit(2);
118             }
119             memset(region2, 0, size);
120             munmap(region2, size);
121             _exit(0);
122         },
123         ::testing::ExitedWithCode(0), "");
124 
125     memset(&data, 0, size);
126     void *region2;
127     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region2));
128     ASSERT_EQ(0, memcmp(region2, &data, size));
129     EXPECT_EQ(0, munmap(region2, size));
130 }
131 
TEST(AshmemTest,FileOperationsTest)132 TEST(AshmemTest, FileOperationsTest) {
133     unique_fd fd;
134     void* region = nullptr;
135 
136     // Allocate a 4-page buffer, but leave page-sized holes on either side
137     constexpr size_t size = PAGE_SIZE * 4;
138     constexpr size_t dataSize = PAGE_SIZE * 2;
139     constexpr size_t holeSize = PAGE_SIZE;
140     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
141     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, dataSize, PROT_READ | PROT_WRITE, &region, holeSize));
142 
143     uint8_t data[dataSize];
144     FillData(data, dataSize);
145     memcpy(region, data, dataSize);
146 
147     constexpr off_t dataStart = holeSize;
148     constexpr off_t dataEnd = dataStart + dataSize;
149 
150     // The sequence of seeks below looks something like this:
151     //
152     // [    ][data][data][    ]
153     // --^                          lseek(99, SEEK_SET)
154     //   ------^                    lseek(dataStart, SEEK_CUR)
155     // ------^                      lseek(0, SEEK_DATA)
156     //       ------------^          lseek(dataStart, SEEK_HOLE)
157     //                      ^--     lseek(-99, SEEK_END)
158     //                ^------       lseek(-dataStart, SEEK_CUR)
159     const struct {
160         // lseek() parameters
161         off_t offset;
162         int whence;
163         // Expected lseek() return value
164         off_t ret;
165     } seeks[] = {
166         {99, SEEK_SET, 99},         {dataStart, SEEK_CUR, dataStart + 99},
167         {0, SEEK_DATA, dataStart},  {dataStart, SEEK_HOLE, dataEnd},
168         {-99, SEEK_END, size - 99}, {-dataStart, SEEK_CUR, dataEnd - 99},
169     };
170     for (const auto& cfg : seeks) {
171         errno = 0;
172         ASSERT_TRUE(ashmem_valid(fd));
173         auto off = lseek(fd, cfg.offset, cfg.whence);
174         ASSERT_EQ(cfg.ret, off) << "lseek(" << cfg.offset << ", " << cfg.whence << ") failed"
175                                 << (errno ? ": " : "") << (errno ? strerror(errno) : "");
176 
177         if (off >= dataStart && off < dataEnd) {
178             off_t dataOff = off - dataStart;
179             ssize_t readSize = dataSize - dataOff;
180             uint8_t buf[readSize];
181 
182             ASSERT_EQ(readSize, TEMP_FAILURE_RETRY(read(fd, buf, readSize)));
183             EXPECT_EQ(0, memcmp(buf, data + dataOff, readSize));
184         }
185     }
186 
187     EXPECT_EQ(0, munmap(region, dataSize));
188 }
189 
TEST(AshmemTest,ProtTest)190 TEST(AshmemTest, ProtTest) {
191     unique_fd fd;
192     constexpr size_t size = PAGE_SIZE;
193     void *region;
194 
195     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ));
196     TestProtDenied(fd, size, PROT_WRITE);
197     TestProtIs(fd, PROT_READ);
198     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region));
199     EXPECT_EQ(0, munmap(region, size));
200 
201     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE));
202     TestProtDenied(fd, size, PROT_READ);
203     TestProtIs(fd, PROT_WRITE);
204     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, &region));
205     EXPECT_EQ(0, munmap(region, size));
206 
207     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
208     TestProtIs(fd, PROT_READ | PROT_WRITE);
209     ASSERT_EQ(0, ashmem_set_prot_region(fd, PROT_READ));
210     errno = 0;
211     ASSERT_EQ(-1, ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE))
212         << "kernel shouldn't allow adding protection bits";
213     EXPECT_EQ(EINVAL, errno);
214     TestProtIs(fd, PROT_READ);
215     TestProtDenied(fd, size, PROT_WRITE);
216 }
217 
TEST(AshmemTest,ForkProtTest)218 TEST(AshmemTest, ForkProtTest) {
219     unique_fd fd;
220     constexpr size_t size = PAGE_SIZE;
221 
222     int protFlags[] = { PROT_READ, PROT_WRITE };
223     for (size_t i = 0; i < arraysize(protFlags); i++) {
224         ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
225         ASSERT_EXIT(
226             {
227                 if (!ashmem_valid(fd)) {
228                     _exit(3);
229                 } else if (ashmem_set_prot_region(fd, protFlags[i]) >= 0) {
230                     _exit(0);
231                 } else {
232                     _exit(1);
233                 }
234             },
235             ::testing::ExitedWithCode(0), "");
236         ASSERT_NO_FATAL_FAILURE(TestProtDenied(fd, size, protFlags[1-i]));
237     }
238 }
239 
TEST(AshmemTest,ForkMultiRegionTest)240 TEST(AshmemTest, ForkMultiRegionTest) {
241     constexpr size_t size = PAGE_SIZE;
242     uint8_t data[size];
243     FillData(data, size);
244 
245     constexpr int nRegions = 16;
246     unique_fd fd[nRegions];
247     for (int i = 0; i < nRegions; i++) {
248         ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd[i], PROT_READ | PROT_WRITE));
249         void* region = nullptr;
250         ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, &region));
251         memcpy(region, &data, size);
252         ASSERT_EQ(0, memcmp(region, &data, size));
253         EXPECT_EQ(0, munmap(region, size));
254     }
255 
256     ASSERT_EXIT({
257         for (int i = 0; i < nRegions; i++) {
258             if (!ashmem_valid(fd[i])) {
259                 _exit(3);
260             }
261             void *region = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd[i], 0);
262             if (region == MAP_FAILED) {
263                 _exit(1);
264             }
265             if (memcmp(region, &data, size) != 0) {
266                 munmap(region, size);
267                 _exit(2);
268             }
269             memset(region, 0, size);
270             munmap(region, size);
271         }
272         _exit(0);
273     }, ::testing::ExitedWithCode(0), "");
274 
275     memset(&data, 0, size);
276     for (int i = 0; i < nRegions; i++) {
277         void *region;
278         ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, &region));
279         ASSERT_EQ(0, memcmp(region, &data, size));
280         EXPECT_EQ(0, munmap(region, size));
281     }
282 }
283