1beb69e81SSuren Baghdasaryan // SPDX-License-Identifier: GPL-2.0-only 2beb69e81SSuren Baghdasaryan /* 3beb69e81SSuren Baghdasaryan * Copyright 2022 Google LLC. 4beb69e81SSuren Baghdasaryan * Author: Suren Baghdasaryan <surenb@google.com> 5beb69e81SSuren Baghdasaryan * 6beb69e81SSuren Baghdasaryan * Permission to use, copy, modify, and distribute this software for any 7beb69e81SSuren Baghdasaryan * purpose with or without fee is hereby granted, provided that the above 8beb69e81SSuren Baghdasaryan * copyright notice and this permission notice appear in all copies. 9beb69e81SSuren Baghdasaryan * 10beb69e81SSuren Baghdasaryan * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11beb69e81SSuren Baghdasaryan * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12beb69e81SSuren Baghdasaryan * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13beb69e81SSuren Baghdasaryan * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14beb69e81SSuren Baghdasaryan * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15beb69e81SSuren Baghdasaryan * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16beb69e81SSuren Baghdasaryan * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17beb69e81SSuren Baghdasaryan */ 18beb69e81SSuren Baghdasaryan /* 19beb69e81SSuren Baghdasaryan * Fork a child that concurrently modifies address space while the main 20beb69e81SSuren Baghdasaryan * process is reading /proc/$PID/maps and verifying the results. Address 21beb69e81SSuren Baghdasaryan * space modifications include: 22beb69e81SSuren Baghdasaryan * VMA splitting and merging 23beb69e81SSuren Baghdasaryan * 24beb69e81SSuren Baghdasaryan */ 25beb69e81SSuren Baghdasaryan #define _GNU_SOURCE 26beb69e81SSuren Baghdasaryan #include "../kselftest_harness.h" 27beb69e81SSuren Baghdasaryan #include <errno.h> 28beb69e81SSuren Baghdasaryan #include <fcntl.h> 29beb69e81SSuren Baghdasaryan #include <pthread.h> 30beb69e81SSuren Baghdasaryan #include <stdbool.h> 31beb69e81SSuren Baghdasaryan #include <stdio.h> 32beb69e81SSuren Baghdasaryan #include <stdlib.h> 33beb69e81SSuren Baghdasaryan #include <string.h> 34beb69e81SSuren Baghdasaryan #include <unistd.h> 35beb69e81SSuren Baghdasaryan #include <sys/mman.h> 36beb69e81SSuren Baghdasaryan #include <sys/stat.h> 37beb69e81SSuren Baghdasaryan #include <sys/types.h> 38beb69e81SSuren Baghdasaryan #include <sys/wait.h> 39beb69e81SSuren Baghdasaryan 40beb69e81SSuren Baghdasaryan /* /proc/pid/maps parsing routines */ 41beb69e81SSuren Baghdasaryan struct page_content { 42beb69e81SSuren Baghdasaryan char *data; 43beb69e81SSuren Baghdasaryan ssize_t size; 44beb69e81SSuren Baghdasaryan }; 45beb69e81SSuren Baghdasaryan 46beb69e81SSuren Baghdasaryan #define LINE_MAX_SIZE 256 47beb69e81SSuren Baghdasaryan 48beb69e81SSuren Baghdasaryan struct line_content { 49beb69e81SSuren Baghdasaryan char text[LINE_MAX_SIZE]; 50beb69e81SSuren Baghdasaryan unsigned long start_addr; 51beb69e81SSuren Baghdasaryan unsigned long end_addr; 52beb69e81SSuren Baghdasaryan }; 53beb69e81SSuren Baghdasaryan 54beb69e81SSuren Baghdasaryan enum test_state { 55beb69e81SSuren Baghdasaryan INIT, 56beb69e81SSuren Baghdasaryan CHILD_READY, 57beb69e81SSuren Baghdasaryan PARENT_READY, 58beb69e81SSuren Baghdasaryan SETUP_READY, 59beb69e81SSuren Baghdasaryan SETUP_MODIFY_MAPS, 60beb69e81SSuren Baghdasaryan SETUP_MAPS_MODIFIED, 61beb69e81SSuren Baghdasaryan SETUP_RESTORE_MAPS, 62beb69e81SSuren Baghdasaryan SETUP_MAPS_RESTORED, 63beb69e81SSuren Baghdasaryan TEST_READY, 64beb69e81SSuren Baghdasaryan TEST_DONE, 65beb69e81SSuren Baghdasaryan }; 66beb69e81SSuren Baghdasaryan 67beb69e81SSuren Baghdasaryan struct vma_modifier_info; 68beb69e81SSuren Baghdasaryan 69beb69e81SSuren Baghdasaryan FIXTURE(proc_maps_race) 70beb69e81SSuren Baghdasaryan { 71beb69e81SSuren Baghdasaryan struct vma_modifier_info *mod_info; 72beb69e81SSuren Baghdasaryan struct page_content page1; 73beb69e81SSuren Baghdasaryan struct page_content page2; 74beb69e81SSuren Baghdasaryan struct line_content last_line; 75beb69e81SSuren Baghdasaryan struct line_content first_line; 76beb69e81SSuren Baghdasaryan unsigned long duration_sec; 77beb69e81SSuren Baghdasaryan int shared_mem_size; 78beb69e81SSuren Baghdasaryan int page_size; 79beb69e81SSuren Baghdasaryan int vma_count; 80aadc099cSSuren Baghdasaryan bool verbose; 81beb69e81SSuren Baghdasaryan int maps_fd; 82beb69e81SSuren Baghdasaryan pid_t pid; 83beb69e81SSuren Baghdasaryan }; 84beb69e81SSuren Baghdasaryan 85beb69e81SSuren Baghdasaryan typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self); 86beb69e81SSuren Baghdasaryan typedef bool (*vma_mod_result_check_op)(struct line_content *mod_last_line, 87beb69e81SSuren Baghdasaryan struct line_content *mod_first_line, 88beb69e81SSuren Baghdasaryan struct line_content *restored_last_line, 89beb69e81SSuren Baghdasaryan struct line_content *restored_first_line); 90beb69e81SSuren Baghdasaryan 91beb69e81SSuren Baghdasaryan struct vma_modifier_info { 92beb69e81SSuren Baghdasaryan int vma_count; 93beb69e81SSuren Baghdasaryan void *addr; 94beb69e81SSuren Baghdasaryan int prot; 95beb69e81SSuren Baghdasaryan void *next_addr; 96beb69e81SSuren Baghdasaryan vma_modifier_op vma_modify; 97beb69e81SSuren Baghdasaryan vma_modifier_op vma_restore; 98beb69e81SSuren Baghdasaryan vma_mod_result_check_op vma_mod_check; 99beb69e81SSuren Baghdasaryan pthread_mutex_t sync_lock; 100beb69e81SSuren Baghdasaryan pthread_cond_t sync_cond; 101beb69e81SSuren Baghdasaryan enum test_state curr_state; 102beb69e81SSuren Baghdasaryan bool exit; 103beb69e81SSuren Baghdasaryan void *child_mapped_addr[]; 104beb69e81SSuren Baghdasaryan }; 105beb69e81SSuren Baghdasaryan 106beb69e81SSuren Baghdasaryan 107beb69e81SSuren Baghdasaryan static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self) 108beb69e81SSuren Baghdasaryan { 109beb69e81SSuren Baghdasaryan ssize_t bytes_read; 110beb69e81SSuren Baghdasaryan 111beb69e81SSuren Baghdasaryan if (lseek(self->maps_fd, 0, SEEK_SET) < 0) 112beb69e81SSuren Baghdasaryan return false; 113beb69e81SSuren Baghdasaryan 114beb69e81SSuren Baghdasaryan bytes_read = read(self->maps_fd, self->page1.data, self->page_size); 115beb69e81SSuren Baghdasaryan if (bytes_read <= 0) 116beb69e81SSuren Baghdasaryan return false; 117beb69e81SSuren Baghdasaryan 118beb69e81SSuren Baghdasaryan self->page1.size = bytes_read; 119beb69e81SSuren Baghdasaryan 120beb69e81SSuren Baghdasaryan bytes_read = read(self->maps_fd, self->page2.data, self->page_size); 121beb69e81SSuren Baghdasaryan if (bytes_read <= 0) 122beb69e81SSuren Baghdasaryan return false; 123beb69e81SSuren Baghdasaryan 124beb69e81SSuren Baghdasaryan self->page2.size = bytes_read; 125beb69e81SSuren Baghdasaryan 126beb69e81SSuren Baghdasaryan return true; 127beb69e81SSuren Baghdasaryan } 128beb69e81SSuren Baghdasaryan 129beb69e81SSuren Baghdasaryan static void copy_first_line(struct page_content *page, char *first_line) 130beb69e81SSuren Baghdasaryan { 131beb69e81SSuren Baghdasaryan char *pos = strchr(page->data, '\n'); 132beb69e81SSuren Baghdasaryan 133beb69e81SSuren Baghdasaryan strncpy(first_line, page->data, pos - page->data); 134beb69e81SSuren Baghdasaryan first_line[pos - page->data] = '\0'; 135beb69e81SSuren Baghdasaryan } 136beb69e81SSuren Baghdasaryan 137beb69e81SSuren Baghdasaryan static void copy_last_line(struct page_content *page, char *last_line) 138beb69e81SSuren Baghdasaryan { 139beb69e81SSuren Baghdasaryan /* Get the last line in the first page */ 140beb69e81SSuren Baghdasaryan const char *end = page->data + page->size - 1; 141beb69e81SSuren Baghdasaryan /* skip last newline */ 142beb69e81SSuren Baghdasaryan const char *pos = end - 1; 143beb69e81SSuren Baghdasaryan 144beb69e81SSuren Baghdasaryan /* search previous newline */ 145beb69e81SSuren Baghdasaryan while (pos[-1] != '\n') 146beb69e81SSuren Baghdasaryan pos--; 147beb69e81SSuren Baghdasaryan strncpy(last_line, pos, end - pos); 148beb69e81SSuren Baghdasaryan last_line[end - pos] = '\0'; 149beb69e81SSuren Baghdasaryan } 150beb69e81SSuren Baghdasaryan 151beb69e81SSuren Baghdasaryan /* Read the last line of the first page and the first line of the second page */ 152beb69e81SSuren Baghdasaryan static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self, 153beb69e81SSuren Baghdasaryan struct line_content *last_line, 154beb69e81SSuren Baghdasaryan struct line_content *first_line) 155beb69e81SSuren Baghdasaryan { 156beb69e81SSuren Baghdasaryan if (!read_two_pages(self)) 157beb69e81SSuren Baghdasaryan return false; 158beb69e81SSuren Baghdasaryan 159beb69e81SSuren Baghdasaryan copy_last_line(&self->page1, last_line->text); 160beb69e81SSuren Baghdasaryan copy_first_line(&self->page2, first_line->text); 161beb69e81SSuren Baghdasaryan 162beb69e81SSuren Baghdasaryan return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr, 163beb69e81SSuren Baghdasaryan &last_line->end_addr) == 2 && 164beb69e81SSuren Baghdasaryan sscanf(first_line->text, "%lx-%lx", &first_line->start_addr, 165beb69e81SSuren Baghdasaryan &first_line->end_addr) == 2; 166beb69e81SSuren Baghdasaryan } 167beb69e81SSuren Baghdasaryan 168beb69e81SSuren Baghdasaryan /* Thread synchronization routines */ 169beb69e81SSuren Baghdasaryan static void wait_for_state(struct vma_modifier_info *mod_info, enum test_state state) 170beb69e81SSuren Baghdasaryan { 171beb69e81SSuren Baghdasaryan pthread_mutex_lock(&mod_info->sync_lock); 172beb69e81SSuren Baghdasaryan while (mod_info->curr_state != state) 173beb69e81SSuren Baghdasaryan pthread_cond_wait(&mod_info->sync_cond, &mod_info->sync_lock); 174beb69e81SSuren Baghdasaryan pthread_mutex_unlock(&mod_info->sync_lock); 175beb69e81SSuren Baghdasaryan } 176beb69e81SSuren Baghdasaryan 177beb69e81SSuren Baghdasaryan static void signal_state(struct vma_modifier_info *mod_info, enum test_state state) 178beb69e81SSuren Baghdasaryan { 179beb69e81SSuren Baghdasaryan pthread_mutex_lock(&mod_info->sync_lock); 180beb69e81SSuren Baghdasaryan mod_info->curr_state = state; 181beb69e81SSuren Baghdasaryan pthread_cond_signal(&mod_info->sync_cond); 182beb69e81SSuren Baghdasaryan pthread_mutex_unlock(&mod_info->sync_lock); 183beb69e81SSuren Baghdasaryan } 184beb69e81SSuren Baghdasaryan 185beb69e81SSuren Baghdasaryan static void stop_vma_modifier(struct vma_modifier_info *mod_info) 186beb69e81SSuren Baghdasaryan { 187beb69e81SSuren Baghdasaryan wait_for_state(mod_info, SETUP_READY); 188beb69e81SSuren Baghdasaryan mod_info->exit = true; 189beb69e81SSuren Baghdasaryan signal_state(mod_info, SETUP_MODIFY_MAPS); 190beb69e81SSuren Baghdasaryan } 191beb69e81SSuren Baghdasaryan 192aadc099cSSuren Baghdasaryan static void print_first_lines(char *text, int nr) 193aadc099cSSuren Baghdasaryan { 194aadc099cSSuren Baghdasaryan const char *end = text; 195aadc099cSSuren Baghdasaryan 196aadc099cSSuren Baghdasaryan while (nr && (end = strchr(end, '\n')) != NULL) { 197aadc099cSSuren Baghdasaryan nr--; 198aadc099cSSuren Baghdasaryan end++; 199aadc099cSSuren Baghdasaryan } 200aadc099cSSuren Baghdasaryan 201aadc099cSSuren Baghdasaryan if (end) { 202aadc099cSSuren Baghdasaryan int offs = end - text; 203aadc099cSSuren Baghdasaryan 204aadc099cSSuren Baghdasaryan text[offs] = '\0'; 205*ab5ac789SSukrut Heroorkar printf("%s", text); 206aadc099cSSuren Baghdasaryan text[offs] = '\n'; 207aadc099cSSuren Baghdasaryan printf("\n"); 208aadc099cSSuren Baghdasaryan } else { 209*ab5ac789SSukrut Heroorkar printf("%s", text); 210aadc099cSSuren Baghdasaryan } 211aadc099cSSuren Baghdasaryan } 212aadc099cSSuren Baghdasaryan 213aadc099cSSuren Baghdasaryan static void print_last_lines(char *text, int nr) 214aadc099cSSuren Baghdasaryan { 215aadc099cSSuren Baghdasaryan const char *start = text + strlen(text); 216aadc099cSSuren Baghdasaryan 217aadc099cSSuren Baghdasaryan nr++; /* to ignore the last newline */ 218aadc099cSSuren Baghdasaryan while (nr) { 219aadc099cSSuren Baghdasaryan while (start > text && *start != '\n') 220aadc099cSSuren Baghdasaryan start--; 221aadc099cSSuren Baghdasaryan nr--; 222aadc099cSSuren Baghdasaryan start--; 223aadc099cSSuren Baghdasaryan } 224*ab5ac789SSukrut Heroorkar printf("%s", start); 225aadc099cSSuren Baghdasaryan } 226aadc099cSSuren Baghdasaryan 227aadc099cSSuren Baghdasaryan static void print_boundaries(const char *title, FIXTURE_DATA(proc_maps_race) *self) 228aadc099cSSuren Baghdasaryan { 229aadc099cSSuren Baghdasaryan if (!self->verbose) 230aadc099cSSuren Baghdasaryan return; 231aadc099cSSuren Baghdasaryan 232aadc099cSSuren Baghdasaryan printf("%s", title); 233aadc099cSSuren Baghdasaryan /* Print 3 boundary lines from each page */ 234aadc099cSSuren Baghdasaryan print_last_lines(self->page1.data, 3); 235aadc099cSSuren Baghdasaryan printf("-----------------page boundary-----------------\n"); 236aadc099cSSuren Baghdasaryan print_first_lines(self->page2.data, 3); 237aadc099cSSuren Baghdasaryan } 238aadc099cSSuren Baghdasaryan 239aadc099cSSuren Baghdasaryan static bool print_boundaries_on(bool condition, const char *title, 240aadc099cSSuren Baghdasaryan FIXTURE_DATA(proc_maps_race) *self) 241aadc099cSSuren Baghdasaryan { 242aadc099cSSuren Baghdasaryan if (self->verbose && condition) 243aadc099cSSuren Baghdasaryan print_boundaries(title, self); 244aadc099cSSuren Baghdasaryan 245aadc099cSSuren Baghdasaryan return condition; 246aadc099cSSuren Baghdasaryan } 247aadc099cSSuren Baghdasaryan 248aadc099cSSuren Baghdasaryan static void report_test_start(const char *name, bool verbose) 249aadc099cSSuren Baghdasaryan { 250aadc099cSSuren Baghdasaryan if (verbose) 251aadc099cSSuren Baghdasaryan printf("==== %s ====\n", name); 252aadc099cSSuren Baghdasaryan } 253aadc099cSSuren Baghdasaryan 254aadc099cSSuren Baghdasaryan static struct timespec print_ts; 255aadc099cSSuren Baghdasaryan 256aadc099cSSuren Baghdasaryan static void start_test_loop(struct timespec *ts, bool verbose) 257aadc099cSSuren Baghdasaryan { 258aadc099cSSuren Baghdasaryan if (verbose) 259aadc099cSSuren Baghdasaryan print_ts.tv_sec = ts->tv_sec; 260aadc099cSSuren Baghdasaryan } 261aadc099cSSuren Baghdasaryan 262aadc099cSSuren Baghdasaryan static void end_test_iteration(struct timespec *ts, bool verbose) 263aadc099cSSuren Baghdasaryan { 264aadc099cSSuren Baghdasaryan if (!verbose) 265aadc099cSSuren Baghdasaryan return; 266aadc099cSSuren Baghdasaryan 267aadc099cSSuren Baghdasaryan /* Update every second */ 268aadc099cSSuren Baghdasaryan if (print_ts.tv_sec == ts->tv_sec) 269aadc099cSSuren Baghdasaryan return; 270aadc099cSSuren Baghdasaryan 271aadc099cSSuren Baghdasaryan printf("."); 272aadc099cSSuren Baghdasaryan fflush(stdout); 273aadc099cSSuren Baghdasaryan print_ts.tv_sec = ts->tv_sec; 274aadc099cSSuren Baghdasaryan } 275aadc099cSSuren Baghdasaryan 276aadc099cSSuren Baghdasaryan static void end_test_loop(bool verbose) 277aadc099cSSuren Baghdasaryan { 278aadc099cSSuren Baghdasaryan if (verbose) 279aadc099cSSuren Baghdasaryan printf("\n"); 280aadc099cSSuren Baghdasaryan } 281aadc099cSSuren Baghdasaryan 282beb69e81SSuren Baghdasaryan static bool capture_mod_pattern(FIXTURE_DATA(proc_maps_race) *self, 283beb69e81SSuren Baghdasaryan struct line_content *mod_last_line, 284beb69e81SSuren Baghdasaryan struct line_content *mod_first_line, 285beb69e81SSuren Baghdasaryan struct line_content *restored_last_line, 286beb69e81SSuren Baghdasaryan struct line_content *restored_first_line) 287beb69e81SSuren Baghdasaryan { 288aadc099cSSuren Baghdasaryan print_boundaries("Before modification", self); 289aadc099cSSuren Baghdasaryan 290beb69e81SSuren Baghdasaryan signal_state(self->mod_info, SETUP_MODIFY_MAPS); 291beb69e81SSuren Baghdasaryan wait_for_state(self->mod_info, SETUP_MAPS_MODIFIED); 292beb69e81SSuren Baghdasaryan 293beb69e81SSuren Baghdasaryan /* Copy last line of the first page and first line of the last page */ 294beb69e81SSuren Baghdasaryan if (!read_boundary_lines(self, mod_last_line, mod_first_line)) 295beb69e81SSuren Baghdasaryan return false; 296beb69e81SSuren Baghdasaryan 297aadc099cSSuren Baghdasaryan print_boundaries("After modification", self); 298aadc099cSSuren Baghdasaryan 299beb69e81SSuren Baghdasaryan signal_state(self->mod_info, SETUP_RESTORE_MAPS); 300beb69e81SSuren Baghdasaryan wait_for_state(self->mod_info, SETUP_MAPS_RESTORED); 301beb69e81SSuren Baghdasaryan 302beb69e81SSuren Baghdasaryan /* Copy last line of the first page and first line of the last page */ 303beb69e81SSuren Baghdasaryan if (!read_boundary_lines(self, restored_last_line, restored_first_line)) 304beb69e81SSuren Baghdasaryan return false; 305beb69e81SSuren Baghdasaryan 306aadc099cSSuren Baghdasaryan print_boundaries("After restore", self); 307aadc099cSSuren Baghdasaryan 308beb69e81SSuren Baghdasaryan if (!self->mod_info->vma_mod_check(mod_last_line, mod_first_line, 309beb69e81SSuren Baghdasaryan restored_last_line, restored_first_line)) 310beb69e81SSuren Baghdasaryan return false; 311beb69e81SSuren Baghdasaryan 312beb69e81SSuren Baghdasaryan /* 313beb69e81SSuren Baghdasaryan * The content of these lines after modify+resore should be the same 314beb69e81SSuren Baghdasaryan * as the original. 315beb69e81SSuren Baghdasaryan */ 316beb69e81SSuren Baghdasaryan return strcmp(restored_last_line->text, self->last_line.text) == 0 && 317beb69e81SSuren Baghdasaryan strcmp(restored_first_line->text, self->first_line.text) == 0; 318beb69e81SSuren Baghdasaryan } 319beb69e81SSuren Baghdasaryan 320beb69e81SSuren Baghdasaryan static inline bool split_vma(FIXTURE_DATA(proc_maps_race) *self) 321beb69e81SSuren Baghdasaryan { 322beb69e81SSuren Baghdasaryan return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot | PROT_EXEC, 323beb69e81SSuren Baghdasaryan MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 324beb69e81SSuren Baghdasaryan } 325beb69e81SSuren Baghdasaryan 326beb69e81SSuren Baghdasaryan static inline bool merge_vma(FIXTURE_DATA(proc_maps_race) *self) 327beb69e81SSuren Baghdasaryan { 328beb69e81SSuren Baghdasaryan return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot, 329beb69e81SSuren Baghdasaryan MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 330beb69e81SSuren Baghdasaryan } 331beb69e81SSuren Baghdasaryan 332beb69e81SSuren Baghdasaryan static inline bool check_split_result(struct line_content *mod_last_line, 333beb69e81SSuren Baghdasaryan struct line_content *mod_first_line, 334beb69e81SSuren Baghdasaryan struct line_content *restored_last_line, 335beb69e81SSuren Baghdasaryan struct line_content *restored_first_line) 336beb69e81SSuren Baghdasaryan { 337beb69e81SSuren Baghdasaryan /* Make sure vmas at the boundaries are changing */ 338beb69e81SSuren Baghdasaryan return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 339beb69e81SSuren Baghdasaryan strcmp(mod_first_line->text, restored_first_line->text) != 0; 340beb69e81SSuren Baghdasaryan } 341beb69e81SSuren Baghdasaryan 342b11d9e2dSSuren Baghdasaryan static inline bool shrink_vma(FIXTURE_DATA(proc_maps_race) *self) 343b11d9e2dSSuren Baghdasaryan { 344b11d9e2dSSuren Baghdasaryan return mremap(self->mod_info->addr, self->page_size * 3, 345b11d9e2dSSuren Baghdasaryan self->page_size, 0) != MAP_FAILED; 346b11d9e2dSSuren Baghdasaryan } 347b11d9e2dSSuren Baghdasaryan 348b11d9e2dSSuren Baghdasaryan static inline bool expand_vma(FIXTURE_DATA(proc_maps_race) *self) 349b11d9e2dSSuren Baghdasaryan { 350b11d9e2dSSuren Baghdasaryan return mremap(self->mod_info->addr, self->page_size, 351b11d9e2dSSuren Baghdasaryan self->page_size * 3, 0) != MAP_FAILED; 352b11d9e2dSSuren Baghdasaryan } 353b11d9e2dSSuren Baghdasaryan 354b11d9e2dSSuren Baghdasaryan static inline bool check_shrink_result(struct line_content *mod_last_line, 355b11d9e2dSSuren Baghdasaryan struct line_content *mod_first_line, 356b11d9e2dSSuren Baghdasaryan struct line_content *restored_last_line, 357b11d9e2dSSuren Baghdasaryan struct line_content *restored_first_line) 358b11d9e2dSSuren Baghdasaryan { 359b11d9e2dSSuren Baghdasaryan /* Make sure only the last vma of the first page is changing */ 360b11d9e2dSSuren Baghdasaryan return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 361b11d9e2dSSuren Baghdasaryan strcmp(mod_first_line->text, restored_first_line->text) == 0; 362b11d9e2dSSuren Baghdasaryan } 363b11d9e2dSSuren Baghdasaryan 3646a45336bSSuren Baghdasaryan static inline bool remap_vma(FIXTURE_DATA(proc_maps_race) *self) 3656a45336bSSuren Baghdasaryan { 3666a45336bSSuren Baghdasaryan /* 3676a45336bSSuren Baghdasaryan * Remap the last page of the next vma into the middle of the vma. 3686a45336bSSuren Baghdasaryan * This splits the current vma and the first and middle parts (the 3696a45336bSSuren Baghdasaryan * parts at lower addresses) become the last vma objserved in the 3706a45336bSSuren Baghdasaryan * first page and the first vma observed in the last page. 3716a45336bSSuren Baghdasaryan */ 3726a45336bSSuren Baghdasaryan return mremap(self->mod_info->next_addr + self->page_size * 2, self->page_size, 3736a45336bSSuren Baghdasaryan self->page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP, 3746a45336bSSuren Baghdasaryan self->mod_info->addr + self->page_size) != MAP_FAILED; 3756a45336bSSuren Baghdasaryan } 3766a45336bSSuren Baghdasaryan 3776a45336bSSuren Baghdasaryan static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self) 3786a45336bSSuren Baghdasaryan { 3796a45336bSSuren Baghdasaryan return mprotect(self->mod_info->addr + self->page_size, self->page_size, 3806a45336bSSuren Baghdasaryan self->mod_info->prot) == 0; 3816a45336bSSuren Baghdasaryan } 3826a45336bSSuren Baghdasaryan 3836a45336bSSuren Baghdasaryan static inline bool check_remap_result(struct line_content *mod_last_line, 3846a45336bSSuren Baghdasaryan struct line_content *mod_first_line, 3856a45336bSSuren Baghdasaryan struct line_content *restored_last_line, 3866a45336bSSuren Baghdasaryan struct line_content *restored_first_line) 3876a45336bSSuren Baghdasaryan { 3886a45336bSSuren Baghdasaryan /* Make sure vmas at the boundaries are changing */ 3896a45336bSSuren Baghdasaryan return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 3906a45336bSSuren Baghdasaryan strcmp(mod_first_line->text, restored_first_line->text) != 0; 3916a45336bSSuren Baghdasaryan } 3926a45336bSSuren Baghdasaryan 393beb69e81SSuren Baghdasaryan FIXTURE_SETUP(proc_maps_race) 394beb69e81SSuren Baghdasaryan { 395aadc099cSSuren Baghdasaryan const char *verbose = getenv("VERBOSE"); 396beb69e81SSuren Baghdasaryan const char *duration = getenv("DURATION"); 397beb69e81SSuren Baghdasaryan struct vma_modifier_info *mod_info; 398beb69e81SSuren Baghdasaryan pthread_mutexattr_t mutex_attr; 399beb69e81SSuren Baghdasaryan pthread_condattr_t cond_attr; 400beb69e81SSuren Baghdasaryan unsigned long duration_sec; 401beb69e81SSuren Baghdasaryan char fname[32]; 402beb69e81SSuren Baghdasaryan 403beb69e81SSuren Baghdasaryan self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); 404aadc099cSSuren Baghdasaryan self->verbose = verbose && !strncmp(verbose, "1", 1); 405beb69e81SSuren Baghdasaryan duration_sec = duration ? atol(duration) : 0; 406beb69e81SSuren Baghdasaryan self->duration_sec = duration_sec ? duration_sec : 5UL; 407beb69e81SSuren Baghdasaryan 408beb69e81SSuren Baghdasaryan /* 409beb69e81SSuren Baghdasaryan * Have to map enough vmas for /proc/pid/maps to contain more than one 410beb69e81SSuren Baghdasaryan * page worth of vmas. Assume at least 32 bytes per line in maps output 411beb69e81SSuren Baghdasaryan */ 412beb69e81SSuren Baghdasaryan self->vma_count = self->page_size / 32 + 1; 413beb69e81SSuren Baghdasaryan self->shared_mem_size = sizeof(struct vma_modifier_info) + self->vma_count * sizeof(void *); 414beb69e81SSuren Baghdasaryan 415beb69e81SSuren Baghdasaryan /* map shared memory for communication with the child process */ 416beb69e81SSuren Baghdasaryan self->mod_info = (struct vma_modifier_info *)mmap(NULL, self->shared_mem_size, 417beb69e81SSuren Baghdasaryan PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 418beb69e81SSuren Baghdasaryan ASSERT_NE(self->mod_info, MAP_FAILED); 419beb69e81SSuren Baghdasaryan mod_info = self->mod_info; 420beb69e81SSuren Baghdasaryan 421beb69e81SSuren Baghdasaryan /* Initialize shared members */ 422beb69e81SSuren Baghdasaryan pthread_mutexattr_init(&mutex_attr); 423beb69e81SSuren Baghdasaryan pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); 424beb69e81SSuren Baghdasaryan ASSERT_EQ(pthread_mutex_init(&mod_info->sync_lock, &mutex_attr), 0); 425beb69e81SSuren Baghdasaryan pthread_condattr_init(&cond_attr); 426beb69e81SSuren Baghdasaryan pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); 427beb69e81SSuren Baghdasaryan ASSERT_EQ(pthread_cond_init(&mod_info->sync_cond, &cond_attr), 0); 428beb69e81SSuren Baghdasaryan mod_info->vma_count = self->vma_count; 429beb69e81SSuren Baghdasaryan mod_info->curr_state = INIT; 430beb69e81SSuren Baghdasaryan mod_info->exit = false; 431beb69e81SSuren Baghdasaryan 432beb69e81SSuren Baghdasaryan self->pid = fork(); 433beb69e81SSuren Baghdasaryan if (!self->pid) { 434beb69e81SSuren Baghdasaryan /* Child process modifying the address space */ 435beb69e81SSuren Baghdasaryan int prot = PROT_READ | PROT_WRITE; 436beb69e81SSuren Baghdasaryan int i; 437beb69e81SSuren Baghdasaryan 438beb69e81SSuren Baghdasaryan for (i = 0; i < mod_info->vma_count; i++) { 439beb69e81SSuren Baghdasaryan mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot, 440beb69e81SSuren Baghdasaryan MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 441beb69e81SSuren Baghdasaryan ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED); 442beb69e81SSuren Baghdasaryan /* change protection in adjacent maps to prevent merging */ 443beb69e81SSuren Baghdasaryan prot ^= PROT_WRITE; 444beb69e81SSuren Baghdasaryan } 445beb69e81SSuren Baghdasaryan signal_state(mod_info, CHILD_READY); 446beb69e81SSuren Baghdasaryan wait_for_state(mod_info, PARENT_READY); 447beb69e81SSuren Baghdasaryan while (true) { 448beb69e81SSuren Baghdasaryan signal_state(mod_info, SETUP_READY); 449beb69e81SSuren Baghdasaryan wait_for_state(mod_info, SETUP_MODIFY_MAPS); 450beb69e81SSuren Baghdasaryan if (mod_info->exit) 451beb69e81SSuren Baghdasaryan break; 452beb69e81SSuren Baghdasaryan 453beb69e81SSuren Baghdasaryan ASSERT_TRUE(mod_info->vma_modify(self)); 454beb69e81SSuren Baghdasaryan signal_state(mod_info, SETUP_MAPS_MODIFIED); 455beb69e81SSuren Baghdasaryan wait_for_state(mod_info, SETUP_RESTORE_MAPS); 456beb69e81SSuren Baghdasaryan ASSERT_TRUE(mod_info->vma_restore(self)); 457beb69e81SSuren Baghdasaryan signal_state(mod_info, SETUP_MAPS_RESTORED); 458beb69e81SSuren Baghdasaryan 459beb69e81SSuren Baghdasaryan wait_for_state(mod_info, TEST_READY); 460beb69e81SSuren Baghdasaryan while (mod_info->curr_state != TEST_DONE) { 461beb69e81SSuren Baghdasaryan ASSERT_TRUE(mod_info->vma_modify(self)); 462beb69e81SSuren Baghdasaryan ASSERT_TRUE(mod_info->vma_restore(self)); 463beb69e81SSuren Baghdasaryan } 464beb69e81SSuren Baghdasaryan } 465beb69e81SSuren Baghdasaryan for (i = 0; i < mod_info->vma_count; i++) 466beb69e81SSuren Baghdasaryan munmap(mod_info->child_mapped_addr[i], self->page_size * 3); 467beb69e81SSuren Baghdasaryan 468beb69e81SSuren Baghdasaryan exit(0); 469beb69e81SSuren Baghdasaryan } 470beb69e81SSuren Baghdasaryan 471beb69e81SSuren Baghdasaryan sprintf(fname, "/proc/%d/maps", self->pid); 472beb69e81SSuren Baghdasaryan self->maps_fd = open(fname, O_RDONLY); 473beb69e81SSuren Baghdasaryan ASSERT_NE(self->maps_fd, -1); 474beb69e81SSuren Baghdasaryan 475beb69e81SSuren Baghdasaryan /* Wait for the child to map the VMAs */ 476beb69e81SSuren Baghdasaryan wait_for_state(mod_info, CHILD_READY); 477beb69e81SSuren Baghdasaryan 478beb69e81SSuren Baghdasaryan /* Read first two pages */ 479beb69e81SSuren Baghdasaryan self->page1.data = malloc(self->page_size); 480beb69e81SSuren Baghdasaryan ASSERT_NE(self->page1.data, NULL); 481beb69e81SSuren Baghdasaryan self->page2.data = malloc(self->page_size); 482beb69e81SSuren Baghdasaryan ASSERT_NE(self->page2.data, NULL); 483beb69e81SSuren Baghdasaryan 484beb69e81SSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 485beb69e81SSuren Baghdasaryan 486beb69e81SSuren Baghdasaryan /* 487beb69e81SSuren Baghdasaryan * Find the addresses corresponding to the last line in the first page 488beb69e81SSuren Baghdasaryan * and the first line in the last page. 489beb69e81SSuren Baghdasaryan */ 490beb69e81SSuren Baghdasaryan mod_info->addr = NULL; 491beb69e81SSuren Baghdasaryan mod_info->next_addr = NULL; 492beb69e81SSuren Baghdasaryan for (int i = 0; i < mod_info->vma_count; i++) { 493beb69e81SSuren Baghdasaryan if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) { 494beb69e81SSuren Baghdasaryan mod_info->addr = mod_info->child_mapped_addr[i]; 495beb69e81SSuren Baghdasaryan mod_info->prot = PROT_READ; 496beb69e81SSuren Baghdasaryan /* Even VMAs have write permission */ 497beb69e81SSuren Baghdasaryan if ((i % 2) == 0) 498beb69e81SSuren Baghdasaryan mod_info->prot |= PROT_WRITE; 499beb69e81SSuren Baghdasaryan } else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) { 500beb69e81SSuren Baghdasaryan mod_info->next_addr = mod_info->child_mapped_addr[i]; 501beb69e81SSuren Baghdasaryan } 502beb69e81SSuren Baghdasaryan 503beb69e81SSuren Baghdasaryan if (mod_info->addr && mod_info->next_addr) 504beb69e81SSuren Baghdasaryan break; 505beb69e81SSuren Baghdasaryan } 506beb69e81SSuren Baghdasaryan ASSERT_TRUE(mod_info->addr && mod_info->next_addr); 507beb69e81SSuren Baghdasaryan 508beb69e81SSuren Baghdasaryan signal_state(mod_info, PARENT_READY); 509beb69e81SSuren Baghdasaryan 510beb69e81SSuren Baghdasaryan } 511beb69e81SSuren Baghdasaryan 512beb69e81SSuren Baghdasaryan FIXTURE_TEARDOWN(proc_maps_race) 513beb69e81SSuren Baghdasaryan { 514beb69e81SSuren Baghdasaryan int status; 515beb69e81SSuren Baghdasaryan 516beb69e81SSuren Baghdasaryan stop_vma_modifier(self->mod_info); 517beb69e81SSuren Baghdasaryan 518beb69e81SSuren Baghdasaryan free(self->page2.data); 519beb69e81SSuren Baghdasaryan free(self->page1.data); 520beb69e81SSuren Baghdasaryan 521beb69e81SSuren Baghdasaryan for (int i = 0; i < self->vma_count; i++) 522beb69e81SSuren Baghdasaryan munmap(self->mod_info->child_mapped_addr[i], self->page_size); 523beb69e81SSuren Baghdasaryan close(self->maps_fd); 524beb69e81SSuren Baghdasaryan waitpid(self->pid, &status, 0); 525beb69e81SSuren Baghdasaryan munmap(self->mod_info, self->shared_mem_size); 526beb69e81SSuren Baghdasaryan } 527beb69e81SSuren Baghdasaryan 528beb69e81SSuren Baghdasaryan TEST_F(proc_maps_race, test_maps_tearing_from_split) 529beb69e81SSuren Baghdasaryan { 530beb69e81SSuren Baghdasaryan struct vma_modifier_info *mod_info = self->mod_info; 531beb69e81SSuren Baghdasaryan 532beb69e81SSuren Baghdasaryan struct line_content split_last_line; 533beb69e81SSuren Baghdasaryan struct line_content split_first_line; 534beb69e81SSuren Baghdasaryan struct line_content restored_last_line; 535beb69e81SSuren Baghdasaryan struct line_content restored_first_line; 536beb69e81SSuren Baghdasaryan 537beb69e81SSuren Baghdasaryan wait_for_state(mod_info, SETUP_READY); 538beb69e81SSuren Baghdasaryan 539beb69e81SSuren Baghdasaryan /* re-read the file to avoid using stale data from previous test */ 540beb69e81SSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 541beb69e81SSuren Baghdasaryan 542beb69e81SSuren Baghdasaryan mod_info->vma_modify = split_vma; 543beb69e81SSuren Baghdasaryan mod_info->vma_restore = merge_vma; 544beb69e81SSuren Baghdasaryan mod_info->vma_mod_check = check_split_result; 545beb69e81SSuren Baghdasaryan 546aadc099cSSuren Baghdasaryan report_test_start("Tearing from split", self->verbose); 547beb69e81SSuren Baghdasaryan ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line, 548beb69e81SSuren Baghdasaryan &restored_last_line, &restored_first_line)); 549beb69e81SSuren Baghdasaryan 550beb69e81SSuren Baghdasaryan /* Now start concurrent modifications for self->duration_sec */ 551beb69e81SSuren Baghdasaryan signal_state(mod_info, TEST_READY); 552beb69e81SSuren Baghdasaryan 553beb69e81SSuren Baghdasaryan struct line_content new_last_line; 554beb69e81SSuren Baghdasaryan struct line_content new_first_line; 555beb69e81SSuren Baghdasaryan struct timespec start_ts, end_ts; 556beb69e81SSuren Baghdasaryan 557beb69e81SSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 558aadc099cSSuren Baghdasaryan start_test_loop(&start_ts, self->verbose); 559beb69e81SSuren Baghdasaryan do { 560beb69e81SSuren Baghdasaryan bool last_line_changed; 561beb69e81SSuren Baghdasaryan bool first_line_changed; 562beb69e81SSuren Baghdasaryan 563beb69e81SSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 564beb69e81SSuren Baghdasaryan 565beb69e81SSuren Baghdasaryan /* Check if we read vmas after split */ 566beb69e81SSuren Baghdasaryan if (!strcmp(new_last_line.text, split_last_line.text)) { 567beb69e81SSuren Baghdasaryan /* 568beb69e81SSuren Baghdasaryan * The vmas should be consistent with split results, 569beb69e81SSuren Baghdasaryan * however if vma was concurrently restored after a 570beb69e81SSuren Baghdasaryan * split, it can be reported twice (first the original 571beb69e81SSuren Baghdasaryan * split one, then the same vma but extended after the 572beb69e81SSuren Baghdasaryan * merge) because we found it as the next vma again. 573beb69e81SSuren Baghdasaryan * In that case new first line will be the same as the 574beb69e81SSuren Baghdasaryan * last restored line. 575beb69e81SSuren Baghdasaryan */ 576aadc099cSSuren Baghdasaryan ASSERT_FALSE(print_boundaries_on( 577aadc099cSSuren Baghdasaryan strcmp(new_first_line.text, split_first_line.text) && 578aadc099cSSuren Baghdasaryan strcmp(new_first_line.text, restored_last_line.text), 579aadc099cSSuren Baghdasaryan "Split result invalid", self)); 580beb69e81SSuren Baghdasaryan } else { 581beb69e81SSuren Baghdasaryan /* The vmas should be consistent with merge results */ 582aadc099cSSuren Baghdasaryan ASSERT_FALSE(print_boundaries_on( 583aadc099cSSuren Baghdasaryan strcmp(new_last_line.text, restored_last_line.text), 584aadc099cSSuren Baghdasaryan "Merge result invalid", self)); 585aadc099cSSuren Baghdasaryan ASSERT_FALSE(print_boundaries_on( 586aadc099cSSuren Baghdasaryan strcmp(new_first_line.text, restored_first_line.text), 587aadc099cSSuren Baghdasaryan "Merge result invalid", self)); 588beb69e81SSuren Baghdasaryan } 589beb69e81SSuren Baghdasaryan /* 590beb69e81SSuren Baghdasaryan * First and last lines should change in unison. If the last 591beb69e81SSuren Baghdasaryan * line changed then the first line should change as well and 592beb69e81SSuren Baghdasaryan * vice versa. 593beb69e81SSuren Baghdasaryan */ 594beb69e81SSuren Baghdasaryan last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0; 595beb69e81SSuren Baghdasaryan first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0; 596beb69e81SSuren Baghdasaryan ASSERT_EQ(last_line_changed, first_line_changed); 597beb69e81SSuren Baghdasaryan 598beb69e81SSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 599aadc099cSSuren Baghdasaryan end_test_iteration(&end_ts, self->verbose); 600beb69e81SSuren Baghdasaryan } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 601aadc099cSSuren Baghdasaryan end_test_loop(self->verbose); 602beb69e81SSuren Baghdasaryan 603beb69e81SSuren Baghdasaryan /* Signal the modifyer thread to stop and wait until it exits */ 604beb69e81SSuren Baghdasaryan signal_state(mod_info, TEST_DONE); 605beb69e81SSuren Baghdasaryan } 606beb69e81SSuren Baghdasaryan 607b11d9e2dSSuren Baghdasaryan TEST_F(proc_maps_race, test_maps_tearing_from_resize) 608b11d9e2dSSuren Baghdasaryan { 609b11d9e2dSSuren Baghdasaryan struct vma_modifier_info *mod_info = self->mod_info; 610b11d9e2dSSuren Baghdasaryan 611b11d9e2dSSuren Baghdasaryan struct line_content shrunk_last_line; 612b11d9e2dSSuren Baghdasaryan struct line_content shrunk_first_line; 613b11d9e2dSSuren Baghdasaryan struct line_content restored_last_line; 614b11d9e2dSSuren Baghdasaryan struct line_content restored_first_line; 615b11d9e2dSSuren Baghdasaryan 616b11d9e2dSSuren Baghdasaryan wait_for_state(mod_info, SETUP_READY); 617b11d9e2dSSuren Baghdasaryan 618b11d9e2dSSuren Baghdasaryan /* re-read the file to avoid using stale data from previous test */ 619b11d9e2dSSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 620b11d9e2dSSuren Baghdasaryan 621b11d9e2dSSuren Baghdasaryan mod_info->vma_modify = shrink_vma; 622b11d9e2dSSuren Baghdasaryan mod_info->vma_restore = expand_vma; 623b11d9e2dSSuren Baghdasaryan mod_info->vma_mod_check = check_shrink_result; 624b11d9e2dSSuren Baghdasaryan 625aadc099cSSuren Baghdasaryan report_test_start("Tearing from resize", self->verbose); 626b11d9e2dSSuren Baghdasaryan ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line, 627b11d9e2dSSuren Baghdasaryan &restored_last_line, &restored_first_line)); 628b11d9e2dSSuren Baghdasaryan 629b11d9e2dSSuren Baghdasaryan /* Now start concurrent modifications for self->duration_sec */ 630b11d9e2dSSuren Baghdasaryan signal_state(mod_info, TEST_READY); 631b11d9e2dSSuren Baghdasaryan 632b11d9e2dSSuren Baghdasaryan struct line_content new_last_line; 633b11d9e2dSSuren Baghdasaryan struct line_content new_first_line; 634b11d9e2dSSuren Baghdasaryan struct timespec start_ts, end_ts; 635b11d9e2dSSuren Baghdasaryan 636b11d9e2dSSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 637aadc099cSSuren Baghdasaryan start_test_loop(&start_ts, self->verbose); 638b11d9e2dSSuren Baghdasaryan do { 639b11d9e2dSSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 640b11d9e2dSSuren Baghdasaryan 641b11d9e2dSSuren Baghdasaryan /* Check if we read vmas after shrinking it */ 642b11d9e2dSSuren Baghdasaryan if (!strcmp(new_last_line.text, shrunk_last_line.text)) { 643b11d9e2dSSuren Baghdasaryan /* 644b11d9e2dSSuren Baghdasaryan * The vmas should be consistent with shrunk results, 645b11d9e2dSSuren Baghdasaryan * however if the vma was concurrently restored, it 646b11d9e2dSSuren Baghdasaryan * can be reported twice (first as shrunk one, then 647b11d9e2dSSuren Baghdasaryan * as restored one) because we found it as the next vma 648b11d9e2dSSuren Baghdasaryan * again. In that case new first line will be the same 649b11d9e2dSSuren Baghdasaryan * as the last restored line. 650b11d9e2dSSuren Baghdasaryan */ 651aadc099cSSuren Baghdasaryan ASSERT_FALSE(print_boundaries_on( 652aadc099cSSuren Baghdasaryan strcmp(new_first_line.text, shrunk_first_line.text) && 653aadc099cSSuren Baghdasaryan strcmp(new_first_line.text, restored_last_line.text), 654aadc099cSSuren Baghdasaryan "Shrink result invalid", self)); 655b11d9e2dSSuren Baghdasaryan } else { 656b11d9e2dSSuren Baghdasaryan /* The vmas should be consistent with the original/resored state */ 657aadc099cSSuren Baghdasaryan ASSERT_FALSE(print_boundaries_on( 658aadc099cSSuren Baghdasaryan strcmp(new_last_line.text, restored_last_line.text), 659aadc099cSSuren Baghdasaryan "Expand result invalid", self)); 660aadc099cSSuren Baghdasaryan ASSERT_FALSE(print_boundaries_on( 661aadc099cSSuren Baghdasaryan strcmp(new_first_line.text, restored_first_line.text), 662aadc099cSSuren Baghdasaryan "Expand result invalid", self)); 663b11d9e2dSSuren Baghdasaryan } 664b11d9e2dSSuren Baghdasaryan 665b11d9e2dSSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 666aadc099cSSuren Baghdasaryan end_test_iteration(&end_ts, self->verbose); 667b11d9e2dSSuren Baghdasaryan } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 668aadc099cSSuren Baghdasaryan end_test_loop(self->verbose); 669b11d9e2dSSuren Baghdasaryan 670b11d9e2dSSuren Baghdasaryan /* Signal the modifyer thread to stop and wait until it exits */ 671b11d9e2dSSuren Baghdasaryan signal_state(mod_info, TEST_DONE); 672b11d9e2dSSuren Baghdasaryan } 673b11d9e2dSSuren Baghdasaryan 6746a45336bSSuren Baghdasaryan TEST_F(proc_maps_race, test_maps_tearing_from_remap) 6756a45336bSSuren Baghdasaryan { 6766a45336bSSuren Baghdasaryan struct vma_modifier_info *mod_info = self->mod_info; 6776a45336bSSuren Baghdasaryan 6786a45336bSSuren Baghdasaryan struct line_content remapped_last_line; 6796a45336bSSuren Baghdasaryan struct line_content remapped_first_line; 6806a45336bSSuren Baghdasaryan struct line_content restored_last_line; 6816a45336bSSuren Baghdasaryan struct line_content restored_first_line; 6826a45336bSSuren Baghdasaryan 6836a45336bSSuren Baghdasaryan wait_for_state(mod_info, SETUP_READY); 6846a45336bSSuren Baghdasaryan 6856a45336bSSuren Baghdasaryan /* re-read the file to avoid using stale data from previous test */ 6866a45336bSSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 6876a45336bSSuren Baghdasaryan 6886a45336bSSuren Baghdasaryan mod_info->vma_modify = remap_vma; 6896a45336bSSuren Baghdasaryan mod_info->vma_restore = patch_vma; 6906a45336bSSuren Baghdasaryan mod_info->vma_mod_check = check_remap_result; 6916a45336bSSuren Baghdasaryan 692aadc099cSSuren Baghdasaryan report_test_start("Tearing from remap", self->verbose); 6936a45336bSSuren Baghdasaryan ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line, 6946a45336bSSuren Baghdasaryan &restored_last_line, &restored_first_line)); 6956a45336bSSuren Baghdasaryan 6966a45336bSSuren Baghdasaryan /* Now start concurrent modifications for self->duration_sec */ 6976a45336bSSuren Baghdasaryan signal_state(mod_info, TEST_READY); 6986a45336bSSuren Baghdasaryan 6996a45336bSSuren Baghdasaryan struct line_content new_last_line; 7006a45336bSSuren Baghdasaryan struct line_content new_first_line; 7016a45336bSSuren Baghdasaryan struct timespec start_ts, end_ts; 7026a45336bSSuren Baghdasaryan 7036a45336bSSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 704aadc099cSSuren Baghdasaryan start_test_loop(&start_ts, self->verbose); 7056a45336bSSuren Baghdasaryan do { 7066a45336bSSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 7076a45336bSSuren Baghdasaryan 7086a45336bSSuren Baghdasaryan /* Check if we read vmas after remapping it */ 7096a45336bSSuren Baghdasaryan if (!strcmp(new_last_line.text, remapped_last_line.text)) { 7106a45336bSSuren Baghdasaryan /* 7116a45336bSSuren Baghdasaryan * The vmas should be consistent with remap results, 7126a45336bSSuren Baghdasaryan * however if the vma was concurrently restored, it 7136a45336bSSuren Baghdasaryan * can be reported twice (first as split one, then 7146a45336bSSuren Baghdasaryan * as restored one) because we found it as the next vma 7156a45336bSSuren Baghdasaryan * again. In that case new first line will be the same 7166a45336bSSuren Baghdasaryan * as the last restored line. 7176a45336bSSuren Baghdasaryan */ 718aadc099cSSuren Baghdasaryan ASSERT_FALSE(print_boundaries_on( 719aadc099cSSuren Baghdasaryan strcmp(new_first_line.text, remapped_first_line.text) && 720aadc099cSSuren Baghdasaryan strcmp(new_first_line.text, restored_last_line.text), 721aadc099cSSuren Baghdasaryan "Remap result invalid", self)); 7226a45336bSSuren Baghdasaryan } else { 7236a45336bSSuren Baghdasaryan /* The vmas should be consistent with the original/resored state */ 724aadc099cSSuren Baghdasaryan ASSERT_FALSE(print_boundaries_on( 725aadc099cSSuren Baghdasaryan strcmp(new_last_line.text, restored_last_line.text), 726aadc099cSSuren Baghdasaryan "Remap restore result invalid", self)); 727aadc099cSSuren Baghdasaryan ASSERT_FALSE(print_boundaries_on( 728aadc099cSSuren Baghdasaryan strcmp(new_first_line.text, restored_first_line.text), 729aadc099cSSuren Baghdasaryan "Remap restore result invalid", self)); 7306a45336bSSuren Baghdasaryan } 7316a45336bSSuren Baghdasaryan 7326a45336bSSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 733aadc099cSSuren Baghdasaryan end_test_iteration(&end_ts, self->verbose); 7346a45336bSSuren Baghdasaryan } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 735aadc099cSSuren Baghdasaryan end_test_loop(self->verbose); 7366a45336bSSuren Baghdasaryan 7376a45336bSSuren Baghdasaryan /* Signal the modifyer thread to stop and wait until it exits */ 7386a45336bSSuren Baghdasaryan signal_state(mod_info, TEST_DONE); 7396a45336bSSuren Baghdasaryan } 7406a45336bSSuren Baghdasaryan 741beb69e81SSuren Baghdasaryan TEST_HARNESS_MAIN 742