xref: /linux/tools/testing/selftests/proc/proc-maps-race.c (revision 6d536ed691485fa5aa6417252d357c65eb474b75)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright 2022 Google LLC.
4  * Author: Suren Baghdasaryan <surenb@google.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 /*
19  * Fork a child that concurrently modifies address space while the main
20  * process is reading /proc/$PID/maps and /proc/$PID/smaps, verifying the
21  * results. Address space modifications include:
22  *     VMA splitting and merging
23  *
24  */
25 #define _GNU_SOURCE
26 #include "kselftest_harness.h"
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <pthread.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <linux/fs.h>
36 #include <sys/ioctl.h>
37 #include <sys/mman.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 
42 #define min(a, b) \
43 	({ \
44 		typeof(a) _a = (a); \
45 		typeof(b) _b = (b); \
46 		_a < _b ? _a : _b; \
47 	})
48 
49 /* /proc/pid/maps parsing routines */
50 struct page_content {
51 	char *data;
52 	ssize_t size;
53 };
54 
55 #define LINE_MAX_SIZE		256
56 
57 struct line_content {
58 	char text[LINE_MAX_SIZE];
59 	unsigned long start_addr;
60 	unsigned long end_addr;
61 };
62 
63 enum test_state {
64 	INIT,
65 	CHILD_READY,
66 	PARENT_READY,
67 	SETUP_READY,
68 	SETUP_MODIFY_MAPS,
69 	SETUP_MAPS_MODIFIED,
70 	SETUP_RESTORE_MAPS,
71 	SETUP_MAPS_RESTORED,
72 	TEST_READY,
73 	TEST_DONE,
74 };
75 
76 enum maps_file {
77 	MAPS,
78 	SMAPS,
79 };
80 
81 struct vma_modifier_info;
82 
83 FIXTURE(proc_maps_race)
84 {
85 	struct vma_modifier_info *mod_info;
86 	struct page_content page1;
87 	struct page_content page2;
88 	struct line_content last_line;
89 	struct line_content first_line;
90 	unsigned long duration_sec;
91 	enum maps_file maps_file;
92 	int shared_mem_size;
93 	int skip_pages;
94 	int page_size;
95 	int vma_count;
96 	bool verbose;
97 	int maps_fd;
98 	pid_t pid;
99 };
100 
101 FIXTURE_VARIANT(proc_maps_race)
102 {
103 	const enum maps_file maps_file;
104 };
105 
106 FIXTURE_VARIANT_ADD(proc_maps_race, maps) {
107 	.maps_file = MAPS,
108 };
109 
110 FIXTURE_VARIANT_ADD(proc_maps_race, smaps) {
111 	.maps_file = SMAPS,
112 };
113 
114 typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self);
115 typedef bool (*vma_mod_result_check_op)(struct line_content *mod_last_line,
116 					struct line_content *mod_first_line,
117 					struct line_content *restored_last_line,
118 					struct line_content *restored_first_line);
119 
120 struct vma_modifier_info {
121 	int vma_count;
122 	void *addr;
123 	int prot;
124 	void *next_addr;
125 	vma_modifier_op vma_modify;
126 	vma_modifier_op vma_restore;
127 	vma_mod_result_check_op vma_mod_check;
128 	pthread_mutex_t sync_lock;
129 	pthread_cond_t sync_cond;
130 	enum test_state curr_state;
131 	bool exit;
132 	void *child_mapped_addr[];
133 };
134 
135 static bool read_page(FIXTURE_DATA(proc_maps_race) *self,
136 		      struct page_content *page)
137 {
138 	ssize_t  bytes_read;
139 
140 	bytes_read = read(self->maps_fd, page->data, self->page_size);
141 	if (bytes_read <= 0)
142 		return false;
143 
144 	/* Make sure data always ends with a newline character. */
145 	if (page->data[bytes_read - 1] != '\n')
146 		return false;
147 
148 	page->size = bytes_read;
149 
150 	return true;
151 }
152 
153 static bool parse_vma_line(char *line_start, char *line_end,
154 			   unsigned long *start, unsigned long *end)
155 {
156 	bool found;
157 
158 	*line_end = '\0'; /* stop sscanf at the EOL */
159 	found = (sscanf(line_start, "%lx-%lx", start, end) == 2);
160 	*line_end = '\n';
161 
162 	return found;
163 }
164 
165 static int locate_containing_page(FIXTURE_DATA(proc_maps_race) *self,
166 				  unsigned long addr, unsigned long size)
167 {
168 	unsigned long start, end;
169 	int page = 0;
170 
171 	if (lseek(self->maps_fd, 0, SEEK_SET) < 0)
172 		return -1;
173 
174 	while (true) {
175 		char *curr_pos;
176 		char *end_pos;
177 
178 		if (!read_page(self, &self->page1))
179 			return -1;
180 
181 		curr_pos = self->page1.data;
182 		end_pos = self->page1.data + self->page1.size;
183 		while (curr_pos < end_pos) {
184 			char *line_end;
185 
186 			line_end = strchr(curr_pos, '\n');
187 			if (!line_end)
188 				break;
189 
190 			if (parse_vma_line(curr_pos, line_end, &start, &end) &&
191 			    start == addr && end == addr + size)
192 				return page;
193 
194 			curr_pos = line_end + 1;
195 		}
196 		page++;
197 	}
198 
199 	return 0;
200 }
201 
202 static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self)
203 {
204 	if (lseek(self->maps_fd, 0, SEEK_SET) < 0)
205 		return false;
206 
207 	for (int i = 0; i < self->skip_pages; i++)
208 		if (!read_page(self, &self->page1))
209 			return false;
210 
211 	return read_page(self, &self->page1) && read_page(self, &self->page2);
212 }
213 
214 static void copy_line(const char *line_start, const char *line_end,
215 		      char *buf, size_t buf_size)
216 {
217 	size_t len = min(line_end - line_start, buf_size - 1);
218 
219 	strncpy(buf, line_start, len);
220 	buf[len] = '\0';
221 }
222 
223 static void copy_first_line(struct page_content *page, char *first_line,
224 			    size_t line_size)
225 {
226 	copy_line(page->data, strchr(page->data, '\n'), first_line, line_size);
227 }
228 
229 static void copy_last_line(struct page_content *page, char *last_line,
230 			   size_t line_size)
231 {
232 	/* Get the last line in the first page */
233 	const char *end = page->data + page->size - 1;
234 	/* skip last newline */
235 	const char *pos = end - 1;
236 
237 	/* search previous newline */
238 	while (pos[-1] != '\n')
239 		pos--;
240 
241 	copy_line(pos, end, last_line, line_size);
242 }
243 
244 static bool copy_first_entry(struct page_content *page, char *first_line,
245 			     size_t line_size)
246 {
247 	char *start_pos = page->data;
248 
249 	while (start_pos < page->data + page->size) {
250 		unsigned long start_addr;
251 		unsigned long end_addr;
252 		char *end_pos;
253 
254 		end_pos = strchr(start_pos, '\n');
255 		if (!end_pos)
256 			break;
257 
258 		if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) {
259 			copy_line(start_pos, end_pos, first_line, line_size);
260 			return true;
261 		}
262 
263 		start_pos = end_pos + 1;
264 	}
265 
266 	return false;
267 }
268 
269 static bool copy_last_entry(struct page_content *page, char *last_line,
270 			    size_t line_size)
271 {
272 	char *end_pos = page->data + page->size - 1;
273 	char *start_pos;
274 
275 	while (end_pos > page->data) {
276 		unsigned long start_addr;
277 		unsigned long end_addr;
278 
279 		/* skip last newline */
280 		start_pos = end_pos - 1;
281 		/* search previous newline */
282 		while (start_pos > page->data && start_pos[-1] != '\n')
283 			start_pos--;
284 		if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) {
285 			copy_line(start_pos, end_pos, last_line, line_size);
286 			return true;
287 		}
288 
289 		end_pos = start_pos - 1;
290 	}
291 
292 	return false;
293 }
294 
295 /* Read the last line of the first page and the first line of the second page */
296 static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self,
297 				struct line_content *last_line,
298 				struct line_content *first_line)
299 {
300 	if (!read_two_pages(self))
301 		return false;
302 
303 	if (self->maps_file == MAPS) {
304 		copy_last_line(&self->page1, last_line->text, LINE_MAX_SIZE);
305 		copy_first_line(&self->page2, first_line->text, LINE_MAX_SIZE);
306 	} else if (self->maps_file == SMAPS) {
307 		if (!copy_last_entry(&self->page1, last_line->text, LINE_MAX_SIZE) ||
308 		    !copy_first_entry(&self->page2, first_line->text, LINE_MAX_SIZE))
309 			return false;
310 	} else {
311 		return false;
312 	}
313 
314 	return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr,
315 		      &last_line->end_addr) == 2 &&
316 	       sscanf(first_line->text, "%lx-%lx", &first_line->start_addr,
317 		      &first_line->end_addr) == 2;
318 }
319 
320 /* Thread synchronization routines */
321 static void wait_for_state(struct vma_modifier_info *mod_info, enum test_state state)
322 {
323 	pthread_mutex_lock(&mod_info->sync_lock);
324 	while (mod_info->curr_state != state)
325 		pthread_cond_wait(&mod_info->sync_cond, &mod_info->sync_lock);
326 	pthread_mutex_unlock(&mod_info->sync_lock);
327 }
328 
329 static void signal_state(struct vma_modifier_info *mod_info, enum test_state state)
330 {
331 	pthread_mutex_lock(&mod_info->sync_lock);
332 	mod_info->curr_state = state;
333 	pthread_cond_signal(&mod_info->sync_cond);
334 	pthread_mutex_unlock(&mod_info->sync_lock);
335 }
336 
337 static void stop_vma_modifier(struct vma_modifier_info *mod_info)
338 {
339 	wait_for_state(mod_info, SETUP_READY);
340 	mod_info->exit = true;
341 	signal_state(mod_info, SETUP_MODIFY_MAPS);
342 }
343 
344 static void print_first_lines(char *text, int nr)
345 {
346 	const char *end = text;
347 
348 	while (nr && (end = strchr(end, '\n')) != NULL) {
349 		nr--;
350 		end++;
351 	}
352 
353 	if (end) {
354 		int offs = end - text;
355 
356 		text[offs] = '\0';
357 		printf("%s", text);
358 		text[offs] = '\n';
359 		printf("\n");
360 	} else {
361 		printf("%s", text);
362 	}
363 }
364 
365 static void print_last_lines(char *text, int nr)
366 {
367 	const char *start = text + strlen(text);
368 
369 	nr++; /* to ignore the last newline */
370 	while (nr) {
371 		while (start > text && *start != '\n')
372 			start--;
373 		nr--;
374 		start--;
375 	}
376 	printf("%s", start);
377 }
378 
379 static void print_boundaries(const char *title, FIXTURE_DATA(proc_maps_race) *self)
380 {
381 	if (!self->verbose)
382 		return;
383 
384 	printf("%s", title);
385 	/* Print 3 boundary lines from each page */
386 	print_last_lines(self->page1.data, 3);
387 	printf("-----------------page boundary-----------------\n");
388 	print_first_lines(self->page2.data, 3);
389 }
390 
391 static bool print_boundaries_on(bool condition, const char *title,
392 				FIXTURE_DATA(proc_maps_race) *self)
393 {
394 	if (self->verbose && condition)
395 		print_boundaries(title, self);
396 
397 	return condition;
398 }
399 
400 static void report_test_start(const char *name, bool verbose)
401 {
402 	if (verbose)
403 		printf("==== %s ====\n", name);
404 }
405 
406 static struct timespec print_ts;
407 
408 static void start_test_loop(struct timespec *ts, bool verbose)
409 {
410 	if (verbose)
411 		print_ts.tv_sec = ts->tv_sec;
412 }
413 
414 static void end_test_iteration(struct timespec *ts, bool verbose)
415 {
416 	if (!verbose)
417 		return;
418 
419 	/* Update every second */
420 	if (print_ts.tv_sec == ts->tv_sec)
421 		return;
422 
423 	printf(".");
424 	fflush(stdout);
425 	print_ts.tv_sec = ts->tv_sec;
426 }
427 
428 static void end_test_loop(bool verbose)
429 {
430 	if (verbose)
431 		printf("\n");
432 }
433 
434 static bool capture_mod_pattern(FIXTURE_DATA(proc_maps_race) *self,
435 				struct line_content *mod_last_line,
436 				struct line_content *mod_first_line,
437 				struct line_content *restored_last_line,
438 				struct line_content *restored_first_line)
439 {
440 	print_boundaries("Before modification", self);
441 
442 	signal_state(self->mod_info, SETUP_MODIFY_MAPS);
443 	wait_for_state(self->mod_info, SETUP_MAPS_MODIFIED);
444 
445 	/* Copy last line of the first page and first line of the last page */
446 	if (!read_boundary_lines(self, mod_last_line, mod_first_line))
447 		return false;
448 
449 	print_boundaries("After modification", self);
450 
451 	signal_state(self->mod_info, SETUP_RESTORE_MAPS);
452 	wait_for_state(self->mod_info, SETUP_MAPS_RESTORED);
453 
454 	/* Copy last line of the first page and first line of the last page */
455 	if (!read_boundary_lines(self, restored_last_line, restored_first_line))
456 		return false;
457 
458 	print_boundaries("After restore", self);
459 
460 	if (!self->mod_info->vma_mod_check(mod_last_line, mod_first_line,
461 					   restored_last_line, restored_first_line))
462 		return false;
463 
464 	/*
465 	 * The content of these lines after modify+resore should be the same
466 	 * as the original.
467 	 */
468 	return strcmp(restored_last_line->text, self->last_line.text) == 0 &&
469 	       strcmp(restored_first_line->text, self->first_line.text) == 0;
470 }
471 
472 static bool query_addr_at(int maps_fd, void *addr,
473 			  unsigned long *vma_start, unsigned long *vma_end)
474 {
475 	struct procmap_query q;
476 
477 	memset(&q, 0, sizeof(q));
478 	q.size = sizeof(q);
479 	/* Find the VMA at the split address */
480 	q.query_addr = (unsigned long long)addr;
481 	q.query_flags = 0;
482 	if (ioctl(maps_fd, PROCMAP_QUERY, &q))
483 		return false;
484 
485 	*vma_start = q.vma_start;
486 	*vma_end = q.vma_end;
487 
488 	return true;
489 }
490 
491 static inline bool split_vma(FIXTURE_DATA(proc_maps_race) *self)
492 {
493 	return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot | PROT_EXEC,
494 		    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED;
495 }
496 
497 static inline bool merge_vma(FIXTURE_DATA(proc_maps_race) *self)
498 {
499 	return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot,
500 		    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED;
501 }
502 
503 static inline bool check_split_result(struct line_content *mod_last_line,
504 				      struct line_content *mod_first_line,
505 				      struct line_content *restored_last_line,
506 				      struct line_content *restored_first_line)
507 {
508 	/* Make sure vmas at the boundaries are changing */
509 	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
510 	       strcmp(mod_first_line->text, restored_first_line->text) != 0;
511 }
512 
513 static inline bool shrink_vma(FIXTURE_DATA(proc_maps_race) *self)
514 {
515 	return mremap(self->mod_info->addr, self->page_size * 3,
516 		      self->page_size, 0) != MAP_FAILED;
517 }
518 
519 static inline bool expand_vma(FIXTURE_DATA(proc_maps_race) *self)
520 {
521 	return mremap(self->mod_info->addr, self->page_size,
522 		      self->page_size * 3, 0) != MAP_FAILED;
523 }
524 
525 static inline bool check_shrink_result(struct line_content *mod_last_line,
526 				       struct line_content *mod_first_line,
527 				       struct line_content *restored_last_line,
528 				       struct line_content *restored_first_line)
529 {
530 	/* Make sure only the last vma of the first page is changing */
531 	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
532 	       strcmp(mod_first_line->text, restored_first_line->text) == 0;
533 }
534 
535 static inline bool remap_vma(FIXTURE_DATA(proc_maps_race) *self)
536 {
537 	/*
538 	 * Remap the last page of the next vma into the middle of the vma.
539 	 * This splits the current vma and the first and middle parts (the
540 	 * parts at lower addresses) become the last vma objserved in the
541 	 * first page and the first vma observed in the last page.
542 	 */
543 	return mremap(self->mod_info->next_addr + self->page_size * 2, self->page_size,
544 		      self->page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP,
545 		      self->mod_info->addr + self->page_size) != MAP_FAILED;
546 }
547 
548 static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self)
549 {
550 	return mprotect(self->mod_info->addr + self->page_size, self->page_size,
551 			self->mod_info->prot) == 0;
552 }
553 
554 static inline bool check_remap_result(struct line_content *mod_last_line,
555 				      struct line_content *mod_first_line,
556 				      struct line_content *restored_last_line,
557 				      struct line_content *restored_first_line)
558 {
559 	/* Make sure vmas at the boundaries are changing */
560 	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
561 	       strcmp(mod_first_line->text, restored_first_line->text) != 0;
562 }
563 
564 FIXTURE_SETUP(proc_maps_race)
565 {
566 	const char *verbose = getenv("VERBOSE");
567 	const char *duration = getenv("DURATION");
568 	struct vma_modifier_info *mod_info;
569 	pthread_mutexattr_t mutex_attr;
570 	pthread_condattr_t cond_attr;
571 	unsigned long first_map_addr;
572 	unsigned long last_map_addr;
573 	unsigned long duration_sec;
574 	char fname[32];
575 
576 	self->page_size = (unsigned long)sysconf(_SC_PAGESIZE);
577 	self->verbose = verbose && !strncmp(verbose, "1", 1);
578 	self->maps_file = variant->maps_file;
579 	duration_sec = duration ? atol(duration) : 0;
580 	self->duration_sec = duration_sec ? duration_sec : 5UL;
581 
582 	/*
583 	 * Have to map enough vmas for /proc/pid/maps to contain more than one
584 	 * page worth of vmas. Assume at least 32 bytes per line in maps output
585 	 */
586 	self->vma_count = self->page_size / 32 + 1;
587 	self->shared_mem_size = sizeof(struct vma_modifier_info) + self->vma_count * sizeof(void *);
588 
589 	/* map shared memory for communication with the child process */
590 	self->mod_info = (struct vma_modifier_info *)mmap(NULL, self->shared_mem_size,
591 				PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
592 	ASSERT_NE(self->mod_info, MAP_FAILED);
593 	mod_info = self->mod_info;
594 
595 	/* Initialize shared members */
596 	pthread_mutexattr_init(&mutex_attr);
597 	pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
598 	ASSERT_EQ(pthread_mutex_init(&mod_info->sync_lock, &mutex_attr), 0);
599 	pthread_condattr_init(&cond_attr);
600 	pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
601 	ASSERT_EQ(pthread_cond_init(&mod_info->sync_cond, &cond_attr), 0);
602 	mod_info->vma_count = self->vma_count;
603 	mod_info->curr_state = INIT;
604 	mod_info->exit = false;
605 
606 	self->pid = fork();
607 	if (!self->pid) {
608 		/* Child process modifying the address space */
609 		int prot = PROT_READ | PROT_WRITE;
610 		int i;
611 
612 		for (i = 0; i < mod_info->vma_count; i++) {
613 			mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot,
614 					MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
615 			ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED);
616 			/* change protection in adjacent maps to prevent merging */
617 			prot ^= PROT_WRITE;
618 		}
619 		signal_state(mod_info, CHILD_READY);
620 		wait_for_state(mod_info, PARENT_READY);
621 		while (true) {
622 			signal_state(mod_info, SETUP_READY);
623 			wait_for_state(mod_info, SETUP_MODIFY_MAPS);
624 			if (mod_info->exit)
625 				break;
626 
627 			ASSERT_TRUE(mod_info->vma_modify(self));
628 			signal_state(mod_info, SETUP_MAPS_MODIFIED);
629 			wait_for_state(mod_info, SETUP_RESTORE_MAPS);
630 			ASSERT_TRUE(mod_info->vma_restore(self));
631 			signal_state(mod_info, SETUP_MAPS_RESTORED);
632 
633 			wait_for_state(mod_info, TEST_READY);
634 			while (mod_info->curr_state != TEST_DONE) {
635 				ASSERT_TRUE(mod_info->vma_modify(self));
636 				ASSERT_TRUE(mod_info->vma_restore(self));
637 			}
638 		}
639 		for (i = 0; i < mod_info->vma_count; i++)
640 			munmap(mod_info->child_mapped_addr[i], self->page_size * 3);
641 
642 		exit(0);
643 	}
644 
645 	switch (self->maps_file) {
646 	case MAPS:
647 		sprintf(fname, "/proc/%d/maps", self->pid);
648 		break;
649 	case SMAPS:
650 		sprintf(fname, "/proc/%d/smaps", self->pid);
651 		break;
652 	default:
653 		ksft_exit_fail();
654 	}
655 	self->maps_fd = open(fname, O_RDONLY);
656 	ASSERT_NE(self->maps_fd, -1);
657 
658 	/* Wait for the child to map the VMAs */
659 	wait_for_state(mod_info, CHILD_READY);
660 
661 	/* Read first two pages */
662 	self->page1.data = malloc(self->page_size);
663 	ASSERT_NE(self->page1.data, NULL);
664 	self->page2.data = malloc(self->page_size);
665 	ASSERT_NE(self->page2.data, NULL);
666 
667 	first_map_addr = (unsigned long)mod_info->child_mapped_addr[0];
668 	last_map_addr = (unsigned long)mod_info->child_mapped_addr[mod_info->vma_count - 1];
669 
670 	self->skip_pages = locate_containing_page(self,
671 					min(first_map_addr, last_map_addr),
672 					self->page_size * 3);
673 	ASSERT_NE(self->skip_pages, -1);
674 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
675 
676 	/*
677 	 * Find the addresses corresponding to the last line in the first page
678 	 * and the first line in the last page.
679 	 */
680 	mod_info->addr = NULL;
681 	mod_info->next_addr = NULL;
682 	for (int i = 0; i < mod_info->vma_count; i++) {
683 		if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) {
684 			mod_info->addr = mod_info->child_mapped_addr[i];
685 			mod_info->prot = PROT_READ;
686 			/* Even VMAs have write permission */
687 			if ((i % 2) == 0)
688 				mod_info->prot |= PROT_WRITE;
689 		} else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) {
690 			mod_info->next_addr = mod_info->child_mapped_addr[i];
691 		}
692 
693 		if (mod_info->addr && mod_info->next_addr)
694 			break;
695 	}
696 	ASSERT_TRUE(mod_info->addr && mod_info->next_addr);
697 
698 	signal_state(mod_info, PARENT_READY);
699 }
700 
701 FIXTURE_TEARDOWN(proc_maps_race)
702 {
703 	int status;
704 
705 	stop_vma_modifier(self->mod_info);
706 
707 	free(self->page2.data);
708 	free(self->page1.data);
709 
710 	for (int i = 0; i < self->vma_count; i++)
711 		munmap(self->mod_info->child_mapped_addr[i], self->page_size);
712 	close(self->maps_fd);
713 	waitpid(self->pid, &status, 0);
714 	munmap(self->mod_info, self->shared_mem_size);
715 }
716 
717 TEST_F(proc_maps_race, test_maps_tearing_from_split)
718 {
719 	struct vma_modifier_info *mod_info = self->mod_info;
720 
721 	struct line_content split_last_line;
722 	struct line_content split_first_line;
723 	struct line_content restored_last_line;
724 	struct line_content restored_first_line;
725 
726 	wait_for_state(mod_info, SETUP_READY);
727 
728 	/* re-read the file to avoid using stale data from previous test */
729 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
730 
731 	mod_info->vma_modify = split_vma;
732 	mod_info->vma_restore = merge_vma;
733 	mod_info->vma_mod_check = check_split_result;
734 
735 	report_test_start("Tearing from split", self->verbose);
736 	ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line,
737 					&restored_last_line, &restored_first_line));
738 
739 	/* Now start concurrent modifications for self->duration_sec */
740 	signal_state(mod_info, TEST_READY);
741 
742 	struct line_content new_last_line;
743 	struct line_content new_first_line;
744 	struct timespec start_ts, end_ts;
745 
746 	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
747 	start_test_loop(&start_ts, self->verbose);
748 	do {
749 		bool last_line_changed;
750 		bool first_line_changed;
751 		unsigned long vma_start;
752 		unsigned long vma_end;
753 
754 		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
755 
756 		/* Check if we read vmas after split */
757 		if (!strcmp(new_last_line.text, split_last_line.text)) {
758 			/*
759 			 * The vmas should be consistent with split results,
760 			 * however if vma was concurrently restored after a
761 			 * split, it can be reported twice (first the original
762 			 * split one, then the same vma but extended after the
763 			 * merge) because we found it as the next vma again.
764 			 * In that case new first line will be the same as the
765 			 * last restored line.
766 			 */
767 			ASSERT_FALSE(print_boundaries_on(
768 					strcmp(new_first_line.text, split_first_line.text) &&
769 					strcmp(new_first_line.text, restored_last_line.text),
770 					"Split result invalid", self));
771 		} else {
772 			/* The vmas should be consistent with merge results */
773 			ASSERT_FALSE(print_boundaries_on(
774 					strcmp(new_last_line.text, restored_last_line.text),
775 					"Merge result invalid", self));
776 			ASSERT_FALSE(print_boundaries_on(
777 					strcmp(new_first_line.text, restored_first_line.text),
778 					"Merge result invalid", self));
779 		}
780 		/*
781 		 * First and last lines should change in unison. If the last
782 		 * line changed then the first line should change as well and
783 		 * vice versa.
784 		 */
785 		last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0;
786 		first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0;
787 		ASSERT_EQ(last_line_changed, first_line_changed);
788 		if (self->maps_file == MAPS) {
789 			/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
790 			ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
791 						  &vma_start, &vma_end));
792 			/*
793 			 * The vma at the split address can be either the same as
794 			 * original one (if read before the split) or the same as the
795 			 * first line in the second page (if read after the split).
796 			 */
797 			ASSERT_TRUE((vma_start == self->last_line.start_addr &&
798 				     vma_end == self->last_line.end_addr) ||
799 				    (vma_start == split_first_line.start_addr &&
800 				     vma_end == split_first_line.end_addr));
801 		}
802 		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
803 		end_test_iteration(&end_ts, self->verbose);
804 	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
805 	end_test_loop(self->verbose);
806 
807 	/* Signal the modifyer thread to stop and wait until it exits */
808 	signal_state(mod_info, TEST_DONE);
809 }
810 
811 TEST_F(proc_maps_race, test_maps_tearing_from_resize)
812 {
813 	struct vma_modifier_info *mod_info = self->mod_info;
814 
815 	struct line_content shrunk_last_line;
816 	struct line_content shrunk_first_line;
817 	struct line_content restored_last_line;
818 	struct line_content restored_first_line;
819 
820 	wait_for_state(mod_info, SETUP_READY);
821 
822 	/* re-read the file to avoid using stale data from previous test */
823 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
824 
825 	mod_info->vma_modify = shrink_vma;
826 	mod_info->vma_restore = expand_vma;
827 	mod_info->vma_mod_check = check_shrink_result;
828 
829 	report_test_start("Tearing from resize", self->verbose);
830 	ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line,
831 					&restored_last_line, &restored_first_line));
832 
833 	/* Now start concurrent modifications for self->duration_sec */
834 	signal_state(mod_info, TEST_READY);
835 
836 	struct line_content new_last_line;
837 	struct line_content new_first_line;
838 	struct timespec start_ts, end_ts;
839 
840 	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
841 	start_test_loop(&start_ts, self->verbose);
842 	do {
843 		unsigned long vma_start;
844 		unsigned long vma_end;
845 
846 		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
847 
848 		/* Check if we read vmas after shrinking it */
849 		if (!strcmp(new_last_line.text, shrunk_last_line.text)) {
850 			/*
851 			 * The vmas should be consistent with shrunk results,
852 			 * however if the vma was concurrently restored, it
853 			 * can be reported twice (first as shrunk one, then
854 			 * as restored one) because we found it as the next vma
855 			 * again. In that case new first line will be the same
856 			 * as the last restored line.
857 			 */
858 			ASSERT_FALSE(print_boundaries_on(
859 					strcmp(new_first_line.text, shrunk_first_line.text) &&
860 					strcmp(new_first_line.text, restored_last_line.text),
861 					"Shrink result invalid", self));
862 		} else {
863 			/* The vmas should be consistent with the original/resored state */
864 			ASSERT_FALSE(print_boundaries_on(
865 					strcmp(new_last_line.text, restored_last_line.text),
866 					"Expand result invalid", self));
867 			ASSERT_FALSE(print_boundaries_on(
868 					strcmp(new_first_line.text, restored_first_line.text),
869 					"Expand result invalid", self));
870 		}
871 		if (self->maps_file == MAPS) {
872 			/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
873 			ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr,
874 						  &vma_start, &vma_end));
875 			/*
876 			 * The vma should stay at the same address and have either the
877 			 * original size of 3 pages or 1 page if read after shrinking.
878 			 */
879 			ASSERT_TRUE(vma_start == self->last_line.start_addr &&
880 				    (vma_end - vma_start == self->page_size * 3 ||
881 				     vma_end - vma_start == self->page_size));
882 		}
883 		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
884 		end_test_iteration(&end_ts, self->verbose);
885 	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
886 	end_test_loop(self->verbose);
887 
888 	/* Signal the modifyer thread to stop and wait until it exits */
889 	signal_state(mod_info, TEST_DONE);
890 }
891 
892 TEST_F(proc_maps_race, test_maps_tearing_from_remap)
893 {
894 	struct vma_modifier_info *mod_info = self->mod_info;
895 
896 	struct line_content remapped_last_line;
897 	struct line_content remapped_first_line;
898 	struct line_content restored_last_line;
899 	struct line_content restored_first_line;
900 
901 	wait_for_state(mod_info, SETUP_READY);
902 
903 	/* re-read the file to avoid using stale data from previous test */
904 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
905 
906 	mod_info->vma_modify = remap_vma;
907 	mod_info->vma_restore = patch_vma;
908 	mod_info->vma_mod_check = check_remap_result;
909 
910 	report_test_start("Tearing from remap", self->verbose);
911 	ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line,
912 					&restored_last_line, &restored_first_line));
913 
914 	/* Now start concurrent modifications for self->duration_sec */
915 	signal_state(mod_info, TEST_READY);
916 
917 	struct line_content new_last_line;
918 	struct line_content new_first_line;
919 	struct timespec start_ts, end_ts;
920 
921 	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
922 	start_test_loop(&start_ts, self->verbose);
923 	do {
924 		unsigned long vma_start;
925 		unsigned long vma_end;
926 
927 		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
928 
929 		/* Check if we read vmas after remapping it */
930 		if (!strcmp(new_last_line.text, remapped_last_line.text)) {
931 			/*
932 			 * The vmas should be consistent with remap results,
933 			 * however if the vma was concurrently restored, it
934 			 * can be reported twice (first as split one, then
935 			 * as restored one) because we found it as the next vma
936 			 * again. In that case new first line will be the same
937 			 * as the last restored line.
938 			 */
939 			ASSERT_FALSE(print_boundaries_on(
940 					strcmp(new_first_line.text, remapped_first_line.text) &&
941 					strcmp(new_first_line.text, restored_last_line.text),
942 					"Remap result invalid", self));
943 		} else {
944 			/* The vmas should be consistent with the original/resored state */
945 			ASSERT_FALSE(print_boundaries_on(
946 					strcmp(new_last_line.text, restored_last_line.text),
947 					"Remap restore result invalid", self));
948 			ASSERT_FALSE(print_boundaries_on(
949 					strcmp(new_first_line.text, restored_first_line.text),
950 					"Remap restore result invalid", self));
951 		}
952 		if (self->maps_file == MAPS) {
953 			/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
954 			ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
955 						  &vma_start, &vma_end));
956 			/*
957 			 * The vma should either stay at the same address and have the
958 			 * original size of 3 pages or we should find the remapped vma
959 			 * at the remap destination address with size of 1 page.
960 			 */
961 			ASSERT_TRUE((vma_start == self->last_line.start_addr &&
962 				     vma_end - vma_start == self->page_size * 3) ||
963 				    (vma_start == self->last_line.start_addr + self->page_size &&
964 				     vma_end - vma_start == self->page_size));
965 		}
966 		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
967 		end_test_iteration(&end_ts, self->verbose);
968 	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
969 	end_test_loop(self->verbose);
970 
971 	/* Signal the modifyer thread to stop and wait until it exits */
972 	signal_state(mod_info, TEST_DONE);
973 }
974 
975 TEST_HARNESS_MAIN
976