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(cmd, sizeof(cmd), "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("%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("%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("%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("%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("%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("No kernel support for MREMAP_DONTUNMAP\n");
348		ksft_finished();
349	}
350
351	ksft_set_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