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; 80beb69e81SSuren Baghdasaryan int maps_fd; 81beb69e81SSuren Baghdasaryan pid_t pid; 82beb69e81SSuren Baghdasaryan }; 83beb69e81SSuren Baghdasaryan 84beb69e81SSuren Baghdasaryan typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self); 85beb69e81SSuren Baghdasaryan typedef bool (*vma_mod_result_check_op)(struct line_content *mod_last_line, 86beb69e81SSuren Baghdasaryan struct line_content *mod_first_line, 87beb69e81SSuren Baghdasaryan struct line_content *restored_last_line, 88beb69e81SSuren Baghdasaryan struct line_content *restored_first_line); 89beb69e81SSuren Baghdasaryan 90beb69e81SSuren Baghdasaryan struct vma_modifier_info { 91beb69e81SSuren Baghdasaryan int vma_count; 92beb69e81SSuren Baghdasaryan void *addr; 93beb69e81SSuren Baghdasaryan int prot; 94beb69e81SSuren Baghdasaryan void *next_addr; 95beb69e81SSuren Baghdasaryan vma_modifier_op vma_modify; 96beb69e81SSuren Baghdasaryan vma_modifier_op vma_restore; 97beb69e81SSuren Baghdasaryan vma_mod_result_check_op vma_mod_check; 98beb69e81SSuren Baghdasaryan pthread_mutex_t sync_lock; 99beb69e81SSuren Baghdasaryan pthread_cond_t sync_cond; 100beb69e81SSuren Baghdasaryan enum test_state curr_state; 101beb69e81SSuren Baghdasaryan bool exit; 102beb69e81SSuren Baghdasaryan void *child_mapped_addr[]; 103beb69e81SSuren Baghdasaryan }; 104beb69e81SSuren Baghdasaryan 105beb69e81SSuren Baghdasaryan 106beb69e81SSuren Baghdasaryan static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self) 107beb69e81SSuren Baghdasaryan { 108beb69e81SSuren Baghdasaryan ssize_t bytes_read; 109beb69e81SSuren Baghdasaryan 110beb69e81SSuren Baghdasaryan if (lseek(self->maps_fd, 0, SEEK_SET) < 0) 111beb69e81SSuren Baghdasaryan return false; 112beb69e81SSuren Baghdasaryan 113beb69e81SSuren Baghdasaryan bytes_read = read(self->maps_fd, self->page1.data, self->page_size); 114beb69e81SSuren Baghdasaryan if (bytes_read <= 0) 115beb69e81SSuren Baghdasaryan return false; 116beb69e81SSuren Baghdasaryan 117beb69e81SSuren Baghdasaryan self->page1.size = bytes_read; 118beb69e81SSuren Baghdasaryan 119beb69e81SSuren Baghdasaryan bytes_read = read(self->maps_fd, self->page2.data, self->page_size); 120beb69e81SSuren Baghdasaryan if (bytes_read <= 0) 121beb69e81SSuren Baghdasaryan return false; 122beb69e81SSuren Baghdasaryan 123beb69e81SSuren Baghdasaryan self->page2.size = bytes_read; 124beb69e81SSuren Baghdasaryan 125beb69e81SSuren Baghdasaryan return true; 126beb69e81SSuren Baghdasaryan } 127beb69e81SSuren Baghdasaryan 128beb69e81SSuren Baghdasaryan static void copy_first_line(struct page_content *page, char *first_line) 129beb69e81SSuren Baghdasaryan { 130beb69e81SSuren Baghdasaryan char *pos = strchr(page->data, '\n'); 131beb69e81SSuren Baghdasaryan 132beb69e81SSuren Baghdasaryan strncpy(first_line, page->data, pos - page->data); 133beb69e81SSuren Baghdasaryan first_line[pos - page->data] = '\0'; 134beb69e81SSuren Baghdasaryan } 135beb69e81SSuren Baghdasaryan 136beb69e81SSuren Baghdasaryan static void copy_last_line(struct page_content *page, char *last_line) 137beb69e81SSuren Baghdasaryan { 138beb69e81SSuren Baghdasaryan /* Get the last line in the first page */ 139beb69e81SSuren Baghdasaryan const char *end = page->data + page->size - 1; 140beb69e81SSuren Baghdasaryan /* skip last newline */ 141beb69e81SSuren Baghdasaryan const char *pos = end - 1; 142beb69e81SSuren Baghdasaryan 143beb69e81SSuren Baghdasaryan /* search previous newline */ 144beb69e81SSuren Baghdasaryan while (pos[-1] != '\n') 145beb69e81SSuren Baghdasaryan pos--; 146beb69e81SSuren Baghdasaryan strncpy(last_line, pos, end - pos); 147beb69e81SSuren Baghdasaryan last_line[end - pos] = '\0'; 148beb69e81SSuren Baghdasaryan } 149beb69e81SSuren Baghdasaryan 150beb69e81SSuren Baghdasaryan /* Read the last line of the first page and the first line of the second page */ 151beb69e81SSuren Baghdasaryan static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self, 152beb69e81SSuren Baghdasaryan struct line_content *last_line, 153beb69e81SSuren Baghdasaryan struct line_content *first_line) 154beb69e81SSuren Baghdasaryan { 155beb69e81SSuren Baghdasaryan if (!read_two_pages(self)) 156beb69e81SSuren Baghdasaryan return false; 157beb69e81SSuren Baghdasaryan 158beb69e81SSuren Baghdasaryan copy_last_line(&self->page1, last_line->text); 159beb69e81SSuren Baghdasaryan copy_first_line(&self->page2, first_line->text); 160beb69e81SSuren Baghdasaryan 161beb69e81SSuren Baghdasaryan return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr, 162beb69e81SSuren Baghdasaryan &last_line->end_addr) == 2 && 163beb69e81SSuren Baghdasaryan sscanf(first_line->text, "%lx-%lx", &first_line->start_addr, 164beb69e81SSuren Baghdasaryan &first_line->end_addr) == 2; 165beb69e81SSuren Baghdasaryan } 166beb69e81SSuren Baghdasaryan 167beb69e81SSuren Baghdasaryan /* Thread synchronization routines */ 168beb69e81SSuren Baghdasaryan static void wait_for_state(struct vma_modifier_info *mod_info, enum test_state state) 169beb69e81SSuren Baghdasaryan { 170beb69e81SSuren Baghdasaryan pthread_mutex_lock(&mod_info->sync_lock); 171beb69e81SSuren Baghdasaryan while (mod_info->curr_state != state) 172beb69e81SSuren Baghdasaryan pthread_cond_wait(&mod_info->sync_cond, &mod_info->sync_lock); 173beb69e81SSuren Baghdasaryan pthread_mutex_unlock(&mod_info->sync_lock); 174beb69e81SSuren Baghdasaryan } 175beb69e81SSuren Baghdasaryan 176beb69e81SSuren Baghdasaryan static void signal_state(struct vma_modifier_info *mod_info, enum test_state state) 177beb69e81SSuren Baghdasaryan { 178beb69e81SSuren Baghdasaryan pthread_mutex_lock(&mod_info->sync_lock); 179beb69e81SSuren Baghdasaryan mod_info->curr_state = state; 180beb69e81SSuren Baghdasaryan pthread_cond_signal(&mod_info->sync_cond); 181beb69e81SSuren Baghdasaryan pthread_mutex_unlock(&mod_info->sync_lock); 182beb69e81SSuren Baghdasaryan } 183beb69e81SSuren Baghdasaryan 184beb69e81SSuren Baghdasaryan static void stop_vma_modifier(struct vma_modifier_info *mod_info) 185beb69e81SSuren Baghdasaryan { 186beb69e81SSuren Baghdasaryan wait_for_state(mod_info, SETUP_READY); 187beb69e81SSuren Baghdasaryan mod_info->exit = true; 188beb69e81SSuren Baghdasaryan signal_state(mod_info, SETUP_MODIFY_MAPS); 189beb69e81SSuren Baghdasaryan } 190beb69e81SSuren Baghdasaryan 191beb69e81SSuren Baghdasaryan static bool capture_mod_pattern(FIXTURE_DATA(proc_maps_race) *self, 192beb69e81SSuren Baghdasaryan struct line_content *mod_last_line, 193beb69e81SSuren Baghdasaryan struct line_content *mod_first_line, 194beb69e81SSuren Baghdasaryan struct line_content *restored_last_line, 195beb69e81SSuren Baghdasaryan struct line_content *restored_first_line) 196beb69e81SSuren Baghdasaryan { 197beb69e81SSuren Baghdasaryan signal_state(self->mod_info, SETUP_MODIFY_MAPS); 198beb69e81SSuren Baghdasaryan wait_for_state(self->mod_info, SETUP_MAPS_MODIFIED); 199beb69e81SSuren Baghdasaryan 200beb69e81SSuren Baghdasaryan /* Copy last line of the first page and first line of the last page */ 201beb69e81SSuren Baghdasaryan if (!read_boundary_lines(self, mod_last_line, mod_first_line)) 202beb69e81SSuren Baghdasaryan return false; 203beb69e81SSuren Baghdasaryan 204beb69e81SSuren Baghdasaryan signal_state(self->mod_info, SETUP_RESTORE_MAPS); 205beb69e81SSuren Baghdasaryan wait_for_state(self->mod_info, SETUP_MAPS_RESTORED); 206beb69e81SSuren Baghdasaryan 207beb69e81SSuren Baghdasaryan /* Copy last line of the first page and first line of the last page */ 208beb69e81SSuren Baghdasaryan if (!read_boundary_lines(self, restored_last_line, restored_first_line)) 209beb69e81SSuren Baghdasaryan return false; 210beb69e81SSuren Baghdasaryan 211beb69e81SSuren Baghdasaryan if (!self->mod_info->vma_mod_check(mod_last_line, mod_first_line, 212beb69e81SSuren Baghdasaryan restored_last_line, restored_first_line)) 213beb69e81SSuren Baghdasaryan return false; 214beb69e81SSuren Baghdasaryan 215beb69e81SSuren Baghdasaryan /* 216beb69e81SSuren Baghdasaryan * The content of these lines after modify+resore should be the same 217beb69e81SSuren Baghdasaryan * as the original. 218beb69e81SSuren Baghdasaryan */ 219beb69e81SSuren Baghdasaryan return strcmp(restored_last_line->text, self->last_line.text) == 0 && 220beb69e81SSuren Baghdasaryan strcmp(restored_first_line->text, self->first_line.text) == 0; 221beb69e81SSuren Baghdasaryan } 222beb69e81SSuren Baghdasaryan 223beb69e81SSuren Baghdasaryan static inline bool split_vma(FIXTURE_DATA(proc_maps_race) *self) 224beb69e81SSuren Baghdasaryan { 225beb69e81SSuren Baghdasaryan return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot | PROT_EXEC, 226beb69e81SSuren Baghdasaryan MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 227beb69e81SSuren Baghdasaryan } 228beb69e81SSuren Baghdasaryan 229beb69e81SSuren Baghdasaryan static inline bool merge_vma(FIXTURE_DATA(proc_maps_race) *self) 230beb69e81SSuren Baghdasaryan { 231beb69e81SSuren Baghdasaryan return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot, 232beb69e81SSuren Baghdasaryan MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED; 233beb69e81SSuren Baghdasaryan } 234beb69e81SSuren Baghdasaryan 235beb69e81SSuren Baghdasaryan static inline bool check_split_result(struct line_content *mod_last_line, 236beb69e81SSuren Baghdasaryan struct line_content *mod_first_line, 237beb69e81SSuren Baghdasaryan struct line_content *restored_last_line, 238beb69e81SSuren Baghdasaryan struct line_content *restored_first_line) 239beb69e81SSuren Baghdasaryan { 240beb69e81SSuren Baghdasaryan /* Make sure vmas at the boundaries are changing */ 241beb69e81SSuren Baghdasaryan return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 242beb69e81SSuren Baghdasaryan strcmp(mod_first_line->text, restored_first_line->text) != 0; 243beb69e81SSuren Baghdasaryan } 244beb69e81SSuren Baghdasaryan 245b11d9e2dSSuren Baghdasaryan static inline bool shrink_vma(FIXTURE_DATA(proc_maps_race) *self) 246b11d9e2dSSuren Baghdasaryan { 247b11d9e2dSSuren Baghdasaryan return mremap(self->mod_info->addr, self->page_size * 3, 248b11d9e2dSSuren Baghdasaryan self->page_size, 0) != MAP_FAILED; 249b11d9e2dSSuren Baghdasaryan } 250b11d9e2dSSuren Baghdasaryan 251b11d9e2dSSuren Baghdasaryan static inline bool expand_vma(FIXTURE_DATA(proc_maps_race) *self) 252b11d9e2dSSuren Baghdasaryan { 253b11d9e2dSSuren Baghdasaryan return mremap(self->mod_info->addr, self->page_size, 254b11d9e2dSSuren Baghdasaryan self->page_size * 3, 0) != MAP_FAILED; 255b11d9e2dSSuren Baghdasaryan } 256b11d9e2dSSuren Baghdasaryan 257b11d9e2dSSuren Baghdasaryan static inline bool check_shrink_result(struct line_content *mod_last_line, 258b11d9e2dSSuren Baghdasaryan struct line_content *mod_first_line, 259b11d9e2dSSuren Baghdasaryan struct line_content *restored_last_line, 260b11d9e2dSSuren Baghdasaryan struct line_content *restored_first_line) 261b11d9e2dSSuren Baghdasaryan { 262b11d9e2dSSuren Baghdasaryan /* Make sure only the last vma of the first page is changing */ 263b11d9e2dSSuren Baghdasaryan return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 264b11d9e2dSSuren Baghdasaryan strcmp(mod_first_line->text, restored_first_line->text) == 0; 265b11d9e2dSSuren Baghdasaryan } 266b11d9e2dSSuren Baghdasaryan 267*6a45336bSSuren Baghdasaryan static inline bool remap_vma(FIXTURE_DATA(proc_maps_race) *self) 268*6a45336bSSuren Baghdasaryan { 269*6a45336bSSuren Baghdasaryan /* 270*6a45336bSSuren Baghdasaryan * Remap the last page of the next vma into the middle of the vma. 271*6a45336bSSuren Baghdasaryan * This splits the current vma and the first and middle parts (the 272*6a45336bSSuren Baghdasaryan * parts at lower addresses) become the last vma objserved in the 273*6a45336bSSuren Baghdasaryan * first page and the first vma observed in the last page. 274*6a45336bSSuren Baghdasaryan */ 275*6a45336bSSuren Baghdasaryan return mremap(self->mod_info->next_addr + self->page_size * 2, self->page_size, 276*6a45336bSSuren Baghdasaryan self->page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP, 277*6a45336bSSuren Baghdasaryan self->mod_info->addr + self->page_size) != MAP_FAILED; 278*6a45336bSSuren Baghdasaryan } 279*6a45336bSSuren Baghdasaryan 280*6a45336bSSuren Baghdasaryan static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self) 281*6a45336bSSuren Baghdasaryan { 282*6a45336bSSuren Baghdasaryan return mprotect(self->mod_info->addr + self->page_size, self->page_size, 283*6a45336bSSuren Baghdasaryan self->mod_info->prot) == 0; 284*6a45336bSSuren Baghdasaryan } 285*6a45336bSSuren Baghdasaryan 286*6a45336bSSuren Baghdasaryan static inline bool check_remap_result(struct line_content *mod_last_line, 287*6a45336bSSuren Baghdasaryan struct line_content *mod_first_line, 288*6a45336bSSuren Baghdasaryan struct line_content *restored_last_line, 289*6a45336bSSuren Baghdasaryan struct line_content *restored_first_line) 290*6a45336bSSuren Baghdasaryan { 291*6a45336bSSuren Baghdasaryan /* Make sure vmas at the boundaries are changing */ 292*6a45336bSSuren Baghdasaryan return strcmp(mod_last_line->text, restored_last_line->text) != 0 && 293*6a45336bSSuren Baghdasaryan strcmp(mod_first_line->text, restored_first_line->text) != 0; 294*6a45336bSSuren Baghdasaryan } 295*6a45336bSSuren Baghdasaryan 296beb69e81SSuren Baghdasaryan FIXTURE_SETUP(proc_maps_race) 297beb69e81SSuren Baghdasaryan { 298beb69e81SSuren Baghdasaryan const char *duration = getenv("DURATION"); 299beb69e81SSuren Baghdasaryan struct vma_modifier_info *mod_info; 300beb69e81SSuren Baghdasaryan pthread_mutexattr_t mutex_attr; 301beb69e81SSuren Baghdasaryan pthread_condattr_t cond_attr; 302beb69e81SSuren Baghdasaryan unsigned long duration_sec; 303beb69e81SSuren Baghdasaryan char fname[32]; 304beb69e81SSuren Baghdasaryan 305beb69e81SSuren Baghdasaryan self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); 306beb69e81SSuren Baghdasaryan duration_sec = duration ? atol(duration) : 0; 307beb69e81SSuren Baghdasaryan self->duration_sec = duration_sec ? duration_sec : 5UL; 308beb69e81SSuren Baghdasaryan 309beb69e81SSuren Baghdasaryan /* 310beb69e81SSuren Baghdasaryan * Have to map enough vmas for /proc/pid/maps to contain more than one 311beb69e81SSuren Baghdasaryan * page worth of vmas. Assume at least 32 bytes per line in maps output 312beb69e81SSuren Baghdasaryan */ 313beb69e81SSuren Baghdasaryan self->vma_count = self->page_size / 32 + 1; 314beb69e81SSuren Baghdasaryan self->shared_mem_size = sizeof(struct vma_modifier_info) + self->vma_count * sizeof(void *); 315beb69e81SSuren Baghdasaryan 316beb69e81SSuren Baghdasaryan /* map shared memory for communication with the child process */ 317beb69e81SSuren Baghdasaryan self->mod_info = (struct vma_modifier_info *)mmap(NULL, self->shared_mem_size, 318beb69e81SSuren Baghdasaryan PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 319beb69e81SSuren Baghdasaryan ASSERT_NE(self->mod_info, MAP_FAILED); 320beb69e81SSuren Baghdasaryan mod_info = self->mod_info; 321beb69e81SSuren Baghdasaryan 322beb69e81SSuren Baghdasaryan /* Initialize shared members */ 323beb69e81SSuren Baghdasaryan pthread_mutexattr_init(&mutex_attr); 324beb69e81SSuren Baghdasaryan pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); 325beb69e81SSuren Baghdasaryan ASSERT_EQ(pthread_mutex_init(&mod_info->sync_lock, &mutex_attr), 0); 326beb69e81SSuren Baghdasaryan pthread_condattr_init(&cond_attr); 327beb69e81SSuren Baghdasaryan pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); 328beb69e81SSuren Baghdasaryan ASSERT_EQ(pthread_cond_init(&mod_info->sync_cond, &cond_attr), 0); 329beb69e81SSuren Baghdasaryan mod_info->vma_count = self->vma_count; 330beb69e81SSuren Baghdasaryan mod_info->curr_state = INIT; 331beb69e81SSuren Baghdasaryan mod_info->exit = false; 332beb69e81SSuren Baghdasaryan 333beb69e81SSuren Baghdasaryan self->pid = fork(); 334beb69e81SSuren Baghdasaryan if (!self->pid) { 335beb69e81SSuren Baghdasaryan /* Child process modifying the address space */ 336beb69e81SSuren Baghdasaryan int prot = PROT_READ | PROT_WRITE; 337beb69e81SSuren Baghdasaryan int i; 338beb69e81SSuren Baghdasaryan 339beb69e81SSuren Baghdasaryan for (i = 0; i < mod_info->vma_count; i++) { 340beb69e81SSuren Baghdasaryan mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot, 341beb69e81SSuren Baghdasaryan MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 342beb69e81SSuren Baghdasaryan ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED); 343beb69e81SSuren Baghdasaryan /* change protection in adjacent maps to prevent merging */ 344beb69e81SSuren Baghdasaryan prot ^= PROT_WRITE; 345beb69e81SSuren Baghdasaryan } 346beb69e81SSuren Baghdasaryan signal_state(mod_info, CHILD_READY); 347beb69e81SSuren Baghdasaryan wait_for_state(mod_info, PARENT_READY); 348beb69e81SSuren Baghdasaryan while (true) { 349beb69e81SSuren Baghdasaryan signal_state(mod_info, SETUP_READY); 350beb69e81SSuren Baghdasaryan wait_for_state(mod_info, SETUP_MODIFY_MAPS); 351beb69e81SSuren Baghdasaryan if (mod_info->exit) 352beb69e81SSuren Baghdasaryan break; 353beb69e81SSuren Baghdasaryan 354beb69e81SSuren Baghdasaryan ASSERT_TRUE(mod_info->vma_modify(self)); 355beb69e81SSuren Baghdasaryan signal_state(mod_info, SETUP_MAPS_MODIFIED); 356beb69e81SSuren Baghdasaryan wait_for_state(mod_info, SETUP_RESTORE_MAPS); 357beb69e81SSuren Baghdasaryan ASSERT_TRUE(mod_info->vma_restore(self)); 358beb69e81SSuren Baghdasaryan signal_state(mod_info, SETUP_MAPS_RESTORED); 359beb69e81SSuren Baghdasaryan 360beb69e81SSuren Baghdasaryan wait_for_state(mod_info, TEST_READY); 361beb69e81SSuren Baghdasaryan while (mod_info->curr_state != TEST_DONE) { 362beb69e81SSuren Baghdasaryan ASSERT_TRUE(mod_info->vma_modify(self)); 363beb69e81SSuren Baghdasaryan ASSERT_TRUE(mod_info->vma_restore(self)); 364beb69e81SSuren Baghdasaryan } 365beb69e81SSuren Baghdasaryan } 366beb69e81SSuren Baghdasaryan for (i = 0; i < mod_info->vma_count; i++) 367beb69e81SSuren Baghdasaryan munmap(mod_info->child_mapped_addr[i], self->page_size * 3); 368beb69e81SSuren Baghdasaryan 369beb69e81SSuren Baghdasaryan exit(0); 370beb69e81SSuren Baghdasaryan } 371beb69e81SSuren Baghdasaryan 372beb69e81SSuren Baghdasaryan sprintf(fname, "/proc/%d/maps", self->pid); 373beb69e81SSuren Baghdasaryan self->maps_fd = open(fname, O_RDONLY); 374beb69e81SSuren Baghdasaryan ASSERT_NE(self->maps_fd, -1); 375beb69e81SSuren Baghdasaryan 376beb69e81SSuren Baghdasaryan /* Wait for the child to map the VMAs */ 377beb69e81SSuren Baghdasaryan wait_for_state(mod_info, CHILD_READY); 378beb69e81SSuren Baghdasaryan 379beb69e81SSuren Baghdasaryan /* Read first two pages */ 380beb69e81SSuren Baghdasaryan self->page1.data = malloc(self->page_size); 381beb69e81SSuren Baghdasaryan ASSERT_NE(self->page1.data, NULL); 382beb69e81SSuren Baghdasaryan self->page2.data = malloc(self->page_size); 383beb69e81SSuren Baghdasaryan ASSERT_NE(self->page2.data, NULL); 384beb69e81SSuren Baghdasaryan 385beb69e81SSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 386beb69e81SSuren Baghdasaryan 387beb69e81SSuren Baghdasaryan /* 388beb69e81SSuren Baghdasaryan * Find the addresses corresponding to the last line in the first page 389beb69e81SSuren Baghdasaryan * and the first line in the last page. 390beb69e81SSuren Baghdasaryan */ 391beb69e81SSuren Baghdasaryan mod_info->addr = NULL; 392beb69e81SSuren Baghdasaryan mod_info->next_addr = NULL; 393beb69e81SSuren Baghdasaryan for (int i = 0; i < mod_info->vma_count; i++) { 394beb69e81SSuren Baghdasaryan if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) { 395beb69e81SSuren Baghdasaryan mod_info->addr = mod_info->child_mapped_addr[i]; 396beb69e81SSuren Baghdasaryan mod_info->prot = PROT_READ; 397beb69e81SSuren Baghdasaryan /* Even VMAs have write permission */ 398beb69e81SSuren Baghdasaryan if ((i % 2) == 0) 399beb69e81SSuren Baghdasaryan mod_info->prot |= PROT_WRITE; 400beb69e81SSuren Baghdasaryan } else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) { 401beb69e81SSuren Baghdasaryan mod_info->next_addr = mod_info->child_mapped_addr[i]; 402beb69e81SSuren Baghdasaryan } 403beb69e81SSuren Baghdasaryan 404beb69e81SSuren Baghdasaryan if (mod_info->addr && mod_info->next_addr) 405beb69e81SSuren Baghdasaryan break; 406beb69e81SSuren Baghdasaryan } 407beb69e81SSuren Baghdasaryan ASSERT_TRUE(mod_info->addr && mod_info->next_addr); 408beb69e81SSuren Baghdasaryan 409beb69e81SSuren Baghdasaryan signal_state(mod_info, PARENT_READY); 410beb69e81SSuren Baghdasaryan 411beb69e81SSuren Baghdasaryan } 412beb69e81SSuren Baghdasaryan 413beb69e81SSuren Baghdasaryan FIXTURE_TEARDOWN(proc_maps_race) 414beb69e81SSuren Baghdasaryan { 415beb69e81SSuren Baghdasaryan int status; 416beb69e81SSuren Baghdasaryan 417beb69e81SSuren Baghdasaryan stop_vma_modifier(self->mod_info); 418beb69e81SSuren Baghdasaryan 419beb69e81SSuren Baghdasaryan free(self->page2.data); 420beb69e81SSuren Baghdasaryan free(self->page1.data); 421beb69e81SSuren Baghdasaryan 422beb69e81SSuren Baghdasaryan for (int i = 0; i < self->vma_count; i++) 423beb69e81SSuren Baghdasaryan munmap(self->mod_info->child_mapped_addr[i], self->page_size); 424beb69e81SSuren Baghdasaryan close(self->maps_fd); 425beb69e81SSuren Baghdasaryan waitpid(self->pid, &status, 0); 426beb69e81SSuren Baghdasaryan munmap(self->mod_info, self->shared_mem_size); 427beb69e81SSuren Baghdasaryan } 428beb69e81SSuren Baghdasaryan 429beb69e81SSuren Baghdasaryan TEST_F(proc_maps_race, test_maps_tearing_from_split) 430beb69e81SSuren Baghdasaryan { 431beb69e81SSuren Baghdasaryan struct vma_modifier_info *mod_info = self->mod_info; 432beb69e81SSuren Baghdasaryan 433beb69e81SSuren Baghdasaryan struct line_content split_last_line; 434beb69e81SSuren Baghdasaryan struct line_content split_first_line; 435beb69e81SSuren Baghdasaryan struct line_content restored_last_line; 436beb69e81SSuren Baghdasaryan struct line_content restored_first_line; 437beb69e81SSuren Baghdasaryan 438beb69e81SSuren Baghdasaryan wait_for_state(mod_info, SETUP_READY); 439beb69e81SSuren Baghdasaryan 440beb69e81SSuren Baghdasaryan /* re-read the file to avoid using stale data from previous test */ 441beb69e81SSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 442beb69e81SSuren Baghdasaryan 443beb69e81SSuren Baghdasaryan mod_info->vma_modify = split_vma; 444beb69e81SSuren Baghdasaryan mod_info->vma_restore = merge_vma; 445beb69e81SSuren Baghdasaryan mod_info->vma_mod_check = check_split_result; 446beb69e81SSuren Baghdasaryan 447beb69e81SSuren Baghdasaryan ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line, 448beb69e81SSuren Baghdasaryan &restored_last_line, &restored_first_line)); 449beb69e81SSuren Baghdasaryan 450beb69e81SSuren Baghdasaryan /* Now start concurrent modifications for self->duration_sec */ 451beb69e81SSuren Baghdasaryan signal_state(mod_info, TEST_READY); 452beb69e81SSuren Baghdasaryan 453beb69e81SSuren Baghdasaryan struct line_content new_last_line; 454beb69e81SSuren Baghdasaryan struct line_content new_first_line; 455beb69e81SSuren Baghdasaryan struct timespec start_ts, end_ts; 456beb69e81SSuren Baghdasaryan 457beb69e81SSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 458beb69e81SSuren Baghdasaryan do { 459beb69e81SSuren Baghdasaryan bool last_line_changed; 460beb69e81SSuren Baghdasaryan bool first_line_changed; 461beb69e81SSuren Baghdasaryan 462beb69e81SSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 463beb69e81SSuren Baghdasaryan 464beb69e81SSuren Baghdasaryan /* Check if we read vmas after split */ 465beb69e81SSuren Baghdasaryan if (!strcmp(new_last_line.text, split_last_line.text)) { 466beb69e81SSuren Baghdasaryan /* 467beb69e81SSuren Baghdasaryan * The vmas should be consistent with split results, 468beb69e81SSuren Baghdasaryan * however if vma was concurrently restored after a 469beb69e81SSuren Baghdasaryan * split, it can be reported twice (first the original 470beb69e81SSuren Baghdasaryan * split one, then the same vma but extended after the 471beb69e81SSuren Baghdasaryan * merge) because we found it as the next vma again. 472beb69e81SSuren Baghdasaryan * In that case new first line will be the same as the 473beb69e81SSuren Baghdasaryan * last restored line. 474beb69e81SSuren Baghdasaryan */ 475beb69e81SSuren Baghdasaryan ASSERT_FALSE(strcmp(new_first_line.text, split_first_line.text) && 476beb69e81SSuren Baghdasaryan strcmp(new_first_line.text, restored_last_line.text)); 477beb69e81SSuren Baghdasaryan } else { 478beb69e81SSuren Baghdasaryan /* The vmas should be consistent with merge results */ 479beb69e81SSuren Baghdasaryan ASSERT_FALSE(strcmp(new_last_line.text, restored_last_line.text)); 480beb69e81SSuren Baghdasaryan ASSERT_FALSE(strcmp(new_first_line.text, restored_first_line.text)); 481beb69e81SSuren Baghdasaryan } 482beb69e81SSuren Baghdasaryan /* 483beb69e81SSuren Baghdasaryan * First and last lines should change in unison. If the last 484beb69e81SSuren Baghdasaryan * line changed then the first line should change as well and 485beb69e81SSuren Baghdasaryan * vice versa. 486beb69e81SSuren Baghdasaryan */ 487beb69e81SSuren Baghdasaryan last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0; 488beb69e81SSuren Baghdasaryan first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0; 489beb69e81SSuren Baghdasaryan ASSERT_EQ(last_line_changed, first_line_changed); 490beb69e81SSuren Baghdasaryan 491beb69e81SSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 492beb69e81SSuren Baghdasaryan } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 493beb69e81SSuren Baghdasaryan 494beb69e81SSuren Baghdasaryan /* Signal the modifyer thread to stop and wait until it exits */ 495beb69e81SSuren Baghdasaryan signal_state(mod_info, TEST_DONE); 496beb69e81SSuren Baghdasaryan } 497beb69e81SSuren Baghdasaryan 498b11d9e2dSSuren Baghdasaryan TEST_F(proc_maps_race, test_maps_tearing_from_resize) 499b11d9e2dSSuren Baghdasaryan { 500b11d9e2dSSuren Baghdasaryan struct vma_modifier_info *mod_info = self->mod_info; 501b11d9e2dSSuren Baghdasaryan 502b11d9e2dSSuren Baghdasaryan struct line_content shrunk_last_line; 503b11d9e2dSSuren Baghdasaryan struct line_content shrunk_first_line; 504b11d9e2dSSuren Baghdasaryan struct line_content restored_last_line; 505b11d9e2dSSuren Baghdasaryan struct line_content restored_first_line; 506b11d9e2dSSuren Baghdasaryan 507b11d9e2dSSuren Baghdasaryan wait_for_state(mod_info, SETUP_READY); 508b11d9e2dSSuren Baghdasaryan 509b11d9e2dSSuren Baghdasaryan /* re-read the file to avoid using stale data from previous test */ 510b11d9e2dSSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 511b11d9e2dSSuren Baghdasaryan 512b11d9e2dSSuren Baghdasaryan mod_info->vma_modify = shrink_vma; 513b11d9e2dSSuren Baghdasaryan mod_info->vma_restore = expand_vma; 514b11d9e2dSSuren Baghdasaryan mod_info->vma_mod_check = check_shrink_result; 515b11d9e2dSSuren Baghdasaryan 516b11d9e2dSSuren Baghdasaryan ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line, 517b11d9e2dSSuren Baghdasaryan &restored_last_line, &restored_first_line)); 518b11d9e2dSSuren Baghdasaryan 519b11d9e2dSSuren Baghdasaryan /* Now start concurrent modifications for self->duration_sec */ 520b11d9e2dSSuren Baghdasaryan signal_state(mod_info, TEST_READY); 521b11d9e2dSSuren Baghdasaryan 522b11d9e2dSSuren Baghdasaryan struct line_content new_last_line; 523b11d9e2dSSuren Baghdasaryan struct line_content new_first_line; 524b11d9e2dSSuren Baghdasaryan struct timespec start_ts, end_ts; 525b11d9e2dSSuren Baghdasaryan 526b11d9e2dSSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 527b11d9e2dSSuren Baghdasaryan do { 528b11d9e2dSSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 529b11d9e2dSSuren Baghdasaryan 530b11d9e2dSSuren Baghdasaryan /* Check if we read vmas after shrinking it */ 531b11d9e2dSSuren Baghdasaryan if (!strcmp(new_last_line.text, shrunk_last_line.text)) { 532b11d9e2dSSuren Baghdasaryan /* 533b11d9e2dSSuren Baghdasaryan * The vmas should be consistent with shrunk results, 534b11d9e2dSSuren Baghdasaryan * however if the vma was concurrently restored, it 535b11d9e2dSSuren Baghdasaryan * can be reported twice (first as shrunk one, then 536b11d9e2dSSuren Baghdasaryan * as restored one) because we found it as the next vma 537b11d9e2dSSuren Baghdasaryan * again. In that case new first line will be the same 538b11d9e2dSSuren Baghdasaryan * as the last restored line. 539b11d9e2dSSuren Baghdasaryan */ 540b11d9e2dSSuren Baghdasaryan ASSERT_FALSE(strcmp(new_first_line.text, shrunk_first_line.text) && 541b11d9e2dSSuren Baghdasaryan strcmp(new_first_line.text, restored_last_line.text)); 542b11d9e2dSSuren Baghdasaryan } else { 543b11d9e2dSSuren Baghdasaryan /* The vmas should be consistent with the original/resored state */ 544b11d9e2dSSuren Baghdasaryan ASSERT_FALSE(strcmp(new_last_line.text, restored_last_line.text)); 545b11d9e2dSSuren Baghdasaryan ASSERT_FALSE(strcmp(new_first_line.text, restored_first_line.text)); 546b11d9e2dSSuren Baghdasaryan } 547b11d9e2dSSuren Baghdasaryan 548b11d9e2dSSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 549b11d9e2dSSuren Baghdasaryan } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 550b11d9e2dSSuren Baghdasaryan 551b11d9e2dSSuren Baghdasaryan /* Signal the modifyer thread to stop and wait until it exits */ 552b11d9e2dSSuren Baghdasaryan signal_state(mod_info, TEST_DONE); 553b11d9e2dSSuren Baghdasaryan } 554b11d9e2dSSuren Baghdasaryan 555*6a45336bSSuren Baghdasaryan TEST_F(proc_maps_race, test_maps_tearing_from_remap) 556*6a45336bSSuren Baghdasaryan { 557*6a45336bSSuren Baghdasaryan struct vma_modifier_info *mod_info = self->mod_info; 558*6a45336bSSuren Baghdasaryan 559*6a45336bSSuren Baghdasaryan struct line_content remapped_last_line; 560*6a45336bSSuren Baghdasaryan struct line_content remapped_first_line; 561*6a45336bSSuren Baghdasaryan struct line_content restored_last_line; 562*6a45336bSSuren Baghdasaryan struct line_content restored_first_line; 563*6a45336bSSuren Baghdasaryan 564*6a45336bSSuren Baghdasaryan wait_for_state(mod_info, SETUP_READY); 565*6a45336bSSuren Baghdasaryan 566*6a45336bSSuren Baghdasaryan /* re-read the file to avoid using stale data from previous test */ 567*6a45336bSSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); 568*6a45336bSSuren Baghdasaryan 569*6a45336bSSuren Baghdasaryan mod_info->vma_modify = remap_vma; 570*6a45336bSSuren Baghdasaryan mod_info->vma_restore = patch_vma; 571*6a45336bSSuren Baghdasaryan mod_info->vma_mod_check = check_remap_result; 572*6a45336bSSuren Baghdasaryan 573*6a45336bSSuren Baghdasaryan ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line, 574*6a45336bSSuren Baghdasaryan &restored_last_line, &restored_first_line)); 575*6a45336bSSuren Baghdasaryan 576*6a45336bSSuren Baghdasaryan /* Now start concurrent modifications for self->duration_sec */ 577*6a45336bSSuren Baghdasaryan signal_state(mod_info, TEST_READY); 578*6a45336bSSuren Baghdasaryan 579*6a45336bSSuren Baghdasaryan struct line_content new_last_line; 580*6a45336bSSuren Baghdasaryan struct line_content new_first_line; 581*6a45336bSSuren Baghdasaryan struct timespec start_ts, end_ts; 582*6a45336bSSuren Baghdasaryan 583*6a45336bSSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts); 584*6a45336bSSuren Baghdasaryan do { 585*6a45336bSSuren Baghdasaryan ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line)); 586*6a45336bSSuren Baghdasaryan 587*6a45336bSSuren Baghdasaryan /* Check if we read vmas after remapping it */ 588*6a45336bSSuren Baghdasaryan if (!strcmp(new_last_line.text, remapped_last_line.text)) { 589*6a45336bSSuren Baghdasaryan /* 590*6a45336bSSuren Baghdasaryan * The vmas should be consistent with remap results, 591*6a45336bSSuren Baghdasaryan * however if the vma was concurrently restored, it 592*6a45336bSSuren Baghdasaryan * can be reported twice (first as split one, then 593*6a45336bSSuren Baghdasaryan * as restored one) because we found it as the next vma 594*6a45336bSSuren Baghdasaryan * again. In that case new first line will be the same 595*6a45336bSSuren Baghdasaryan * as the last restored line. 596*6a45336bSSuren Baghdasaryan */ 597*6a45336bSSuren Baghdasaryan ASSERT_FALSE(strcmp(new_first_line.text, remapped_first_line.text) && 598*6a45336bSSuren Baghdasaryan strcmp(new_first_line.text, restored_last_line.text)); 599*6a45336bSSuren Baghdasaryan } else { 600*6a45336bSSuren Baghdasaryan /* The vmas should be consistent with the original/resored state */ 601*6a45336bSSuren Baghdasaryan ASSERT_FALSE(strcmp(new_last_line.text, restored_last_line.text)); 602*6a45336bSSuren Baghdasaryan ASSERT_FALSE(strcmp(new_first_line.text, restored_first_line.text)); 603*6a45336bSSuren Baghdasaryan } 604*6a45336bSSuren Baghdasaryan 605*6a45336bSSuren Baghdasaryan clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); 606*6a45336bSSuren Baghdasaryan } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); 607*6a45336bSSuren Baghdasaryan 608*6a45336bSSuren Baghdasaryan /* Signal the modifyer thread to stop and wait until it exits */ 609*6a45336bSSuren Baghdasaryan signal_state(mod_info, TEST_DONE); 610*6a45336bSSuren Baghdasaryan } 611*6a45336bSSuren Baghdasaryan 612beb69e81SSuren Baghdasaryan TEST_HARNESS_MAIN 613