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 19 unsigned long page_size; 20 char *page_buffer; 21 22 static 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. 42 static 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. 69 static 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. 95 static 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. 128 static 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. 181 static 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. 226 static 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. 275 static 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 333 int 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