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
FIXTURE(proc_maps_race)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
read_two_pages(FIXTURE_DATA (proc_maps_race)* self)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
copy_first_line(struct page_content * page,char * first_line)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
copy_last_line(struct page_content * page,char * last_line)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 */
read_boundary_lines(FIXTURE_DATA (proc_maps_race)* self,struct line_content * last_line,struct line_content * first_line)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 */
wait_for_state(struct vma_modifier_info * mod_info,enum test_state state)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
signal_state(struct vma_modifier_info * mod_info,enum test_state state)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
stop_vma_modifier(struct vma_modifier_info * mod_info)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
print_first_lines(char * text,int nr)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
print_last_lines(char * text,int nr)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
print_boundaries(const char * title,FIXTURE_DATA (proc_maps_race)* self)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
print_boundaries_on(bool condition,const char * title,FIXTURE_DATA (proc_maps_race)* self)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
report_test_start(const char * name,bool verbose)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
start_test_loop(struct timespec * ts,bool verbose)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
end_test_iteration(struct timespec * ts,bool verbose)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
end_test_loop(bool verbose)276aadc099cSSuren Baghdasaryan static void end_test_loop(bool verbose)
277aadc099cSSuren Baghdasaryan {
278aadc099cSSuren Baghdasaryan if (verbose)
279aadc099cSSuren Baghdasaryan printf("\n");
280aadc099cSSuren Baghdasaryan }
281aadc099cSSuren Baghdasaryan
capture_mod_pattern(FIXTURE_DATA (proc_maps_race)* self,struct line_content * mod_last_line,struct line_content * mod_first_line,struct line_content * restored_last_line,struct line_content * restored_first_line)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
split_vma(FIXTURE_DATA (proc_maps_race)* self)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
merge_vma(FIXTURE_DATA (proc_maps_race)* self)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
check_split_result(struct line_content * mod_last_line,struct line_content * mod_first_line,struct line_content * restored_last_line,struct line_content * restored_first_line)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
shrink_vma(FIXTURE_DATA (proc_maps_race)* self)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
expand_vma(FIXTURE_DATA (proc_maps_race)* self)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
check_shrink_result(struct line_content * mod_last_line,struct line_content * mod_first_line,struct line_content * restored_last_line,struct line_content * restored_first_line)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
remap_vma(FIXTURE_DATA (proc_maps_race)* self)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
patch_vma(FIXTURE_DATA (proc_maps_race)* self)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
check_remap_result(struct line_content * mod_last_line,struct line_content * mod_first_line,struct line_content * restored_last_line,struct line_content * restored_first_line)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
FIXTURE_SETUP(proc_maps_race)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
FIXTURE_TEARDOWN(proc_maps_race)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
TEST_F(proc_maps_race,test_maps_tearing_from_split)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
TEST_F(proc_maps_race,test_maps_tearing_from_resize)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
TEST_F(proc_maps_race,test_maps_tearing_from_remap)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