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