• 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 <android-base/unique_fd.h>
18 #include <cutils/ashmem.h>
19 #include <gtest/gtest.h>
20 #include <linux/fs.h>
21 #include <sys/mman.h>
22 
23 using android::base::unique_fd;
24 
TestCreateRegion(size_t size,unique_fd & fd,int prot)25 void 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 
TestMmap(const unique_fd & fd,size_t size,int prot,void ** region,off_t off=0)33 void 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);
35     ASSERT_NE(MAP_FAILED, *region);
36 }
37 
TestProtDenied(const unique_fd & fd,size_t size,int prot)38 void 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 
TestProtIs(const unique_fd & fd,int prot)42 void TestProtIs(const unique_fd& fd, int prot) {
43     EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK));
44 }
45 
FillData(uint8_t * data,size_t dataLen)46 void FillData(uint8_t* data, size_t dataLen) {
47     for (size_t i = 0; i < dataLen; i++) {
48         data[i] = i & 0xFF;
49     }
50 }
51 
TEST(AshmemTest,BasicTest)52 TEST(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 
TEST(AshmemTest,ForkTest)74 TEST(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 
TEST(AshmemTest,FileOperationsTest)109 TEST(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 
TEST(AshmemTest,ProtTest)166 TEST(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);
173     TestProtIs(fd, PROT_READ);
174     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);
179     TestProtIs(fd, PROT_WRITE);
180     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, &region));
181     EXPECT_EQ(0, munmap(region, size));
182 
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);
192 }
193 
TEST(AshmemTest,ForkProtTest)194 TEST(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 
TEST(AshmemTest,ForkMultiRegionTest)212 TEST(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 }
252