1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2022 Google LLC. 4 * Author: Suren Baghdasaryan <surenb@google.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 /* 19 * Fork a child that concurrently modifies address space while the main 20 * process is reading /proc/$PID/maps and /proc/$PID/smaps, verifying the 21 * results. Address space modifications include: 22 * VMA splitting and merging 23 * 24 */ 25 #define _GNU_SOURCE 26 #include "kselftest_harness.h" 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <pthread.h> 30 #include <stdbool.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <linux/fs.h> 36 #include <sys/ioctl.h> 37 #include <sys/mman.h> 38 #include <sys/stat.h> 39 #include <sys/types.h> 40 #include <sys/wait.h> 41 42 #define min(a, b) \ 43 ({ \ 44 typeof(a) _a = (a); \ 45 typeof(b) _b = (b); \ 46 _a < _b ? _a : _b; \ 47 }) 48 49 /* /proc/pid/maps parsing routines */ 50 struct page_content { 51 char *data; 52 ssize_t size; 53 }; 54 55 #define LINE_MAX_SIZE 256 56 57 struct line_content { 58 char text[LINE_MAX_SIZE]; 59 unsigned long start_addr; 60 unsigned long end_addr; 61 }; 62 63 enum test_state { 64 INIT, 65 CHILD_READY, 66 PARENT_READY, 67 SETUP_READY, 68 SETUP_MODIFY_MAPS, 69 SETUP_MAPS_MODIFIED, 70 SETUP_RESTORE_MAPS, 71 SETUP_MAPS_RESTORED, 72 TEST_READY, 73 TEST_DONE, 74 }; 75 76 enum maps_file { 77 MAPS, 78 SMAPS, 79 }; 80 81 struct vma_modifier_info; 82 83 enum smaps_rollup_stat { 84 Rss, 85 Pss, 86 Pss_Dirty, 87 Pss_Anon, 88 Pss_File, 89 Pss_Shmem, 90 Shared_Clean, 91 Shared_Dirty, 92 Private_Clean, 93 Private_Dirty, 94 Referenced, 95 Anonymous, 96 KSM, 97 LazyFree, 98 AnonHugePages, 99 ShmemPmdMapped, 100 FilePmdMapped, 101 Shared_Hugetlb, 102 Private_Hugetlb, 103 Swap, 104 SwapPss, 105 Locked, 106 RollupFieldCount 107 }; 108 109 static const char *smaps_rollup_stat_names[RollupFieldCount] = { 110 "Rss", 111 "Pss", 112 "Pss_Dirty", 113 "Pss_Anon", 114 "Pss_File", 115 "Pss_Shmem", 116 "Shared_Clean", 117 "Shared_Dirty", 118 "Private_Clean", 119 "Private_Dirty", 120 "Referenced", 121 "Anonymous", 122 "KSM", 123 "LazyFree", 124 "AnonHugePages", 125 "ShmemPmdMapped", 126 "FilePmdMapped", 127 "Shared_Hugetlb", 128 "Private_Hugetlb", 129 "Swap", 130 "SwapPss", 131 "Locked", 132 }; 133 134 struct smaps_rollup_stats { 135 unsigned long values[RollupFieldCount]; 136 }; 137 138 FIXTURE(proc_maps_race) 139 { 140 struct vma_modifier_info *mod_info; 141 struct page_content page1; 142 struct page_content page2; 143 struct line_content last_line; 144 struct line_content first_line; 145 unsigned long duration_sec; 146 enum maps_file maps_file; 147 int shared_mem_size; 148 int skip_pages; 149 int rollup_fd; 150 int page_size; 151 int vma_count; 152 bool verbose; 153 int maps_fd; 154 pid_t pid; 155 }; 156 157 FIXTURE_VARIANT(proc_maps_race) 158 { 159 const enum maps_file maps_file; 160 }; 161 162 FIXTURE_VARIANT_ADD(proc_maps_race, maps) { 163 .maps_file = MAPS, 164 }; 165 166 FIXTURE_VARIANT_ADD(proc_maps_race, smaps) { 167 .maps_file = SMAPS, 168 }; 169 170 typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self); 171 typedef bool (*vma_mod_result_check_op)(struct line_content *mod_last_line, 172 struct line_content *mod_first_line, 173 struct line_content *restored_last_line, 174 struct line_content *restored_first_line); 175 176 struct vma_modifier_info { 177 int vma_count; 178 void *addr; 179 int prot; 180 void *next_addr; 181 vma_modifier_op vma_modify; 182 vma_modifier_op vma_restore; 183 vma_mod_result_check_op vma_mod_check; 184 pthread_mutex_t sync_lock; 185 pthread_cond_t sync_cond; 186 enum test_state curr_state; 187 bool exit; 188 void *child_mapped_addr[]; 189 }; 190 191 static bool read_page(FIXTURE_DATA(proc_maps_race) *self, int fd, 192 struct page_content *page) 193 { 194 ssize_t bytes_read; 195 196 bytes_read = read(fd, page->data, self->page_size); 197 if (bytes_read <= 0) 198 return false; 199 200 /* Make sure data always ends with a newline character. */ 201 if (page->data[bytes_read - 1] != '\n') 202 return false; 203 204 page->size = bytes_read; 205 206 return true; 207 } 208 209 static bool parse_vma_line(char *line_start, char *line_end, 210 unsigned long *start, unsigned long *end) 211 { 212 bool found; 213 214 *line_end = '\0'; /* stop sscanf at the EOL */ 215 found = (sscanf(line_start, "%lx-%lx", start, end) == 2); 216 *line_end = '\n'; 217 218 return found; 219 } 220 221 static int locate_containing_page(FIXTURE_DATA(proc_maps_race) *self, 222 unsigned long addr, unsigned long size) 223 { 224 unsigned long start, end; 225 int page = 0; 226 227 if (lseek(self->maps_fd, 0, SEEK_SET) < 0) 228 return -1; 229 230 while (true) { 231 char *curr_pos; 232 char *end_pos; 233 234 if (!read_page(self, self->maps_fd, &self->page1)) 235 return -1; 236 237 curr_pos = self->page1.data; 238 end_pos = self->page1.data + self->page1.size; 239 while (curr_pos < end_pos) { 240 char *line_end; 241 242 line_end = strchr(curr_pos, '\n'); 243 if (!line_end) 244 break; 245 246 if (parse_vma_line(curr_pos, line_end, &start, &end) && 247 start == addr && end == addr + size) 248 return page; 249 250 curr_pos = line_end + 1; 251 } 252 page++; 253 } 254 255 return 0; 256 } 257 258 static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self) 259 { 260 if (lseek(self->maps_fd, 0, SEEK_SET) < 0) 261 return false; 262 263 for (int i = 0; i < self->skip_pages; i++) 264 if (!read_page(self, self->maps_fd, &self->page1)) 265 return false; 266 267 return read_page(self, self->maps_fd, &self->page1) && 268 read_page(self, self->maps_fd, &self->page2); 269 } 270 271 static void copy_line(const char *line_start, const char *line_end, 272 char *buf, size_t buf_size) 273 { 274 size_t len = min(line_end - line_start, buf_size - 1); 275 276 strncpy(buf, line_start, len); 277 buf[len] = '\0'; 278 } 279 280 static void copy_first_line(struct page_content *page, char *first_line, 281 size_t line_size) 282 { 283 copy_line(page->data, strchr(page->data, '\n'), first_line, line_size); 284 } 285 286 static void copy_last_line(struct page_content *page, char *last_line, 287 size_t line_size) 288 { 289 /* Get the last line in the first page */ 290 const char *end = page->data + page->size - 1; 291 /* skip last newline */ 292 const char *pos = end - 1; 293 294 /* search previous newline */ 295 while (pos[-1] != '\n') 296 pos--; 297 298 copy_line(pos, end, last_line, line_size); 299 } 300 301 static bool copy_first_entry(struct page_content *page, char *first_line, 302 size_t line_size) 303 { 304 char *start_pos = page->data; 305 306 while (start_pos < page->data + page->size) { 307 unsigned long start_addr; 308 unsigned long end_addr; 309 char *end_pos; 310 311 end_pos = strchr(start_pos, '\n'); 312 if (!end_pos) 313 break; 314 315 if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) { 316 copy_line(start_pos, end_pos, first_line, line_size); 317 return true; 318 } 319 320 start_pos = end_pos + 1; 321 } 322 323 return false; 324 } 325 326 static bool copy_last_entry(struct page_content *page, char *last_line, 327 size_t line_size) 328 { 329 char *end_pos = page->data + page->size - 1; 330 char *start_pos; 331 332 while (end_pos > page->data) { 333 unsigned long start_addr; 334 unsigned long end_addr; 335 336 /* skip last newline */ 337 start_pos = end_pos - 1; 338 /* search previous newline */ 339 while (start_pos > page->data && start_pos[-1] != '\n') 340 start_pos--; 341 if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) { 342 copy_line(start_pos, end_pos, last_line, line_size); 343 return true; 344 } 345 346 end_pos = start_pos - 1; 347 } 348 349 return false; 350 } 351 352 /* Read the last line of the first page and the first line of the second page */ 353 static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self, 354 struct line_content *last_line, 355 struct line_content *first_line) 356 { 357 if (!read_two_pages(self)) 358 return false; 359 360 if (self->maps_file == MAPS) { 361 copy_last_line(&self->page1, last_line->text, LINE_MAX_SIZE); 362 copy_first_line(&self->page2, first_line->text, LINE_MAX_SIZE); 363 } else if (self->maps_file == SMAPS) { 364 if (!copy_last_entry(&self->page1, last_line->text, LINE_MAX_SIZE) || 365 !copy_first_entry(&self->page2, first_line->text, LINE_MAX_SIZE)) 366 return false; 367 } else { 368 return false; 369 } 370 371 return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr, 372 &last_line->end_addr) == 2 && 373 sscanf(first_line->text, "%lx-%lx", &first_line->start_addr, 374 &first_line->end_addr) == 2; 375 } 376 377 static bool parse_smaps_rollup(FIXTURE_DATA(proc_maps_race) *self, 378 struct smaps_rollup_stats *stats) 379 { 380 unsigned int dev_maj, dev_min, inode; 381 unsigned long start, end, offs; 382 unsigned long value; 383 char name[32], perm[5]; 384 char *curr_pos; 385 char *end_pos; 386 char *line_end; 387 388 if (lseek(self->rollup_fd, 0, SEEK_SET) < 0) 389 return false; 390 391 if (!read_page(self, self->rollup_fd, &self->page1)) 392 return false; 393 394 curr_pos = self->page1.data; 395 end_pos = self->page1.data + self->page1.size; 396 397 line_end = strchr(curr_pos, '\n'); 398 if (!line_end) 399 return false; 400 401 if (sscanf(curr_pos, "%lx-%lx %4s %lx %u:%u %u %31s", 402 &start, &end, perm, &offs, &dev_maj, &dev_min, &inode, name) != 8) 403 return false; 404 405 if (strcmp(name, "[rollup]")) 406 return false; 407 408 for (int stat = 0; stat < ARRAY_SIZE(smaps_rollup_stat_names); stat++) { 409 int len; 410 411 curr_pos = line_end + 1; 412 if (curr_pos >= end_pos) 413 return false; 414 415 line_end = strchr(curr_pos, '\n'); 416 if (!line_end) 417 return false; 418 419 if (sscanf(curr_pos, "%31s %lu kB", name, &value) != 2) 420 return false; 421 422 len = strlen(name); 423 if (name[len - 1] != ':' || strncmp(name, smaps_rollup_stat_names[stat], len - 1)) 424 return false; 425 426 stats->values[stat] = value; 427 } 428 429 return true; 430 } 431 432 /* Thread synchronization routines */ 433 static void wait_for_state(struct vma_modifier_info *mod_info, enum test_state state) 434 { 435 pthread_mutex_lock(&mod_info->sync_lock); 436 while (mod_info->curr_state != state) 437 pthread_cond_wait(&mod_info->sync_cond, &mod_info->sync_lock); 438 pthread_mutex_unlock(&mod_info->sync_lock); 439 } 440 441 static void signal_state(struct vma_modifier_info *mod_info, enum test_state state) 442 { 443 pthread_mutex_lock(&mod_info->sync_lock); 444 mod_info->curr_state = state; 445 pthread_cond_signal(&mod_info->sync_cond); 446 pthread_mutex_unlock(&mod_info->sync_lock); 447 } 448 449 static void stop_vma_modifier(struct vma_modifier_info *mod_info) 450 { 451 wait_for_state(mod_info, SETUP_READY); 452 mod_info->exit = true; 453 signal_state(mod_info, SETUP_MODIFY_MAPS); 454 } 455 456 static void print_first_lines(char *text, int nr) 457 { 458 const char *end = text; 459 460 while (nr && (end = strchr(end, '\n')) != NULL) { 461 nr--; 462 end++; 463 } 464 465 if (end) { 466 int offs = end - text; 467 468 text[offs] = '\0'; 469 printf("%s", text); 470 text[offs] = '\n'; 471 printf("\n"); 472 } else { 473 printf("%s", text); 474 } 475 } 476 477 static void print_last_lines(char *text, int nr) 478 { 479 const char *start = text + strlen(text); 480 481 nr++; /* to ignore the last newline */ 482 while (nr) { 483 while (start > text && *start != '\n') 484 start--; 485 nr--; 486 start--; 487 } 488 printf("%s", start); 489 } 490 491 static void print_boundaries(const char *title, FIXTURE_DATA(proc_maps_race) *self) 492 { 493 if (!self->verbose) 494 return; 495 496 printf("%s", title); 497 /* Print 3 boundary lines from each page */ 498 print_last_lines(self->page1.data, 3); 499 printf("-----------------page boundary-----------------\n"); 500 print_first_lines(self->page2.data, 3); 501 } 502 503 static bool print_boundaries_on(bool condition, const char *title, 504 FIXTURE_DATA(proc_maps_race) *self) 505 { 506 if (self->verbose && condition) 507 print_boundaries(title, self); 508 509 return condition; 510 } 511 512 static void print_smaps_rollup_stats(const char *title, FIXTURE_DATA(proc_maps_race) *self, 513 struct smaps_rollup_stats *stats) 514 { 515 printf("%s", title); 516 for (int stat = 0; stat < ARRAY_SIZE(smaps_rollup_stat_names); stat++) 517 printf("%64s %lu kB\n", smaps_rollup_stat_names[stat], stats->values[stat]); 518 } 519 520 static bool cmp_smaps_rollup_stat(struct smaps_rollup_stats *s1, 521 struct smaps_rollup_stats *s2, enum smaps_rollup_stat stat) 522 { 523 return s1->values[stat] == s2->values[stat]; 524 } 525 526 static bool compare_smaps_rollup(FIXTURE_DATA(proc_maps_race) *self, 527 struct smaps_rollup_stats *expected, 528 struct smaps_rollup_stats *actual) 529 { 530 /* 531 * Clean/dirty metrics might change but Pss-related ones 532 * should stay constant. 533 */ 534 if (cmp_smaps_rollup_stat(expected, actual, Pss) && 535 cmp_smaps_rollup_stat(expected, actual, Pss_Anon) && 536 cmp_smaps_rollup_stat(expected, actual, Pss_File) && 537 cmp_smaps_rollup_stat(expected, actual, Pss_Shmem)) 538 return true; 539 540 print_smaps_rollup_stats("Expected stats:", self, expected); 541 print_smaps_rollup_stats("Actual stats:", self, actual); 542 543 return false; 544 } 545 546 static void report_test_start(const char *name, bool verbose) 547 { 548 if (verbose) 549 printf("==== %s ====\n", name); 550 } 551 552 static struct timespec print_ts; 553 554 static void start_test_loop(struct timespec *ts, bool verbose) 555 { 556 if (verbose) 557 print_ts.tv_sec = ts->tv_sec; 558 } 559 560 static void end_test_iteration(struct timespec *ts, bool verbose) 561 { 562 if (!verbose) 563 return; 564 565 /* Update every second */ 566 if (print_ts.tv_sec == ts->tv_sec) 567 return; 568 569 printf("."); 570 fflush(stdout); 571 print_ts.tv_sec = ts->tv_sec; 572 } 573 574 static void end_test_loop(bool verbose) 575 { 576 if (verbose) 577 printf("\n"); 578 } 579 580 static bool capture_mod_pattern(FIXTURE_DATA(proc_maps_race) *self, 581 struct line_content *mod_last_line, 582 struct line_content *mod_first_line, 583 struct line_content *restored_last_line, 584 struct line_content *restored_first_line) 585 { 586 print_boundaries("Before modification", self); 587 588 signal_state(self->mod_info, SETUP_MODIFY_MAPS); 589 wait_for_state(self->mod_info, SETUP_MAPS_MODIFIED); 590 591 /* Copy last line of the first page and first line of the last page */ 592 if (!read_boundary_lines(self, mod_last_line, mod_first_line)) 593 return false; 594 595 print_boundaries("After modification", self); 596 597 signal_state(self->mod_info, SETUP_RESTORE_MAPS); 598 wait_for_state(self->mod_info, SETUP_MAPS_RESTORED); 599 600 /* Copy last line of the first page and first line of the last page */ 601 if (!read_boundary_lines(self, restored_last_line, restored_first_line)) 602 return false; 603 604 print_boundaries("After restore", self); 605 606 if (!self->mod_info->vma_mod_check(mod_last_line, mod_first_line, 607 restored_last_line, restored_first_line)) 608 return false; 609 610 /* 611 * The content of these lines after modify+resore should be the same 612 * as the original. 613 */ 614 return strcmp(restored_last_line->text, self->last_line.text) == 0 && 615 strcmp(restored_first_line->text, self->first_line.text) == 0; 616 } 617 618 static bool query_addr_at(int maps_fd, void *addr, 619 unsigned long *vma_start, unsigned long *vma_end) 620 { 621 struct procmap_query q; 622 623 memset(&q, 0, sizeof(q)); 624 q.size = sizeof(q); 625 /* Find the VMA at the split address */ 626 q.query_addr = (unsigned long long)addr; 627 q.query_flags = 0; 628 if (ioctl(maps_fd, PROCMAP_QUERY, &q)) 629 return false; 630 631 *vma_start = q.vma_start; 632 *vma_end = q.vma_end; 633 634 return true; 635 } 636 637 static inline bool split_vma(FIXTURE_DATA(proc_maps_race) *self) 638 { 639 /* PROT_NONE differs from both readable neighbors. */ 640 return mmap(self->mod_info->addr, self->page_size, PROT_NONE, 641 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 642 } 643 644 static inline bool merge_vma(FIXTURE_DATA(proc_maps_race) *self) 645 { 646 return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot, 647 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 648 } 649 650 static inline bool check_split_result(struct line_content *mod_last_line, 651 struct line_content *mod_first_line, 652 struct line_content *restored_last_line, 653 struct line_content *restored_first_line) 654 { 655 /* Make sure vmas at the boundaries are changing */ 656 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 657 strcmp(mod_first_line->text, restored_first_line->text) != 0; 658 } 659 660 static inline bool shrink_vma(FIXTURE_DATA(proc_maps_race) *self) 661 { 662 return mremap(self->mod_info->addr, self->page_size * 3, 663 self->page_size, 0) != MAP_FAILED; 664 } 665 666 static inline bool expand_vma(FIXTURE_DATA(proc_maps_race) *self) 667 { 668 return mremap(self->mod_info->addr, self->page_size, 669 self->page_size * 3, 0) != MAP_FAILED; 670 } 671 672 static inline bool check_shrink_result(struct line_content *mod_last_line, 673 struct line_content *mod_first_line, 674 struct line_content *restored_last_line, 675 struct line_content *restored_first_line) 676 { 677 /* Make sure only the last vma of the first page is changing */ 678 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 679 strcmp(mod_first_line->text, restored_first_line->text) == 0; 680 } 681 682 static inline bool remap_vma(FIXTURE_DATA(proc_maps_race) *self) 683 { 684 /* 685 * Remap the last page of the next vma into the middle of the vma. 686 * This splits the current vma and the first and middle parts (the 687 * parts at lower addresses) become the last vma objserved in the 688 * first page and the first vma observed in the last page. 689 */ 690 return mremap(self->mod_info->next_addr + self->page_size * 2, self->page_size, 691 self->page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP, 692 self->mod_info->addr + self->page_size) != MAP_FAILED; 693 } 694 695 static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self) 696 { 697 return mprotect(self->mod_info->addr + self->page_size, self->page_size, 698 self->mod_info->prot) == 0; 699 } 700 701 static inline bool check_remap_result(struct line_content *mod_last_line, 702 struct line_content *mod_first_line, 703 struct line_content *restored_last_line, 704 struct line_content *restored_first_line) 705 { 706 /* Make sure vmas at the boundaries are changing */ 707 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 708 strcmp(mod_first_line->text, restored_first_line->text) != 0; 709 } 710 711 FIXTURE_SETUP(proc_maps_race) 712 { 713 const char *verbose = getenv("VERBOSE"); 714 const char *duration = getenv("DURATION"); 715 struct vma_modifier_info *mod_info; 716 pthread_mutexattr_t mutex_attr; 717 pthread_condattr_t cond_attr; 718 unsigned long first_map_addr; 719 unsigned long last_map_addr; 720 unsigned long duration_sec; 721 char rollup_fname[32]; 722 char fname[32]; 723 724 self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); 725 self->verbose = verbose && !strncmp(verbose, "1", 1); 726 self->maps_file = variant->maps_file; 727 duration_sec = duration ? atol(duration) : 0; 728 self->duration_sec = duration_sec ? duration_sec : 5UL; 729 730 /* 731 * Have to map enough vmas for /proc/pid/maps to contain more than one 732 * page worth of vmas. Assume at least 32 bytes per line in maps output 733 */ 734 self->vma_count = self->page_size / 32 + 1; 735 self->shared_mem_size = sizeof(struct vma_modifier_info) + self->vma_count * sizeof(void *); 736 737 /* map shared memory for communication with the child process */ 738 self->mod_info = (struct vma_modifier_info *)mmap(NULL, self->shared_mem_size, 739 PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 740 ASSERT_NE(self->mod_info, MAP_FAILED); 741 mod_info = self->mod_info; 742 743 /* Initialize shared members */ 744 pthread_mutexattr_init(&mutex_attr); 745 pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); 746 ASSERT_EQ(pthread_mutex_init(&mod_info->sync_lock, &mutex_attr), 0); 747 pthread_condattr_init(&cond_attr); 748 pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); 749 ASSERT_EQ(pthread_cond_init(&mod_info->sync_cond, &cond_attr), 0); 750 mod_info->vma_count = self->vma_count; 751 mod_info->curr_state = INIT; 752 mod_info->exit = false; 753 754 self->pid = fork(); 755 if (!self->pid) { 756 /* Child process modifying the address space */ 757 int prot = PROT_READ | PROT_WRITE; 758 int i; 759 760 for (i = 0; i < mod_info->vma_count; i++) { 761 mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot, 762 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 763 ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED); 764 /* change protection in adjacent maps to prevent merging */ 765 prot ^= PROT_WRITE; 766 } 767 signal_state(mod_info, CHILD_READY); 768 wait_for_state(mod_info, PARENT_READY); 769 while (true) { 770 signal_state(mod_info, SETUP_READY); 771 wait_for_state(mod_info, SETUP_MODIFY_MAPS); 772 if (mod_info->exit) 773 break; 774 775 ASSERT_TRUE(mod_info->vma_modify(self)); 776 signal_state(mod_info, SETUP_MAPS_MODIFIED); 777 wait_for_state(mod_info, SETUP_RESTORE_MAPS); 778 ASSERT_TRUE(mod_info->vma_restore(self)); 779 signal_state(mod_info, SETUP_MAPS_RESTORED); 780 781 wait_for_state(mod_info, TEST_READY); 782 while (mod_info->curr_state != TEST_DONE) { 783 ASSERT_TRUE(mod_info->vma_modify(self)); 784 ASSERT_TRUE(mod_info->vma_restore(self)); 785 } 786 } 787 for (i = 0; i < mod_info->vma_count; i++) 788 munmap(mod_info->child_mapped_addr[i], self->page_size * 3); 789 790 exit(0); 791 } 792 793 switch (self->maps_file) { 794 case MAPS: 795 sprintf(fname, "/proc/%d/maps", self->pid); 796 break; 797 case SMAPS: 798 sprintf(fname, "/proc/%d/smaps", self->pid); 799 sprintf(rollup_fname, "/proc/%d/smaps_rollup", self->pid); 800 self->rollup_fd = open(rollup_fname, O_RDONLY); 801 ASSERT_NE(self->rollup_fd, -1); 802 break; 803 default: 804 ksft_exit_fail(); 805 } 806 self->maps_fd = open(fname, O_RDONLY); 807 ASSERT_NE(self->maps_fd, -1); 808 809 /* Wait for the child to map the VMAs */ 810 wait_for_state(mod_info, CHILD_READY); 811 812 /* Read first two pages */ 813 self->page1.data = malloc(self->page_size); 814 ASSERT_NE(self->page1.data, NULL); 815 self->page2.data = malloc(self->page_size); 816 ASSERT_NE(self->page2.data, NULL); 817 818 first_map_addr = (unsigned long)mod_info->child_mapped_addr[0]; 819 last_map_addr = (unsigned long)mod_info->child_mapped_addr[mod_info->vma_count - 1]; 820 821 self->skip_pages = locate_containing_page(self, 822 min(first_map_addr, last_map_addr), 823 self->page_size * 3); 824 ASSERT_NE(self->skip_pages, -1); 825 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 826 827 /* 828 * Find the addresses corresponding to the last line in the first page 829 * and the first line in the last page. 830 */ 831 mod_info->addr = NULL; 832 mod_info->next_addr = NULL; 833 for (int i = 0; i < mod_info->vma_count; i++) { 834 if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) { 835 mod_info->addr = mod_info->child_mapped_addr[i]; 836 mod_info->prot = PROT_READ; 837 /* Even VMAs have write permission */ 838 if ((i % 2) == 0) 839 mod_info->prot |= PROT_WRITE; 840 } else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) { 841 mod_info->next_addr = mod_info->child_mapped_addr[i]; 842 } 843 844 if (mod_info->addr && mod_info->next_addr) 845 break; 846 } 847 ASSERT_TRUE(mod_info->addr && mod_info->next_addr); 848 849 signal_state(mod_info, PARENT_READY); 850 } 851 852 FIXTURE_TEARDOWN(proc_maps_race) 853 { 854 int status; 855 856 stop_vma_modifier(self->mod_info); 857 858 free(self->page2.data); 859 free(self->page1.data); 860 861 for (int i = 0; i < self->vma_count; i++) 862 munmap(self->mod_info->child_mapped_addr[i], self->page_size); 863 close(self->maps_fd); 864 if (self->maps_file == SMAPS) 865 close(self->rollup_fd); 866 waitpid(self->pid, &status, 0); 867 munmap(self->mod_info, self->shared_mem_size); 868 } 869 870 TEST_F(proc_maps_race, test_maps_tearing_from_split) 871 { 872 struct vma_modifier_info *mod_info = self->mod_info; 873 874 struct line_content split_last_line; 875 struct line_content split_first_line; 876 struct line_content restored_last_line; 877 struct line_content restored_first_line; 878 struct smaps_rollup_stats orig_stats; 879 880 wait_for_state(mod_info, SETUP_READY); 881 882 /* re-read the file to avoid using stale data from previous test */ 883 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 884 885 mod_info->vma_modify = split_vma; 886 mod_info->vma_restore = merge_vma; 887 mod_info->vma_mod_check = check_split_result; 888 889 report_test_start("Tearing from split", self->verbose); 890 ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line, 891 &restored_last_line, &restored_first_line)); 892 if (self->maps_file == SMAPS) 893 ASSERT_TRUE(parse_smaps_rollup(self, &orig_stats)); 894 895 /* Now start concurrent modifications for self->duration_sec */ 896 signal_state(mod_info, TEST_READY); 897 898 struct line_content new_last_line; 899 struct line_content new_first_line; 900 struct timespec start_ts, end_ts; 901 902 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 903 start_test_loop(&start_ts, self->verbose); 904 do { 905 bool last_line_changed; 906 bool first_line_changed; 907 unsigned long vma_start; 908 unsigned long vma_end; 909 910 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 911 912 /* Check if we read vmas after split */ 913 if (!strcmp(new_last_line.text, split_last_line.text)) { 914 /* 915 * The vmas should be consistent with split results, 916 * however if vma was concurrently restored after a 917 * split, it can be reported twice (first the original 918 * split one, then the same vma but extended after the 919 * merge) because we found it as the next vma again. 920 * In that case new first line will be the same as the 921 * last restored line. 922 */ 923 ASSERT_FALSE(print_boundaries_on( 924 strcmp(new_first_line.text, split_first_line.text) && 925 strcmp(new_first_line.text, restored_last_line.text), 926 "Split result invalid", self)); 927 } else { 928 /* The vmas should be consistent with merge results */ 929 ASSERT_FALSE(print_boundaries_on( 930 strcmp(new_last_line.text, restored_last_line.text), 931 "Merge result invalid", self)); 932 ASSERT_FALSE(print_boundaries_on( 933 strcmp(new_first_line.text, restored_first_line.text), 934 "Merge result invalid", self)); 935 } 936 /* 937 * First and last lines should change in unison. If the last 938 * line changed then the first line should change as well and 939 * vice versa. 940 */ 941 last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0; 942 first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0; 943 ASSERT_EQ(last_line_changed, first_line_changed); 944 if (self->maps_file == MAPS) { 945 /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ 946 ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size, 947 &vma_start, &vma_end)); 948 /* 949 * The vma at the split address can be either the same as 950 * original one (if read before the split) or the same as the 951 * first line in the second page (if read after the split). 952 */ 953 ASSERT_TRUE((vma_start == self->last_line.start_addr && 954 vma_end == self->last_line.end_addr) || 955 (vma_start == split_first_line.start_addr && 956 vma_end == split_first_line.end_addr)); 957 } else { 958 struct smaps_rollup_stats stats; 959 960 ASSERT_TRUE(parse_smaps_rollup(self, &stats)); 961 ASSERT_TRUE(compare_smaps_rollup(self, &orig_stats, &stats)); 962 } 963 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 964 end_test_iteration(&end_ts, self->verbose); 965 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 966 end_test_loop(self->verbose); 967 968 /* Signal the modifyer thread to stop and wait until it exits */ 969 signal_state(mod_info, TEST_DONE); 970 } 971 972 TEST_F(proc_maps_race, test_maps_tearing_from_resize) 973 { 974 struct vma_modifier_info *mod_info = self->mod_info; 975 976 struct line_content shrunk_last_line; 977 struct line_content shrunk_first_line; 978 struct line_content restored_last_line; 979 struct line_content restored_first_line; 980 struct smaps_rollup_stats orig_stats; 981 982 wait_for_state(mod_info, SETUP_READY); 983 984 /* re-read the file to avoid using stale data from previous test */ 985 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 986 987 mod_info->vma_modify = shrink_vma; 988 mod_info->vma_restore = expand_vma; 989 mod_info->vma_mod_check = check_shrink_result; 990 991 report_test_start("Tearing from resize", self->verbose); 992 ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line, 993 &restored_last_line, &restored_first_line)); 994 if (self->maps_file == SMAPS) 995 ASSERT_TRUE(parse_smaps_rollup(self, &orig_stats)); 996 997 /* Now start concurrent modifications for self->duration_sec */ 998 signal_state(mod_info, TEST_READY); 999 1000 struct line_content new_last_line; 1001 struct line_content new_first_line; 1002 struct timespec start_ts, end_ts; 1003 1004 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 1005 start_test_loop(&start_ts, self->verbose); 1006 do { 1007 unsigned long vma_start; 1008 unsigned long vma_end; 1009 1010 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 1011 1012 /* Check if we read vmas after shrinking it */ 1013 if (!strcmp(new_last_line.text, shrunk_last_line.text)) { 1014 /* 1015 * The vmas should be consistent with shrunk results, 1016 * however if the vma was concurrently restored, it 1017 * can be reported twice (first as shrunk one, then 1018 * as restored one) because we found it as the next vma 1019 * again. In that case new first line will be the same 1020 * as the last restored line. 1021 */ 1022 ASSERT_FALSE(print_boundaries_on( 1023 strcmp(new_first_line.text, shrunk_first_line.text) && 1024 strcmp(new_first_line.text, restored_last_line.text), 1025 "Shrink result invalid", self)); 1026 } else { 1027 /* The vmas should be consistent with the original/resored state */ 1028 ASSERT_FALSE(print_boundaries_on( 1029 strcmp(new_last_line.text, restored_last_line.text), 1030 "Expand result invalid", self)); 1031 ASSERT_FALSE(print_boundaries_on( 1032 strcmp(new_first_line.text, restored_first_line.text), 1033 "Expand result invalid", self)); 1034 } 1035 if (self->maps_file == MAPS) { 1036 /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ 1037 ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr, 1038 &vma_start, &vma_end)); 1039 /* 1040 * The vma should stay at the same address and have either the 1041 * original size of 3 pages or 1 page if read after shrinking. 1042 */ 1043 ASSERT_TRUE(vma_start == self->last_line.start_addr && 1044 (vma_end - vma_start == self->page_size * 3 || 1045 vma_end - vma_start == self->page_size)); 1046 } else { 1047 struct smaps_rollup_stats stats; 1048 1049 ASSERT_TRUE(parse_smaps_rollup(self, &stats)); 1050 ASSERT_TRUE(compare_smaps_rollup(self, &orig_stats, &stats)); 1051 } 1052 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 1053 end_test_iteration(&end_ts, self->verbose); 1054 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 1055 end_test_loop(self->verbose); 1056 1057 /* Signal the modifyer thread to stop and wait until it exits */ 1058 signal_state(mod_info, TEST_DONE); 1059 } 1060 1061 TEST_F(proc_maps_race, test_maps_tearing_from_remap) 1062 { 1063 struct vma_modifier_info *mod_info = self->mod_info; 1064 1065 struct line_content remapped_last_line; 1066 struct line_content remapped_first_line; 1067 struct line_content restored_last_line; 1068 struct line_content restored_first_line; 1069 struct smaps_rollup_stats orig_stats; 1070 1071 wait_for_state(mod_info, SETUP_READY); 1072 1073 /* re-read the file to avoid using stale data from previous test */ 1074 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 1075 1076 mod_info->vma_modify = remap_vma; 1077 mod_info->vma_restore = patch_vma; 1078 mod_info->vma_mod_check = check_remap_result; 1079 1080 report_test_start("Tearing from remap", self->verbose); 1081 ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line, 1082 &restored_last_line, &restored_first_line)); 1083 if (self->maps_file == SMAPS) 1084 ASSERT_TRUE(parse_smaps_rollup(self, &orig_stats)); 1085 1086 /* Now start concurrent modifications for self->duration_sec */ 1087 signal_state(mod_info, TEST_READY); 1088 1089 struct line_content new_last_line; 1090 struct line_content new_first_line; 1091 struct timespec start_ts, end_ts; 1092 1093 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 1094 start_test_loop(&start_ts, self->verbose); 1095 do { 1096 unsigned long vma_start; 1097 unsigned long vma_end; 1098 1099 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 1100 1101 /* Check if we read vmas after remapping it */ 1102 if (!strcmp(new_last_line.text, remapped_last_line.text)) { 1103 /* 1104 * The vmas should be consistent with remap results, 1105 * however if the vma was concurrently restored, it 1106 * can be reported twice (first as split one, then 1107 * as restored one) because we found it as the next vma 1108 * again. In that case new first line will be the same 1109 * as the last restored line. 1110 */ 1111 ASSERT_FALSE(print_boundaries_on( 1112 strcmp(new_first_line.text, remapped_first_line.text) && 1113 strcmp(new_first_line.text, restored_last_line.text), 1114 "Remap result invalid", self)); 1115 } else { 1116 /* The vmas should be consistent with the original/resored state */ 1117 ASSERT_FALSE(print_boundaries_on( 1118 strcmp(new_last_line.text, restored_last_line.text), 1119 "Remap restore result invalid", self)); 1120 ASSERT_FALSE(print_boundaries_on( 1121 strcmp(new_first_line.text, restored_first_line.text), 1122 "Remap restore result invalid", self)); 1123 } 1124 if (self->maps_file == MAPS) { 1125 /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ 1126 ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size, 1127 &vma_start, &vma_end)); 1128 /* 1129 * The vma should either stay at the same address and have the 1130 * original size of 3 pages or we should find the remapped vma 1131 * at the remap destination address with size of 1 page. 1132 */ 1133 ASSERT_TRUE((vma_start == self->last_line.start_addr && 1134 vma_end - vma_start == self->page_size * 3) || 1135 (vma_start == self->last_line.start_addr + self->page_size && 1136 vma_end - vma_start == self->page_size)); 1137 } else { 1138 struct smaps_rollup_stats stats; 1139 1140 ASSERT_TRUE(parse_smaps_rollup(self, &stats)); 1141 ASSERT_TRUE(compare_smaps_rollup(self, &orig_stats, &stats)); 1142 } 1143 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 1144 end_test_iteration(&end_ts, self->verbose); 1145 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 1146 end_test_loop(self->verbose); 1147 1148 /* Signal the modifyer thread to stop and wait until it exits */ 1149 signal_state(mod_info, TEST_DONE); 1150 } 1151 1152 TEST_HARNESS_MAIN 1153