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 verifying the results. Address 21 * 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 <sys/mman.h> 36 #include <sys/stat.h> 37 #include <sys/types.h> 38 #include <sys/wait.h> 39 40 /* /proc/pid/maps parsing routines */ 41 struct page_content { 42 char *data; 43 ssize_t size; 44 }; 45 46 #define LINE_MAX_SIZE 256 47 48 struct line_content { 49 char text[LINE_MAX_SIZE]; 50 unsigned long start_addr; 51 unsigned long end_addr; 52 }; 53 54 enum test_state { 55 INIT, 56 CHILD_READY, 57 PARENT_READY, 58 SETUP_READY, 59 SETUP_MODIFY_MAPS, 60 SETUP_MAPS_MODIFIED, 61 SETUP_RESTORE_MAPS, 62 SETUP_MAPS_RESTORED, 63 TEST_READY, 64 TEST_DONE, 65 }; 66 67 struct vma_modifier_info; 68 69 FIXTURE(proc_maps_race) 70 { 71 struct vma_modifier_info *mod_info; 72 struct page_content page1; 73 struct page_content page2; 74 struct line_content last_line; 75 struct line_content first_line; 76 unsigned long duration_sec; 77 int shared_mem_size; 78 int page_size; 79 int vma_count; 80 bool verbose; 81 int maps_fd; 82 pid_t pid; 83 }; 84 85 typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self); 86 typedef bool (*vma_mod_result_check_op)(struct line_content *mod_last_line, 87 struct line_content *mod_first_line, 88 struct line_content *restored_last_line, 89 struct line_content *restored_first_line); 90 91 struct vma_modifier_info { 92 int vma_count; 93 void *addr; 94 int prot; 95 void *next_addr; 96 vma_modifier_op vma_modify; 97 vma_modifier_op vma_restore; 98 vma_mod_result_check_op vma_mod_check; 99 pthread_mutex_t sync_lock; 100 pthread_cond_t sync_cond; 101 enum test_state curr_state; 102 bool exit; 103 void *child_mapped_addr[]; 104 }; 105 106 107 static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self) 108 { 109 ssize_t bytes_read; 110 111 if (lseek(self->maps_fd, 0, SEEK_SET) < 0) 112 return false; 113 114 bytes_read = read(self->maps_fd, self->page1.data, self->page_size); 115 if (bytes_read <= 0) 116 return false; 117 118 self->page1.size = bytes_read; 119 120 bytes_read = read(self->maps_fd, self->page2.data, self->page_size); 121 if (bytes_read <= 0) 122 return false; 123 124 self->page2.size = bytes_read; 125 126 return true; 127 } 128 129 static void copy_first_line(struct page_content *page, char *first_line) 130 { 131 char *pos = strchr(page->data, '\n'); 132 133 strncpy(first_line, page->data, pos - page->data); 134 first_line[pos - page->data] = '\0'; 135 } 136 137 static void copy_last_line(struct page_content *page, char *last_line) 138 { 139 /* Get the last line in the first page */ 140 const char *end = page->data + page->size - 1; 141 /* skip last newline */ 142 const char *pos = end - 1; 143 144 /* search previous newline */ 145 while (pos[-1] != '\n') 146 pos--; 147 strncpy(last_line, pos, end - pos); 148 last_line[end - pos] = '\0'; 149 } 150 151 /* Read the last line of the first page and the first line of the second page */ 152 static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self, 153 struct line_content *last_line, 154 struct line_content *first_line) 155 { 156 if (!read_two_pages(self)) 157 return false; 158 159 copy_last_line(&self->page1, last_line->text); 160 copy_first_line(&self->page2, first_line->text); 161 162 return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr, 163 &last_line->end_addr) == 2 && 164 sscanf(first_line->text, "%lx-%lx", &first_line->start_addr, 165 &first_line->end_addr) == 2; 166 } 167 168 /* Thread synchronization routines */ 169 static void wait_for_state(struct vma_modifier_info *mod_info, enum test_state state) 170 { 171 pthread_mutex_lock(&mod_info->sync_lock); 172 while (mod_info->curr_state != state) 173 pthread_cond_wait(&mod_info->sync_cond, &mod_info->sync_lock); 174 pthread_mutex_unlock(&mod_info->sync_lock); 175 } 176 177 static void signal_state(struct vma_modifier_info *mod_info, enum test_state state) 178 { 179 pthread_mutex_lock(&mod_info->sync_lock); 180 mod_info->curr_state = state; 181 pthread_cond_signal(&mod_info->sync_cond); 182 pthread_mutex_unlock(&mod_info->sync_lock); 183 } 184 185 static void stop_vma_modifier(struct vma_modifier_info *mod_info) 186 { 187 wait_for_state(mod_info, SETUP_READY); 188 mod_info->exit = true; 189 signal_state(mod_info, SETUP_MODIFY_MAPS); 190 } 191 192 static void print_first_lines(char *text, int nr) 193 { 194 const char *end = text; 195 196 while (nr && (end = strchr(end, '\n')) != NULL) { 197 nr--; 198 end++; 199 } 200 201 if (end) { 202 int offs = end - text; 203 204 text[offs] = '\0'; 205 printf("%s", text); 206 text[offs] = '\n'; 207 printf("\n"); 208 } else { 209 printf("%s", text); 210 } 211 } 212 213 static void print_last_lines(char *text, int nr) 214 { 215 const char *start = text + strlen(text); 216 217 nr++; /* to ignore the last newline */ 218 while (nr) { 219 while (start > text && *start != '\n') 220 start--; 221 nr--; 222 start--; 223 } 224 printf("%s", start); 225 } 226 227 static void print_boundaries(const char *title, FIXTURE_DATA(proc_maps_race) *self) 228 { 229 if (!self->verbose) 230 return; 231 232 printf("%s", title); 233 /* Print 3 boundary lines from each page */ 234 print_last_lines(self->page1.data, 3); 235 printf("-----------------page boundary-----------------\n"); 236 print_first_lines(self->page2.data, 3); 237 } 238 239 static bool print_boundaries_on(bool condition, const char *title, 240 FIXTURE_DATA(proc_maps_race) *self) 241 { 242 if (self->verbose && condition) 243 print_boundaries(title, self); 244 245 return condition; 246 } 247 248 static void report_test_start(const char *name, bool verbose) 249 { 250 if (verbose) 251 printf("==== %s ====\n", name); 252 } 253 254 static struct timespec print_ts; 255 256 static void start_test_loop(struct timespec *ts, bool verbose) 257 { 258 if (verbose) 259 print_ts.tv_sec = ts->tv_sec; 260 } 261 262 static void end_test_iteration(struct timespec *ts, bool verbose) 263 { 264 if (!verbose) 265 return; 266 267 /* Update every second */ 268 if (print_ts.tv_sec == ts->tv_sec) 269 return; 270 271 printf("."); 272 fflush(stdout); 273 print_ts.tv_sec = ts->tv_sec; 274 } 275 276 static void end_test_loop(bool verbose) 277 { 278 if (verbose) 279 printf("\n"); 280 } 281 282 static bool capture_mod_pattern(FIXTURE_DATA(proc_maps_race) *self, 283 struct line_content *mod_last_line, 284 struct line_content *mod_first_line, 285 struct line_content *restored_last_line, 286 struct line_content *restored_first_line) 287 { 288 print_boundaries("Before modification", self); 289 290 signal_state(self->mod_info, SETUP_MODIFY_MAPS); 291 wait_for_state(self->mod_info, SETUP_MAPS_MODIFIED); 292 293 /* Copy last line of the first page and first line of the last page */ 294 if (!read_boundary_lines(self, mod_last_line, mod_first_line)) 295 return false; 296 297 print_boundaries("After modification", self); 298 299 signal_state(self->mod_info, SETUP_RESTORE_MAPS); 300 wait_for_state(self->mod_info, SETUP_MAPS_RESTORED); 301 302 /* Copy last line of the first page and first line of the last page */ 303 if (!read_boundary_lines(self, restored_last_line, restored_first_line)) 304 return false; 305 306 print_boundaries("After restore", self); 307 308 if (!self->mod_info->vma_mod_check(mod_last_line, mod_first_line, 309 restored_last_line, restored_first_line)) 310 return false; 311 312 /* 313 * The content of these lines after modify+resore should be the same 314 * as the original. 315 */ 316 return strcmp(restored_last_line->text, self->last_line.text) == 0 && 317 strcmp(restored_first_line->text, self->first_line.text) == 0; 318 } 319 320 static inline bool split_vma(FIXTURE_DATA(proc_maps_race) *self) 321 { 322 return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot | PROT_EXEC, 323 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 324 } 325 326 static inline bool merge_vma(FIXTURE_DATA(proc_maps_race) *self) 327 { 328 return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot, 329 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 330 } 331 332 static inline bool check_split_result(struct line_content *mod_last_line, 333 struct line_content *mod_first_line, 334 struct line_content *restored_last_line, 335 struct line_content *restored_first_line) 336 { 337 /* Make sure vmas at the boundaries are changing */ 338 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 339 strcmp(mod_first_line->text, restored_first_line->text) != 0; 340 } 341 342 static inline bool shrink_vma(FIXTURE_DATA(proc_maps_race) *self) 343 { 344 return mremap(self->mod_info->addr, self->page_size * 3, 345 self->page_size, 0) != MAP_FAILED; 346 } 347 348 static inline bool expand_vma(FIXTURE_DATA(proc_maps_race) *self) 349 { 350 return mremap(self->mod_info->addr, self->page_size, 351 self->page_size * 3, 0) != MAP_FAILED; 352 } 353 354 static inline bool check_shrink_result(struct line_content *mod_last_line, 355 struct line_content *mod_first_line, 356 struct line_content *restored_last_line, 357 struct line_content *restored_first_line) 358 { 359 /* Make sure only the last vma of the first page is changing */ 360 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 361 strcmp(mod_first_line->text, restored_first_line->text) == 0; 362 } 363 364 static inline bool remap_vma(FIXTURE_DATA(proc_maps_race) *self) 365 { 366 /* 367 * Remap the last page of the next vma into the middle of the vma. 368 * This splits the current vma and the first and middle parts (the 369 * parts at lower addresses) become the last vma objserved in the 370 * first page and the first vma observed in the last page. 371 */ 372 return mremap(self->mod_info->next_addr + self->page_size * 2, self->page_size, 373 self->page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP, 374 self->mod_info->addr + self->page_size) != MAP_FAILED; 375 } 376 377 static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self) 378 { 379 return mprotect(self->mod_info->addr + self->page_size, self->page_size, 380 self->mod_info->prot) == 0; 381 } 382 383 static inline bool check_remap_result(struct line_content *mod_last_line, 384 struct line_content *mod_first_line, 385 struct line_content *restored_last_line, 386 struct line_content *restored_first_line) 387 { 388 /* Make sure vmas at the boundaries are changing */ 389 return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 390 strcmp(mod_first_line->text, restored_first_line->text) != 0; 391 } 392 393 FIXTURE_SETUP(proc_maps_race) 394 { 395 const char *verbose = getenv("VERBOSE"); 396 const char *duration = getenv("DURATION"); 397 struct vma_modifier_info *mod_info; 398 pthread_mutexattr_t mutex_attr; 399 pthread_condattr_t cond_attr; 400 unsigned long duration_sec; 401 char fname[32]; 402 403 self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); 404 self->verbose = verbose && !strncmp(verbose, "1", 1); 405 duration_sec = duration ? atol(duration) : 0; 406 self->duration_sec = duration_sec ? duration_sec : 5UL; 407 408 /* 409 * Have to map enough vmas for /proc/pid/maps to contain more than one 410 * page worth of vmas. Assume at least 32 bytes per line in maps output 411 */ 412 self->vma_count = self->page_size / 32 + 1; 413 self->shared_mem_size = sizeof(struct vma_modifier_info) + self->vma_count * sizeof(void *); 414 415 /* map shared memory for communication with the child process */ 416 self->mod_info = (struct vma_modifier_info *)mmap(NULL, self->shared_mem_size, 417 PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 418 ASSERT_NE(self->mod_info, MAP_FAILED); 419 mod_info = self->mod_info; 420 421 /* Initialize shared members */ 422 pthread_mutexattr_init(&mutex_attr); 423 pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); 424 ASSERT_EQ(pthread_mutex_init(&mod_info->sync_lock, &mutex_attr), 0); 425 pthread_condattr_init(&cond_attr); 426 pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); 427 ASSERT_EQ(pthread_cond_init(&mod_info->sync_cond, &cond_attr), 0); 428 mod_info->vma_count = self->vma_count; 429 mod_info->curr_state = INIT; 430 mod_info->exit = false; 431 432 self->pid = fork(); 433 if (!self->pid) { 434 /* Child process modifying the address space */ 435 int prot = PROT_READ | PROT_WRITE; 436 int i; 437 438 for (i = 0; i < mod_info->vma_count; i++) { 439 mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot, 440 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 441 ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED); 442 /* change protection in adjacent maps to prevent merging */ 443 prot ^= PROT_WRITE; 444 } 445 signal_state(mod_info, CHILD_READY); 446 wait_for_state(mod_info, PARENT_READY); 447 while (true) { 448 signal_state(mod_info, SETUP_READY); 449 wait_for_state(mod_info, SETUP_MODIFY_MAPS); 450 if (mod_info->exit) 451 break; 452 453 ASSERT_TRUE(mod_info->vma_modify(self)); 454 signal_state(mod_info, SETUP_MAPS_MODIFIED); 455 wait_for_state(mod_info, SETUP_RESTORE_MAPS); 456 ASSERT_TRUE(mod_info->vma_restore(self)); 457 signal_state(mod_info, SETUP_MAPS_RESTORED); 458 459 wait_for_state(mod_info, TEST_READY); 460 while (mod_info->curr_state != TEST_DONE) { 461 ASSERT_TRUE(mod_info->vma_modify(self)); 462 ASSERT_TRUE(mod_info->vma_restore(self)); 463 } 464 } 465 for (i = 0; i < mod_info->vma_count; i++) 466 munmap(mod_info->child_mapped_addr[i], self->page_size * 3); 467 468 exit(0); 469 } 470 471 sprintf(fname, "/proc/%d/maps", self->pid); 472 self->maps_fd = open(fname, O_RDONLY); 473 ASSERT_NE(self->maps_fd, -1); 474 475 /* Wait for the child to map the VMAs */ 476 wait_for_state(mod_info, CHILD_READY); 477 478 /* Read first two pages */ 479 self->page1.data = malloc(self->page_size); 480 ASSERT_NE(self->page1.data, NULL); 481 self->page2.data = malloc(self->page_size); 482 ASSERT_NE(self->page2.data, NULL); 483 484 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 485 486 /* 487 * Find the addresses corresponding to the last line in the first page 488 * and the first line in the last page. 489 */ 490 mod_info->addr = NULL; 491 mod_info->next_addr = NULL; 492 for (int i = 0; i < mod_info->vma_count; i++) { 493 if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) { 494 mod_info->addr = mod_info->child_mapped_addr[i]; 495 mod_info->prot = PROT_READ; 496 /* Even VMAs have write permission */ 497 if ((i % 2) == 0) 498 mod_info->prot |= PROT_WRITE; 499 } else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) { 500 mod_info->next_addr = mod_info->child_mapped_addr[i]; 501 } 502 503 if (mod_info->addr && mod_info->next_addr) 504 break; 505 } 506 ASSERT_TRUE(mod_info->addr && mod_info->next_addr); 507 508 signal_state(mod_info, PARENT_READY); 509 510 } 511 512 FIXTURE_TEARDOWN(proc_maps_race) 513 { 514 int status; 515 516 stop_vma_modifier(self->mod_info); 517 518 free(self->page2.data); 519 free(self->page1.data); 520 521 for (int i = 0; i < self->vma_count; i++) 522 munmap(self->mod_info->child_mapped_addr[i], self->page_size); 523 close(self->maps_fd); 524 waitpid(self->pid, &status, 0); 525 munmap(self->mod_info, self->shared_mem_size); 526 } 527 528 TEST_F(proc_maps_race, test_maps_tearing_from_split) 529 { 530 struct vma_modifier_info *mod_info = self->mod_info; 531 532 struct line_content split_last_line; 533 struct line_content split_first_line; 534 struct line_content restored_last_line; 535 struct line_content restored_first_line; 536 537 wait_for_state(mod_info, SETUP_READY); 538 539 /* re-read the file to avoid using stale data from previous test */ 540 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 541 542 mod_info->vma_modify = split_vma; 543 mod_info->vma_restore = merge_vma; 544 mod_info->vma_mod_check = check_split_result; 545 546 report_test_start("Tearing from split", self->verbose); 547 ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line, 548 &restored_last_line, &restored_first_line)); 549 550 /* Now start concurrent modifications for self->duration_sec */ 551 signal_state(mod_info, TEST_READY); 552 553 struct line_content new_last_line; 554 struct line_content new_first_line; 555 struct timespec start_ts, end_ts; 556 557 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 558 start_test_loop(&start_ts, self->verbose); 559 do { 560 bool last_line_changed; 561 bool first_line_changed; 562 563 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 564 565 /* Check if we read vmas after split */ 566 if (!strcmp(new_last_line.text, split_last_line.text)) { 567 /* 568 * The vmas should be consistent with split results, 569 * however if vma was concurrently restored after a 570 * split, it can be reported twice (first the original 571 * split one, then the same vma but extended after the 572 * merge) because we found it as the next vma again. 573 * In that case new first line will be the same as the 574 * last restored line. 575 */ 576 ASSERT_FALSE(print_boundaries_on( 577 strcmp(new_first_line.text, split_first_line.text) && 578 strcmp(new_first_line.text, restored_last_line.text), 579 "Split result invalid", self)); 580 } else { 581 /* The vmas should be consistent with merge results */ 582 ASSERT_FALSE(print_boundaries_on( 583 strcmp(new_last_line.text, restored_last_line.text), 584 "Merge result invalid", self)); 585 ASSERT_FALSE(print_boundaries_on( 586 strcmp(new_first_line.text, restored_first_line.text), 587 "Merge result invalid", self)); 588 } 589 /* 590 * First and last lines should change in unison. If the last 591 * line changed then the first line should change as well and 592 * vice versa. 593 */ 594 last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0; 595 first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0; 596 ASSERT_EQ(last_line_changed, first_line_changed); 597 598 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 599 end_test_iteration(&end_ts, self->verbose); 600 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 601 end_test_loop(self->verbose); 602 603 /* Signal the modifyer thread to stop and wait until it exits */ 604 signal_state(mod_info, TEST_DONE); 605 } 606 607 TEST_F(proc_maps_race, test_maps_tearing_from_resize) 608 { 609 struct vma_modifier_info *mod_info = self->mod_info; 610 611 struct line_content shrunk_last_line; 612 struct line_content shrunk_first_line; 613 struct line_content restored_last_line; 614 struct line_content restored_first_line; 615 616 wait_for_state(mod_info, SETUP_READY); 617 618 /* re-read the file to avoid using stale data from previous test */ 619 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 620 621 mod_info->vma_modify = shrink_vma; 622 mod_info->vma_restore = expand_vma; 623 mod_info->vma_mod_check = check_shrink_result; 624 625 report_test_start("Tearing from resize", self->verbose); 626 ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line, 627 &restored_last_line, &restored_first_line)); 628 629 /* Now start concurrent modifications for self->duration_sec */ 630 signal_state(mod_info, TEST_READY); 631 632 struct line_content new_last_line; 633 struct line_content new_first_line; 634 struct timespec start_ts, end_ts; 635 636 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 637 start_test_loop(&start_ts, self->verbose); 638 do { 639 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 640 641 /* Check if we read vmas after shrinking it */ 642 if (!strcmp(new_last_line.text, shrunk_last_line.text)) { 643 /* 644 * The vmas should be consistent with shrunk results, 645 * however if the vma was concurrently restored, it 646 * can be reported twice (first as shrunk one, then 647 * as restored one) because we found it as the next vma 648 * again. In that case new first line will be the same 649 * as the last restored line. 650 */ 651 ASSERT_FALSE(print_boundaries_on( 652 strcmp(new_first_line.text, shrunk_first_line.text) && 653 strcmp(new_first_line.text, restored_last_line.text), 654 "Shrink result invalid", self)); 655 } else { 656 /* The vmas should be consistent with the original/resored state */ 657 ASSERT_FALSE(print_boundaries_on( 658 strcmp(new_last_line.text, restored_last_line.text), 659 "Expand result invalid", self)); 660 ASSERT_FALSE(print_boundaries_on( 661 strcmp(new_first_line.text, restored_first_line.text), 662 "Expand result invalid", self)); 663 } 664 665 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 666 end_test_iteration(&end_ts, self->verbose); 667 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 668 end_test_loop(self->verbose); 669 670 /* Signal the modifyer thread to stop and wait until it exits */ 671 signal_state(mod_info, TEST_DONE); 672 } 673 674 TEST_F(proc_maps_race, test_maps_tearing_from_remap) 675 { 676 struct vma_modifier_info *mod_info = self->mod_info; 677 678 struct line_content remapped_last_line; 679 struct line_content remapped_first_line; 680 struct line_content restored_last_line; 681 struct line_content restored_first_line; 682 683 wait_for_state(mod_info, SETUP_READY); 684 685 /* re-read the file to avoid using stale data from previous test */ 686 ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 687 688 mod_info->vma_modify = remap_vma; 689 mod_info->vma_restore = patch_vma; 690 mod_info->vma_mod_check = check_remap_result; 691 692 report_test_start("Tearing from remap", self->verbose); 693 ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line, 694 &restored_last_line, &restored_first_line)); 695 696 /* Now start concurrent modifications for self->duration_sec */ 697 signal_state(mod_info, TEST_READY); 698 699 struct line_content new_last_line; 700 struct line_content new_first_line; 701 struct timespec start_ts, end_ts; 702 703 clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 704 start_test_loop(&start_ts, self->verbose); 705 do { 706 ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 707 708 /* Check if we read vmas after remapping it */ 709 if (!strcmp(new_last_line.text, remapped_last_line.text)) { 710 /* 711 * The vmas should be consistent with remap results, 712 * however if the vma was concurrently restored, it 713 * can be reported twice (first as split one, then 714 * as restored one) because we found it as the next vma 715 * again. In that case new first line will be the same 716 * as the last restored line. 717 */ 718 ASSERT_FALSE(print_boundaries_on( 719 strcmp(new_first_line.text, remapped_first_line.text) && 720 strcmp(new_first_line.text, restored_last_line.text), 721 "Remap result invalid", self)); 722 } else { 723 /* The vmas should be consistent with the original/resored state */ 724 ASSERT_FALSE(print_boundaries_on( 725 strcmp(new_last_line.text, restored_last_line.text), 726 "Remap restore result invalid", self)); 727 ASSERT_FALSE(print_boundaries_on( 728 strcmp(new_first_line.text, restored_first_line.text), 729 "Remap restore result invalid", self)); 730 } 731 732 clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 733 end_test_iteration(&end_ts, self->verbose); 734 } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 735 end_test_loop(self->verbose); 736 737 /* Signal the modifyer thread to stop and wait until it exits */ 738 signal_state(mod_info, TEST_DONE); 739 } 740 741 TEST_HARNESS_MAIN 742