1// SPDX-License-Identifier: GPL-2.0
2
3/*
4 * Tests for mremap w/ MREMAP_DONTUNMAP.
5 *
6 * Copyright 2020, Brian Geffon <bgeffon@google.com>
7 */
8#define _GNU_SOURCE
9#include <sys/mman.h>
10#include <linux/mman.h>
11#include <errno.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <unistd.h>
16
17#include "../kselftest.h"
18
19unsigned long page_size;
20char *page_buffer;
21
22static void dump_maps(void)
23{
24 char cmd[32];
25
26 snprintf(buf: cmd, size: sizeof(cmd), fmt: "cat /proc/%d/maps", getpid());
27 system(cmd);
28}
29
30#define BUG_ON(condition, description) \
31 do { \
32 if (condition) { \
33 dump_maps(); \
34 ksft_exit_fail_msg("[FAIL]\t%s:%d\t%s:%s\n", \
35 __func__, __LINE__, (description), \
36 strerror(errno)); \
37 } \
38 } while (0)
39
40// Try a simple operation for to "test" for kernel support this prevents
41// reporting tests as failed when it's run on an older kernel.
42static int kernel_support_for_mremap_dontunmap()
43{
44 int ret = 0;
45 unsigned long num_pages = 1;
46 void *source_mapping = mmap(NULL, num_pages * page_size, PROT_NONE,
47 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
48 BUG_ON(source_mapping == MAP_FAILED, "mmap");
49
50 // This simple remap should only fail if MREMAP_DONTUNMAP isn't
51 // supported.
52 void *dest_mapping =
53 mremap(source_mapping, num_pages * page_size, num_pages * page_size,
54 MREMAP_DONTUNMAP | MREMAP_MAYMOVE, 0);
55 if (dest_mapping == MAP_FAILED) {
56 ret = errno;
57 } else {
58 BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
59 "unable to unmap destination mapping");
60 }
61
62 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
63 "unable to unmap source mapping");
64 return ret;
65}
66
67// This helper will just validate that an entire mapping contains the expected
68// byte.
69static int check_region_contains_byte(void *addr, unsigned long size, char byte)
70{
71 BUG_ON(size & (page_size - 1),
72 "check_region_contains_byte expects page multiples");
73 BUG_ON((unsigned long)addr & (page_size - 1),
74 "check_region_contains_byte expects page alignment");
75
76 memset(page_buffer, byte, page_size);
77
78 unsigned long num_pages = size / page_size;
79 unsigned long i;
80
81 // Compare each page checking that it contains our expected byte.
82 for (i = 0; i < num_pages; ++i) {
83 int ret =
84 memcmp(addr + (i * page_size), page_buffer, page_size);
85 if (ret) {
86 return ret;
87 }
88 }
89
90 return 0;
91}
92
93// this test validates that MREMAP_DONTUNMAP moves the pagetables while leaving
94// the source mapping mapped.
95static void mremap_dontunmap_simple()
96{
97 unsigned long num_pages = 5;
98
99 void *source_mapping =
100 mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
101 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
102 BUG_ON(source_mapping == MAP_FAILED, "mmap");
103
104 memset(source_mapping, 'a', num_pages * page_size);
105
106 // Try to just move the whole mapping anywhere (not fixed).
107 void *dest_mapping =
108 mremap(source_mapping, num_pages * page_size, num_pages * page_size,
109 MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
110 BUG_ON(dest_mapping == MAP_FAILED, "mremap");
111
112 // Validate that the pages have been moved, we know they were moved if
113 // the dest_mapping contains a's.
114 BUG_ON(check_region_contains_byte
115 (dest_mapping, num_pages * page_size, 'a') != 0,
116 "pages did not migrate");
117 BUG_ON(check_region_contains_byte
118 (source_mapping, num_pages * page_size, 0) != 0,
119 "source should have no ptes");
120
121 BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
122 "unable to unmap destination mapping");
123 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
124 "unable to unmap source mapping");
125 ksft_test_result_pass(msg: "%s\n", __func__);
126}
127
128// This test validates that MREMAP_DONTUNMAP on a shared mapping works as expected.
129static void mremap_dontunmap_simple_shmem()
130{
131 unsigned long num_pages = 5;
132
133 int mem_fd = memfd_create("memfd", MFD_CLOEXEC);
134 BUG_ON(mem_fd < 0, "memfd_create");
135
136 BUG_ON(ftruncate(mem_fd, num_pages * page_size) < 0,
137 "ftruncate");
138
139 void *source_mapping =
140 mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
141 MAP_FILE | MAP_SHARED, mem_fd, 0);
142 BUG_ON(source_mapping == MAP_FAILED, "mmap");
143
144 BUG_ON(close(mem_fd) < 0, "close");
145
146 memset(source_mapping, 'a', num_pages * page_size);
147
148 // Try to just move the whole mapping anywhere (not fixed).
149 void *dest_mapping =
150 mremap(source_mapping, num_pages * page_size, num_pages * page_size,
151 MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
152 if (dest_mapping == MAP_FAILED && errno == EINVAL) {
153 // Old kernel which doesn't support MREMAP_DONTUNMAP on shmem.
154 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
155 "unable to unmap source mapping");
156 return;
157 }
158
159 BUG_ON(dest_mapping == MAP_FAILED, "mremap");
160
161 // Validate that the pages have been moved, we know they were moved if
162 // the dest_mapping contains a's.
163 BUG_ON(check_region_contains_byte
164 (dest_mapping, num_pages * page_size, 'a') != 0,
165 "pages did not migrate");
166
167 // Because the region is backed by shmem, we will actually see the same
168 // memory at the source location still.
169 BUG_ON(check_region_contains_byte
170 (source_mapping, num_pages * page_size, 'a') != 0,
171 "source should have no ptes");
172
173 BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
174 "unable to unmap destination mapping");
175 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
176 "unable to unmap source mapping");
177 ksft_test_result_pass(msg: "%s\n", __func__);
178}
179
180// This test validates MREMAP_DONTUNMAP will move page tables to a specific
181// destination using MREMAP_FIXED, also while validating that the source
182// remains intact.
183static void mremap_dontunmap_simple_fixed()
184{
185 unsigned long num_pages = 5;
186
187 // Since we want to guarantee that we can remap to a point, we will
188 // create a mapping up front.
189 void *dest_mapping =
190 mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
191 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
192 BUG_ON(dest_mapping == MAP_FAILED, "mmap");
193 memset(dest_mapping, 'X', num_pages * page_size);
194
195 void *source_mapping =
196 mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
197 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
198 BUG_ON(source_mapping == MAP_FAILED, "mmap");
199 memset(source_mapping, 'a', num_pages * page_size);
200
201 void *remapped_mapping =
202 mremap(source_mapping, num_pages * page_size, num_pages * page_size,
203 MREMAP_FIXED | MREMAP_DONTUNMAP | MREMAP_MAYMOVE,
204 dest_mapping);
205 BUG_ON(remapped_mapping == MAP_FAILED, "mremap");
206 BUG_ON(remapped_mapping != dest_mapping,
207 "mremap should have placed the remapped mapping at dest_mapping");
208
209 // The dest mapping will have been unmap by mremap so we expect the Xs
210 // to be gone and replaced with a's.
211 BUG_ON(check_region_contains_byte
212 (dest_mapping, num_pages * page_size, 'a') != 0,
213 "pages did not migrate");
214
215 // And the source mapping will have had its ptes dropped.
216 BUG_ON(check_region_contains_byte
217 (source_mapping, num_pages * page_size, 0) != 0,
218 "source should have no ptes");
219
220 BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
221 "unable to unmap destination mapping");
222 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
223 "unable to unmap source mapping");
224 ksft_test_result_pass(msg: "%s\n", __func__);
225}
226
227// This test validates that we can MREMAP_DONTUNMAP for a portion of an
228// existing mapping.
229static void mremap_dontunmap_partial_mapping()
230{
231 /*
232 * source mapping:
233 * --------------
234 * | aaaaaaaaaa |
235 * --------------
236 * to become:
237 * --------------
238 * | aaaaa00000 |
239 * --------------
240 * With the destination mapping containing 5 pages of As.
241 * ---------
242 * | aaaaa |
243 * ---------
244 */
245 unsigned long num_pages = 10;
246 void *source_mapping =
247 mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
248 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
249 BUG_ON(source_mapping == MAP_FAILED, "mmap");
250 memset(source_mapping, 'a', num_pages * page_size);
251
252 // We will grab the last 5 pages of the source and move them.
253 void *dest_mapping =
254 mremap(source_mapping + (5 * page_size), 5 * page_size,
255 5 * page_size,
256 MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
257 BUG_ON(dest_mapping == MAP_FAILED, "mremap");
258
259 // We expect the first 5 pages of the source to contain a's and the
260 // final 5 pages to contain zeros.
261 BUG_ON(check_region_contains_byte(source_mapping, 5 * page_size, 'a') !=
262 0, "first 5 pages of source should have original pages");
263 BUG_ON(check_region_contains_byte
264 (source_mapping + (5 * page_size), 5 * page_size, 0) != 0,
265 "final 5 pages of source should have no ptes");
266
267 // Finally we expect the destination to have 5 pages worth of a's.
268 BUG_ON(check_region_contains_byte(dest_mapping, 5 * page_size, 'a') !=
269 0, "dest mapping should contain ptes from the source");
270
271 BUG_ON(munmap(dest_mapping, 5 * page_size) == -1,
272 "unable to unmap destination mapping");
273 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
274 "unable to unmap source mapping");
275 ksft_test_result_pass(msg: "%s\n", __func__);
276}
277
278// This test validates that we can remap over only a portion of a mapping.
279static void mremap_dontunmap_partial_mapping_overwrite(void)
280{
281 /*
282 * source mapping:
283 * ---------
284 * |aaaaa|
285 * ---------
286 * dest mapping initially:
287 * -----------
288 * |XXXXXXXXXX|
289 * ------------
290 * Source to become:
291 * ---------
292 * |00000|
293 * ---------
294 * With the destination mapping containing 5 pages of As.
295 * ------------
296 * |aaaaaXXXXX|
297 * ------------
298 */
299 void *source_mapping =
300 mmap(NULL, 5 * page_size, PROT_READ | PROT_WRITE,
301 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
302 BUG_ON(source_mapping == MAP_FAILED, "mmap");
303 memset(source_mapping, 'a', 5 * page_size);
304
305 void *dest_mapping =
306 mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE,
307 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
308 BUG_ON(dest_mapping == MAP_FAILED, "mmap");
309 memset(dest_mapping, 'X', 10 * page_size);
310
311 // We will grab the last 5 pages of the source and move them.
312 void *remapped_mapping =
313 mremap(source_mapping, 5 * page_size,
314 5 * page_size,
315 MREMAP_DONTUNMAP | MREMAP_MAYMOVE | MREMAP_FIXED, dest_mapping);
316 BUG_ON(dest_mapping == MAP_FAILED, "mremap");
317 BUG_ON(dest_mapping != remapped_mapping, "expected to remap to dest_mapping");
318
319 BUG_ON(check_region_contains_byte(source_mapping, 5 * page_size, 0) !=
320 0, "first 5 pages of source should have no ptes");
321
322 // Finally we expect the destination to have 5 pages worth of a's.
323 BUG_ON(check_region_contains_byte(dest_mapping, 5 * page_size, 'a') != 0,
324 "dest mapping should contain ptes from the source");
325
326 // Finally the last 5 pages shouldn't have been touched.
327 BUG_ON(check_region_contains_byte(dest_mapping + (5 * page_size),
328 5 * page_size, 'X') != 0,
329 "dest mapping should have retained the last 5 pages");
330
331 BUG_ON(munmap(dest_mapping, 10 * page_size) == -1,
332 "unable to unmap destination mapping");
333 BUG_ON(munmap(source_mapping, 5 * page_size) == -1,
334 "unable to unmap source mapping");
335 ksft_test_result_pass(msg: "%s\n", __func__);
336}
337
338int main(void)
339{
340 ksft_print_header();
341
342 page_size = sysconf(_SC_PAGE_SIZE);
343
344 // test for kernel support for MREMAP_DONTUNMAP skipping the test if
345 // not.
346 if (kernel_support_for_mremap_dontunmap() != 0) {
347 ksft_print_msg(msg: "No kernel support for MREMAP_DONTUNMAP\n");
348 ksft_finished();
349 }
350
351 ksft_set_plan(plan: 5);
352
353 // Keep a page sized buffer around for when we need it.
354 page_buffer =
355 mmap(NULL, page_size, PROT_READ | PROT_WRITE,
356 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
357 BUG_ON(page_buffer == MAP_FAILED, "unable to mmap a page.");
358
359 mremap_dontunmap_simple();
360 mremap_dontunmap_simple_shmem();
361 mremap_dontunmap_simple_fixed();
362 mremap_dontunmap_partial_mapping();
363 mremap_dontunmap_partial_mapping_overwrite();
364
365 BUG_ON(munmap(page_buffer, page_size) == -1,
366 "unable to unmap page buffer");
367
368 ksft_finished();
369}
370

source code of linux/tools/testing/selftests/mm/mremap_dontunmap.c