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			fprintf(stderr, "[FAIL]\t%s():%d\t%s:%s\n", __func__, \
34				__LINE__, (description), strerror(errno));    \
35			dump_maps();					  \
36			exit(1);					      \
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}
126
127// This test validates that MREMAP_DONTUNMAP on a shared mapping works as expected.
128static void mremap_dontunmap_simple_shmem()
129{
130	unsigned long num_pages = 5;
131
132	int mem_fd = memfd_create("memfd", MFD_CLOEXEC);
133	BUG_ON(mem_fd < 0, "memfd_create");
134
135	BUG_ON(ftruncate(mem_fd, num_pages * page_size) < 0,
136			"ftruncate");
137
138	void *source_mapping =
139	    mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
140		 MAP_FILE | MAP_SHARED, mem_fd, 0);
141	BUG_ON(source_mapping == MAP_FAILED, "mmap");
142
143	BUG_ON(close(mem_fd) < 0, "close");
144
145	memset(source_mapping, 'a', num_pages * page_size);
146
147	// Try to just move the whole mapping anywhere (not fixed).
148	void *dest_mapping =
149	    mremap(source_mapping, num_pages * page_size, num_pages * page_size,
150		   MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
151	if (dest_mapping == MAP_FAILED && errno == EINVAL) {
152		// Old kernel which doesn't support MREMAP_DONTUNMAP on shmem.
153		BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
154			"unable to unmap source mapping");
155		return;
156	}
157
158	BUG_ON(dest_mapping == MAP_FAILED, "mremap");
159
160	// Validate that the pages have been moved, we know they were moved if
161	// the dest_mapping contains a's.
162	BUG_ON(check_region_contains_byte
163	       (dest_mapping, num_pages * page_size, 'a') != 0,
164	       "pages did not migrate");
165
166	// Because the region is backed by shmem, we will actually see the same
167	// memory at the source location still.
168	BUG_ON(check_region_contains_byte
169	       (source_mapping, num_pages * page_size, 'a') != 0,
170	       "source should have no ptes");
171
172	BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
173	       "unable to unmap destination mapping");
174	BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
175	       "unable to unmap source mapping");
176}
177
178// This test validates MREMAP_DONTUNMAP will move page tables to a specific
179// destination using MREMAP_FIXED, also while validating that the source
180// remains intact.
181static void mremap_dontunmap_simple_fixed()
182{
183	unsigned long num_pages = 5;
184
185	// Since we want to guarantee that we can remap to a point, we will
186	// create a mapping up front.
187	void *dest_mapping =
188	    mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
189		 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
190	BUG_ON(dest_mapping == MAP_FAILED, "mmap");
191	memset(dest_mapping, 'X', num_pages * page_size);
192
193	void *source_mapping =
194	    mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
195		 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
196	BUG_ON(source_mapping == MAP_FAILED, "mmap");
197	memset(source_mapping, 'a', num_pages * page_size);
198
199	void *remapped_mapping =
200	    mremap(source_mapping, num_pages * page_size, num_pages * page_size,
201		   MREMAP_FIXED | MREMAP_DONTUNMAP | MREMAP_MAYMOVE,
202		   dest_mapping);
203	BUG_ON(remapped_mapping == MAP_FAILED, "mremap");
204	BUG_ON(remapped_mapping != dest_mapping,
205	       "mremap should have placed the remapped mapping at dest_mapping");
206
207	// The dest mapping will have been unmap by mremap so we expect the Xs
208	// to be gone and replaced with a's.
209	BUG_ON(check_region_contains_byte
210	       (dest_mapping, num_pages * page_size, 'a') != 0,
211	       "pages did not migrate");
212
213	// And the source mapping will have had its ptes dropped.
214	BUG_ON(check_region_contains_byte
215	       (source_mapping, num_pages * page_size, 0) != 0,
216	       "source should have no ptes");
217
218	BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
219	       "unable to unmap destination mapping");
220	BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
221	       "unable to unmap source mapping");
222}
223
224// This test validates that we can MREMAP_DONTUNMAP for a portion of an
225// existing mapping.
226static void mremap_dontunmap_partial_mapping()
227{
228	/*
229	 *  source mapping:
230	 *  --------------
231	 *  | aaaaaaaaaa |
232	 *  --------------
233	 *  to become:
234	 *  --------------
235	 *  | aaaaa00000 |
236	 *  --------------
237	 *  With the destination mapping containing 5 pages of As.
238	 *  ---------
239	 *  | aaaaa |
240	 *  ---------
241	 */
242	unsigned long num_pages = 10;
243	void *source_mapping =
244	    mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
245		 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
246	BUG_ON(source_mapping == MAP_FAILED, "mmap");
247	memset(source_mapping, 'a', num_pages * page_size);
248
249	// We will grab the last 5 pages of the source and move them.
250	void *dest_mapping =
251	    mremap(source_mapping + (5 * page_size), 5 * page_size,
252		   5 * page_size,
253		   MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
254	BUG_ON(dest_mapping == MAP_FAILED, "mremap");
255
256	// We expect the first 5 pages of the source to contain a's and the
257	// final 5 pages to contain zeros.
258	BUG_ON(check_region_contains_byte(source_mapping, 5 * page_size, 'a') !=
259	       0, "first 5 pages of source should have original pages");
260	BUG_ON(check_region_contains_byte
261	       (source_mapping + (5 * page_size), 5 * page_size, 0) != 0,
262	       "final 5 pages of source should have no ptes");
263
264	// Finally we expect the destination to have 5 pages worth of a's.
265	BUG_ON(check_region_contains_byte(dest_mapping, 5 * page_size, 'a') !=
266	       0, "dest mapping should contain ptes from the source");
267
268	BUG_ON(munmap(dest_mapping, 5 * page_size) == -1,
269	       "unable to unmap destination mapping");
270	BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
271	       "unable to unmap source mapping");
272}
273
274// This test validates that we can remap over only a portion of a mapping.
275static void mremap_dontunmap_partial_mapping_overwrite(void)
276{
277	/*
278	 *  source mapping:
279	 *  ---------
280	 *  |aaaaa|
281	 *  ---------
282	 *  dest mapping initially:
283	 *  -----------
284	 *  |XXXXXXXXXX|
285	 *  ------------
286	 *  Source to become:
287	 *  ---------
288	 *  |00000|
289	 *  ---------
290	 *  With the destination mapping containing 5 pages of As.
291	 *  ------------
292	 *  |aaaaaXXXXX|
293	 *  ------------
294	 */
295	void *source_mapping =
296	    mmap(NULL, 5 * page_size, PROT_READ | PROT_WRITE,
297		 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
298	BUG_ON(source_mapping == MAP_FAILED, "mmap");
299	memset(source_mapping, 'a', 5 * page_size);
300
301	void *dest_mapping =
302	    mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE,
303		 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
304	BUG_ON(dest_mapping == MAP_FAILED, "mmap");
305	memset(dest_mapping, 'X', 10 * page_size);
306
307	// We will grab the last 5 pages of the source and move them.
308	void *remapped_mapping =
309	    mremap(source_mapping, 5 * page_size,
310		   5 * page_size,
311		   MREMAP_DONTUNMAP | MREMAP_MAYMOVE | MREMAP_FIXED, dest_mapping);
312	BUG_ON(dest_mapping == MAP_FAILED, "mremap");
313	BUG_ON(dest_mapping != remapped_mapping, "expected to remap to dest_mapping");
314
315	BUG_ON(check_region_contains_byte(source_mapping, 5 * page_size, 0) !=
316	       0, "first 5 pages of source should have no ptes");
317
318	// Finally we expect the destination to have 5 pages worth of a's.
319	BUG_ON(check_region_contains_byte(dest_mapping, 5 * page_size, 'a') != 0,
320			"dest mapping should contain ptes from the source");
321
322	// Finally the last 5 pages shouldn't have been touched.
323	BUG_ON(check_region_contains_byte(dest_mapping + (5 * page_size),
324				5 * page_size, 'X') != 0,
325			"dest mapping should have retained the last 5 pages");
326
327	BUG_ON(munmap(dest_mapping, 10 * page_size) == -1,
328	       "unable to unmap destination mapping");
329	BUG_ON(munmap(source_mapping, 5 * page_size) == -1,
330	       "unable to unmap source mapping");
331}
332
333int main(void)
334{
335	page_size = sysconf(_SC_PAGE_SIZE);
336
337	// test for kernel support for MREMAP_DONTUNMAP skipping the test if
338	// not.
339	if (kernel_support_for_mremap_dontunmap() != 0) {
340		printf("No kernel support for MREMAP_DONTUNMAP\n");
341		return KSFT_SKIP;
342	}
343
344	// Keep a page sized buffer around for when we need it.
345	page_buffer =
346	    mmap(NULL, page_size, PROT_READ | PROT_WRITE,
347		 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
348	BUG_ON(page_buffer == MAP_FAILED, "unable to mmap a page.");
349
350	mremap_dontunmap_simple();
351	mremap_dontunmap_simple_shmem();
352	mremap_dontunmap_simple_fixed();
353	mremap_dontunmap_partial_mapping();
354	mremap_dontunmap_partial_mapping_overwrite();
355
356	BUG_ON(munmap(page_buffer, page_size) == -1,
357	       "unable to unmap page buffer");
358
359	printf("OK\n");
360	return 0;
361}
362