brintos

brintos / linux-shallow public Read only

0
0
Text · 11.7 KiB · 1d75084 Raw
370 lines · c
1// SPDX-License-Identifier: GPL-2.02 3/*4 * Tests for mremap w/ MREMAP_DONTUNMAP.5 *6 * Copyright 2020, Brian Geffon <bgeffon@google.com>7 */8#define _GNU_SOURCE9#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 prevents41// 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't51	// 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 expected68// 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 leaving94// 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 if113	// the dest_mapping contains a's.114	BUG_ON(check_region_contains_byte115	       (dest_mapping, num_pages * page_size, 'a') != 0,116	       "pages did not migrate");117	BUG_ON(check_region_contains_byte118	       (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 if162	// the dest_mapping contains a's.163	BUG_ON(check_region_contains_byte164	       (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 same168	// memory at the source location still.169	BUG_ON(check_region_contains_byte170	       (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 specific181// destination using MREMAP_FIXED, also while validating that the source182// 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 will188	// 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 Xs210	// to be gone and replaced with a's.211	BUG_ON(check_region_contains_byte212	       (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_byte217	       (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 an228// 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 the260	// 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_byte264	       (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 if345	// 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