File Index
Files and directories
Sign in
FILE

proc-maps-race.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/proc/proc-maps-race.c C
  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 verifying the results. Address
 21 * 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/* /proc/pid/maps parsing routines */
 43struct page_content {
 44	char *data;
 45	ssize_t size;
 46};
 47
 48#define LINE_MAX_SIZE		256
 49
 50struct line_content {
 51	char text[LINE_MAX_SIZE];
 52	unsigned long start_addr;
 53	unsigned long end_addr;
 54};
 55
 56enum test_state {
 57	INIT,
 58	CHILD_READY,
 59	PARENT_READY,
 60	SETUP_READY,
 61	SETUP_MODIFY_MAPS,
 62	SETUP_MAPS_MODIFIED,
 63	SETUP_RESTORE_MAPS,
 64	SETUP_MAPS_RESTORED,
 65	TEST_READY,
 66	TEST_DONE,
 67};
 68
 69struct vma_modifier_info;
 70
 71FIXTURE(proc_maps_race)
 72{
 73	struct vma_modifier_info *mod_info;
 74	struct page_content page1;
 75	struct page_content page2;
 76	struct line_content last_line;
 77	struct line_content first_line;
 78	unsigned long duration_sec;
 79	int shared_mem_size;
 80	int page_size;
 81	int vma_count;
 82	bool verbose;
 83	int maps_fd;
 84	pid_t pid;
 85};
 86
 87typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self);
 88typedef bool (*vma_mod_result_check_op)(struct line_content *mod_last_line,
 89					struct line_content *mod_first_line,
 90					struct line_content *restored_last_line,
 91					struct line_content *restored_first_line);
 92
 93struct vma_modifier_info {
 94	int vma_count;
 95	void *addr;
 96	int prot;
 97	void *next_addr;
 98	vma_modifier_op vma_modify;
 99	vma_modifier_op vma_restore;
100	vma_mod_result_check_op vma_mod_check;
101	pthread_mutex_t sync_lock;
102	pthread_cond_t sync_cond;
103	enum test_state curr_state;
104	bool exit;
105	void *child_mapped_addr[];
106};
107
108
109static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self)
110{
111	ssize_t  bytes_read;
112
113	if (lseek(self->maps_fd, 0, SEEK_SET) < 0)
114		return false;
115
116	bytes_read = read(self->maps_fd, self->page1.data, self->page_size);
117	if (bytes_read <= 0)
118		return false;
119
120	self->page1.size = bytes_read;
121
122	bytes_read = read(self->maps_fd, self->page2.data, self->page_size);
123	if (bytes_read <= 0)
124		return false;
125
126	self->page2.size = bytes_read;
127
128	return true;
129}
130
131static void copy_first_line(struct page_content *page, char *first_line)
132{
133	char *pos = strchr(page->data, '\n');
134
135	strncpy(first_line, page->data, pos - page->data);
136	first_line[pos - page->data] = '\0';
137}
138
139static void copy_last_line(struct page_content *page, char *last_line)
140{
141	/* Get the last line in the first page */
142	const char *end = page->data + page->size - 1;
143	/* skip last newline */
144	const char *pos = end - 1;
145
146	/* search previous newline */
147	while (pos[-1] != '\n')
148		pos--;
149	strncpy(last_line, pos, end - pos);
150	last_line[end - pos] = '\0';
151}
152
153/* Read the last line of the first page and the first line of the second page */
154static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self,
155				struct line_content *last_line,
156				struct line_content *first_line)
157{
158	if (!read_two_pages(self))
159		return false;
160
161	copy_last_line(&self->page1, last_line->text);
162	copy_first_line(&self->page2, first_line->text);
163
164	return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr,
165		      &last_line->end_addr) == 2 &&
166	       sscanf(first_line->text, "%lx-%lx", &first_line->start_addr,
167		      &first_line->end_addr) == 2;
168}
169
170/* Thread synchronization routines */
171static void wait_for_state(struct vma_modifier_info *mod_info, enum test_state state)
172{
173	pthread_mutex_lock(&mod_info->sync_lock);
174	while (mod_info->curr_state != state)
175		pthread_cond_wait(&mod_info->sync_cond, &mod_info->sync_lock);
176	pthread_mutex_unlock(&mod_info->sync_lock);
177}
178
179static void signal_state(struct vma_modifier_info *mod_info, enum test_state state)
180{
181	pthread_mutex_lock(&mod_info->sync_lock);
182	mod_info->curr_state = state;
183	pthread_cond_signal(&mod_info->sync_cond);
184	pthread_mutex_unlock(&mod_info->sync_lock);
185}
186
187static void stop_vma_modifier(struct vma_modifier_info *mod_info)
188{
189	wait_for_state(mod_info, SETUP_READY);
190	mod_info->exit = true;
191	signal_state(mod_info, SETUP_MODIFY_MAPS);
192}
193
194static void print_first_lines(char *text, int nr)
195{
196	const char *end = text;
197
198	while (nr && (end = strchr(end, '\n')) != NULL) {
199		nr--;
200		end++;
201	}
202
203	if (end) {
204		int offs = end - text;
205
206		text[offs] = '\0';
207		printf("%s", text);
208		text[offs] = '\n';
209		printf("\n");
210	} else {
211		printf("%s", text);
212	}
213}
214
215static void print_last_lines(char *text, int nr)
216{
217	const char *start = text + strlen(text);
218
219	nr++; /* to ignore the last newline */
220	while (nr) {
221		while (start > text && *start != '\n')
222			start--;
223		nr--;
224		start--;
225	}
226	printf("%s", start);
227}
228
229static void print_boundaries(const char *title, FIXTURE_DATA(proc_maps_race) *self)
230{
231	if (!self->verbose)
232		return;
233
234	printf("%s", title);
235	/* Print 3 boundary lines from each page */
236	print_last_lines(self->page1.data, 3);
237	printf("-----------------page boundary-----------------\n");
238	print_first_lines(self->page2.data, 3);
239}
240
241static bool print_boundaries_on(bool condition, const char *title,
242				FIXTURE_DATA(proc_maps_race) *self)
243{
244	if (self->verbose && condition)
245		print_boundaries(title, self);
246
247	return condition;
248}
249
250static void report_test_start(const char *name, bool verbose)
251{
252	if (verbose)
253		printf("==== %s ====\n", name);
254}
255
256static struct timespec print_ts;
257
258static void start_test_loop(struct timespec *ts, bool verbose)
259{
260	if (verbose)
261		print_ts.tv_sec = ts->tv_sec;
262}
263
264static void end_test_iteration(struct timespec *ts, bool verbose)
265{
266	if (!verbose)
267		return;
268
269	/* Update every second */
270	if (print_ts.tv_sec == ts->tv_sec)
271		return;
272
273	printf(".");
274	fflush(stdout);
275	print_ts.tv_sec = ts->tv_sec;
276}
277
278static void end_test_loop(bool verbose)
279{
280	if (verbose)
281		printf("\n");
282}
283
284static bool capture_mod_pattern(FIXTURE_DATA(proc_maps_race) *self,
285				struct line_content *mod_last_line,
286				struct line_content *mod_first_line,
287				struct line_content *restored_last_line,
288				struct line_content *restored_first_line)
289{
290	print_boundaries("Before modification", self);
291
292	signal_state(self->mod_info, SETUP_MODIFY_MAPS);
293	wait_for_state(self->mod_info, SETUP_MAPS_MODIFIED);
294
295	/* Copy last line of the first page and first line of the last page */
296	if (!read_boundary_lines(self, mod_last_line, mod_first_line))
297		return false;
298
299	print_boundaries("After modification", self);
300
301	signal_state(self->mod_info, SETUP_RESTORE_MAPS);
302	wait_for_state(self->mod_info, SETUP_MAPS_RESTORED);
303
304	/* Copy last line of the first page and first line of the last page */
305	if (!read_boundary_lines(self, restored_last_line, restored_first_line))
306		return false;
307
308	print_boundaries("After restore", self);
309
310	if (!self->mod_info->vma_mod_check(mod_last_line, mod_first_line,
311					   restored_last_line, restored_first_line))
312		return false;
313
314	/*
315	 * The content of these lines after modify+resore should be the same
316	 * as the original.
317	 */
318	return strcmp(restored_last_line->text, self->last_line.text) == 0 &&
319	       strcmp(restored_first_line->text, self->first_line.text) == 0;
320}
321
322static bool query_addr_at(int maps_fd, void *addr,
323			  unsigned long *vma_start, unsigned long *vma_end)
324{
325	struct procmap_query q;
326
327	memset(&q, 0, sizeof(q));
328	q.size = sizeof(q);
329	/* Find the VMA at the split address */
330	q.query_addr = (unsigned long long)addr;
331	q.query_flags = 0;
332	if (ioctl(maps_fd, PROCMAP_QUERY, &q))
333		return false;
334
335	*vma_start = q.vma_start;
336	*vma_end = q.vma_end;
337
338	return true;
339}
340
341static inline bool split_vma(FIXTURE_DATA(proc_maps_race) *self)
342{
343	return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot | PROT_EXEC,
344		    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED;
345}
346
347static inline bool merge_vma(FIXTURE_DATA(proc_maps_race) *self)
348{
349	return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot,
350		    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED;
351}
352
353static inline bool check_split_result(struct line_content *mod_last_line,
354				      struct line_content *mod_first_line,
355				      struct line_content *restored_last_line,
356				      struct line_content *restored_first_line)
357{
358	/* Make sure vmas at the boundaries are changing */
359	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
360	       strcmp(mod_first_line->text, restored_first_line->text) != 0;
361}
362
363static inline bool shrink_vma(FIXTURE_DATA(proc_maps_race) *self)
364{
365	return mremap(self->mod_info->addr, self->page_size * 3,
366		      self->page_size, 0) != MAP_FAILED;
367}
368
369static inline bool expand_vma(FIXTURE_DATA(proc_maps_race) *self)
370{
371	return mremap(self->mod_info->addr, self->page_size,
372		      self->page_size * 3, 0) != MAP_FAILED;
373}
374
375static inline bool check_shrink_result(struct line_content *mod_last_line,
376				       struct line_content *mod_first_line,
377				       struct line_content *restored_last_line,
378				       struct line_content *restored_first_line)
379{
380	/* Make sure only the last vma of the first page is changing */
381	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
382	       strcmp(mod_first_line->text, restored_first_line->text) == 0;
383}
384
385static inline bool remap_vma(FIXTURE_DATA(proc_maps_race) *self)
386{
387	/*
388	 * Remap the last page of the next vma into the middle of the vma.
389	 * This splits the current vma and the first and middle parts (the
390	 * parts at lower addresses) become the last vma objserved in the
391	 * first page and the first vma observed in the last page.
392	 */
393	return mremap(self->mod_info->next_addr + self->page_size * 2, self->page_size,
394		      self->page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP,
395		      self->mod_info->addr + self->page_size) != MAP_FAILED;
396}
397
398static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self)
399{
400	return mprotect(self->mod_info->addr + self->page_size, self->page_size,
401			self->mod_info->prot) == 0;
402}
403
404static inline bool check_remap_result(struct line_content *mod_last_line,
405				      struct line_content *mod_first_line,
406				      struct line_content *restored_last_line,
407				      struct line_content *restored_first_line)
408{
409	/* Make sure vmas at the boundaries are changing */
410	return strcmp(mod_last_line->text, restored_last_line->text) != 0 &&
411	       strcmp(mod_first_line->text, restored_first_line->text) != 0;
412}
413
414FIXTURE_SETUP(proc_maps_race)
415{
416	const char *verbose = getenv("VERBOSE");
417	const char *duration = getenv("DURATION");
418	struct vma_modifier_info *mod_info;
419	pthread_mutexattr_t mutex_attr;
420	pthread_condattr_t cond_attr;
421	unsigned long duration_sec;
422	char fname[32];
423
424	self->page_size = (unsigned long)sysconf(_SC_PAGESIZE);
425	self->verbose = verbose && !strncmp(verbose, "1", 1);
426	duration_sec = duration ? atol(duration) : 0;
427	self->duration_sec = duration_sec ? duration_sec : 5UL;
428
429	/*
430	 * Have to map enough vmas for /proc/pid/maps to contain more than one
431	 * page worth of vmas. Assume at least 32 bytes per line in maps output
432	 */
433	self->vma_count = self->page_size / 32 + 1;
434	self->shared_mem_size = sizeof(struct vma_modifier_info) + self->vma_count * sizeof(void *);
435
436	/* map shared memory for communication with the child process */
437	self->mod_info = (struct vma_modifier_info *)mmap(NULL, self->shared_mem_size,
438				PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
439	ASSERT_NE(self->mod_info, MAP_FAILED);
440	mod_info = self->mod_info;
441
442	/* Initialize shared members */
443	pthread_mutexattr_init(&mutex_attr);
444	pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
445	ASSERT_EQ(pthread_mutex_init(&mod_info->sync_lock, &mutex_attr), 0);
446	pthread_condattr_init(&cond_attr);
447	pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
448	ASSERT_EQ(pthread_cond_init(&mod_info->sync_cond, &cond_attr), 0);
449	mod_info->vma_count = self->vma_count;
450	mod_info->curr_state = INIT;
451	mod_info->exit = false;
452
453	self->pid = fork();
454	if (!self->pid) {
455		/* Child process modifying the address space */
456		int prot = PROT_READ | PROT_WRITE;
457		int i;
458
459		for (i = 0; i < mod_info->vma_count; i++) {
460			mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot,
461					MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
462			ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED);
463			/* change protection in adjacent maps to prevent merging */
464			prot ^= PROT_WRITE;
465		}
466		signal_state(mod_info, CHILD_READY);
467		wait_for_state(mod_info, PARENT_READY);
468		while (true) {
469			signal_state(mod_info, SETUP_READY);
470			wait_for_state(mod_info, SETUP_MODIFY_MAPS);
471			if (mod_info->exit)
472				break;
473
474			ASSERT_TRUE(mod_info->vma_modify(self));
475			signal_state(mod_info, SETUP_MAPS_MODIFIED);
476			wait_for_state(mod_info, SETUP_RESTORE_MAPS);
477			ASSERT_TRUE(mod_info->vma_restore(self));
478			signal_state(mod_info, SETUP_MAPS_RESTORED);
479
480			wait_for_state(mod_info, TEST_READY);
481			while (mod_info->curr_state != TEST_DONE) {
482				ASSERT_TRUE(mod_info->vma_modify(self));
483				ASSERT_TRUE(mod_info->vma_restore(self));
484			}
485		}
486		for (i = 0; i < mod_info->vma_count; i++)
487			munmap(mod_info->child_mapped_addr[i], self->page_size * 3);
488
489		exit(0);
490	}
491
492	sprintf(fname, "/proc/%d/maps", self->pid);
493	self->maps_fd = open(fname, O_RDONLY);
494	ASSERT_NE(self->maps_fd, -1);
495
496	/* Wait for the child to map the VMAs */
497	wait_for_state(mod_info, CHILD_READY);
498
499	/* Read first two pages */
500	self->page1.data = malloc(self->page_size);
501	ASSERT_NE(self->page1.data, NULL);
502	self->page2.data = malloc(self->page_size);
503	ASSERT_NE(self->page2.data, NULL);
504
505	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
506
507	/*
508	 * Find the addresses corresponding to the last line in the first page
509	 * and the first line in the last page.
510	 */
511	mod_info->addr = NULL;
512	mod_info->next_addr = NULL;
513	for (int i = 0; i < mod_info->vma_count; i++) {
514		if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) {
515			mod_info->addr = mod_info->child_mapped_addr[i];
516			mod_info->prot = PROT_READ;
517			/* Even VMAs have write permission */
518			if ((i % 2) == 0)
519				mod_info->prot |= PROT_WRITE;
520		} else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) {
521			mod_info->next_addr = mod_info->child_mapped_addr[i];
522		}
523
524		if (mod_info->addr && mod_info->next_addr)
525			break;
526	}
527	ASSERT_TRUE(mod_info->addr && mod_info->next_addr);
528
529	signal_state(mod_info, PARENT_READY);
530
531}
532
533FIXTURE_TEARDOWN(proc_maps_race)
534{
535	int status;
536
537	stop_vma_modifier(self->mod_info);
538
539	free(self->page2.data);
540	free(self->page1.data);
541
542	for (int i = 0; i < self->vma_count; i++)
543		munmap(self->mod_info->child_mapped_addr[i], self->page_size);
544	close(self->maps_fd);
545	waitpid(self->pid, &status, 0);
546	munmap(self->mod_info, self->shared_mem_size);
547}
548
549TEST_F(proc_maps_race, test_maps_tearing_from_split)
550{
551	struct vma_modifier_info *mod_info = self->mod_info;
552
553	struct line_content split_last_line;
554	struct line_content split_first_line;
555	struct line_content restored_last_line;
556	struct line_content restored_first_line;
557
558	wait_for_state(mod_info, SETUP_READY);
559
560	/* re-read the file to avoid using stale data from previous test */
561	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
562
563	mod_info->vma_modify = split_vma;
564	mod_info->vma_restore = merge_vma;
565	mod_info->vma_mod_check = check_split_result;
566
567	report_test_start("Tearing from split", self->verbose);
568	ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line,
569					&restored_last_line, &restored_first_line));
570
571	/* Now start concurrent modifications for self->duration_sec */
572	signal_state(mod_info, TEST_READY);
573
574	struct line_content new_last_line;
575	struct line_content new_first_line;
576	struct timespec start_ts, end_ts;
577
578	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
579	start_test_loop(&start_ts, self->verbose);
580	do {
581		bool last_line_changed;
582		bool first_line_changed;
583		unsigned long vma_start;
584		unsigned long vma_end;
585
586		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
587
588		/* Check if we read vmas after split */
589		if (!strcmp(new_last_line.text, split_last_line.text)) {
590			/*
591			 * The vmas should be consistent with split results,
592			 * however if vma was concurrently restored after a
593			 * split, it can be reported twice (first the original
594			 * split one, then the same vma but extended after the
595			 * merge) because we found it as the next vma again.
596			 * In that case new first line will be the same as the
597			 * last restored line.
598			 */
599			ASSERT_FALSE(print_boundaries_on(
600					strcmp(new_first_line.text, split_first_line.text) &&
601					strcmp(new_first_line.text, restored_last_line.text),
602					"Split result invalid", self));
603		} else {
604			/* The vmas should be consistent with merge results */
605			ASSERT_FALSE(print_boundaries_on(
606					strcmp(new_last_line.text, restored_last_line.text),
607					"Merge result invalid", self));
608			ASSERT_FALSE(print_boundaries_on(
609					strcmp(new_first_line.text, restored_first_line.text),
610					"Merge result invalid", self));
611		}
612		/*
613		 * First and last lines should change in unison. If the last
614		 * line changed then the first line should change as well and
615		 * vice versa.
616		 */
617		last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0;
618		first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0;
619		ASSERT_EQ(last_line_changed, first_line_changed);
620
621		/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
622		ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
623					  &vma_start, &vma_end));
624		/*
625		 * The vma at the split address can be either the same as
626		 * original one (if read before the split) or the same as the
627		 * first line in the second page (if read after the split).
628		 */
629		ASSERT_TRUE((vma_start == self->last_line.start_addr &&
630			     vma_end == self->last_line.end_addr) ||
631			    (vma_start == split_first_line.start_addr &&
632			     vma_end == split_first_line.end_addr));
633
634		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
635		end_test_iteration(&end_ts, self->verbose);
636	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
637	end_test_loop(self->verbose);
638
639	/* Signal the modifyer thread to stop and wait until it exits */
640	signal_state(mod_info, TEST_DONE);
641}
642
643TEST_F(proc_maps_race, test_maps_tearing_from_resize)
644{
645	struct vma_modifier_info *mod_info = self->mod_info;
646
647	struct line_content shrunk_last_line;
648	struct line_content shrunk_first_line;
649	struct line_content restored_last_line;
650	struct line_content restored_first_line;
651
652	wait_for_state(mod_info, SETUP_READY);
653
654	/* re-read the file to avoid using stale data from previous test */
655	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
656
657	mod_info->vma_modify = shrink_vma;
658	mod_info->vma_restore = expand_vma;
659	mod_info->vma_mod_check = check_shrink_result;
660
661	report_test_start("Tearing from resize", self->verbose);
662	ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line,
663					&restored_last_line, &restored_first_line));
664
665	/* Now start concurrent modifications for self->duration_sec */
666	signal_state(mod_info, TEST_READY);
667
668	struct line_content new_last_line;
669	struct line_content new_first_line;
670	struct timespec start_ts, end_ts;
671
672	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
673	start_test_loop(&start_ts, self->verbose);
674	do {
675		unsigned long vma_start;
676		unsigned long vma_end;
677
678		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
679
680		/* Check if we read vmas after shrinking it */
681		if (!strcmp(new_last_line.text, shrunk_last_line.text)) {
682			/*
683			 * The vmas should be consistent with shrunk results,
684			 * however if the vma was concurrently restored, it
685			 * can be reported twice (first as shrunk one, then
686			 * as restored one) because we found it as the next vma
687			 * again. In that case new first line will be the same
688			 * as the last restored line.
689			 */
690			ASSERT_FALSE(print_boundaries_on(
691					strcmp(new_first_line.text, shrunk_first_line.text) &&
692					strcmp(new_first_line.text, restored_last_line.text),
693					"Shrink result invalid", self));
694		} else {
695			/* The vmas should be consistent with the original/resored state */
696			ASSERT_FALSE(print_boundaries_on(
697					strcmp(new_last_line.text, restored_last_line.text),
698					"Expand result invalid", self));
699			ASSERT_FALSE(print_boundaries_on(
700					strcmp(new_first_line.text, restored_first_line.text),
701					"Expand result invalid", self));
702		}
703
704		/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
705		ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr, &vma_start, &vma_end));
706		/*
707		 * The vma should stay at the same address and have either the
708		 * original size of 3 pages or 1 page if read after shrinking.
709		 */
710		ASSERT_TRUE(vma_start == self->last_line.start_addr &&
711			    (vma_end - vma_start == self->page_size * 3 ||
712			     vma_end - vma_start == self->page_size));
713
714		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
715		end_test_iteration(&end_ts, self->verbose);
716	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
717	end_test_loop(self->verbose);
718
719	/* Signal the modifyer thread to stop and wait until it exits */
720	signal_state(mod_info, TEST_DONE);
721}
722
723TEST_F(proc_maps_race, test_maps_tearing_from_remap)
724{
725	struct vma_modifier_info *mod_info = self->mod_info;
726
727	struct line_content remapped_last_line;
728	struct line_content remapped_first_line;
729	struct line_content restored_last_line;
730	struct line_content restored_first_line;
731
732	wait_for_state(mod_info, SETUP_READY);
733
734	/* re-read the file to avoid using stale data from previous test */
735	ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
736
737	mod_info->vma_modify = remap_vma;
738	mod_info->vma_restore = patch_vma;
739	mod_info->vma_mod_check = check_remap_result;
740
741	report_test_start("Tearing from remap", self->verbose);
742	ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line,
743					&restored_last_line, &restored_first_line));
744
745	/* Now start concurrent modifications for self->duration_sec */
746	signal_state(mod_info, TEST_READY);
747
748	struct line_content new_last_line;
749	struct line_content new_first_line;
750	struct timespec start_ts, end_ts;
751
752	clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
753	start_test_loop(&start_ts, self->verbose);
754	do {
755		unsigned long vma_start;
756		unsigned long vma_end;
757
758		ASSERT_TRUE(read_boundary_lines(self, &new_last_line, &new_first_line));
759
760		/* Check if we read vmas after remapping it */
761		if (!strcmp(new_last_line.text, remapped_last_line.text)) {
762			/*
763			 * The vmas should be consistent with remap results,
764			 * however if the vma was concurrently restored, it
765			 * can be reported twice (first as split one, then
766			 * as restored one) because we found it as the next vma
767			 * again. In that case new first line will be the same
768			 * as the last restored line.
769			 */
770			ASSERT_FALSE(print_boundaries_on(
771					strcmp(new_first_line.text, remapped_first_line.text) &&
772					strcmp(new_first_line.text, restored_last_line.text),
773					"Remap result invalid", self));
774		} else {
775			/* The vmas should be consistent with the original/resored state */
776			ASSERT_FALSE(print_boundaries_on(
777					strcmp(new_last_line.text, restored_last_line.text),
778					"Remap restore result invalid", self));
779			ASSERT_FALSE(print_boundaries_on(
780					strcmp(new_first_line.text, restored_first_line.text),
781					"Remap restore result invalid", self));
782		}
783
784		/* Check if PROCMAP_QUERY ioclt() finds the right VMA */
785		ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
786					  &vma_start, &vma_end));
787		/*
788		 * The vma should either stay at the same address and have the
789		 * original size of 3 pages or we should find the remapped vma
790		 * at the remap destination address with size of 1 page.
791		 */
792		ASSERT_TRUE((vma_start == self->last_line.start_addr &&
793			     vma_end - vma_start == self->page_size * 3) ||
794			    (vma_start == self->last_line.start_addr + self->page_size &&
795			     vma_end - vma_start == self->page_size));
796
797		clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
798		end_test_iteration(&end_ts, self->verbose);
799	} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
800	end_test_loop(self->verbose);
801
802	/* Signal the modifyer thread to stop and wait until it exits */
803	signal_state(mod_info, TEST_DONE);
804}
805
806TEST_HARNESS_MAIN
~/ Read-only file preview.