1 /*
2 * Copyright (C) 2014 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 <fcntl.h>
18 #include <sys/mman.h>
19 #include <sys/user.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22
23 #include <android-base/file.h>
24 #include <gtest/gtest.h>
25
26 #include "utils.h"
27
28 static const size_t kPageSize = getpagesize();
29
TEST(sys_mman,mmap_std)30 TEST(sys_mman, mmap_std) {
31 void* map = mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
32 ASSERT_NE(MAP_FAILED, map);
33 ASSERT_EQ(0, munmap(map, 4096));
34 }
35
TEST(sys_mman,mmap64_std)36 TEST(sys_mman, mmap64_std) {
37 void* map = mmap64(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
38 ASSERT_NE(MAP_FAILED, map);
39 ASSERT_EQ(0, munmap(map, 4096));
40 }
41
TEST(sys_mman,mmap_file_bad_offset)42 TEST(sys_mman, mmap_file_bad_offset) {
43 TemporaryFile tf;
44
45 void* map = mmap(nullptr, 100, PROT_READ, MAP_SHARED, tf.fd, 1);
46 ASSERT_EQ(MAP_FAILED, map);
47 }
48
TEST(sys_mman,mmap64_file_bad_offset)49 TEST(sys_mman, mmap64_file_bad_offset) {
50 TemporaryFile tf;
51
52 void* map = mmap64(nullptr, 100, PROT_READ, MAP_SHARED, tf.fd, 1);
53 ASSERT_EQ(MAP_FAILED, map);
54 }
55
56 #define STR_SSIZE(str) static_cast<ssize_t>(sizeof(str))
57
58 #define STRING_MSG "012345678\nabcdefgh\n"
59 #define INITIAL_MSG "000000000\n00000000\n"
60
TEST(sys_mman,mmap_file_read)61 TEST(sys_mman, mmap_file_read) {
62 TemporaryFile tf;
63
64 ASSERT_EQ(STR_SSIZE(STRING_MSG), write(tf.fd, STRING_MSG, sizeof(STRING_MSG)));
65
66 void* map = mmap(nullptr, sizeof(STRING_MSG), PROT_READ, MAP_SHARED, tf.fd, 0);
67 ASSERT_NE(MAP_FAILED, map);
68
69 char* data = reinterpret_cast<char*>(map);
70 ASSERT_STREQ(STRING_MSG, data);
71
72 ASSERT_EQ(0, munmap(map, sizeof(STRING_MSG)));
73 }
74
TEST(sys_mman,mmap_file_write)75 TEST(sys_mman, mmap_file_write) {
76 TemporaryFile tf;
77
78 ASSERT_EQ(STR_SSIZE(INITIAL_MSG), write(tf.fd, INITIAL_MSG, sizeof(INITIAL_MSG)));
79 lseek(tf.fd, 0, SEEK_SET);
80
81 void* map = mmap(nullptr, sizeof(STRING_MSG), PROT_WRITE, MAP_SHARED, tf.fd, 0);
82 ASSERT_NE(MAP_FAILED, map);
83 close(tf.fd);
84
85 memcpy(map, STRING_MSG, sizeof(STRING_MSG));
86
87 ASSERT_EQ(0, munmap(map, sizeof(STRING_MSG)));
88
89 tf.fd = open(tf.path, O_RDWR);
90 char buf[sizeof(STRING_MSG)];
91 memset(buf, 0, sizeof(STRING_MSG));
92 ASSERT_EQ(STR_SSIZE(STRING_MSG), read(tf.fd, buf, sizeof(STRING_MSG)));
93
94 ASSERT_STREQ(STRING_MSG, buf);
95 }
96
97 #define PAGE0_MSG "00PAGE00"
98 #define PAGE1_MSG "111PAGE111"
99 #define PAGE2_MSG "2222PAGE2222"
100 #define END_MSG "E"
101
TEST(sys_mman,mmap_file_read_at_offset)102 TEST(sys_mman, mmap_file_read_at_offset) {
103 TemporaryFile tf;
104 size_t pagesize = sysconf(_SC_PAGESIZE);
105
106 // Create the file with three pages worth of data.
107 ASSERT_EQ(STR_SSIZE(PAGE0_MSG), write(tf.fd, PAGE0_MSG, sizeof(PAGE0_MSG)));
108 ASSERT_NE(-1, lseek(tf.fd, pagesize, SEEK_SET));
109 ASSERT_EQ(STR_SSIZE(PAGE1_MSG), write(tf.fd, PAGE1_MSG, sizeof(PAGE1_MSG)));
110 ASSERT_NE(-1, lseek(tf.fd, 2 * pagesize, SEEK_SET));
111 ASSERT_EQ(STR_SSIZE(PAGE2_MSG), write(tf.fd, PAGE2_MSG, sizeof(PAGE2_MSG)));
112 ASSERT_NE(-1, lseek(tf.fd, 3 * pagesize - sizeof(END_MSG), SEEK_SET));
113 ASSERT_EQ(STR_SSIZE(END_MSG), write(tf.fd, END_MSG, sizeof(END_MSG)));
114
115 ASSERT_NE(-1, lseek(tf.fd, 0, SEEK_SET));
116
117 void* map = mmap(nullptr, pagesize, PROT_READ, MAP_SHARED, tf.fd, pagesize);
118 ASSERT_NE(MAP_FAILED, map);
119
120 char* data = reinterpret_cast<char*>(map);
121 ASSERT_STREQ(PAGE1_MSG, data);
122
123 ASSERT_EQ(0, munmap(map, pagesize));
124
125 map = mmap(nullptr, pagesize, PROT_READ, MAP_SHARED, tf.fd, 2 * pagesize);
126 ASSERT_NE(MAP_FAILED, map);
127
128 data = reinterpret_cast<char*>(map);
129 ASSERT_STREQ(PAGE2_MSG, data);
130 ASSERT_STREQ(END_MSG, data+pagesize-sizeof(END_MSG));
131
132 ASSERT_EQ(0, munmap(map, pagesize));
133 }
134
135 #define NEWPAGE1_MSG "1NEW1PAGE1"
136 #define NEWPAGE2_MSG "22NEW22PAGE22"
137
TEST(sys_mman,mmap_file_write_at_offset)138 TEST(sys_mman, mmap_file_write_at_offset) {
139 TemporaryFile tf;
140 size_t pagesize = sysconf(_SC_PAGESIZE);
141
142 // Create the file with three pages worth of data.
143 ASSERT_EQ(STR_SSIZE(PAGE0_MSG), write(tf.fd, PAGE0_MSG, sizeof(PAGE0_MSG)));
144 ASSERT_NE(-1, lseek(tf.fd, pagesize, SEEK_SET));
145 ASSERT_EQ(STR_SSIZE(PAGE1_MSG), write(tf.fd, PAGE1_MSG, sizeof(PAGE1_MSG)));
146 ASSERT_NE(-1, lseek(tf.fd, 2 * pagesize, SEEK_SET));
147 ASSERT_EQ(STR_SSIZE(PAGE2_MSG), write(tf.fd, PAGE2_MSG, sizeof(PAGE2_MSG)));
148 ASSERT_NE(-1, lseek(tf.fd, 3 * pagesize - sizeof(END_MSG), SEEK_SET));
149 ASSERT_EQ(STR_SSIZE(END_MSG), write(tf.fd, END_MSG, sizeof(END_MSG)));
150
151 ASSERT_NE(-1, lseek(tf.fd, 0, SEEK_SET));
152
153 void* map = mmap(nullptr, pagesize, PROT_WRITE, MAP_SHARED, tf.fd, pagesize);
154 ASSERT_NE(MAP_FAILED, map);
155 close(tf.fd);
156
157 memcpy(map, NEWPAGE1_MSG, sizeof(NEWPAGE1_MSG));
158 ASSERT_EQ(0, munmap(map, pagesize));
159
160 tf.fd = open(tf.path, O_RDWR);
161 map = mmap(nullptr, pagesize, PROT_WRITE, MAP_SHARED, tf.fd, 2 * pagesize);
162 ASSERT_NE(MAP_FAILED, map);
163 close(tf.fd);
164
165 memcpy(map, NEWPAGE2_MSG, sizeof(NEWPAGE2_MSG));
166 ASSERT_EQ(0, munmap(map, pagesize));
167
168 tf.fd = open(tf.path, O_RDWR);
169 char buf[pagesize];
170 ASSERT_EQ(static_cast<ssize_t>(pagesize), read(tf.fd, buf, pagesize));
171 ASSERT_STREQ(PAGE0_MSG, buf);
172 ASSERT_NE(-1, lseek(tf.fd, pagesize, SEEK_SET));
173 ASSERT_EQ(static_cast<ssize_t>(pagesize), read(tf.fd, buf, pagesize));
174 ASSERT_STREQ(NEWPAGE1_MSG, buf);
175 ASSERT_NE(-1, lseek(tf.fd, 2 * pagesize, SEEK_SET));
176 ASSERT_EQ(static_cast<ssize_t>(pagesize), read(tf.fd, buf, pagesize));
177 ASSERT_STREQ(NEWPAGE2_MSG, buf);
178 ASSERT_STREQ(END_MSG, buf+pagesize-sizeof(END_MSG));
179 }
180
TEST(sys_mman,posix_madvise)181 TEST(sys_mman, posix_madvise) {
182 TemporaryFile tempfile;
183 size_t pagesize = sysconf(_SC_PAGESIZE);
184 char buf[pagesize];
185
186 // Prepare environment.
187 ASSERT_EQ(static_cast<ssize_t>(pagesize), write(tempfile.fd, buf, pagesize));
188 void* map = mmap(nullptr, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, tempfile.fd, 0);
189 ASSERT_NE(MAP_FAILED, map);
190
191 // Verify different options of posix_madvise.
192 ASSERT_EQ(0, posix_madvise(map, pagesize, POSIX_MADV_NORMAL));
193 ASSERT_EQ(0, posix_madvise(map, pagesize, POSIX_MADV_SEQUENTIAL));
194 ASSERT_EQ(0, posix_madvise(map, pagesize, POSIX_MADV_RANDOM));
195 ASSERT_EQ(0, posix_madvise(map, pagesize, POSIX_MADV_WILLNEED));
196
197 ASSERT_EQ(0, munmap(map, pagesize));
198 }
199
200 // Verify that memory can still access after posix_madvise(POSIX_MADV_DONTNEED).
201 // We should test on MAP_ANONYMOUS memory to verify whether the memory is discarded,
202 // because the content of non MAP_ANONYMOUS memory can be reread from file.
TEST(sys_mman,posix_madvise_POSIX_MADV_DONTNEED)203 TEST(sys_mman, posix_madvise_POSIX_MADV_DONTNEED) {
204 size_t pagesize = sysconf(_SC_PAGESIZE);
205
206 void* map = mmap(nullptr, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
207 ASSERT_NE(MAP_FAILED, map);
208
209 int* int_ptr = reinterpret_cast<int*>(map);
210 for (int i = 0; i < static_cast<int>(pagesize / sizeof(int)); ++i) {
211 *int_ptr++ = i;
212 }
213
214 ASSERT_EQ(0, posix_madvise(map, pagesize, POSIX_MADV_DONTNEED));
215
216 int_ptr = reinterpret_cast<int*>(map);
217 for (int i = 0; i < static_cast<int>(pagesize / sizeof(int)); ++i) {
218 ASSERT_EQ(i, *int_ptr++);
219 }
220
221 ASSERT_EQ(0, munmap(map, pagesize));
222 }
223
TEST(sys_mman,mremap)224 TEST(sys_mman, mremap) {
225 #pragma clang diagnostic push
226 #pragma clang diagnostic ignored "-Wnonnull"
227 ASSERT_EQ(MAP_FAILED, mremap(nullptr, 0, 0, 0));
228 #pragma clang diagnostic pop
229 }
230
231 constexpr size_t kHuge = size_t(PTRDIFF_MAX) + 1;
232
TEST(sys_mman,mmap_PTRDIFF_MAX)233 TEST(sys_mman, mmap_PTRDIFF_MAX) {
234 ASSERT_EQ(MAP_FAILED, mmap(nullptr, kHuge, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
235 }
236
TEST(sys_mman,mremap_PTRDIFF_MAX)237 TEST(sys_mman, mremap_PTRDIFF_MAX) {
238 void* map = mmap(nullptr, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
239 ASSERT_NE(MAP_FAILED, map);
240
241 ASSERT_EQ(MAP_FAILED, mremap(map, kPageSize, kHuge, MREMAP_MAYMOVE));
242
243 ASSERT_EQ(0, munmap(map, kPageSize));
244 }
245
TEST(sys_mman,mremap_MREMAP_FIXED)246 TEST(sys_mman, mremap_MREMAP_FIXED) {
247 // We're not trying to test the kernel here; that's external/ltp's job.
248 // We just want to check that optional argument (mremap() is varargs)
249 // gets passed through in an MREMAP_FIXED call.
250 void* vma1 = mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
251 ASSERT_NE(MAP_FAILED, vma1);
252
253 void* vma2 = mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
254 ASSERT_NE(MAP_FAILED, vma2);
255
256 void* vma3 = mremap(vma1, getpagesize(), getpagesize(), MREMAP_FIXED | MREMAP_MAYMOVE, vma2);
257 ASSERT_EQ(vma2, vma3);
258 }
259
TEST(sys_mman,mmap_bug_27265969)260 TEST(sys_mman, mmap_bug_27265969) {
261 char* base = reinterpret_cast<char*>(
262 mmap(nullptr, kPageSize * 2, PROT_EXEC | PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
263 // Some kernels had bugs that would cause segfaults here...
264 __builtin___clear_cache(base, base + (kPageSize * 2));
265 }
266
TEST(sys_mman,mlock)267 TEST(sys_mman, mlock) {
268 void* map = mmap(nullptr, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
269 ASSERT_NE(MAP_FAILED, map);
270
271 // Not really anything we can assert about this.
272 mlock(map, kPageSize);
273
274 ASSERT_EQ(0, munmap(map, kPageSize));
275 }
276
TEST(sys_mman,mlock2)277 TEST(sys_mman, mlock2) {
278 #if defined(__GLIBC__)
279 GTEST_SKIP() << "needs glibc 2.27";
280 #else
281 void* map = mmap(nullptr, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
282 ASSERT_NE(MAP_FAILED, map);
283
284 // Not really anything we can assert about this.
285 mlock2(map, kPageSize, MLOCK_ONFAULT);
286
287 ASSERT_EQ(0, munmap(map, kPageSize));
288 #endif
289 }
290
TEST(sys_mman,memfd_create)291 TEST(sys_mman, memfd_create) {
292 #if defined(__GLIBC__)
293 GTEST_SKIP() << "needs glibc 2.27";
294 #else
295 // Is the MFD_CLOEXEC flag obeyed?
296 errno = 0;
297 int fd = memfd_create("doesn't matter", 0);
298 if (fd == -1 && errno == ENOSYS) GTEST_SKIP() << "no memfd_create() in this kernel";
299 ASSERT_NE(-1, fd) << strerror(errno);
300
301 int f = fcntl(fd, F_GETFD);
302 ASSERT_NE(-1, f);
303 ASSERT_FALSE(f & FD_CLOEXEC);
304 close(fd);
305
306 errno = 0;
307 fd = memfd_create("doesn't matter", MFD_CLOEXEC);
308 f = fcntl(fd, F_GETFD);
309 ASSERT_NE(-1, f);
310 ASSERT_TRUE(f & FD_CLOEXEC);
311
312 // Can we read and write?
313 std::string expected("hello, world!");
314 ASSERT_TRUE(android::base::WriteStringToFd(expected, fd));
315 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
316 std::string actual;
317 ASSERT_TRUE(android::base::ReadFdToString(fd, &actual));
318 ASSERT_EQ(expected, actual);
319
320 close(fd);
321 #endif
322 }
323