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 FIXTURE(proc_maps_race) 84 { 85 struct vma_modifier_info *mod_info; 86 struct page_content page1; 87 struct page_content page2; 88 struct line_content last_line; 89 struct line_content first_line; 90 unsigned long duration_sec; 91 enum maps_file maps_file; 92 int shared_mem_size; 93 int skip_pages; 94 int page_size; 95 int vma_count; 96 bool verbose; 97 int maps_fd; 98 pid_t pid; 99 }; 100 101 FIXTURE_VARIANT(proc_maps_race) 102 { 103 const enum maps_file maps_file; 104 }; 105 106 FIXTURE_VARIANT_ADD(proc_maps_race, maps) { 107 .maps_file = MAPS, 108 }; 109 110 FIXTURE_VARIANT_ADD(proc_maps_race, smaps) { 111 .maps_file = SMAPS, 112 }; 113 114 typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self); 115 typedef bool (*vma_mod_result_check_op)(struct line_content *mod_last_line, 116 struct line_content *mod_first_line, 117 struct line_content *restored_last_line, 118 struct line_content *restored_first_line); 119 120 struct vma_modifier_info { 121 int vma_count; 122 void *addr; 123 int prot; 124 void *next_addr; 125 vma_modifier_op vma_modify; 126 vma_modifier_op vma_restore; 127 vma_mod_result_check_op vma_mod_check; 128 pthread_mutex_t sync_lock; 129 pthread_cond_t sync_cond; 130 enum test_state curr_state; 131 bool exit; 132 void *child_mapped_addr[]; 133 }; 134 135 static bool read_page(FIXTURE_DATA(proc_maps_race) *self, 136 struct page_content *page) 137 { 138 ssize_t bytes_read; 139 140 bytes_read = read(self->maps_fd, page->data, self->page_size); 141 if (bytes_read <= 0) 142 return false; 143 144 /* Make sure data always ends with a newline character. */ 145 if (page->data[bytes_read - 1] != '\n') 146 return false; 147 148 page->size = bytes_read; 149 150 return true; 151 } 152 153 static bool parse_vma_line(char *line_start, char *line_end, 154 unsigned long *start, unsigned long *end) 155 { 156 bool found; 157 158 *line_end = '\0'; /* stop sscanf at the EOL */ 159 found = (sscanf(line_start, "%lx-%lx", start, end) == 2); 160 *line_end = '\n'; 161 162 return found; 163 } 164 165 static int locate_containing_page(FIXTURE_DATA(proc_maps_race) *self, 166 unsigned long addr, unsigned long size) 167 { 168 unsigned long start, end; 169 int page = 0; 170 171 if (lseek(self->maps_fd, 0, SEEK_SET) < 0) 172 return -1; 173 174 while (true) { 175 char *curr_pos; 176 char *end_pos; 177 178 if (!read_page(self, &self->page1)) 179 return -1; 180 181 curr_pos = self->page1.data; 182 end_pos = self->page1.data + self->page1.size; 183 while (curr_pos < end_pos) { 184 char *line_end; 185 186 line_end = strchr(curr_pos, '\n'); 187 if (!line_end) 188 break; 189 190 if (parse_vma_line(curr_pos, line_end, &start, &end) && 191 start == addr && end == addr + size) 192 return page; 193 194 curr_pos = line_end + 1; 195 } 196 page++; 197 } 198 199 return 0; 200 } 201 202 static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self) 203 { 204 if (lseek(self->maps_fd, 0, SEEK_SET) < 0) 205 return false; 206 207 for (int i = 0; i < self->skip_pages; i++) 208 if (!read_page(self, &self->page1)) 209 return false; 210 211 return read_page(self, &self->page1) && read_page(self, &self->page2); 212 } 213 214 static void copy_line(const char *line_start, const char *line_end, 215 char *buf, size_t buf_size) 216 { 217 size_t len = min(line_end - line_start, buf_size - 1); 218 219 strncpy(buf, line_start, len); 220 buf[len] = '\0'; 221 } 222 223 static void copy_first_line(struct page_content *page, char *first_line, 224 size_t line_size) 225 { 226 copy_line(page->data, strchr(page->data, '\n'), first_line, line_size); 227 } 228 229 static void copy_last_line(struct page_content *page, char *last_line, 230 size_t line_size) 231 { 232 /* Get the last line in the first page */ 233 const char *end = page->data + page->size - 1; 234 /* skip last newline */ 235 const char *pos = end - 1; 236 237 /* search previous newline */ 238 while (pos[-1] != '\n') 239 pos--; 240 241 copy_line(pos, end, last_line, line_size); 242 } 243 244 static bool copy_first_entry(struct page_content *page, char *first_line, 245 size_t line_size) 246 { 247 char *start_pos = page->data; 248 249 while (start_pos < page->data + page->size) { 250 unsigned long start_addr; 251 unsigned long end_addr; 252 char *end_pos; 253 254 end_pos = strchr(start_pos, '\n'); 255 if (!end_pos) 256 break; 257 258 if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) { 259 copy_line(start_pos, end_pos, first_line, line_size); 260 return true; 261 } 262 263 start_pos = end_pos + 1; 264 } 265 266 return false; 267 } 268 269 static bool copy_last_entry(struct page_content *page, char *last_line, 270 size_t line_size) 271 { 272 char *end_pos = page->data + page->size - 1; 273 char *start_pos; 274 275 while (end_pos > page->data) { 276 unsigned long start_addr; 277 unsigned long end_addr; 278 279 /* skip last newline */ 280 start_pos = end_pos - 1; 281 /* search previous newline */ 282 while (start_pos > page->data && start_pos[-1] != '\n') 283 start_pos--; 284 if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) { 285 copy_line(start_pos, end_pos, last_line, line_size); 286 return true; 287 } 288 289 end_pos = start_pos - 1; 290 } 291 292 return false; 293 } 294 295 /* Read the last line of the first page and the first line of the second page */ 296 static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self, 297 struct line_content *last_line, 298 struct line_content *first_line) 299 { 300 if (!read_two_pages(self)) 301 return false; 302 303 if (self->maps_file == MAPS) { 304 copy_last_line(&self->page1, last_line->text, LINE_MAX_SIZE); 305 copy_first_line(&self->page2, first_line->text, LINE_MAX_SIZE); 306 } else if (self->maps_file == SMAPS) { 307 if (!copy_last_entry(&self->page1, last_line->text, LINE_MAX_SIZE) || 308 !copy_first_entry(&self->page2, first_line->text, LINE_MAX_SIZE)) 309 return false; 310 } else { 311 return false; 312 } 313 314 return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr, 315 &last_line->end_addr) == 2 && 316 sscanf(first_line->text, "%lx-%lx", &first_line->start_addr, 317 &first_line->end_addr) == 2; 318 } 319 320 /* Thread synchronization routines */ 321 static void wait_for_state(struct vma_modifier_info *mod_info, enum test_state state) 322 { 323 pthread_mutex_lock(&mod_info->sync_lock); 324 while (mod_info->curr_state != state) 325 pthread_cond_wait(&mod_info->sync_cond, &mod_info->sync_lock); 326 pthread_mutex_unlock(&mod_info->sync_lock); 327 } 328 329 static void signal_state(struct vma_modifier_info *mod_info, enum test_state state) 330 { 331 pthread_mutex_lock(&mod_info->sync_lock); 332 mod_info->curr_state = state; 333 pthread_cond_signal(&mod_info->sync_cond); 334 pthread_mutex_unlock(&mod_info->sync_lock); 335 } 336 337 static void stop_vma_modifier(struct vma_modifier_info *mod_info) 338 { 339 wait_for_state(mod_info, SETUP_READY); 340 mod_info->exit = true; 341 signal_state(mod_info, SETUP_MODIFY_MAPS); 342 } 343 344 static void print_first_lines(char *text, int nr) 345 { 346 const char *end = text; 347 348 while (nr && (end = strchr(end, '\n')) != NULL) { 349 nr--; 350 end++; 351 } 352 353 if (end) { 354 int offs = end - text; 355 356 text[offs] = '\0'; 357 printf("%s", text); 358 text[offs] = '\n'; 359 printf("\n"); 360 } else { 361 printf("%s", text); 362 } 363 } 364 365 static void print_last_lines(char *text, int nr) 366 { 367 const char *start = text + strlen(text); 368 369 nr++; /* to ignore the last newline */ 370 while (nr) { 371 while (start > text && *start != '\n') 372 start--; 373 nr--; 374 start--; 375 } 376 printf("%s", start); 377 } 378 379 static void print_boundaries(const char *title, FIXTURE_DATA(proc_maps_race) *self) 380 { 381 if (!self->verbose) 382 return; 383 384 printf("%s", title); 385 /* Print 3 boundary lines from each page */ 386 print_last_lines(self->page1.data, 3); 387 printf("-----------------page boundary-----------------\n"); 388 print_first_lines(self->page2.data, 3); 389 } 390 391 static bool print_boundaries_on(bool condition, const char *title, 392 FIXTURE_DATA(proc_maps_race) *self) 393 { 394 if (self->verbose && condition) 395 print_boundaries(title, self); 396 397 return condition; 398 } 399 400 static void report_test_start(const char *name, bool verbose) 401 { 402 if (verbose) 403 printf("==== %s ====\n", name); 404 } 405 406 static struct timespec print_ts; 407 408 static void start_test_loop(struct timespec *ts, bool verbose) 409 { 410 if (verbose) 411 print_ts.tv_sec = ts->tv_sec; 412 } 413 414 static void end_test_iteration(struct timespec *ts, bool verbose) 415 { 416 if (!verbose) 417 return; 418 419 /* Update every second */ 420 if (print_ts.tv_sec == ts->tv_sec) 421 return; 422 423 printf("."); 424 fflush(stdout); 425 print_ts.tv_sec = ts->tv_sec; 426 } 427 428 static void end_test_loop(bool verbose) 429 { 430 if (verbose) 431 printf("\n"); 432 } 433 434 static bool capture_mod_pattern(FIXTURE_DATA(proc_maps_race) *self, 435 struct line_content *mod_last_line, 436 struct line_content *mod_first_line, 437 struct line_content *restored_last_line, 438 struct line_content *restored_first_line) 439 { 440 print_boundaries("Before modification", self); 441 442 signal_state(self->mod_info, SETUP_MODIFY_MAPS); 443 wait_for_state(self->mod_info, SETUP_MAPS_MODIFIED); 444 445 /* Copy last line of the first page and first line of the last page */ 446 if (!read_boundary_lines(self, mod_last_line, mod_first_line)) 447 return false; 448 449 print_boundaries("After modification", self); 450 451 signal_state(self->mod_info, SETUP_RESTORE_MAPS); 452 wait_for_state(self->mod_info, SETUP_MAPS_RESTORED); 453 454 /* Copy last line of the first page and first line of the last page */ 455 if (!read_boundary_lines(self, restored_last_line, restored_first_line)) 456 return false; 457 458 print_boundaries("After restore", self); 459 460 if (!self->mod_info->vma_mod_check(mod_last_line, mod_first_line, 461 restored_last_line, restored_first_line)) 462 return false; 463 464 /* 465 * The content of these lines after modify+resore should be the same 466 * as the original. 467 */ 468 return strcmp(restored_last_line->text, self->last_line.text) == 0 && 469 strcmp(restored_first_line->text, self->first_line.text) == 0; 470 } 471 472 static bool query_addr_at(int maps_fd, void *addr, 473 unsigned long *vma_start, unsigned long *vma_end) 474 { 475 struct procmap_query q; 476 477 memset(&q, 0, sizeof(q)); 478 q.size = sizeof(q); 479 /* Find the VMA at the split address */ 480 q.query_addr = (unsigned long long)addr; 481 q.query_flags = 0; 482 if (ioctl(maps_fd, PROCMAP_QUERY, &q)) 483 return false; 484 485 *vma_start = q.vma_start; 486 *vma_end = q.vma_end; 487 488 return true; 489 } 490 491 static inline bool split_vma(FIXTURE_DATA(proc_maps_race) *self) 492 { 493 /* PROT_NONE differs from both readable neighbors. */ 494 return mmap(self->mod_info->addr, self->page_size, PROT_NONE, 495 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 496 } 497 498 static inline bool merge_vma(FIXTURE_DATA(proc_maps_race) *self) 499 { 500 return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot, 501 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 502 } 503 504 static inline bool check_split_result(struct line_content *mod_last_line, 505 struct line_content *mod_first_line, 506 struct line_content *restored_last_line, 507 struct line_content *restored_first_line) 508 { 509 /* Make sure vmas at the boundaries are changing */ 510 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 511 strcmp(mod_first_line->text, restored_first_line->text) != 0; 512 } 513 514 static inline bool shrink_vma(FIXTURE_DATA(proc_maps_race) *self) 515 { 516 return mremap(self->mod_info->addr, self->page_size * 3, 517 self->page_size, 0) != MAP_FAILED; 518 } 519 520 static inline bool expand_vma(FIXTURE_DATA(proc_maps_race) *self) 521 { 522 return mremap(self->mod_info->addr, self->page_size, 523 self->page_size * 3, 0) != MAP_FAILED; 524 } 525 526 static inline bool check_shrink_result(struct line_content *mod_last_line, 527 struct line_content *mod_first_line, 528 struct line_content *restored_last_line, 529 struct line_content *restored_first_line) 530 { 531 /* Make sure only the last vma of the first page is changing */ 532 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 533 strcmp(mod_first_line->text, restored_first_line->text) == 0; 534 } 535 536 static inline bool remap_vma(FIXTURE_DATA(proc_maps_race) *self) 537 { 538 /* 539 * Remap the last page of the next vma into the middle of the vma. 540 * This splits the current vma and the first and middle parts (the 541 * parts at lower addresses) become the last vma objserved in the 542 * first page and the first vma observed in the last page. 543 */ 544 return mremap(self->mod_info->next_addr + self->page_size * 2, self->page_size, 545 self->page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP, 546 self->mod_info->addr + self->page_size) != MAP_FAILED; 547 } 548 549 static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self) 550 { 551 return mprotect(self->mod_info->addr + self->page_size, self->page_size, 552 self->mod_info->prot) == 0; 553 } 554 555 static inline bool check_remap_result(struct line_content *mod_last_line, 556 struct line_content *mod_first_line, 557 struct line_content *restored_last_line, 558 struct line_content *restored_first_line) 559 { 560 /* Make sure vmas at the boundaries are changing */ 561 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 562 strcmp(mod_first_line->text, restored_first_line->text) != 0; 563 } 564 565 FIXTURE_SETUP(proc_maps_race) 566 { 567 const char *verbose = getenv("VERBOSE"); 568 const char *duration = getenv("DURATION"); 569 struct vma_modifier_info *mod_info; 570 pthread_mutexattr_t mutex_attr; 571 pthread_condattr_t cond_attr; 572 unsigned long first_map_addr; 573 unsigned long last_map_addr; 574 unsigned long duration_sec; 575 char fname[32]; 576 577 self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); 578 self->verbose = verbose && !strncmp(verbose, "1", 1); 579 self->maps_file = variant->maps_file; 580 duration_sec = duration ? atol(duration) : 0; 581 self->duration_sec = duration_sec ? duration_sec : 5UL; 582 583 /* 584 * Have to map enough vmas for /proc/pid/maps to contain more than one 585 * page worth of vmas. Assume at least 32 bytes per line in maps output 586 */ 587 self->vma_count = self->page_size / 32 + 1; 588 self->shared_mem_size = sizeof(struct vma_modifier_info) + self->vma_count * sizeof(void *); 589 590 /* map shared memory for communication with the child process */ 591 self->mod_info = (struct vma_modifier_info *)mmap(NULL, self->shared_mem_size, 592 PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 593 ASSERT_NE(self->mod_info, MAP_FAILED); 594 mod_info = self->mod_info; 595 596 /* Initialize shared members */ 597 pthread_mutexattr_init(&mutex_attr); 598 pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); 599 ASSERT_EQ(pthread_mutex_init(&mod_info->sync_lock, &mutex_attr), 0); 600 pthread_condattr_init(&cond_attr); 601 pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); 602 ASSERT_EQ(pthread_cond_init(&mod_info->sync_cond, &cond_attr), 0); 603 mod_info->vma_count = self->vma_count; 604 mod_info->curr_state = INIT; 605 mod_info->exit = false; 606 607 self->pid = fork(); 608 if (!self->pid) { 609 /* Child process modifying the address space */ 610 int prot = PROT_READ | PROT_WRITE; 611 int i; 612 613 for (i = 0; i < mod_info->vma_count; i++) { 614 mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot, 615 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 616 ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED); 617 /* change protection in adjacent maps to prevent merging */ 618 prot ^= PROT_WRITE; 619 } 620 signal_state(mod_info, CHILD_READY); 621 wait_for_state(mod_info, PARENT_READY); 622 while (true) { 623 signal_state(mod_info, SETUP_READY); 624 wait_for_state(mod_info, SETUP_MODIFY_MAPS); 625 if (mod_info->exit) 626 break; 627 628 ASSERT_TRUE(mod_info->vma_modify(self)); 629 signal_state(mod_info, SETUP_MAPS_MODIFIED); 630 wait_for_state(mod_info, SETUP_RESTORE_MAPS); 631 ASSERT_TRUE(mod_info->vma_restore(self)); 632 signal_state(mod_info, SETUP_MAPS_RESTORED); 633 634 wait_for_state(mod_info, TEST_READY); 635 while (mod_info->curr_state != TEST_DONE) { 636 ASSERT_TRUE(mod_info->vma_modify(self)); 637 ASSERT_TRUE(mod_info->vma_restore(self)); 638 } 639 } 640 for (i = 0; i < mod_info->vma_count; i++) 641 munmap(mod_info->child_mapped_addr[i], self->page_size * 3); 642 643 exit(0); 644 } 645 646 switch (self->maps_file) { 647 case MAPS: 648 sprintf(fname, "/proc/%d/maps", self->pid); 649 break; 650 case SMAPS: 651 sprintf(fname, "/proc/%d/smaps", self->pid); 652 break; 653 default: 654 ksft_exit_fail(); 655 } 656 self->maps_fd = open(fname, O_RDONLY); 657 ASSERT_NE(self->maps_fd, -1); 658 659 /* Wait for the child to map the VMAs */ 660 wait_for_state(mod_info, CHILD_READY); 661 662 /* Read first two pages */ 663 self->page1.data = malloc(self->page_size); 664 ASSERT_NE(self->page1.data, NULL); 665 self->page2.data = malloc(self->page_size); 666 ASSERT_NE(self->page2.data, NULL); 667 668 first_map_addr = (unsigned long)mod_info->child_mapped_addr[0]; 669 last_map_addr = (unsigned long)mod_info->child_mapped_addr[mod_info->vma_count - 1]; 670 671 self->skip_pages = locate_containing_page(self, 672 min(first_map_addr, last_map_addr), 673 self->page_size * 3); 674 ASSERT_NE(self->skip_pages, -1); 675 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 676 677 /* 678 * Find the addresses corresponding to the last line in the first page 679 * and the first line in the last page. 680 */ 681 mod_info->addr = NULL; 682 mod_info->next_addr = NULL; 683 for (int i = 0; i < mod_info->vma_count; i++) { 684 if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) { 685 mod_info->addr = mod_info->child_mapped_addr[i]; 686 mod_info->prot = PROT_READ; 687 /* Even VMAs have write permission */ 688 if ((i % 2) == 0) 689 mod_info->prot |= PROT_WRITE; 690 } else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) { 691 mod_info->next_addr = mod_info->child_mapped_addr[i]; 692 } 693 694 if (mod_info->addr && mod_info->next_addr) 695 break; 696 } 697 ASSERT_TRUE(mod_info->addr && mod_info->next_addr); 698 699 signal_state(mod_info, PARENT_READY); 700 } 701 702 FIXTURE_TEARDOWN(proc_maps_race) 703 { 704 int status; 705 706 stop_vma_modifier(self->mod_info); 707 708 free(self->page2.data); 709 free(self->page1.data); 710 711 for (int i = 0; i < self->vma_count; i++) 712 munmap(self->mod_info->child_mapped_addr[i], self->page_size); 713 close(self->maps_fd); 714 waitpid(self->pid, &status, 0); 715 munmap(self->mod_info, self->shared_mem_size); 716 } 717 718 TEST_F(proc_maps_race, test_maps_tearing_from_split) 719 { 720 struct vma_modifier_info *mod_info = self->mod_info; 721 722 struct line_content split_last_line; 723 struct line_content split_first_line; 724 struct line_content restored_last_line; 725 struct line_content restored_first_line; 726 727 wait_for_state(mod_info, SETUP_READY); 728 729 /* re-read the file to avoid using stale data from previous test */ 730 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 731 732 mod_info->vma_modify = split_vma; 733 mod_info->vma_restore = merge_vma; 734 mod_info->vma_mod_check = check_split_result; 735 736 report_test_start("Tearing from split", self->verbose); 737 ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line, 738 &restored_last_line, &restored_first_line)); 739 740 /* Now start concurrent modifications for self->duration_sec */ 741 signal_state(mod_info, TEST_READY); 742 743 struct line_content new_last_line; 744 struct line_content new_first_line; 745 struct timespec start_ts, end_ts; 746 747 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 748 start_test_loop(&start_ts, self->verbose); 749 do { 750 bool last_line_changed; 751 bool first_line_changed; 752 unsigned long vma_start; 753 unsigned long vma_end; 754 755 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 756 757 /* Check if we read vmas after split */ 758 if (!strcmp(new_last_line.text, split_last_line.text)) { 759 /* 760 * The vmas should be consistent with split results, 761 * however if vma was concurrently restored after a 762 * split, it can be reported twice (first the original 763 * split one, then the same vma but extended after the 764 * merge) because we found it as the next vma again. 765 * In that case new first line will be the same as the 766 * last restored line. 767 */ 768 ASSERT_FALSE(print_boundaries_on( 769 strcmp(new_first_line.text, split_first_line.text) && 770 strcmp(new_first_line.text, restored_last_line.text), 771 "Split result invalid", self)); 772 } else { 773 /* The vmas should be consistent with merge results */ 774 ASSERT_FALSE(print_boundaries_on( 775 strcmp(new_last_line.text, restored_last_line.text), 776 "Merge result invalid", self)); 777 ASSERT_FALSE(print_boundaries_on( 778 strcmp(new_first_line.text, restored_first_line.text), 779 "Merge result invalid", self)); 780 } 781 /* 782 * First and last lines should change in unison. If the last 783 * line changed then the first line should change as well and 784 * vice versa. 785 */ 786 last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0; 787 first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0; 788 ASSERT_EQ(last_line_changed, first_line_changed); 789 if (self->maps_file == MAPS) { 790 /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ 791 ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size, 792 &vma_start, &vma_end)); 793 /* 794 * The vma at the split address can be either the same as 795 * original one (if read before the split) or the same as the 796 * first line in the second page (if read after the split). 797 */ 798 ASSERT_TRUE((vma_start == self->last_line.start_addr && 799 vma_end == self->last_line.end_addr) || 800 (vma_start == split_first_line.start_addr && 801 vma_end == split_first_line.end_addr)); 802 } 803 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 804 end_test_iteration(&end_ts, self->verbose); 805 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 806 end_test_loop(self->verbose); 807 808 /* Signal the modifyer thread to stop and wait until it exits */ 809 signal_state(mod_info, TEST_DONE); 810 } 811 812 TEST_F(proc_maps_race, test_maps_tearing_from_resize) 813 { 814 struct vma_modifier_info *mod_info = self->mod_info; 815 816 struct line_content shrunk_last_line; 817 struct line_content shrunk_first_line; 818 struct line_content restored_last_line; 819 struct line_content restored_first_line; 820 821 wait_for_state(mod_info, SETUP_READY); 822 823 /* re-read the file to avoid using stale data from previous test */ 824 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 825 826 mod_info->vma_modify = shrink_vma; 827 mod_info->vma_restore = expand_vma; 828 mod_info->vma_mod_check = check_shrink_result; 829 830 report_test_start("Tearing from resize", self->verbose); 831 ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line, 832 &restored_last_line, &restored_first_line)); 833 834 /* Now start concurrent modifications for self->duration_sec */ 835 signal_state(mod_info, TEST_READY); 836 837 struct line_content new_last_line; 838 struct line_content new_first_line; 839 struct timespec start_ts, end_ts; 840 841 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 842 start_test_loop(&start_ts, self->verbose); 843 do { 844 unsigned long vma_start; 845 unsigned long vma_end; 846 847 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 848 849 /* Check if we read vmas after shrinking it */ 850 if (!strcmp(new_last_line.text, shrunk_last_line.text)) { 851 /* 852 * The vmas should be consistent with shrunk results, 853 * however if the vma was concurrently restored, it 854 * can be reported twice (first as shrunk one, then 855 * as restored one) because we found it as the next vma 856 * again. In that case new first line will be the same 857 * as the last restored line. 858 */ 859 ASSERT_FALSE(print_boundaries_on( 860 strcmp(new_first_line.text, shrunk_first_line.text) && 861 strcmp(new_first_line.text, restored_last_line.text), 862 "Shrink result invalid", self)); 863 } else { 864 /* The vmas should be consistent with the original/resored state */ 865 ASSERT_FALSE(print_boundaries_on( 866 strcmp(new_last_line.text, restored_last_line.text), 867 "Expand result invalid", self)); 868 ASSERT_FALSE(print_boundaries_on( 869 strcmp(new_first_line.text, restored_first_line.text), 870 "Expand result invalid", self)); 871 } 872 if (self->maps_file == MAPS) { 873 /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ 874 ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr, 875 &vma_start, &vma_end)); 876 /* 877 * The vma should stay at the same address and have either the 878 * original size of 3 pages or 1 page if read after shrinking. 879 */ 880 ASSERT_TRUE(vma_start == self->last_line.start_addr && 881 (vma_end - vma_start == self->page_size * 3 || 882 vma_end - vma_start == self->page_size)); 883 } 884 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 885 end_test_iteration(&end_ts, self->verbose); 886 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 887 end_test_loop(self->verbose); 888 889 /* Signal the modifyer thread to stop and wait until it exits */ 890 signal_state(mod_info, TEST_DONE); 891 } 892 893 TEST_F(proc_maps_race, test_maps_tearing_from_remap) 894 { 895 struct vma_modifier_info *mod_info = self->mod_info; 896 897 struct line_content remapped_last_line; 898 struct line_content remapped_first_line; 899 struct line_content restored_last_line; 900 struct line_content restored_first_line; 901 902 wait_for_state(mod_info, SETUP_READY); 903 904 /* re-read the file to avoid using stale data from previous test */ 905 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 906 907 mod_info->vma_modify = remap_vma; 908 mod_info->vma_restore = patch_vma; 909 mod_info->vma_mod_check = check_remap_result; 910 911 report_test_start("Tearing from remap", self->verbose); 912 ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line, 913 &restored_last_line, &restored_first_line)); 914 915 /* Now start concurrent modifications for self->duration_sec */ 916 signal_state(mod_info, TEST_READY); 917 918 struct line_content new_last_line; 919 struct line_content new_first_line; 920 struct timespec start_ts, end_ts; 921 922 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 923 start_test_loop(&start_ts, self->verbose); 924 do { 925 unsigned long vma_start; 926 unsigned long vma_end; 927 928 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 929 930 /* Check if we read vmas after remapping it */ 931 if (!strcmp(new_last_line.text, remapped_last_line.text)) { 932 /* 933 * The vmas should be consistent with remap results, 934 * however if the vma was concurrently restored, it 935 * can be reported twice (first as split one, then 936 * as restored one) because we found it as the next vma 937 * again. In that case new first line will be the same 938 * as the last restored line. 939 */ 940 ASSERT_FALSE(print_boundaries_on( 941 strcmp(new_first_line.text, remapped_first_line.text) && 942 strcmp(new_first_line.text, restored_last_line.text), 943 "Remap result invalid", self)); 944 } else { 945 /* The vmas should be consistent with the original/resored state */ 946 ASSERT_FALSE(print_boundaries_on( 947 strcmp(new_last_line.text, restored_last_line.text), 948 "Remap restore result invalid", self)); 949 ASSERT_FALSE(print_boundaries_on( 950 strcmp(new_first_line.text, restored_first_line.text), 951 "Remap restore result invalid", self)); 952 } 953 if (self->maps_file == MAPS) { 954 /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ 955 ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size, 956 &vma_start, &vma_end)); 957 /* 958 * The vma should either stay at the same address and have the 959 * original size of 3 pages or we should find the remapped vma 960 * at the remap destination address with size of 1 page. 961 */ 962 ASSERT_TRUE((vma_start == self->last_line.start_addr && 963 vma_end - vma_start == self->page_size * 3) || 964 (vma_start == self->last_line.start_addr + self->page_size && 965 vma_end - vma_start == self->page_size)); 966 } 967 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 968 end_test_iteration(&end_ts, self->verbose); 969 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 970 end_test_loop(self->verbose); 971 972 /* Signal the modifyer thread to stop and wait until it exits */ 973 signal_state(mod_info, TEST_DONE); 974 } 975 976 TEST_HARNESS_MAIN 977