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