Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.18-rc5 679 lines 14 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2#define _GNU_SOURCE 3#include <sys/mman.h> 4#include <stdint.h> 5#include <unistd.h> 6#include <string.h> 7#include <sys/time.h> 8#include <sys/resource.h> 9#include <stdbool.h> 10#include "mlock2.h" 11 12#include "../kselftest.h" 13 14struct vm_boundaries { 15 unsigned long start; 16 unsigned long end; 17}; 18 19static int get_vm_area(unsigned long addr, struct vm_boundaries *area) 20{ 21 FILE *file; 22 int ret = 1; 23 char line[1024] = {0}; 24 char *end_addr; 25 char *stop; 26 unsigned long start; 27 unsigned long end; 28 29 if (!area) 30 return ret; 31 32 file = fopen("/proc/self/maps", "r"); 33 if (!file) { 34 perror("fopen"); 35 return ret; 36 } 37 38 memset(area, 0, sizeof(struct vm_boundaries)); 39 40 while(fgets(line, 1024, file)) { 41 end_addr = strchr(line, '-'); 42 if (!end_addr) { 43 printf("cannot parse /proc/self/maps\n"); 44 goto out; 45 } 46 *end_addr = '\0'; 47 end_addr++; 48 stop = strchr(end_addr, ' '); 49 if (!stop) { 50 printf("cannot parse /proc/self/maps\n"); 51 goto out; 52 } 53 stop = '\0'; 54 55 sscanf(line, "%lx", &start); 56 sscanf(end_addr, "%lx", &end); 57 58 if (start <= addr && end > addr) { 59 area->start = start; 60 area->end = end; 61 ret = 0; 62 goto out; 63 } 64 } 65out: 66 fclose(file); 67 return ret; 68} 69 70static uint64_t get_pageflags(unsigned long addr) 71{ 72 FILE *file; 73 uint64_t pfn; 74 unsigned long offset; 75 76 file = fopen("/proc/self/pagemap", "r"); 77 if (!file) { 78 perror("fopen pagemap"); 79 _exit(1); 80 } 81 82 offset = addr / getpagesize() * sizeof(pfn); 83 84 if (fseek(file, offset, SEEK_SET)) { 85 perror("fseek pagemap"); 86 _exit(1); 87 } 88 89 if (fread(&pfn, sizeof(pfn), 1, file) != 1) { 90 perror("fread pagemap"); 91 _exit(1); 92 } 93 94 fclose(file); 95 return pfn; 96} 97 98static uint64_t get_kpageflags(unsigned long pfn) 99{ 100 uint64_t flags; 101 FILE *file; 102 103 file = fopen("/proc/kpageflags", "r"); 104 if (!file) { 105 perror("fopen kpageflags"); 106 _exit(1); 107 } 108 109 if (fseek(file, pfn * sizeof(flags), SEEK_SET)) { 110 perror("fseek kpageflags"); 111 _exit(1); 112 } 113 114 if (fread(&flags, sizeof(flags), 1, file) != 1) { 115 perror("fread kpageflags"); 116 _exit(1); 117 } 118 119 fclose(file); 120 return flags; 121} 122 123#define VMFLAGS "VmFlags:" 124 125static bool is_vmflag_set(unsigned long addr, const char *vmflag) 126{ 127 char *line = NULL; 128 char *flags; 129 size_t size = 0; 130 bool ret = false; 131 FILE *smaps; 132 133 smaps = seek_to_smaps_entry(addr); 134 if (!smaps) { 135 printf("Unable to parse /proc/self/smaps\n"); 136 goto out; 137 } 138 139 while (getline(&line, &size, smaps) > 0) { 140 if (!strstr(line, VMFLAGS)) { 141 free(line); 142 line = NULL; 143 size = 0; 144 continue; 145 } 146 147 flags = line + strlen(VMFLAGS); 148 ret = (strstr(flags, vmflag) != NULL); 149 goto out; 150 } 151 152out: 153 free(line); 154 fclose(smaps); 155 return ret; 156} 157 158#define SIZE "Size:" 159#define RSS "Rss:" 160#define LOCKED "lo" 161 162static bool is_vma_lock_on_fault(unsigned long addr) 163{ 164 bool ret = false; 165 bool locked; 166 FILE *smaps = NULL; 167 unsigned long vma_size, vma_rss; 168 char *line = NULL; 169 char *value; 170 size_t size = 0; 171 172 locked = is_vmflag_set(addr, LOCKED); 173 if (!locked) 174 goto out; 175 176 smaps = seek_to_smaps_entry(addr); 177 if (!smaps) { 178 printf("Unable to parse /proc/self/smaps\n"); 179 goto out; 180 } 181 182 while (getline(&line, &size, smaps) > 0) { 183 if (!strstr(line, SIZE)) { 184 free(line); 185 line = NULL; 186 size = 0; 187 continue; 188 } 189 190 value = line + strlen(SIZE); 191 if (sscanf(value, "%lu kB", &vma_size) < 1) { 192 printf("Unable to parse smaps entry for Size\n"); 193 goto out; 194 } 195 break; 196 } 197 198 while (getline(&line, &size, smaps) > 0) { 199 if (!strstr(line, RSS)) { 200 free(line); 201 line = NULL; 202 size = 0; 203 continue; 204 } 205 206 value = line + strlen(RSS); 207 if (sscanf(value, "%lu kB", &vma_rss) < 1) { 208 printf("Unable to parse smaps entry for Rss\n"); 209 goto out; 210 } 211 break; 212 } 213 214 ret = locked && (vma_rss < vma_size); 215out: 216 free(line); 217 if (smaps) 218 fclose(smaps); 219 return ret; 220} 221 222#define PRESENT_BIT 0x8000000000000000ULL 223#define PFN_MASK 0x007FFFFFFFFFFFFFULL 224#define UNEVICTABLE_BIT (1UL << 18) 225 226static int lock_check(char *map) 227{ 228 unsigned long page_size = getpagesize(); 229 uint64_t page1_flags, page2_flags; 230 231 page1_flags = get_pageflags((unsigned long)map); 232 page2_flags = get_pageflags((unsigned long)map + page_size); 233 234 /* Both pages should be present */ 235 if (((page1_flags & PRESENT_BIT) == 0) || 236 ((page2_flags & PRESENT_BIT) == 0)) { 237 printf("Failed to make both pages present\n"); 238 return 1; 239 } 240 241 page1_flags = get_kpageflags(page1_flags & PFN_MASK); 242 page2_flags = get_kpageflags(page2_flags & PFN_MASK); 243 244 /* Both pages should be unevictable */ 245 if (((page1_flags & UNEVICTABLE_BIT) == 0) || 246 ((page2_flags & UNEVICTABLE_BIT) == 0)) { 247 printf("Failed to make both pages unevictable\n"); 248 return 1; 249 } 250 251 if (!is_vmflag_set((unsigned long)map, LOCKED)) { 252 printf("VMA flag %s is missing on page 1\n", LOCKED); 253 return 1; 254 } 255 256 if (!is_vmflag_set((unsigned long)map + page_size, LOCKED)) { 257 printf("VMA flag %s is missing on page 2\n", LOCKED); 258 return 1; 259 } 260 261 return 0; 262} 263 264static int unlock_lock_check(char *map) 265{ 266 unsigned long page_size = getpagesize(); 267 uint64_t page1_flags, page2_flags; 268 269 page1_flags = get_pageflags((unsigned long)map); 270 page2_flags = get_pageflags((unsigned long)map + page_size); 271 page1_flags = get_kpageflags(page1_flags & PFN_MASK); 272 page2_flags = get_kpageflags(page2_flags & PFN_MASK); 273 274 if ((page1_flags & UNEVICTABLE_BIT) || (page2_flags & UNEVICTABLE_BIT)) { 275 printf("A page is still marked unevictable after unlock\n"); 276 return 1; 277 } 278 279 if (is_vmflag_set((unsigned long)map, LOCKED)) { 280 printf("VMA flag %s is present on page 1 after unlock\n", LOCKED); 281 return 1; 282 } 283 284 if (is_vmflag_set((unsigned long)map + page_size, LOCKED)) { 285 printf("VMA flag %s is present on page 2 after unlock\n", LOCKED); 286 return 1; 287 } 288 289 return 0; 290} 291 292static int test_mlock_lock() 293{ 294 char *map; 295 int ret = 1; 296 unsigned long page_size = getpagesize(); 297 298 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 299 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 300 if (map == MAP_FAILED) { 301 perror("test_mlock_locked mmap"); 302 goto out; 303 } 304 305 if (mlock2_(map, 2 * page_size, 0)) { 306 if (errno == ENOSYS) { 307 printf("Cannot call new mlock family, skipping test\n"); 308 _exit(KSFT_SKIP); 309 } 310 perror("mlock2(0)"); 311 goto unmap; 312 } 313 314 if (lock_check(map)) 315 goto unmap; 316 317 /* Now unlock and recheck attributes */ 318 if (munlock(map, 2 * page_size)) { 319 perror("munlock()"); 320 goto unmap; 321 } 322 323 ret = unlock_lock_check(map); 324 325unmap: 326 munmap(map, 2 * page_size); 327out: 328 return ret; 329} 330 331static int onfault_check(char *map) 332{ 333 unsigned long page_size = getpagesize(); 334 uint64_t page1_flags, page2_flags; 335 336 page1_flags = get_pageflags((unsigned long)map); 337 page2_flags = get_pageflags((unsigned long)map + page_size); 338 339 /* Neither page should be present */ 340 if ((page1_flags & PRESENT_BIT) || (page2_flags & PRESENT_BIT)) { 341 printf("Pages were made present by MLOCK_ONFAULT\n"); 342 return 1; 343 } 344 345 *map = 'a'; 346 page1_flags = get_pageflags((unsigned long)map); 347 page2_flags = get_pageflags((unsigned long)map + page_size); 348 349 /* Only page 1 should be present */ 350 if ((page1_flags & PRESENT_BIT) == 0) { 351 printf("Page 1 is not present after fault\n"); 352 return 1; 353 } else if (page2_flags & PRESENT_BIT) { 354 printf("Page 2 was made present\n"); 355 return 1; 356 } 357 358 page1_flags = get_kpageflags(page1_flags & PFN_MASK); 359 360 /* Page 1 should be unevictable */ 361 if ((page1_flags & UNEVICTABLE_BIT) == 0) { 362 printf("Failed to make faulted page unevictable\n"); 363 return 1; 364 } 365 366 if (!is_vma_lock_on_fault((unsigned long)map)) { 367 printf("VMA is not marked for lock on fault\n"); 368 return 1; 369 } 370 371 if (!is_vma_lock_on_fault((unsigned long)map + page_size)) { 372 printf("VMA is not marked for lock on fault\n"); 373 return 1; 374 } 375 376 return 0; 377} 378 379static int unlock_onfault_check(char *map) 380{ 381 unsigned long page_size = getpagesize(); 382 uint64_t page1_flags; 383 384 page1_flags = get_pageflags((unsigned long)map); 385 page1_flags = get_kpageflags(page1_flags & PFN_MASK); 386 387 if (page1_flags & UNEVICTABLE_BIT) { 388 printf("Page 1 is still marked unevictable after unlock\n"); 389 return 1; 390 } 391 392 if (is_vma_lock_on_fault((unsigned long)map) || 393 is_vma_lock_on_fault((unsigned long)map + page_size)) { 394 printf("VMA is still lock on fault after unlock\n"); 395 return 1; 396 } 397 398 return 0; 399} 400 401static int test_mlock_onfault() 402{ 403 char *map; 404 int ret = 1; 405 unsigned long page_size = getpagesize(); 406 407 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 408 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 409 if (map == MAP_FAILED) { 410 perror("test_mlock_locked mmap"); 411 goto out; 412 } 413 414 if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) { 415 if (errno == ENOSYS) { 416 printf("Cannot call new mlock family, skipping test\n"); 417 _exit(KSFT_SKIP); 418 } 419 perror("mlock2(MLOCK_ONFAULT)"); 420 goto unmap; 421 } 422 423 if (onfault_check(map)) 424 goto unmap; 425 426 /* Now unlock and recheck attributes */ 427 if (munlock(map, 2 * page_size)) { 428 if (errno == ENOSYS) { 429 printf("Cannot call new mlock family, skipping test\n"); 430 _exit(KSFT_SKIP); 431 } 432 perror("munlock()"); 433 goto unmap; 434 } 435 436 ret = unlock_onfault_check(map); 437unmap: 438 munmap(map, 2 * page_size); 439out: 440 return ret; 441} 442 443static int test_lock_onfault_of_present() 444{ 445 char *map; 446 int ret = 1; 447 unsigned long page_size = getpagesize(); 448 uint64_t page1_flags, page2_flags; 449 450 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 451 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 452 if (map == MAP_FAILED) { 453 perror("test_mlock_locked mmap"); 454 goto out; 455 } 456 457 *map = 'a'; 458 459 if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) { 460 if (errno == ENOSYS) { 461 printf("Cannot call new mlock family, skipping test\n"); 462 _exit(KSFT_SKIP); 463 } 464 perror("mlock2(MLOCK_ONFAULT)"); 465 goto unmap; 466 } 467 468 page1_flags = get_pageflags((unsigned long)map); 469 page2_flags = get_pageflags((unsigned long)map + page_size); 470 page1_flags = get_kpageflags(page1_flags & PFN_MASK); 471 page2_flags = get_kpageflags(page2_flags & PFN_MASK); 472 473 /* Page 1 should be unevictable */ 474 if ((page1_flags & UNEVICTABLE_BIT) == 0) { 475 printf("Failed to make present page unevictable\n"); 476 goto unmap; 477 } 478 479 if (!is_vma_lock_on_fault((unsigned long)map) || 480 !is_vma_lock_on_fault((unsigned long)map + page_size)) { 481 printf("VMA with present pages is not marked lock on fault\n"); 482 goto unmap; 483 } 484 ret = 0; 485unmap: 486 munmap(map, 2 * page_size); 487out: 488 return ret; 489} 490 491static int test_munlockall() 492{ 493 char *map; 494 int ret = 1; 495 unsigned long page_size = getpagesize(); 496 497 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 498 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 499 500 if (map == MAP_FAILED) { 501 perror("test_munlockall mmap"); 502 goto out; 503 } 504 505 if (mlockall(MCL_CURRENT)) { 506 perror("mlockall(MCL_CURRENT)"); 507 goto out; 508 } 509 510 if (lock_check(map)) 511 goto unmap; 512 513 if (munlockall()) { 514 perror("munlockall()"); 515 goto unmap; 516 } 517 518 if (unlock_lock_check(map)) 519 goto unmap; 520 521 munmap(map, 2 * page_size); 522 523 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 524 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 525 526 if (map == MAP_FAILED) { 527 perror("test_munlockall second mmap"); 528 goto out; 529 } 530 531 if (mlockall(MCL_CURRENT | MCL_ONFAULT)) { 532 perror("mlockall(MCL_CURRENT | MCL_ONFAULT)"); 533 goto unmap; 534 } 535 536 if (onfault_check(map)) 537 goto unmap; 538 539 if (munlockall()) { 540 perror("munlockall()"); 541 goto unmap; 542 } 543 544 if (unlock_onfault_check(map)) 545 goto unmap; 546 547 if (mlockall(MCL_CURRENT | MCL_FUTURE)) { 548 perror("mlockall(MCL_CURRENT | MCL_FUTURE)"); 549 goto out; 550 } 551 552 if (lock_check(map)) 553 goto unmap; 554 555 if (munlockall()) { 556 perror("munlockall()"); 557 goto unmap; 558 } 559 560 ret = unlock_lock_check(map); 561 562unmap: 563 munmap(map, 2 * page_size); 564out: 565 munlockall(); 566 return ret; 567} 568 569static int test_vma_management(bool call_mlock) 570{ 571 int ret = 1; 572 void *map; 573 unsigned long page_size = getpagesize(); 574 struct vm_boundaries page1; 575 struct vm_boundaries page2; 576 struct vm_boundaries page3; 577 578 map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE, 579 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 580 if (map == MAP_FAILED) { 581 perror("mmap()"); 582 return ret; 583 } 584 585 if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) { 586 if (errno == ENOSYS) { 587 printf("Cannot call new mlock family, skipping test\n"); 588 _exit(KSFT_SKIP); 589 } 590 perror("mlock(ONFAULT)\n"); 591 goto out; 592 } 593 594 if (get_vm_area((unsigned long)map, &page1) || 595 get_vm_area((unsigned long)map + page_size, &page2) || 596 get_vm_area((unsigned long)map + page_size * 2, &page3)) { 597 printf("couldn't find mapping in /proc/self/maps\n"); 598 goto out; 599 } 600 601 /* 602 * Before we unlock a portion, we need to that all three pages are in 603 * the same VMA. If they are not we abort this test (Note that this is 604 * not a failure) 605 */ 606 if (page1.start != page2.start || page2.start != page3.start) { 607 printf("VMAs are not merged to start, aborting test\n"); 608 ret = 0; 609 goto out; 610 } 611 612 if (munlock(map + page_size, page_size)) { 613 perror("munlock()"); 614 goto out; 615 } 616 617 if (get_vm_area((unsigned long)map, &page1) || 618 get_vm_area((unsigned long)map + page_size, &page2) || 619 get_vm_area((unsigned long)map + page_size * 2, &page3)) { 620 printf("couldn't find mapping in /proc/self/maps\n"); 621 goto out; 622 } 623 624 /* All three VMAs should be different */ 625 if (page1.start == page2.start || page2.start == page3.start) { 626 printf("failed to split VMA for munlock\n"); 627 goto out; 628 } 629 630 /* Now unlock the first and third page and check the VMAs again */ 631 if (munlock(map, page_size * 3)) { 632 perror("munlock()"); 633 goto out; 634 } 635 636 if (get_vm_area((unsigned long)map, &page1) || 637 get_vm_area((unsigned long)map + page_size, &page2) || 638 get_vm_area((unsigned long)map + page_size * 2, &page3)) { 639 printf("couldn't find mapping in /proc/self/maps\n"); 640 goto out; 641 } 642 643 /* Now all three VMAs should be the same */ 644 if (page1.start != page2.start || page2.start != page3.start) { 645 printf("failed to merge VMAs after munlock\n"); 646 goto out; 647 } 648 649 ret = 0; 650out: 651 munmap(map, 3 * page_size); 652 return ret; 653} 654 655static int test_mlockall(int (test_function)(bool call_mlock)) 656{ 657 int ret = 1; 658 659 if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) { 660 perror("mlockall"); 661 return ret; 662 } 663 664 ret = test_function(false); 665 munlockall(); 666 return ret; 667} 668 669int main(int argc, char **argv) 670{ 671 int ret = 0; 672 ret += test_mlock_lock(); 673 ret += test_mlock_onfault(); 674 ret += test_munlockall(); 675 ret += test_lock_onfault_of_present(); 676 ret += test_vma_management(true); 677 ret += test_mlockall(test_vma_management); 678 return ret; 679}