xref: /linux/tools/testing/selftests/proc/proc-maps-race.c (revision ca5480fb53fa73cb5816b696b103826daedab1ca)
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 enum smaps_rollup_stat {
84 	Rss,
85 	Pss,
86 	Pss_Dirty,
87 	Pss_Anon,
88 	Pss_File,
89 	Pss_Shmem,
90 	Shared_Clean,
91 	Shared_Dirty,
92 	Private_Clean,
93 	Private_Dirty,
94 	Referenced,
95 	Anonymous,
96 	KSM,
97 	LazyFree,
98 	AnonHugePages,
99 	ShmemPmdMapped,
100 	FilePmdMapped,
101 	Shared_Hugetlb,
102 	Private_Hugetlb,
103 	Swap,
104 	SwapPss,
105 	Locked,
106 	RollupFieldCount
107 };
108 
109 static const char *smaps_rollup_stat_names[RollupFieldCount] = {
110 	"Rss",
111 	"Pss",
112 	"Pss_Dirty",
113 	"Pss_Anon",
114 	"Pss_File",
115 	"Pss_Shmem",
116 	"Shared_Clean",
117 	"Shared_Dirty",
118 	"Private_Clean",
119 	"Private_Dirty",
120 	"Referenced",
121 	"Anonymous",
122 	"KSM",
123 	"LazyFree",
124 	"AnonHugePages",
125 	"ShmemPmdMapped",
126 	"FilePmdMapped",
127 	"Shared_Hugetlb",
128 	"Private_Hugetlb",
129 	"Swap",
130 	"SwapPss",
131 	"Locked",
132 };
133 
134 struct smaps_rollup_stats {
135 	unsigned long values[RollupFieldCount];
136 };
137 
138 FIXTURE(proc_maps_race)
139 {
140 	struct vma_modifier_info *mod_info;
141 	struct page_content page1;
142 	struct page_content page2;
143 	struct line_content last_line;
144 	struct line_content first_line;
145 	unsigned long duration_sec;
146 	enum maps_file maps_file;
147 	int shared_mem_size;
148 	int skip_pages;
149 	int rollup_fd;
150 	int page_size;
151 	int vma_count;
152 	bool verbose;
153 	int maps_fd;
154 	pid_t pid;
155 };
156 
157 FIXTURE_VARIANT(proc_maps_race)
158 {
159 	const enum maps_file maps_file;
160 };
161 
162 FIXTURE_VARIANT_ADD(proc_maps_race, maps) {
163 	.maps_file = MAPS,
164 };
165 
166 FIXTURE_VARIANT_ADD(proc_maps_race, smaps) {
167 	.maps_file = SMAPS,
168 };
169 
170 typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self);
171 typedef bool (*vma_mod_result_check_op)(struct line_content *mod_last_line,
172 					struct line_content *mod_first_line,
173 					struct line_content *restored_last_line,
174 					struct line_content *restored_first_line);
175 
176 struct vma_modifier_info {
177 	int vma_count;
178 	void *addr;
179 	int prot;
180 	void *next_addr;
181 	vma_modifier_op vma_modify;
182 	vma_modifier_op vma_restore;
183 	vma_mod_result_check_op vma_mod_check;
184 	pthread_mutex_t sync_lock;
185 	pthread_cond_t sync_cond;
186 	enum test_state curr_state;
187 	bool exit;
188 	void *child_mapped_addr[];
189 };
190 
191 static bool read_page(FIXTURE_DATA(proc_maps_race) *self, int fd,
192 		      struct page_content *page)
193 {
194 	ssize_t  bytes_read;
195 
196 	bytes_read = read(fd, page->data, self->page_size);
197 	if (bytes_read <= 0)
198 		return false;
199 
200 	/* Make sure data always ends with a newline character. */
201 	if (page->data[bytes_read - 1] != '\n')
202 		return false;
203 
204 	page->size = bytes_read;
205 
206 	return true;
207 }
208 
209 static bool parse_vma_line(char *line_start, char *line_end,
210 			   unsigned long *start, unsigned long *end)
211 {
212 	bool found;
213 
214 	*line_end = '\0'; /* stop sscanf at the EOL */
215 	found = (sscanf(line_start, "%lx-%lx", start, end) == 2);
216 	*line_end = '\n';
217 
218 	return found;
219 }
220 
221 static int locate_containing_page(FIXTURE_DATA(proc_maps_race) *self,
222 				  unsigned long addr, unsigned long size)
223 {
224 	unsigned long start, end;
225 	int page = 0;
226 
227 	if (lseek(self->maps_fd, 0, SEEK_SET) < 0)
228 		return -1;
229 
230 	while (true) {
231 		char *curr_pos;
232 		char *end_pos;
233 
234 		if (!read_page(self, self->maps_fd, &self->page1))
235 			return -1;
236 
237 		curr_pos = self->page1.data;
238 		end_pos = self->page1.data + self->page1.size;
239 		while (curr_pos < end_pos) {
240 			char *line_end;
241 
242 			line_end = strchr(curr_pos, '\n');
243 			if (!line_end)
244 				break;
245 
246 			if (parse_vma_line(curr_pos, line_end, &start, &end) &&
247 			    start == addr && end == addr + size)
248 				return page;
249 
250 			curr_pos = line_end + 1;
251 		}
252 		page++;
253 	}
254 
255 	return 0;
256 }
257 
258 static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self)
259 {
260 	if (lseek(self->maps_fd, 0, SEEK_SET) < 0)
261 		return false;
262 
263 	for (int i = 0; i < self->skip_pages; i++)
264 		if (!read_page(self, self->maps_fd, &self->page1))
265 			return false;
266 
267 	return read_page(self, self->maps_fd, &self->page1) &&
268 	       read_page(self, self->maps_fd, &self->page2);
269 }
270 
271 static void copy_line(const char *line_start, const char *line_end,
272 		      char *buf, size_t buf_size)
273 {
274 	size_t len = min(line_end - line_start, buf_size - 1);
275 
276 	strncpy(buf, line_start, len);
277 	buf[len] = '\0';
278 }
279 
280 static void copy_first_line(struct page_content *page, char *first_line,
281 			    size_t line_size)
282 {
283 	copy_line(page->data, strchr(page->data, '\n'), first_line, line_size);
284 }
285 
286 static void copy_last_line(struct page_content *page, char *last_line,
287 			   size_t line_size)
288 {
289 	/* Get the last line in the first page */
290 	const char *end = page->data + page->size - 1;
291 	/* skip last newline */
292 	const char *pos = end - 1;
293 
294 	/* search previous newline */
295 	while (pos[-1] != '\n')
296 		pos--;
297 
298 	copy_line(pos, end, last_line, line_size);
299 }
300 
301 static bool copy_first_entry(struct page_content *page, char *first_line,
302 			     size_t line_size)
303 {
304 	char *start_pos = page->data;
305 
306 	while (start_pos < page->data + page->size) {
307 		unsigned long start_addr;
308 		unsigned long end_addr;
309 		char *end_pos;
310 
311 		end_pos = strchr(start_pos, '\n');
312 		if (!end_pos)
313 			break;
314 
315 		if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) {
316 			copy_line(start_pos, end_pos, first_line, line_size);
317 			return true;
318 		}
319 
320 		start_pos = end_pos + 1;
321 	}
322 
323 	return false;
324 }
325 
326 static bool copy_last_entry(struct page_content *page, char *last_line,
327 			    size_t line_size)
328 {
329 	char *end_pos = page->data + page->size - 1;
330 	char *start_pos;
331 
332 	while (end_pos > page->data) {
333 		unsigned long start_addr;
334 		unsigned long end_addr;
335 
336 		/* skip last newline */
337 		start_pos = end_pos - 1;
338 		/* search previous newline */
339 		while (start_pos > page->data && start_pos[-1] != '\n')
340 			start_pos--;
341 		if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) {
342 			copy_line(start_pos, end_pos, last_line, line_size);
343 			return true;
344 		}
345 
346 		end_pos = start_pos - 1;
347 	}
348 
349 	return false;
350 }
351 
352 /* Read the last line of the first page and the first line of the second page */
353 static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self,
354 				struct line_content *last_line,
355 				struct line_content *first_line)
356 {
357 	if (!read_two_pages(self))
358 		return false;
359 
360 	if (self->maps_file == MAPS) {
361 		copy_last_line(&self->page1, last_line->text, LINE_MAX_SIZE);
362 		copy_first_line(&self->page2, first_line->text, LINE_MAX_SIZE);
363 	} else if (self->maps_file == SMAPS) {
364 		if (!copy_last_entry(&self->page1, last_line->text, LINE_MAX_SIZE) ||
365 		    !copy_first_entry(&self->page2, first_line->text, LINE_MAX_SIZE))
366 			return false;
367 	} else {
368 		return false;
369 	}
370 
371 	return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr,
372 		      &last_line->end_addr) == 2 &&
373 	       sscanf(first_line->text, "%lx-%lx", &first_line->start_addr,
374 		      &first_line->end_addr) == 2;
375 }
376 
377 static bool parse_smaps_rollup(FIXTURE_DATA(proc_maps_race) *self,
378 		struct smaps_rollup_stats *stats)
379 {
380 	unsigned int dev_maj, dev_min, inode;
381 	unsigned long start, end, offs;
382 	unsigned long value;
383 	char name[32], perm[5];
384 	char *curr_pos;
385 	char *end_pos;
386 	char *line_end;
387 
388 	if (lseek(self->rollup_fd, 0, SEEK_SET) < 0)
389 		return false;
390 
391 	if (!read_page(self, self->rollup_fd, &self->page1))
392 		return false;
393 
394 	curr_pos = self->page1.data;
395 	end_pos = self->page1.data + self->page1.size;
396 
397 	line_end = strchr(curr_pos, '\n');
398 	if (!line_end)
399 		return false;
400 
401 	if (sscanf(curr_pos, "%lx-%lx %4s %lx %u:%u %u %31s",
402 		&start, &end, perm, &offs, &dev_maj, &dev_min, &inode, name) != 8)
403 		return false;
404 
405 	if (strcmp(name, "[rollup]"))
406 		return false;
407 
408 	for (int stat = 0; stat < ARRAY_SIZE(smaps_rollup_stat_names); stat++) {
409 		int len;
410 
411 		curr_pos = line_end + 1;
412 		if (curr_pos >= end_pos)
413 			return false;
414 
415 		line_end = strchr(curr_pos, '\n');
416 		if (!line_end)
417 			return false;
418 
419 		if (sscanf(curr_pos, "%31s %lu kB", name, &value) != 2)
420 			return false;
421 
422 		len = strlen(name);
423 		if (name[len - 1] != ':' || strncmp(name, smaps_rollup_stat_names[stat], len - 1))
424 			return false;
425 
426 		stats->values[stat] = value;
427 	}
428 
429 	return true;
430 }
431 
432 /* Thread synchronization routines */
433 static void wait_for_state(struct vma_modifier_info *mod_info, enum test_state state)
434 {
435 	pthread_mutex_lock(&mod_info->sync_lock);
436 	while (mod_info->curr_state != state)
437 		pthread_cond_wait(&mod_info->sync_cond, &mod_info->sync_lock);
438 	pthread_mutex_unlock(&mod_info->sync_lock);
439 }
440 
441 static void signal_state(struct vma_modifier_info *mod_info, enum test_state state)
442 {
443 	pthread_mutex_lock(&mod_info->sync_lock);
444 	mod_info->curr_state = state;
445 	pthread_cond_signal(&mod_info->sync_cond);
446 	pthread_mutex_unlock(&mod_info->sync_lock);
447 }
448 
449 static void stop_vma_modifier(struct vma_modifier_info *mod_info)
450 {
451 	wait_for_state(mod_info, SETUP_READY);
452 	mod_info->exit = true;
453 	signal_state(mod_info, SETUP_MODIFY_MAPS);
454 }
455 
456 static void print_first_lines(char *text, int nr)
457 {
458 	const char *end = text;
459 
460 	while (nr && (end = strchr(end, '\n')) != NULL) {
461 		nr--;
462 		end++;
463 	}
464 
465 	if (end) {
466 		int offs = end - text;
467 
468 		text[offs] = '\0';
469 		printf("%s", text);
470 		text[offs] = '\n';
471 		printf("\n");
472 	} else {
473 		printf("%s", text);
474 	}
475 }
476 
477 static void print_last_lines(char *text, int nr)
478 {
479 	const char *start = text + strlen(text);
480 
481 	nr++; /* to ignore the last newline */
482 	while (nr) {
483 		while (start > text && *start != '\n')
484 			start--;
485 		nr--;
486 		start--;
487 	}
488 	printf("%s", start);
489 }
490 
491 static void print_boundaries(const char *title, FIXTURE_DATA(proc_maps_race) *self)
492 {
493 	if (!self->verbose)
494 		return;
495 
496 	printf("%s", title);
497 	/* Print 3 boundary lines from each page */
498 	print_last_lines(self->page1.data, 3);
499 	printf("-----------------page boundary-----------------\n");
500 	print_first_lines(self->page2.data, 3);
501 }
502 
503 static bool print_boundaries_on(bool condition, const char *title,
504 				FIXTURE_DATA(proc_maps_race) *self)
505 {
506 	if (self->verbose && condition)
507 		print_boundaries(title, self);
508 
509 	return condition;
510 }
511 
512 static void print_smaps_rollup_stats(const char *title, FIXTURE_DATA(proc_maps_race) *self,
513 		struct smaps_rollup_stats *stats)
514 {
515 	printf("%s", title);
516 	for (int stat = 0; stat < ARRAY_SIZE(smaps_rollup_stat_names); stat++)
517 		printf("%64s %lu kB\n", smaps_rollup_stat_names[stat], stats->values[stat]);
518 }
519 
520 static bool cmp_smaps_rollup_stat(struct smaps_rollup_stats *s1,
521 		struct smaps_rollup_stats *s2, enum smaps_rollup_stat stat)
522 {
523 	return s1->values[stat] == s2->values[stat];
524 }
525 
526 static bool compare_smaps_rollup(FIXTURE_DATA(proc_maps_race) *self,
527 		struct smaps_rollup_stats *expected,
528 		struct smaps_rollup_stats *actual)
529 {
530 	/*
531 	 * Clean/dirty metrics might change but Pss-related ones
532 	 * should stay constant.
533 	 */
534 	if (cmp_smaps_rollup_stat(expected, actual, Pss) &&
535 	    cmp_smaps_rollup_stat(expected, actual, Pss_Anon) &&
536 	    cmp_smaps_rollup_stat(expected, actual, Pss_File) &&
537 	    cmp_smaps_rollup_stat(expected, actual, Pss_Shmem))
538 		return true;
539 
540 	print_smaps_rollup_stats("Expected stats:", self, expected);
541 	print_smaps_rollup_stats("Actual stats:", self, actual);
542 
543 	return false;
544 }
545 
546 static void report_test_start(const char *name, bool verbose)
547 {
548 	if (verbose)
549 		printf("==== %s ====\n", name);
550 }
551 
552 static struct timespec print_ts;
553 
554 static void start_test_loop(struct timespec *ts, bool verbose)
555 {
556 	if (verbose)
557 		print_ts.tv_sec = ts->tv_sec;
558 }
559 
560 static void end_test_iteration(struct timespec *ts, bool verbose)
561 {
562 	if (!verbose)
563 		return;
564 
565 	/* Update every second */
566 	if (print_ts.tv_sec == ts->tv_sec)
567 		return;
568 
569 	printf(".");
570 	fflush(stdout);
571 	print_ts.tv_sec = ts->tv_sec;
572 }
573 
574 static void end_test_loop(bool verbose)
575 {
576 	if (verbose)
577 		printf("\n");
578 }
579 
580 static bool capture_mod_pattern(FIXTURE_DATA(proc_maps_race) *self,
581 				struct line_content *mod_last_line,
582 				struct line_content *mod_first_line,
583 				struct line_content *restored_last_line,
584 				struct line_content *restored_first_line)
585 {
586 	print_boundaries("Before modification", self);
587 
588 	signal_state(self->mod_info, SETUP_MODIFY_MAPS);
589 	wait_for_state(self->mod_info, SETUP_MAPS_MODIFIED);
590 
591 	/* Copy last line of the first page and first line of the last page */
592 	if (!read_boundary_lines(self, mod_last_line, mod_first_line))
593 		return false;
594 
595 	print_boundaries("After modification", self);
596 
597 	signal_state(self->mod_info, SETUP_RESTORE_MAPS);
598 	wait_for_state(self->mod_info, SETUP_MAPS_RESTORED);
599 
600 	/* Copy last line of the first page and first line of the last page */
601 	if (!read_boundary_lines(self, restored_last_line, restored_first_line))
602 		return false;
603 
604 	print_boundaries("After restore", self);
605 
606 	if (!self->mod_info->vma_mod_check(mod_last_line, mod_first_line,
607 					   restored_last_line, restored_first_line))
608 		return false;
609 
610 	/*
611 	 * The content of these lines after modify+resore should be the same
612 	 * as the original.
613 	 */
614 	return strcmp(restored_last_line->text, self->last_line.text) == 0 &&
615 	       strcmp(restored_first_line->text, self->first_line.text) == 0;
616 }
617 
618 static bool query_addr_at(int maps_fd, void *addr,
619 			  unsigned long *vma_start, unsigned long *vma_end)
620 {
621 	struct procmap_query q;
622 
623 	memset(&q, 0, sizeof(q));
624 	q.size = sizeof(q);
625 	/* Find the VMA at the split address */
626 	q.query_addr = (unsigned long long)addr;
627 	q.query_flags = 0;
628 	if (ioctl(maps_fd, PROCMAP_QUERY, &q))
629 		return false;
630 
631 	*vma_start = q.vma_start;
632 	*vma_end = q.vma_end;
633 
634 	return true;
635 }
636 
637 static inline bool split_vma(FIXTURE_DATA(proc_maps_race) *self)
638 {
639 	/* PROT_NONE differs from both readable neighbors. */
640 	return mmap(self->mod_info->addr, self->page_size, PROT_NONE,
641 		    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED;
642 }
643 
644 static inline bool merge_vma(FIXTURE_DATA(proc_maps_race) *self)
645 {
646 	return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot,
647 		    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED;
648 }
649 
650 static inline bool check_split_result(struct line_content *mod_last_line,
651 				      struct line_content *mod_first_line,
652 				      struct line_content *restored_last_line,
653 				      struct line_content *restored_first_line)
654 {
655 	/* Make sure vmas at the boundaries are changing */
656 	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
657 	       strcmp(mod_first_line->text, restored_first_line->text) != 0;
658 }
659 
660 static inline bool shrink_vma(FIXTURE_DATA(proc_maps_race) *self)
661 {
662 	return mremap(self->mod_info->addr, self->page_size * 3,
663 		      self->page_size, 0) != MAP_FAILED;
664 }
665 
666 static inline bool expand_vma(FIXTURE_DATA(proc_maps_race) *self)
667 {
668 	return mremap(self->mod_info->addr, self->page_size,
669 		      self->page_size * 3, 0) != MAP_FAILED;
670 }
671 
672 static inline bool check_shrink_result(struct line_content *mod_last_line,
673 				       struct line_content *mod_first_line,
674 				       struct line_content *restored_last_line,
675 				       struct line_content *restored_first_line)
676 {
677 	/* Make sure only the last vma of the first page is changing */
678 	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
679 	       strcmp(mod_first_line->text, restored_first_line->text) == 0;
680 }
681 
682 static inline bool remap_vma(FIXTURE_DATA(proc_maps_race) *self)
683 {
684 	/*
685 	 * Remap the last page of the next vma into the middle of the vma.
686 	 * This splits the current vma and the first and middle parts (the
687 	 * parts at lower addresses) become the last vma objserved in the
688 	 * first page and the first vma observed in the last page.
689 	 */
690 	return mremap(self->mod_info->next_addr + self->page_size * 2, self->page_size,
691 		      self->page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP,
692 		      self->mod_info->addr + self->page_size) != MAP_FAILED;
693 }
694 
695 static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self)
696 {
697 	return mprotect(self->mod_info->addr + self->page_size, self->page_size,
698 			self->mod_info->prot) == 0;
699 }
700 
701 static inline bool check_remap_result(struct line_content *mod_last_line,
702 				      struct line_content *mod_first_line,
703 				      struct line_content *restored_last_line,
704 				      struct line_content *restored_first_line)
705 {
706 	/* Make sure vmas at the boundaries are changing */
707 	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
708 	       strcmp(mod_first_line->text, restored_first_line->text) != 0;
709 }
710 
711 FIXTURE_SETUP(proc_maps_race)
712 {
713 	const char *verbose = getenv("VERBOSE");
714 	const char *duration = getenv("DURATION");
715 	struct vma_modifier_info *mod_info;
716 	pthread_mutexattr_t mutex_attr;
717 	pthread_condattr_t cond_attr;
718 	unsigned long first_map_addr;
719 	unsigned long last_map_addr;
720 	unsigned long duration_sec;
721 	char rollup_fname[32];
722 	char fname[32];
723 
724 	self->page_size = (unsigned long)sysconf(_SC_PAGESIZE);
725 	self->verbose = verbose && !strncmp(verbose, "1", 1);
726 	self->maps_file = variant->maps_file;
727 	duration_sec = duration ? atol(duration) : 0;
728 	self->duration_sec = duration_sec ? duration_sec : 5UL;
729 
730 	/*
731 	 * Have to map enough vmas for /proc/pid/maps to contain more than one
732 	 * page worth of vmas. Assume at least 32 bytes per line in maps output
733 	 */
734 	self->vma_count = self->page_size / 32 + 1;
735 	self->shared_mem_size = sizeof(struct vma_modifier_info) + self->vma_count * sizeof(void *);
736 
737 	/* map shared memory for communication with the child process */
738 	self->mod_info = (struct vma_modifier_info *)mmap(NULL, self->shared_mem_size,
739 				PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
740 	ASSERT_NE(self->mod_info, MAP_FAILED);
741 	mod_info = self->mod_info;
742 
743 	/* Initialize shared members */
744 	pthread_mutexattr_init(&mutex_attr);
745 	pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
746 	ASSERT_EQ(pthread_mutex_init(&mod_info->sync_lock, &mutex_attr), 0);
747 	pthread_condattr_init(&cond_attr);
748 	pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
749 	ASSERT_EQ(pthread_cond_init(&mod_info->sync_cond, &cond_attr), 0);
750 	mod_info->vma_count = self->vma_count;
751 	mod_info->curr_state = INIT;
752 	mod_info->exit = false;
753 
754 	self->pid = fork();
755 	if (!self->pid) {
756 		/* Child process modifying the address space */
757 		int prot = PROT_READ | PROT_WRITE;
758 		int i;
759 
760 		for (i = 0; i < mod_info->vma_count; i++) {
761 			mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot,
762 					MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
763 			ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED);
764 			/* change protection in adjacent maps to prevent merging */
765 			prot ^= PROT_WRITE;
766 		}
767 		signal_state(mod_info, CHILD_READY);
768 		wait_for_state(mod_info, PARENT_READY);
769 		while (true) {
770 			signal_state(mod_info, SETUP_READY);
771 			wait_for_state(mod_info, SETUP_MODIFY_MAPS);
772 			if (mod_info->exit)
773 				break;
774 
775 			ASSERT_TRUE(mod_info->vma_modify(self));
776 			signal_state(mod_info, SETUP_MAPS_MODIFIED);
777 			wait_for_state(mod_info, SETUP_RESTORE_MAPS);
778 			ASSERT_TRUE(mod_info->vma_restore(self));
779 			signal_state(mod_info, SETUP_MAPS_RESTORED);
780 
781 			wait_for_state(mod_info, TEST_READY);
782 			while (mod_info->curr_state != TEST_DONE) {
783 				ASSERT_TRUE(mod_info->vma_modify(self));
784 				ASSERT_TRUE(mod_info->vma_restore(self));
785 			}
786 		}
787 		for (i = 0; i < mod_info->vma_count; i++)
788 			munmap(mod_info->child_mapped_addr[i], self->page_size * 3);
789 
790 		exit(0);
791 	}
792 
793 	switch (self->maps_file) {
794 	case MAPS:
795 		sprintf(fname, "/proc/%d/maps", self->pid);
796 		break;
797 	case SMAPS:
798 		sprintf(fname, "/proc/%d/smaps", self->pid);
799 		sprintf(rollup_fname, "/proc/%d/smaps_rollup", self->pid);
800 		self->rollup_fd = open(rollup_fname, O_RDONLY);
801 		ASSERT_NE(self->rollup_fd, -1);
802 		break;
803 	default:
804 		ksft_exit_fail();
805 	}
806 	self->maps_fd = open(fname, O_RDONLY);
807 	ASSERT_NE(self->maps_fd, -1);
808 
809 	/* Wait for the child to map the VMAs */
810 	wait_for_state(mod_info, CHILD_READY);
811 
812 	/* Read first two pages */
813 	self->page1.data = malloc(self->page_size);
814 	ASSERT_NE(self->page1.data, NULL);
815 	self->page2.data = malloc(self->page_size);
816 	ASSERT_NE(self->page2.data, NULL);
817 
818 	first_map_addr = (unsigned long)mod_info->child_mapped_addr[0];
819 	last_map_addr = (unsigned long)mod_info->child_mapped_addr[mod_info->vma_count - 1];
820 
821 	self->skip_pages = locate_containing_page(self,
822 					min(first_map_addr, last_map_addr),
823 					self->page_size * 3);
824 	ASSERT_NE(self->skip_pages, -1);
825 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
826 
827 	/*
828 	 * Find the addresses corresponding to the last line in the first page
829 	 * and the first line in the last page.
830 	 */
831 	mod_info->addr = NULL;
832 	mod_info->next_addr = NULL;
833 	for (int i = 0; i < mod_info->vma_count; i++) {
834 		if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) {
835 			mod_info->addr = mod_info->child_mapped_addr[i];
836 			mod_info->prot = PROT_READ;
837 			/* Even VMAs have write permission */
838 			if ((i % 2) == 0)
839 				mod_info->prot |= PROT_WRITE;
840 		} else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) {
841 			mod_info->next_addr = mod_info->child_mapped_addr[i];
842 		}
843 
844 		if (mod_info->addr && mod_info->next_addr)
845 			break;
846 	}
847 	ASSERT_TRUE(mod_info->addr && mod_info->next_addr);
848 
849 	signal_state(mod_info, PARENT_READY);
850 }
851 
852 FIXTURE_TEARDOWN(proc_maps_race)
853 {
854 	int status;
855 
856 	stop_vma_modifier(self->mod_info);
857 
858 	free(self->page2.data);
859 	free(self->page1.data);
860 
861 	for (int i = 0; i < self->vma_count; i++)
862 		munmap(self->mod_info->child_mapped_addr[i], self->page_size);
863 	close(self->maps_fd);
864 	if (self->maps_file == SMAPS)
865 		close(self->rollup_fd);
866 	waitpid(self->pid, &status, 0);
867 	munmap(self->mod_info, self->shared_mem_size);
868 }
869 
870 TEST_F(proc_maps_race, test_maps_tearing_from_split)
871 {
872 	struct vma_modifier_info *mod_info = self->mod_info;
873 
874 	struct line_content split_last_line;
875 	struct line_content split_first_line;
876 	struct line_content restored_last_line;
877 	struct line_content restored_first_line;
878 	struct smaps_rollup_stats orig_stats;
879 
880 	wait_for_state(mod_info, SETUP_READY);
881 
882 	/* re-read the file to avoid using stale data from previous test */
883 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
884 
885 	mod_info->vma_modify = split_vma;
886 	mod_info->vma_restore = merge_vma;
887 	mod_info->vma_mod_check = check_split_result;
888 
889 	report_test_start("Tearing from split", self->verbose);
890 	ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line,
891 					&restored_last_line, &restored_first_line));
892 	if (self->maps_file == SMAPS)
893 		ASSERT_TRUE(parse_smaps_rollup(self, &orig_stats));
894 
895 	/* Now start concurrent modifications for self->duration_sec */
896 	signal_state(mod_info, TEST_READY);
897 
898 	struct line_content new_last_line;
899 	struct line_content new_first_line;
900 	struct timespec start_ts, end_ts;
901 
902 	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
903 	start_test_loop(&start_ts, self->verbose);
904 	do {
905 		bool last_line_changed;
906 		bool first_line_changed;
907 		unsigned long vma_start;
908 		unsigned long vma_end;
909 
910 		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
911 
912 		/* Check if we read vmas after split */
913 		if (!strcmp(new_last_line.text, split_last_line.text)) {
914 			/*
915 			 * The vmas should be consistent with split results,
916 			 * however if vma was concurrently restored after a
917 			 * split, it can be reported twice (first the original
918 			 * split one, then the same vma but extended after the
919 			 * merge) because we found it as the next vma again.
920 			 * In that case new first line will be the same as the
921 			 * last restored line.
922 			 */
923 			ASSERT_FALSE(print_boundaries_on(
924 					strcmp(new_first_line.text, split_first_line.text) &&
925 					strcmp(new_first_line.text, restored_last_line.text),
926 					"Split result invalid", self));
927 		} else {
928 			/* The vmas should be consistent with merge results */
929 			ASSERT_FALSE(print_boundaries_on(
930 					strcmp(new_last_line.text, restored_last_line.text),
931 					"Merge result invalid", self));
932 			ASSERT_FALSE(print_boundaries_on(
933 					strcmp(new_first_line.text, restored_first_line.text),
934 					"Merge result invalid", self));
935 		}
936 		/*
937 		 * First and last lines should change in unison. If the last
938 		 * line changed then the first line should change as well and
939 		 * vice versa.
940 		 */
941 		last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0;
942 		first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0;
943 		ASSERT_EQ(last_line_changed, first_line_changed);
944 		if (self->maps_file == MAPS) {
945 			/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
946 			ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
947 						  &vma_start, &vma_end));
948 			/*
949 			 * The vma at the split address can be either the same as
950 			 * original one (if read before the split) or the same as the
951 			 * first line in the second page (if read after the split).
952 			 */
953 			ASSERT_TRUE((vma_start == self->last_line.start_addr &&
954 				     vma_end == self->last_line.end_addr) ||
955 				    (vma_start == split_first_line.start_addr &&
956 				     vma_end == split_first_line.end_addr));
957 		} else {
958 			struct smaps_rollup_stats stats;
959 
960 			ASSERT_TRUE(parse_smaps_rollup(self, &stats));
961 			ASSERT_TRUE(compare_smaps_rollup(self, &orig_stats, &stats));
962 		}
963 		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
964 		end_test_iteration(&end_ts, self->verbose);
965 	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
966 	end_test_loop(self->verbose);
967 
968 	/* Signal the modifyer thread to stop and wait until it exits */
969 	signal_state(mod_info, TEST_DONE);
970 }
971 
972 TEST_F(proc_maps_race, test_maps_tearing_from_resize)
973 {
974 	struct vma_modifier_info *mod_info = self->mod_info;
975 
976 	struct line_content shrunk_last_line;
977 	struct line_content shrunk_first_line;
978 	struct line_content restored_last_line;
979 	struct line_content restored_first_line;
980 	struct smaps_rollup_stats orig_stats;
981 
982 	wait_for_state(mod_info, SETUP_READY);
983 
984 	/* re-read the file to avoid using stale data from previous test */
985 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
986 
987 	mod_info->vma_modify = shrink_vma;
988 	mod_info->vma_restore = expand_vma;
989 	mod_info->vma_mod_check = check_shrink_result;
990 
991 	report_test_start("Tearing from resize", self->verbose);
992 	ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line,
993 					&restored_last_line, &restored_first_line));
994 	if (self->maps_file == SMAPS)
995 		ASSERT_TRUE(parse_smaps_rollup(self, &orig_stats));
996 
997 	/* Now start concurrent modifications for self->duration_sec */
998 	signal_state(mod_info, TEST_READY);
999 
1000 	struct line_content new_last_line;
1001 	struct line_content new_first_line;
1002 	struct timespec start_ts, end_ts;
1003 
1004 	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
1005 	start_test_loop(&start_ts, self->verbose);
1006 	do {
1007 		unsigned long vma_start;
1008 		unsigned long vma_end;
1009 
1010 		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
1011 
1012 		/* Check if we read vmas after shrinking it */
1013 		if (!strcmp(new_last_line.text, shrunk_last_line.text)) {
1014 			/*
1015 			 * The vmas should be consistent with shrunk results,
1016 			 * however if the vma was concurrently restored, it
1017 			 * can be reported twice (first as shrunk one, then
1018 			 * as restored one) because we found it as the next vma
1019 			 * again. In that case new first line will be the same
1020 			 * as the last restored line.
1021 			 */
1022 			ASSERT_FALSE(print_boundaries_on(
1023 					strcmp(new_first_line.text, shrunk_first_line.text) &&
1024 					strcmp(new_first_line.text, restored_last_line.text),
1025 					"Shrink result invalid", self));
1026 		} else {
1027 			/* The vmas should be consistent with the original/resored state */
1028 			ASSERT_FALSE(print_boundaries_on(
1029 					strcmp(new_last_line.text, restored_last_line.text),
1030 					"Expand result invalid", self));
1031 			ASSERT_FALSE(print_boundaries_on(
1032 					strcmp(new_first_line.text, restored_first_line.text),
1033 					"Expand result invalid", self));
1034 		}
1035 		if (self->maps_file == MAPS) {
1036 			/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
1037 			ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr,
1038 						  &vma_start, &vma_end));
1039 			/*
1040 			 * The vma should stay at the same address and have either the
1041 			 * original size of 3 pages or 1 page if read after shrinking.
1042 			 */
1043 			ASSERT_TRUE(vma_start == self->last_line.start_addr &&
1044 				    (vma_end - vma_start == self->page_size * 3 ||
1045 				     vma_end - vma_start == self->page_size));
1046 		} else {
1047 			struct smaps_rollup_stats stats;
1048 
1049 			ASSERT_TRUE(parse_smaps_rollup(self, &stats));
1050 			ASSERT_TRUE(compare_smaps_rollup(self, &orig_stats, &stats));
1051 		}
1052 		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
1053 		end_test_iteration(&end_ts, self->verbose);
1054 	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
1055 	end_test_loop(self->verbose);
1056 
1057 	/* Signal the modifyer thread to stop and wait until it exits */
1058 	signal_state(mod_info, TEST_DONE);
1059 }
1060 
1061 TEST_F(proc_maps_race, test_maps_tearing_from_remap)
1062 {
1063 	struct vma_modifier_info *mod_info = self->mod_info;
1064 
1065 	struct line_content remapped_last_line;
1066 	struct line_content remapped_first_line;
1067 	struct line_content restored_last_line;
1068 	struct line_content restored_first_line;
1069 	struct smaps_rollup_stats orig_stats;
1070 
1071 	wait_for_state(mod_info, SETUP_READY);
1072 
1073 	/* re-read the file to avoid using stale data from previous test */
1074 	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
1075 
1076 	mod_info->vma_modify = remap_vma;
1077 	mod_info->vma_restore = patch_vma;
1078 	mod_info->vma_mod_check = check_remap_result;
1079 
1080 	report_test_start("Tearing from remap", self->verbose);
1081 	ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line,
1082 					&restored_last_line, &restored_first_line));
1083 	if (self->maps_file == SMAPS)
1084 		ASSERT_TRUE(parse_smaps_rollup(self, &orig_stats));
1085 
1086 	/* Now start concurrent modifications for self->duration_sec */
1087 	signal_state(mod_info, TEST_READY);
1088 
1089 	struct line_content new_last_line;
1090 	struct line_content new_first_line;
1091 	struct timespec start_ts, end_ts;
1092 
1093 	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
1094 	start_test_loop(&start_ts, self->verbose);
1095 	do {
1096 		unsigned long vma_start;
1097 		unsigned long vma_end;
1098 
1099 		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
1100 
1101 		/* Check if we read vmas after remapping it */
1102 		if (!strcmp(new_last_line.text, remapped_last_line.text)) {
1103 			/*
1104 			 * The vmas should be consistent with remap results,
1105 			 * however if the vma was concurrently restored, it
1106 			 * can be reported twice (first as split one, then
1107 			 * as restored one) because we found it as the next vma
1108 			 * again. In that case new first line will be the same
1109 			 * as the last restored line.
1110 			 */
1111 			ASSERT_FALSE(print_boundaries_on(
1112 					strcmp(new_first_line.text, remapped_first_line.text) &&
1113 					strcmp(new_first_line.text, restored_last_line.text),
1114 					"Remap result invalid", self));
1115 		} else {
1116 			/* The vmas should be consistent with the original/resored state */
1117 			ASSERT_FALSE(print_boundaries_on(
1118 					strcmp(new_last_line.text, restored_last_line.text),
1119 					"Remap restore result invalid", self));
1120 			ASSERT_FALSE(print_boundaries_on(
1121 					strcmp(new_first_line.text, restored_first_line.text),
1122 					"Remap restore result invalid", self));
1123 		}
1124 		if (self->maps_file == MAPS) {
1125 			/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
1126 			ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
1127 						  &vma_start, &vma_end));
1128 			/*
1129 			 * The vma should either stay at the same address and have the
1130 			 * original size of 3 pages or we should find the remapped vma
1131 			 * at the remap destination address with size of 1 page.
1132 			 */
1133 			ASSERT_TRUE((vma_start == self->last_line.start_addr &&
1134 				     vma_end - vma_start == self->page_size * 3) ||
1135 				    (vma_start == self->last_line.start_addr + self->page_size &&
1136 				     vma_end - vma_start == self->page_size));
1137 		} else {
1138 			struct smaps_rollup_stats stats;
1139 
1140 			ASSERT_TRUE(parse_smaps_rollup(self, &stats));
1141 			ASSERT_TRUE(compare_smaps_rollup(self, &orig_stats, &stats));
1142 		}
1143 		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
1144 		end_test_iteration(&end_ts, self->verbose);
1145 	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
1146 	end_test_loop(self->verbose);
1147 
1148 	/* Signal the modifyer thread to stop and wait until it exits */
1149 	signal_state(mod_info, TEST_DONE);
1150 }
1151 
1152 TEST_HARNESS_MAIN
1153