xref: /linux/tools/testing/selftests/proc/proc-maps-race.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
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 	/* PROT_NONE differs from both readable neighbors. */
494 	return mmap(self->mod_info->addr, self->page_size, PROT_NONE,
495 		    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED;
496 }
497 
498 static inline bool merge_vma(FIXTURE_DATA(proc_maps_race) *self)
499 {
500 	return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot,
501 		    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED;
502 }
503 
504 static inline bool check_split_result(struct line_content *mod_last_line,
505 				      struct line_content *mod_first_line,
506 				      struct line_content *restored_last_line,
507 				      struct line_content *restored_first_line)
508 {
509 	/* Make sure vmas at the boundaries are changing */
510 	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
511 	       strcmp(mod_first_line->text, restored_first_line->text) != 0;
512 }
513 
514 static inline bool shrink_vma(FIXTURE_DATA(proc_maps_race) *self)
515 {
516 	return mremap(self->mod_info->addr, self->page_size * 3,
517 		      self->page_size, 0) != MAP_FAILED;
518 }
519 
520 static inline bool expand_vma(FIXTURE_DATA(proc_maps_race) *self)
521 {
522 	return mremap(self->mod_info->addr, self->page_size,
523 		      self->page_size * 3, 0) != MAP_FAILED;
524 }
525 
526 static inline bool check_shrink_result(struct line_content *mod_last_line,
527 				       struct line_content *mod_first_line,
528 				       struct line_content *restored_last_line,
529 				       struct line_content *restored_first_line)
530 {
531 	/* Make sure only the last vma of the first page is changing */
532 	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
533 	       strcmp(mod_first_line->text, restored_first_line->text) == 0;
534 }
535 
536 static inline bool remap_vma(FIXTURE_DATA(proc_maps_race) *self)
537 {
538 	/*
539 	 * Remap the last page of the next vma into the middle of the vma.
540 	 * This splits the current vma and the first and middle parts (the
541 	 * parts at lower addresses) become the last vma objserved in the
542 	 * first page and the first vma observed in the last page.
543 	 */
544 	return mremap(self->mod_info->next_addr + self->page_size * 2, self->page_size,
545 		      self->page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP,
546 		      self->mod_info->addr + self->page_size) != MAP_FAILED;
547 }
548 
549 static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self)
550 {
551 	return mprotect(self->mod_info->addr + self->page_size, self->page_size,
552 			self->mod_info->prot) == 0;
553 }
554 
555 static inline bool check_remap_result(struct line_content *mod_last_line,
556 				      struct line_content *mod_first_line,
557 				      struct line_content *restored_last_line,
558 				      struct line_content *restored_first_line)
559 {
560 	/* Make sure vmas at the boundaries are changing */
561 	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
562 	       strcmp(mod_first_line->text, restored_first_line->text) != 0;
563 }
564 
565 FIXTURE_SETUP(proc_maps_race)
566 {
567 	const char *verbose = getenv("VERBOSE");
568 	const char *duration = getenv("DURATION");
569 	struct vma_modifier_info *mod_info;
570 	pthread_mutexattr_t mutex_attr;
571 	pthread_condattr_t cond_attr;
572 	unsigned long first_map_addr;
573 	unsigned long last_map_addr;
574 	unsigned long duration_sec;
575 	char fname[32];
576 
577 	self->page_size = (unsigned long)sysconf(_SC_PAGESIZE);
578 	self->verbose = verbose && !strncmp(verbose, "1", 1);
579 	self->maps_file = variant->maps_file;
580 	duration_sec = duration ? atol(duration) : 0;
581 	self->duration_sec = duration_sec ? duration_sec : 5UL;
582 
583 	/*
584 	 * Have to map enough vmas for /proc/pid/maps to contain more than one
585 	 * page worth of vmas. Assume at least 32 bytes per line in maps output
586 	 */
587 	self->vma_count = self->page_size / 32 + 1;
588 	self->shared_mem_size = sizeof(struct vma_modifier_info) + self->vma_count * sizeof(void *);
589 
590 	/* map shared memory for communication with the child process */
591 	self->mod_info = (struct vma_modifier_info *)mmap(NULL, self->shared_mem_size,
592 				PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
593 	ASSERT_NE(self->mod_info, MAP_FAILED);
594 	mod_info = self->mod_info;
595 
596 	/* Initialize shared members */
597 	pthread_mutexattr_init(&mutex_attr);
598 	pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
599 	ASSERT_EQ(pthread_mutex_init(&mod_info->sync_lock, &mutex_attr), 0);
600 	pthread_condattr_init(&cond_attr);
601 	pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
602 	ASSERT_EQ(pthread_cond_init(&mod_info->sync_cond, &cond_attr), 0);
603 	mod_info->vma_count = self->vma_count;
604 	mod_info->curr_state = INIT;
605 	mod_info->exit = false;
606 
607 	self->pid = fork();
608 	if (!self->pid) {
609 		/* Child process modifying the address space */
610 		int prot = PROT_READ | PROT_WRITE;
611 		int i;
612 
613 		for (i = 0; i < mod_info->vma_count; i++) {
614 			mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot,
615 					MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
616 			ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED);
617 			/* change protection in adjacent maps to prevent merging */
618 			prot ^= PROT_WRITE;
619 		}
620 		signal_state(mod_info, CHILD_READY);
621 		wait_for_state(mod_info, PARENT_READY);
622 		while (true) {
623 			signal_state(mod_info, SETUP_READY);
624 			wait_for_state(mod_info, SETUP_MODIFY_MAPS);
625 			if (mod_info->exit)
626 				break;
627 
628 			ASSERT_TRUE(mod_info->vma_modify(self));
629 			signal_state(mod_info, SETUP_MAPS_MODIFIED);
630 			wait_for_state(mod_info, SETUP_RESTORE_MAPS);
631 			ASSERT_TRUE(mod_info->vma_restore(self));
632 			signal_state(mod_info, SETUP_MAPS_RESTORED);
633 
634 			wait_for_state(mod_info, TEST_READY);
635 			while (mod_info->curr_state != TEST_DONE) {
636 				ASSERT_TRUE(mod_info->vma_modify(self));
637 				ASSERT_TRUE(mod_info->vma_restore(self));
638 			}
639 		}
640 		for (i = 0; i < mod_info->vma_count; i++)
641 			munmap(mod_info->child_mapped_addr[i], self->page_size * 3);
642 
643 		exit(0);
644 	}
645 
646 	switch (self->maps_file) {
647 	case MAPS:
648 		sprintf(fname, "/proc/%d/maps", self->pid);
649 		break;
650 	case SMAPS:
651 		sprintf(fname, "/proc/%d/smaps", self->pid);
652 		break;
653 	default:
654 		ksft_exit_fail();
655 	}
656 	self->maps_fd = open(fname, O_RDONLY);
657 	ASSERT_NE(self->maps_fd, -1);
658 
659 	/* Wait for the child to map the VMAs */
660 	wait_for_state(mod_info, CHILD_READY);
661 
662 	/* Read first two pages */
663 	self->page1.data = malloc(self->page_size);
664 	ASSERT_NE(self->page1.data, NULL);
665 	self->page2.data = malloc(self->page_size);
666 	ASSERT_NE(self->page2.data, NULL);
667 
668 	first_map_addr = (unsigned long)mod_info->child_mapped_addr[0];
669 	last_map_addr = (unsigned long)mod_info->child_mapped_addr[mod_info->vma_count - 1];
670 
671 	self->skip_pages = locate_containing_page(self,
672 					min(first_map_addr, last_map_addr),
673 					self->page_size * 3);
674 	ASSERT_NE(self->skip_pages, -1);
675 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
676 
677 	/*
678 	 * Find the addresses corresponding to the last line in the first page
679 	 * and the first line in the last page.
680 	 */
681 	mod_info->addr = NULL;
682 	mod_info->next_addr = NULL;
683 	for (int i = 0; i < mod_info->vma_count; i++) {
684 		if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) {
685 			mod_info->addr = mod_info->child_mapped_addr[i];
686 			mod_info->prot = PROT_READ;
687 			/* Even VMAs have write permission */
688 			if ((i % 2) == 0)
689 				mod_info->prot |= PROT_WRITE;
690 		} else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) {
691 			mod_info->next_addr = mod_info->child_mapped_addr[i];
692 		}
693 
694 		if (mod_info->addr && mod_info->next_addr)
695 			break;
696 	}
697 	ASSERT_TRUE(mod_info->addr && mod_info->next_addr);
698 
699 	signal_state(mod_info, PARENT_READY);
700 }
701 
702 FIXTURE_TEARDOWN(proc_maps_race)
703 {
704 	int status;
705 
706 	stop_vma_modifier(self->mod_info);
707 
708 	free(self->page2.data);
709 	free(self->page1.data);
710 
711 	for (int i = 0; i < self->vma_count; i++)
712 		munmap(self->mod_info->child_mapped_addr[i], self->page_size);
713 	close(self->maps_fd);
714 	waitpid(self->pid, &status, 0);
715 	munmap(self->mod_info, self->shared_mem_size);
716 }
717 
718 TEST_F(proc_maps_race, test_maps_tearing_from_split)
719 {
720 	struct vma_modifier_info *mod_info = self->mod_info;
721 
722 	struct line_content split_last_line;
723 	struct line_content split_first_line;
724 	struct line_content restored_last_line;
725 	struct line_content restored_first_line;
726 
727 	wait_for_state(mod_info, SETUP_READY);
728 
729 	/* re-read the file to avoid using stale data from previous test */
730 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
731 
732 	mod_info->vma_modify = split_vma;
733 	mod_info->vma_restore = merge_vma;
734 	mod_info->vma_mod_check = check_split_result;
735 
736 	report_test_start("Tearing from split", self->verbose);
737 	ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line,
738 					&restored_last_line, &restored_first_line));
739 
740 	/* Now start concurrent modifications for self->duration_sec */
741 	signal_state(mod_info, TEST_READY);
742 
743 	struct line_content new_last_line;
744 	struct line_content new_first_line;
745 	struct timespec start_ts, end_ts;
746 
747 	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
748 	start_test_loop(&start_ts, self->verbose);
749 	do {
750 		bool last_line_changed;
751 		bool first_line_changed;
752 		unsigned long vma_start;
753 		unsigned long vma_end;
754 
755 		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
756 
757 		/* Check if we read vmas after split */
758 		if (!strcmp(new_last_line.text, split_last_line.text)) {
759 			/*
760 			 * The vmas should be consistent with split results,
761 			 * however if vma was concurrently restored after a
762 			 * split, it can be reported twice (first the original
763 			 * split one, then the same vma but extended after the
764 			 * merge) because we found it as the next vma again.
765 			 * In that case new first line will be the same as the
766 			 * last restored line.
767 			 */
768 			ASSERT_FALSE(print_boundaries_on(
769 					strcmp(new_first_line.text, split_first_line.text) &&
770 					strcmp(new_first_line.text, restored_last_line.text),
771 					"Split result invalid", self));
772 		} else {
773 			/* The vmas should be consistent with merge results */
774 			ASSERT_FALSE(print_boundaries_on(
775 					strcmp(new_last_line.text, restored_last_line.text),
776 					"Merge result invalid", self));
777 			ASSERT_FALSE(print_boundaries_on(
778 					strcmp(new_first_line.text, restored_first_line.text),
779 					"Merge result invalid", self));
780 		}
781 		/*
782 		 * First and last lines should change in unison. If the last
783 		 * line changed then the first line should change as well and
784 		 * vice versa.
785 		 */
786 		last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0;
787 		first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0;
788 		ASSERT_EQ(last_line_changed, first_line_changed);
789 		if (self->maps_file == MAPS) {
790 			/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
791 			ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
792 						  &vma_start, &vma_end));
793 			/*
794 			 * The vma at the split address can be either the same as
795 			 * original one (if read before the split) or the same as the
796 			 * first line in the second page (if read after the split).
797 			 */
798 			ASSERT_TRUE((vma_start == self->last_line.start_addr &&
799 				     vma_end == self->last_line.end_addr) ||
800 				    (vma_start == split_first_line.start_addr &&
801 				     vma_end == split_first_line.end_addr));
802 		}
803 		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
804 		end_test_iteration(&end_ts, self->verbose);
805 	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
806 	end_test_loop(self->verbose);
807 
808 	/* Signal the modifyer thread to stop and wait until it exits */
809 	signal_state(mod_info, TEST_DONE);
810 }
811 
812 TEST_F(proc_maps_race, test_maps_tearing_from_resize)
813 {
814 	struct vma_modifier_info *mod_info = self->mod_info;
815 
816 	struct line_content shrunk_last_line;
817 	struct line_content shrunk_first_line;
818 	struct line_content restored_last_line;
819 	struct line_content restored_first_line;
820 
821 	wait_for_state(mod_info, SETUP_READY);
822 
823 	/* re-read the file to avoid using stale data from previous test */
824 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
825 
826 	mod_info->vma_modify = shrink_vma;
827 	mod_info->vma_restore = expand_vma;
828 	mod_info->vma_mod_check = check_shrink_result;
829 
830 	report_test_start("Tearing from resize", self->verbose);
831 	ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line,
832 					&restored_last_line, &restored_first_line));
833 
834 	/* Now start concurrent modifications for self->duration_sec */
835 	signal_state(mod_info, TEST_READY);
836 
837 	struct line_content new_last_line;
838 	struct line_content new_first_line;
839 	struct timespec start_ts, end_ts;
840 
841 	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
842 	start_test_loop(&start_ts, self->verbose);
843 	do {
844 		unsigned long vma_start;
845 		unsigned long vma_end;
846 
847 		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
848 
849 		/* Check if we read vmas after shrinking it */
850 		if (!strcmp(new_last_line.text, shrunk_last_line.text)) {
851 			/*
852 			 * The vmas should be consistent with shrunk results,
853 			 * however if the vma was concurrently restored, it
854 			 * can be reported twice (first as shrunk one, then
855 			 * as restored one) because we found it as the next vma
856 			 * again. In that case new first line will be the same
857 			 * as the last restored line.
858 			 */
859 			ASSERT_FALSE(print_boundaries_on(
860 					strcmp(new_first_line.text, shrunk_first_line.text) &&
861 					strcmp(new_first_line.text, restored_last_line.text),
862 					"Shrink result invalid", self));
863 		} else {
864 			/* The vmas should be consistent with the original/resored state */
865 			ASSERT_FALSE(print_boundaries_on(
866 					strcmp(new_last_line.text, restored_last_line.text),
867 					"Expand result invalid", self));
868 			ASSERT_FALSE(print_boundaries_on(
869 					strcmp(new_first_line.text, restored_first_line.text),
870 					"Expand result invalid", self));
871 		}
872 		if (self->maps_file == MAPS) {
873 			/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
874 			ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr,
875 						  &vma_start, &vma_end));
876 			/*
877 			 * The vma should stay at the same address and have either the
878 			 * original size of 3 pages or 1 page if read after shrinking.
879 			 */
880 			ASSERT_TRUE(vma_start == self->last_line.start_addr &&
881 				    (vma_end - vma_start == self->page_size * 3 ||
882 				     vma_end - vma_start == self->page_size));
883 		}
884 		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
885 		end_test_iteration(&end_ts, self->verbose);
886 	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
887 	end_test_loop(self->verbose);
888 
889 	/* Signal the modifyer thread to stop and wait until it exits */
890 	signal_state(mod_info, TEST_DONE);
891 }
892 
893 TEST_F(proc_maps_race, test_maps_tearing_from_remap)
894 {
895 	struct vma_modifier_info *mod_info = self->mod_info;
896 
897 	struct line_content remapped_last_line;
898 	struct line_content remapped_first_line;
899 	struct line_content restored_last_line;
900 	struct line_content restored_first_line;
901 
902 	wait_for_state(mod_info, SETUP_READY);
903 
904 	/* re-read the file to avoid using stale data from previous test */
905 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
906 
907 	mod_info->vma_modify = remap_vma;
908 	mod_info->vma_restore = patch_vma;
909 	mod_info->vma_mod_check = check_remap_result;
910 
911 	report_test_start("Tearing from remap", self->verbose);
912 	ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line,
913 					&restored_last_line, &restored_first_line));
914 
915 	/* Now start concurrent modifications for self->duration_sec */
916 	signal_state(mod_info, TEST_READY);
917 
918 	struct line_content new_last_line;
919 	struct line_content new_first_line;
920 	struct timespec start_ts, end_ts;
921 
922 	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
923 	start_test_loop(&start_ts, self->verbose);
924 	do {
925 		unsigned long vma_start;
926 		unsigned long vma_end;
927 
928 		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
929 
930 		/* Check if we read vmas after remapping it */
931 		if (!strcmp(new_last_line.text, remapped_last_line.text)) {
932 			/*
933 			 * The vmas should be consistent with remap results,
934 			 * however if the vma was concurrently restored, it
935 			 * can be reported twice (first as split one, then
936 			 * as restored one) because we found it as the next vma
937 			 * again. In that case new first line will be the same
938 			 * as the last restored line.
939 			 */
940 			ASSERT_FALSE(print_boundaries_on(
941 					strcmp(new_first_line.text, remapped_first_line.text) &&
942 					strcmp(new_first_line.text, restored_last_line.text),
943 					"Remap result invalid", self));
944 		} else {
945 			/* The vmas should be consistent with the original/resored state */
946 			ASSERT_FALSE(print_boundaries_on(
947 					strcmp(new_last_line.text, restored_last_line.text),
948 					"Remap restore result invalid", self));
949 			ASSERT_FALSE(print_boundaries_on(
950 					strcmp(new_first_line.text, restored_first_line.text),
951 					"Remap restore result invalid", self));
952 		}
953 		if (self->maps_file == MAPS) {
954 			/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
955 			ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
956 						  &vma_start, &vma_end));
957 			/*
958 			 * The vma should either stay at the same address and have the
959 			 * original size of 3 pages or we should find the remapped vma
960 			 * at the remap destination address with size of 1 page.
961 			 */
962 			ASSERT_TRUE((vma_start == self->last_line.start_addr &&
963 				     vma_end - vma_start == self->page_size * 3) ||
964 				    (vma_start == self->last_line.start_addr + self->page_size &&
965 				     vma_end - vma_start == self->page_size));
966 		}
967 		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
968 		end_test_iteration(&end_ts, self->verbose);
969 	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
970 	end_test_loop(self->verbose);
971 
972 	/* Signal the modifyer thread to stop and wait until it exits */
973 	signal_state(mod_info, TEST_DONE);
974 }
975 
976 TEST_HARNESS_MAIN
977