xref: /linux/tools/testing/selftests/proc/proc-maps-race.c (revision 91325f31afc1026de28665cf1a7b6e157fa4d39d)
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