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