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 return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot | PROT_EXEC, 494 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 495 } 496 497 static inline bool merge_vma(FIXTURE_DATA(proc_maps_race) *self) 498 { 499 return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot, 500 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 501 } 502 503 static inline bool check_split_result(struct line_content *mod_last_line, 504 struct line_content *mod_first_line, 505 struct line_content *restored_last_line, 506 struct line_content *restored_first_line) 507 { 508 /* Make sure vmas at the boundaries are changing */ 509 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 510 strcmp(mod_first_line->text, restored_first_line->text) != 0; 511 } 512 513 static inline bool shrink_vma(FIXTURE_DATA(proc_maps_race) *self) 514 { 515 return mremap(self->mod_info->addr, self->page_size * 3, 516 self->page_size, 0) != MAP_FAILED; 517 } 518 519 static inline bool expand_vma(FIXTURE_DATA(proc_maps_race) *self) 520 { 521 return mremap(self->mod_info->addr, self->page_size, 522 self->page_size * 3, 0) != MAP_FAILED; 523 } 524 525 static inline bool check_shrink_result(struct line_content *mod_last_line, 526 struct line_content *mod_first_line, 527 struct line_content *restored_last_line, 528 struct line_content *restored_first_line) 529 { 530 /* Make sure only the last vma of the first page is changing */ 531 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 532 strcmp(mod_first_line->text, restored_first_line->text) == 0; 533 } 534 535 static inline bool remap_vma(FIXTURE_DATA(proc_maps_race) *self) 536 { 537 /* 538 * Remap the last page of the next vma into the middle of the vma. 539 * This splits the current vma and the first and middle parts (the 540 * parts at lower addresses) become the last vma objserved in the 541 * first page and the first vma observed in the last page. 542 */ 543 return mremap(self->mod_info->next_addr + self->page_size * 2, self->page_size, 544 self->page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP, 545 self->mod_info->addr + self->page_size) != MAP_FAILED; 546 } 547 548 static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self) 549 { 550 return mprotect(self->mod_info->addr + self->page_size, self->page_size, 551 self->mod_info->prot) == 0; 552 } 553 554 static inline bool check_remap_result(struct line_content *mod_last_line, 555 struct line_content *mod_first_line, 556 struct line_content *restored_last_line, 557 struct line_content *restored_first_line) 558 { 559 /* Make sure vmas at the boundaries are changing */ 560 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 561 strcmp(mod_first_line->text, restored_first_line->text) != 0; 562 } 563 564 FIXTURE_SETUP(proc_maps_race) 565 { 566 const char *verbose = getenv("VERBOSE"); 567 const char *duration = getenv("DURATION"); 568 struct vma_modifier_info *mod_info; 569 pthread_mutexattr_t mutex_attr; 570 pthread_condattr_t cond_attr; 571 unsigned long first_map_addr; 572 unsigned long last_map_addr; 573 unsigned long duration_sec; 574 char fname[32]; 575 576 self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); 577 self->verbose = verbose && !strncmp(verbose, "1", 1); 578 self->maps_file = variant->maps_file; 579 duration_sec = duration ? atol(duration) : 0; 580 self->duration_sec = duration_sec ? duration_sec : 5UL; 581 582 /* 583 * Have to map enough vmas for /proc/pid/maps to contain more than one 584 * page worth of vmas. Assume at least 32 bytes per line in maps output 585 */ 586 self->vma_count = self->page_size / 32 + 1; 587 self->shared_mem_size = sizeof(struct vma_modifier_info) + self->vma_count * sizeof(void *); 588 589 /* map shared memory for communication with the child process */ 590 self->mod_info = (struct vma_modifier_info *)mmap(NULL, self->shared_mem_size, 591 PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 592 ASSERT_NE(self->mod_info, MAP_FAILED); 593 mod_info = self->mod_info; 594 595 /* Initialize shared members */ 596 pthread_mutexattr_init(&mutex_attr); 597 pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); 598 ASSERT_EQ(pthread_mutex_init(&mod_info->sync_lock, &mutex_attr), 0); 599 pthread_condattr_init(&cond_attr); 600 pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); 601 ASSERT_EQ(pthread_cond_init(&mod_info->sync_cond, &cond_attr), 0); 602 mod_info->vma_count = self->vma_count; 603 mod_info->curr_state = INIT; 604 mod_info->exit = false; 605 606 self->pid = fork(); 607 if (!self->pid) { 608 /* Child process modifying the address space */ 609 int prot = PROT_READ | PROT_WRITE; 610 int i; 611 612 for (i = 0; i < mod_info->vma_count; i++) { 613 mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot, 614 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 615 ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED); 616 /* change protection in adjacent maps to prevent merging */ 617 prot ^= PROT_WRITE; 618 } 619 signal_state(mod_info, CHILD_READY); 620 wait_for_state(mod_info, PARENT_READY); 621 while (true) { 622 signal_state(mod_info, SETUP_READY); 623 wait_for_state(mod_info, SETUP_MODIFY_MAPS); 624 if (mod_info->exit) 625 break; 626 627 ASSERT_TRUE(mod_info->vma_modify(self)); 628 signal_state(mod_info, SETUP_MAPS_MODIFIED); 629 wait_for_state(mod_info, SETUP_RESTORE_MAPS); 630 ASSERT_TRUE(mod_info->vma_restore(self)); 631 signal_state(mod_info, SETUP_MAPS_RESTORED); 632 633 wait_for_state(mod_info, TEST_READY); 634 while (mod_info->curr_state != TEST_DONE) { 635 ASSERT_TRUE(mod_info->vma_modify(self)); 636 ASSERT_TRUE(mod_info->vma_restore(self)); 637 } 638 } 639 for (i = 0; i < mod_info->vma_count; i++) 640 munmap(mod_info->child_mapped_addr[i], self->page_size * 3); 641 642 exit(0); 643 } 644 645 switch (self->maps_file) { 646 case MAPS: 647 sprintf(fname, "/proc/%d/maps", self->pid); 648 break; 649 case SMAPS: 650 sprintf(fname, "/proc/%d/smaps", self->pid); 651 break; 652 default: 653 ksft_exit_fail(); 654 } 655 self->maps_fd = open(fname, O_RDONLY); 656 ASSERT_NE(self->maps_fd, -1); 657 658 /* Wait for the child to map the VMAs */ 659 wait_for_state(mod_info, CHILD_READY); 660 661 /* Read first two pages */ 662 self->page1.data = malloc(self->page_size); 663 ASSERT_NE(self->page1.data, NULL); 664 self->page2.data = malloc(self->page_size); 665 ASSERT_NE(self->page2.data, NULL); 666 667 first_map_addr = (unsigned long)mod_info->child_mapped_addr[0]; 668 last_map_addr = (unsigned long)mod_info->child_mapped_addr[mod_info->vma_count - 1]; 669 670 self->skip_pages = locate_containing_page(self, 671 min(first_map_addr, last_map_addr), 672 self->page_size * 3); 673 ASSERT_NE(self->skip_pages, -1); 674 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 675 676 /* 677 * Find the addresses corresponding to the last line in the first page 678 * and the first line in the last page. 679 */ 680 mod_info->addr = NULL; 681 mod_info->next_addr = NULL; 682 for (int i = 0; i < mod_info->vma_count; i++) { 683 if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) { 684 mod_info->addr = mod_info->child_mapped_addr[i]; 685 mod_info->prot = PROT_READ; 686 /* Even VMAs have write permission */ 687 if ((i % 2) == 0) 688 mod_info->prot |= PROT_WRITE; 689 } else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) { 690 mod_info->next_addr = mod_info->child_mapped_addr[i]; 691 } 692 693 if (mod_info->addr && mod_info->next_addr) 694 break; 695 } 696 ASSERT_TRUE(mod_info->addr && mod_info->next_addr); 697 698 signal_state(mod_info, PARENT_READY); 699 } 700 701 FIXTURE_TEARDOWN(proc_maps_race) 702 { 703 int status; 704 705 stop_vma_modifier(self->mod_info); 706 707 free(self->page2.data); 708 free(self->page1.data); 709 710 for (int i = 0; i < self->vma_count; i++) 711 munmap(self->mod_info->child_mapped_addr[i], self->page_size); 712 close(self->maps_fd); 713 waitpid(self->pid, &status, 0); 714 munmap(self->mod_info, self->shared_mem_size); 715 } 716 717 TEST_F(proc_maps_race, test_maps_tearing_from_split) 718 { 719 struct vma_modifier_info *mod_info = self->mod_info; 720 721 struct line_content split_last_line; 722 struct line_content split_first_line; 723 struct line_content restored_last_line; 724 struct line_content restored_first_line; 725 726 wait_for_state(mod_info, SETUP_READY); 727 728 /* re-read the file to avoid using stale data from previous test */ 729 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 730 731 mod_info->vma_modify = split_vma; 732 mod_info->vma_restore = merge_vma; 733 mod_info->vma_mod_check = check_split_result; 734 735 report_test_start("Tearing from split", self->verbose); 736 ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line, 737 &restored_last_line, &restored_first_line)); 738 739 /* Now start concurrent modifications for self->duration_sec */ 740 signal_state(mod_info, TEST_READY); 741 742 struct line_content new_last_line; 743 struct line_content new_first_line; 744 struct timespec start_ts, end_ts; 745 746 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 747 start_test_loop(&start_ts, self->verbose); 748 do { 749 bool last_line_changed; 750 bool first_line_changed; 751 unsigned long vma_start; 752 unsigned long vma_end; 753 754 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 755 756 /* Check if we read vmas after split */ 757 if (!strcmp(new_last_line.text, split_last_line.text)) { 758 /* 759 * The vmas should be consistent with split results, 760 * however if vma was concurrently restored after a 761 * split, it can be reported twice (first the original 762 * split one, then the same vma but extended after the 763 * merge) because we found it as the next vma again. 764 * In that case new first line will be the same as the 765 * last restored line. 766 */ 767 ASSERT_FALSE(print_boundaries_on( 768 strcmp(new_first_line.text, split_first_line.text) && 769 strcmp(new_first_line.text, restored_last_line.text), 770 "Split result invalid", self)); 771 } else { 772 /* The vmas should be consistent with merge results */ 773 ASSERT_FALSE(print_boundaries_on( 774 strcmp(new_last_line.text, restored_last_line.text), 775 "Merge result invalid", self)); 776 ASSERT_FALSE(print_boundaries_on( 777 strcmp(new_first_line.text, restored_first_line.text), 778 "Merge result invalid", self)); 779 } 780 /* 781 * First and last lines should change in unison. If the last 782 * line changed then the first line should change as well and 783 * vice versa. 784 */ 785 last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0; 786 first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0; 787 ASSERT_EQ(last_line_changed, first_line_changed); 788 if (self->maps_file == MAPS) { 789 /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ 790 ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size, 791 &vma_start, &vma_end)); 792 /* 793 * The vma at the split address can be either the same as 794 * original one (if read before the split) or the same as the 795 * first line in the second page (if read after the split). 796 */ 797 ASSERT_TRUE((vma_start == self->last_line.start_addr && 798 vma_end == self->last_line.end_addr) || 799 (vma_start == split_first_line.start_addr && 800 vma_end == split_first_line.end_addr)); 801 } 802 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 803 end_test_iteration(&end_ts, self->verbose); 804 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 805 end_test_loop(self->verbose); 806 807 /* Signal the modifyer thread to stop and wait until it exits */ 808 signal_state(mod_info, TEST_DONE); 809 } 810 811 TEST_F(proc_maps_race, test_maps_tearing_from_resize) 812 { 813 struct vma_modifier_info *mod_info = self->mod_info; 814 815 struct line_content shrunk_last_line; 816 struct line_content shrunk_first_line; 817 struct line_content restored_last_line; 818 struct line_content restored_first_line; 819 820 wait_for_state(mod_info, SETUP_READY); 821 822 /* re-read the file to avoid using stale data from previous test */ 823 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 824 825 mod_info->vma_modify = shrink_vma; 826 mod_info->vma_restore = expand_vma; 827 mod_info->vma_mod_check = check_shrink_result; 828 829 report_test_start("Tearing from resize", self->verbose); 830 ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line, 831 &restored_last_line, &restored_first_line)); 832 833 /* Now start concurrent modifications for self->duration_sec */ 834 signal_state(mod_info, TEST_READY); 835 836 struct line_content new_last_line; 837 struct line_content new_first_line; 838 struct timespec start_ts, end_ts; 839 840 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 841 start_test_loop(&start_ts, self->verbose); 842 do { 843 unsigned long vma_start; 844 unsigned long vma_end; 845 846 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 847 848 /* Check if we read vmas after shrinking it */ 849 if (!strcmp(new_last_line.text, shrunk_last_line.text)) { 850 /* 851 * The vmas should be consistent with shrunk results, 852 * however if the vma was concurrently restored, it 853 * can be reported twice (first as shrunk one, then 854 * as restored one) because we found it as the next vma 855 * again. In that case new first line will be the same 856 * as the last restored line. 857 */ 858 ASSERT_FALSE(print_boundaries_on( 859 strcmp(new_first_line.text, shrunk_first_line.text) && 860 strcmp(new_first_line.text, restored_last_line.text), 861 "Shrink result invalid", self)); 862 } else { 863 /* The vmas should be consistent with the original/resored state */ 864 ASSERT_FALSE(print_boundaries_on( 865 strcmp(new_last_line.text, restored_last_line.text), 866 "Expand result invalid", self)); 867 ASSERT_FALSE(print_boundaries_on( 868 strcmp(new_first_line.text, restored_first_line.text), 869 "Expand result invalid", self)); 870 } 871 if (self->maps_file == MAPS) { 872 /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ 873 ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr, 874 &vma_start, &vma_end)); 875 /* 876 * The vma should stay at the same address and have either the 877 * original size of 3 pages or 1 page if read after shrinking. 878 */ 879 ASSERT_TRUE(vma_start == self->last_line.start_addr && 880 (vma_end - vma_start == self->page_size * 3 || 881 vma_end - vma_start == self->page_size)); 882 } 883 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 884 end_test_iteration(&end_ts, self->verbose); 885 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 886 end_test_loop(self->verbose); 887 888 /* Signal the modifyer thread to stop and wait until it exits */ 889 signal_state(mod_info, TEST_DONE); 890 } 891 892 TEST_F(proc_maps_race, test_maps_tearing_from_remap) 893 { 894 struct vma_modifier_info *mod_info = self->mod_info; 895 896 struct line_content remapped_last_line; 897 struct line_content remapped_first_line; 898 struct line_content restored_last_line; 899 struct line_content restored_first_line; 900 901 wait_for_state(mod_info, SETUP_READY); 902 903 /* re-read the file to avoid using stale data from previous test */ 904 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 905 906 mod_info->vma_modify = remap_vma; 907 mod_info->vma_restore = patch_vma; 908 mod_info->vma_mod_check = check_remap_result; 909 910 report_test_start("Tearing from remap", self->verbose); 911 ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line, 912 &restored_last_line, &restored_first_line)); 913 914 /* Now start concurrent modifications for self->duration_sec */ 915 signal_state(mod_info, TEST_READY); 916 917 struct line_content new_last_line; 918 struct line_content new_first_line; 919 struct timespec start_ts, end_ts; 920 921 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 922 start_test_loop(&start_ts, self->verbose); 923 do { 924 unsigned long vma_start; 925 unsigned long vma_end; 926 927 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 928 929 /* Check if we read vmas after remapping it */ 930 if (!strcmp(new_last_line.text, remapped_last_line.text)) { 931 /* 932 * The vmas should be consistent with remap results, 933 * however if the vma was concurrently restored, it 934 * can be reported twice (first as split one, then 935 * as restored one) because we found it as the next vma 936 * again. In that case new first line will be the same 937 * as the last restored line. 938 */ 939 ASSERT_FALSE(print_boundaries_on( 940 strcmp(new_first_line.text, remapped_first_line.text) && 941 strcmp(new_first_line.text, restored_last_line.text), 942 "Remap result invalid", self)); 943 } else { 944 /* The vmas should be consistent with the original/resored state */ 945 ASSERT_FALSE(print_boundaries_on( 946 strcmp(new_last_line.text, restored_last_line.text), 947 "Remap restore result invalid", self)); 948 ASSERT_FALSE(print_boundaries_on( 949 strcmp(new_first_line.text, restored_first_line.text), 950 "Remap restore result invalid", self)); 951 } 952 if (self->maps_file == MAPS) { 953 /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ 954 ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size, 955 &vma_start, &vma_end)); 956 /* 957 * The vma should either stay at the same address and have the 958 * original size of 3 pages or we should find the remapped vma 959 * at the remap destination address with size of 1 page. 960 */ 961 ASSERT_TRUE((vma_start == self->last_line.start_addr && 962 vma_end - vma_start == self->page_size * 3) || 963 (vma_start == self->last_line.start_addr + self->page_size && 964 vma_end - vma_start == self->page_size)); 965 } 966 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 967 end_test_iteration(&end_ts, self->verbose); 968 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 969 end_test_loop(self->verbose); 970 971 /* Signal the modifyer thread to stop and wait until it exits */ 972 signal_state(mod_info, TEST_DONE); 973 } 974 975 TEST_HARNESS_MAIN 976