xref: /linux/tools/testing/selftests/proc/proc-maps-race.c (revision 8804d970fab45726b3c7cd7f240b31122aa94219)
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 */
43 struct page_content {
44 	char *data;
45 	ssize_t size;
46 };
47 
48 #define LINE_MAX_SIZE		256
49 
50 struct line_content {
51 	char text[LINE_MAX_SIZE];
52 	unsigned long start_addr;
53 	unsigned long end_addr;
54 };
55 
56 enum 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 
69 struct vma_modifier_info;
70 
FIXTURE(proc_maps_race)71 FIXTURE(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 
87 typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self);
88 typedef 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 
93 struct 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 
read_two_pages(FIXTURE_DATA (proc_maps_race)* self)109 static 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 
copy_first_line(struct page_content * page,char * first_line)131 static 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 
copy_last_line(struct page_content * page,char * last_line)139 static 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 */
read_boundary_lines(FIXTURE_DATA (proc_maps_race)* self,struct line_content * last_line,struct line_content * first_line)154 static 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 */
wait_for_state(struct vma_modifier_info * mod_info,enum test_state state)171 static 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 
signal_state(struct vma_modifier_info * mod_info,enum test_state state)179 static 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 
stop_vma_modifier(struct vma_modifier_info * mod_info)187 static 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 
print_first_lines(char * text,int nr)194 static 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 
print_last_lines(char * text,int nr)215 static 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 
print_boundaries(const char * title,FIXTURE_DATA (proc_maps_race)* self)229 static 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 
print_boundaries_on(bool condition,const char * title,FIXTURE_DATA (proc_maps_race)* self)241 static 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 
report_test_start(const char * name,bool verbose)250 static void report_test_start(const char *name, bool verbose)
251 {
252 	if (verbose)
253 		printf("==== %s ====\n", name);
254 }
255 
256 static struct timespec print_ts;
257 
start_test_loop(struct timespec * ts,bool verbose)258 static void start_test_loop(struct timespec *ts, bool verbose)
259 {
260 	if (verbose)
261 		print_ts.tv_sec = ts->tv_sec;
262 }
263 
end_test_iteration(struct timespec * ts,bool verbose)264 static 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 
end_test_loop(bool verbose)278 static void end_test_loop(bool verbose)
279 {
280 	if (verbose)
281 		printf("\n");
282 }
283 
capture_mod_pattern(FIXTURE_DATA (proc_maps_race)* self,struct line_content * mod_last_line,struct line_content * mod_first_line,struct line_content * restored_last_line,struct line_content * restored_first_line)284 static 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 
query_addr_at(int maps_fd,void * addr,unsigned long * vma_start,unsigned long * vma_end)322 static 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 
split_vma(FIXTURE_DATA (proc_maps_race)* self)341 static 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 
merge_vma(FIXTURE_DATA (proc_maps_race)* self)347 static 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 
check_split_result(struct line_content * mod_last_line,struct line_content * mod_first_line,struct line_content * restored_last_line,struct line_content * restored_first_line)353 static 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 
shrink_vma(FIXTURE_DATA (proc_maps_race)* self)363 static 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 
expand_vma(FIXTURE_DATA (proc_maps_race)* self)369 static 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 
check_shrink_result(struct line_content * mod_last_line,struct line_content * mod_first_line,struct line_content * restored_last_line,struct line_content * restored_first_line)375 static 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 
remap_vma(FIXTURE_DATA (proc_maps_race)* self)385 static 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 
patch_vma(FIXTURE_DATA (proc_maps_race)* self)398 static 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 
check_remap_result(struct line_content * mod_last_line,struct line_content * mod_first_line,struct line_content * restored_last_line,struct line_content * restored_first_line)404 static 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 
FIXTURE_SETUP(proc_maps_race)414 FIXTURE_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 
FIXTURE_TEARDOWN(proc_maps_race)533 FIXTURE_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 
TEST_F(proc_maps_race,test_maps_tearing_from_split)549 TEST_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 
TEST_F(proc_maps_race,test_maps_tearing_from_resize)643 TEST_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 
TEST_F(proc_maps_race,test_maps_tearing_from_remap)723 TEST_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 
806 TEST_HARNESS_MAIN
807