• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <sys/mman.h>
4 #include <stdint.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <sys/time.h>
8 #include <sys/resource.h>
9 #include <stdbool.h>
10 #include "mlock2.h"
11 
12 #include "../kselftest.h"
13 
14 struct vm_boundaries {
15 	unsigned long start;
16 	unsigned long end;
17 };
18 
get_vm_area(unsigned long addr,struct vm_boundaries * area)19 static int get_vm_area(unsigned long addr, struct vm_boundaries *area)
20 {
21 	FILE *file;
22 	int ret = 1;
23 	char line[1024] = {0};
24 	char *end_addr;
25 	char *stop;
26 	unsigned long start;
27 	unsigned long end;
28 
29 	if (!area)
30 		return ret;
31 
32 	file = fopen("/proc/self/maps", "r");
33 	if (!file) {
34 		perror("fopen");
35 		return ret;
36 	}
37 
38 	memset(area, 0, sizeof(struct vm_boundaries));
39 
40 	while(fgets(line, 1024, file)) {
41 		end_addr = strchr(line, '-');
42 		if (!end_addr) {
43 			printf("cannot parse /proc/self/maps\n");
44 			goto out;
45 		}
46 		*end_addr = '\0';
47 		end_addr++;
48 		stop = strchr(end_addr, ' ');
49 		if (!stop) {
50 			printf("cannot parse /proc/self/maps\n");
51 			goto out;
52 		}
53 		stop = '\0';
54 
55 		sscanf(line, "%lx", &start);
56 		sscanf(end_addr, "%lx", &end);
57 
58 		if (start <= addr && end > addr) {
59 			area->start = start;
60 			area->end = end;
61 			ret = 0;
62 			goto out;
63 		}
64 	}
65 out:
66 	fclose(file);
67 	return ret;
68 }
69 
get_pageflags(unsigned long addr)70 static uint64_t get_pageflags(unsigned long addr)
71 {
72 	FILE *file;
73 	uint64_t pfn;
74 	unsigned long offset;
75 
76 	file = fopen("/proc/self/pagemap", "r");
77 	if (!file) {
78 		perror("fopen pagemap");
79 		_exit(1);
80 	}
81 
82 	offset = addr / getpagesize() * sizeof(pfn);
83 
84 	if (fseek(file, offset, SEEK_SET)) {
85 		perror("fseek pagemap");
86 		_exit(1);
87 	}
88 
89 	if (fread(&pfn, sizeof(pfn), 1, file) != 1) {
90 		perror("fread pagemap");
91 		_exit(1);
92 	}
93 
94 	fclose(file);
95 	return pfn;
96 }
97 
get_kpageflags(unsigned long pfn)98 static uint64_t get_kpageflags(unsigned long pfn)
99 {
100 	uint64_t flags;
101 	FILE *file;
102 
103 	file = fopen("/proc/kpageflags", "r");
104 	if (!file) {
105 		perror("fopen kpageflags");
106 		_exit(1);
107 	}
108 
109 	if (fseek(file, pfn * sizeof(flags), SEEK_SET)) {
110 		perror("fseek kpageflags");
111 		_exit(1);
112 	}
113 
114 	if (fread(&flags, sizeof(flags), 1, file) != 1) {
115 		perror("fread kpageflags");
116 		_exit(1);
117 	}
118 
119 	fclose(file);
120 	return flags;
121 }
122 
123 #define VMFLAGS "VmFlags:"
124 
is_vmflag_set(unsigned long addr,const char * vmflag)125 static bool is_vmflag_set(unsigned long addr, const char *vmflag)
126 {
127 	char *line = NULL;
128 	char *flags;
129 	size_t size = 0;
130 	bool ret = false;
131 	FILE *smaps;
132 
133 	smaps = seek_to_smaps_entry(addr);
134 	if (!smaps) {
135 		printf("Unable to parse /proc/self/smaps\n");
136 		goto out;
137 	}
138 
139 	while (getline(&line, &size, smaps) > 0) {
140 		if (!strstr(line, VMFLAGS)) {
141 			free(line);
142 			line = NULL;
143 			size = 0;
144 			continue;
145 		}
146 
147 		flags = line + strlen(VMFLAGS);
148 		ret = (strstr(flags, vmflag) != NULL);
149 		goto out;
150 	}
151 
152 out:
153 	free(line);
154 	fclose(smaps);
155 	return ret;
156 }
157 
158 #define SIZE "Size:"
159 #define RSS  "Rss:"
160 #define LOCKED "lo"
161 
is_vma_lock_on_fault(unsigned long addr)162 static bool is_vma_lock_on_fault(unsigned long addr)
163 {
164 	bool ret = false;
165 	bool locked;
166 	FILE *smaps = NULL;
167 	unsigned long vma_size, vma_rss;
168 	char *line = NULL;
169 	char *value;
170 	size_t size = 0;
171 
172 	locked = is_vmflag_set(addr, LOCKED);
173 	if (!locked)
174 		goto out;
175 
176 	smaps = seek_to_smaps_entry(addr);
177 	if (!smaps) {
178 		printf("Unable to parse /proc/self/smaps\n");
179 		goto out;
180 	}
181 
182 	while (getline(&line, &size, smaps) > 0) {
183 		if (!strstr(line, SIZE)) {
184 			free(line);
185 			line = NULL;
186 			size = 0;
187 			continue;
188 		}
189 
190 		value = line + strlen(SIZE);
191 		if (sscanf(value, "%lu kB", &vma_size) < 1) {
192 			printf("Unable to parse smaps entry for Size\n");
193 			goto out;
194 		}
195 		break;
196 	}
197 
198 	while (getline(&line, &size, smaps) > 0) {
199 		if (!strstr(line, RSS)) {
200 			free(line);
201 			line = NULL;
202 			size = 0;
203 			continue;
204 		}
205 
206 		value = line + strlen(RSS);
207 		if (sscanf(value, "%lu kB", &vma_rss) < 1) {
208 			printf("Unable to parse smaps entry for Rss\n");
209 			goto out;
210 		}
211 		break;
212 	}
213 
214 	ret = locked && (vma_rss < vma_size);
215 out:
216 	free(line);
217 	if (smaps)
218 		fclose(smaps);
219 	return ret;
220 }
221 
222 #define PRESENT_BIT     0x8000000000000000ULL
223 #define PFN_MASK        0x007FFFFFFFFFFFFFULL
224 #define UNEVICTABLE_BIT (1UL << 18)
225 
lock_check(char * map)226 static int lock_check(char *map)
227 {
228 	unsigned long page_size = getpagesize();
229 	uint64_t page1_flags, page2_flags;
230 
231 	page1_flags = get_pageflags((unsigned long)map);
232 	page2_flags = get_pageflags((unsigned long)map + page_size);
233 
234 	/* Both pages should be present */
235 	if (((page1_flags & PRESENT_BIT) == 0) ||
236 	    ((page2_flags & PRESENT_BIT) == 0)) {
237 		printf("Failed to make both pages present\n");
238 		return 1;
239 	}
240 
241 	page1_flags = get_kpageflags(page1_flags & PFN_MASK);
242 	page2_flags = get_kpageflags(page2_flags & PFN_MASK);
243 
244 	/* Both pages should be unevictable */
245 	if (((page1_flags & UNEVICTABLE_BIT) == 0) ||
246 	    ((page2_flags & UNEVICTABLE_BIT) == 0)) {
247 		printf("Failed to make both pages unevictable\n");
248 		return 1;
249 	}
250 
251 	if (!is_vmflag_set((unsigned long)map, LOCKED)) {
252 		printf("VMA flag %s is missing on page 1\n", LOCKED);
253 		return 1;
254 	}
255 
256 	if (!is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
257 		printf("VMA flag %s is missing on page 2\n", LOCKED);
258 		return 1;
259 	}
260 
261 	return 0;
262 }
263 
unlock_lock_check(char * map)264 static int unlock_lock_check(char *map)
265 {
266 	unsigned long page_size = getpagesize();
267 	uint64_t page1_flags, page2_flags;
268 
269 	page1_flags = get_pageflags((unsigned long)map);
270 	page2_flags = get_pageflags((unsigned long)map + page_size);
271 	page1_flags = get_kpageflags(page1_flags & PFN_MASK);
272 	page2_flags = get_kpageflags(page2_flags & PFN_MASK);
273 
274 	if ((page1_flags & UNEVICTABLE_BIT) || (page2_flags & UNEVICTABLE_BIT)) {
275 		printf("A page is still marked unevictable after unlock\n");
276 		return 1;
277 	}
278 
279 	if (is_vmflag_set((unsigned long)map, LOCKED)) {
280 		printf("VMA flag %s is present on page 1 after unlock\n", LOCKED);
281 		return 1;
282 	}
283 
284 	if (is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
285 		printf("VMA flag %s is present on page 2 after unlock\n", LOCKED);
286 		return 1;
287 	}
288 
289 	return 0;
290 }
291 
test_mlock_lock()292 static int test_mlock_lock()
293 {
294 	char *map;
295 	int ret = 1;
296 	unsigned long page_size = getpagesize();
297 
298 	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
299 		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
300 	if (map == MAP_FAILED) {
301 		perror("test_mlock_locked mmap");
302 		goto out;
303 	}
304 
305 	if (mlock2_(map, 2 * page_size, 0)) {
306 		if (errno == ENOSYS) {
307 			printf("Cannot call new mlock family, skipping test\n");
308 			_exit(KSFT_SKIP);
309 		}
310 		perror("mlock2(0)");
311 		goto unmap;
312 	}
313 
314 	if (lock_check(map))
315 		goto unmap;
316 
317 	/* Now unlock and recheck attributes */
318 	if (munlock(map, 2 * page_size)) {
319 		perror("munlock()");
320 		goto unmap;
321 	}
322 
323 	ret = unlock_lock_check(map);
324 
325 unmap:
326 	munmap(map, 2 * page_size);
327 out:
328 	return ret;
329 }
330 
onfault_check(char * map)331 static int onfault_check(char *map)
332 {
333 	unsigned long page_size = getpagesize();
334 	uint64_t page1_flags, page2_flags;
335 
336 	page1_flags = get_pageflags((unsigned long)map);
337 	page2_flags = get_pageflags((unsigned long)map + page_size);
338 
339 	/* Neither page should be present */
340 	if ((page1_flags & PRESENT_BIT) || (page2_flags & PRESENT_BIT)) {
341 		printf("Pages were made present by MLOCK_ONFAULT\n");
342 		return 1;
343 	}
344 
345 	*map = 'a';
346 	page1_flags = get_pageflags((unsigned long)map);
347 	page2_flags = get_pageflags((unsigned long)map + page_size);
348 
349 	/* Only page 1 should be present */
350 	if ((page1_flags & PRESENT_BIT) == 0) {
351 		printf("Page 1 is not present after fault\n");
352 		return 1;
353 	} else if (page2_flags & PRESENT_BIT) {
354 		printf("Page 2 was made present\n");
355 		return 1;
356 	}
357 
358 	page1_flags = get_kpageflags(page1_flags & PFN_MASK);
359 
360 	/* Page 1 should be unevictable */
361 	if ((page1_flags & UNEVICTABLE_BIT) == 0) {
362 		printf("Failed to make faulted page unevictable\n");
363 		return 1;
364 	}
365 
366 	if (!is_vma_lock_on_fault((unsigned long)map)) {
367 		printf("VMA is not marked for lock on fault\n");
368 		return 1;
369 	}
370 
371 	if (!is_vma_lock_on_fault((unsigned long)map + page_size)) {
372 		printf("VMA is not marked for lock on fault\n");
373 		return 1;
374 	}
375 
376 	return 0;
377 }
378 
unlock_onfault_check(char * map)379 static int unlock_onfault_check(char *map)
380 {
381 	unsigned long page_size = getpagesize();
382 	uint64_t page1_flags;
383 
384 	page1_flags = get_pageflags((unsigned long)map);
385 	page1_flags = get_kpageflags(page1_flags & PFN_MASK);
386 
387 	if (page1_flags & UNEVICTABLE_BIT) {
388 		printf("Page 1 is still marked unevictable after unlock\n");
389 		return 1;
390 	}
391 
392 	if (is_vma_lock_on_fault((unsigned long)map) ||
393 	    is_vma_lock_on_fault((unsigned long)map + page_size)) {
394 		printf("VMA is still lock on fault after unlock\n");
395 		return 1;
396 	}
397 
398 	return 0;
399 }
400 
test_mlock_onfault()401 static int test_mlock_onfault()
402 {
403 	char *map;
404 	int ret = 1;
405 	unsigned long page_size = getpagesize();
406 
407 	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
408 		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
409 	if (map == MAP_FAILED) {
410 		perror("test_mlock_locked mmap");
411 		goto out;
412 	}
413 
414 	if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
415 		if (errno == ENOSYS) {
416 			printf("Cannot call new mlock family, skipping test\n");
417 			_exit(KSFT_SKIP);
418 		}
419 		perror("mlock2(MLOCK_ONFAULT)");
420 		goto unmap;
421 	}
422 
423 	if (onfault_check(map))
424 		goto unmap;
425 
426 	/* Now unlock and recheck attributes */
427 	if (munlock(map, 2 * page_size)) {
428 		if (errno == ENOSYS) {
429 			printf("Cannot call new mlock family, skipping test\n");
430 			_exit(KSFT_SKIP);
431 		}
432 		perror("munlock()");
433 		goto unmap;
434 	}
435 
436 	ret = unlock_onfault_check(map);
437 unmap:
438 	munmap(map, 2 * page_size);
439 out:
440 	return ret;
441 }
442 
test_lock_onfault_of_present()443 static int test_lock_onfault_of_present()
444 {
445 	char *map;
446 	int ret = 1;
447 	unsigned long page_size = getpagesize();
448 	uint64_t page1_flags, page2_flags;
449 
450 	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
451 		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
452 	if (map == MAP_FAILED) {
453 		perror("test_mlock_locked mmap");
454 		goto out;
455 	}
456 
457 	*map = 'a';
458 
459 	if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
460 		if (errno == ENOSYS) {
461 			printf("Cannot call new mlock family, skipping test\n");
462 			_exit(KSFT_SKIP);
463 		}
464 		perror("mlock2(MLOCK_ONFAULT)");
465 		goto unmap;
466 	}
467 
468 	page1_flags = get_pageflags((unsigned long)map);
469 	page2_flags = get_pageflags((unsigned long)map + page_size);
470 	page1_flags = get_kpageflags(page1_flags & PFN_MASK);
471 	page2_flags = get_kpageflags(page2_flags & PFN_MASK);
472 
473 	/* Page 1 should be unevictable */
474 	if ((page1_flags & UNEVICTABLE_BIT) == 0) {
475 		printf("Failed to make present page unevictable\n");
476 		goto unmap;
477 	}
478 
479 	if (!is_vma_lock_on_fault((unsigned long)map) ||
480 	    !is_vma_lock_on_fault((unsigned long)map + page_size)) {
481 		printf("VMA with present pages is not marked lock on fault\n");
482 		goto unmap;
483 	}
484 	ret = 0;
485 unmap:
486 	munmap(map, 2 * page_size);
487 out:
488 	return ret;
489 }
490 
test_munlockall()491 static int test_munlockall()
492 {
493 	char *map;
494 	int ret = 1;
495 	unsigned long page_size = getpagesize();
496 
497 	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
498 		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
499 
500 	if (map == MAP_FAILED) {
501 		perror("test_munlockall mmap");
502 		goto out;
503 	}
504 
505 	if (mlockall(MCL_CURRENT)) {
506 		perror("mlockall(MCL_CURRENT)");
507 		goto out;
508 	}
509 
510 	if (lock_check(map))
511 		goto unmap;
512 
513 	if (munlockall()) {
514 		perror("munlockall()");
515 		goto unmap;
516 	}
517 
518 	if (unlock_lock_check(map))
519 		goto unmap;
520 
521 	munmap(map, 2 * page_size);
522 
523 	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
524 		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
525 
526 	if (map == MAP_FAILED) {
527 		perror("test_munlockall second mmap");
528 		goto out;
529 	}
530 
531 	if (mlockall(MCL_CURRENT | MCL_ONFAULT)) {
532 		perror("mlockall(MCL_CURRENT | MCL_ONFAULT)");
533 		goto unmap;
534 	}
535 
536 	if (onfault_check(map))
537 		goto unmap;
538 
539 	if (munlockall()) {
540 		perror("munlockall()");
541 		goto unmap;
542 	}
543 
544 	if (unlock_onfault_check(map))
545 		goto unmap;
546 
547 	if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
548 		perror("mlockall(MCL_CURRENT | MCL_FUTURE)");
549 		goto out;
550 	}
551 
552 	if (lock_check(map))
553 		goto unmap;
554 
555 	if (munlockall()) {
556 		perror("munlockall()");
557 		goto unmap;
558 	}
559 
560 	ret = unlock_lock_check(map);
561 
562 unmap:
563 	munmap(map, 2 * page_size);
564 out:
565 	munlockall();
566 	return ret;
567 }
568 
test_vma_management(bool call_mlock)569 static int test_vma_management(bool call_mlock)
570 {
571 	int ret = 1;
572 	void *map;
573 	unsigned long page_size = getpagesize();
574 	struct vm_boundaries page1;
575 	struct vm_boundaries page2;
576 	struct vm_boundaries page3;
577 
578 	map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
579 		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
580 	if (map == MAP_FAILED) {
581 		perror("mmap()");
582 		return ret;
583 	}
584 
585 	if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) {
586 		if (errno == ENOSYS) {
587 			printf("Cannot call new mlock family, skipping test\n");
588 			_exit(KSFT_SKIP);
589 		}
590 		perror("mlock(ONFAULT)\n");
591 		goto out;
592 	}
593 
594 	if (get_vm_area((unsigned long)map, &page1) ||
595 	    get_vm_area((unsigned long)map + page_size, &page2) ||
596 	    get_vm_area((unsigned long)map + page_size * 2, &page3)) {
597 		printf("couldn't find mapping in /proc/self/maps\n");
598 		goto out;
599 	}
600 
601 	/*
602 	 * Before we unlock a portion, we need to that all three pages are in
603 	 * the same VMA.  If they are not we abort this test (Note that this is
604 	 * not a failure)
605 	 */
606 	if (page1.start != page2.start || page2.start != page3.start) {
607 		printf("VMAs are not merged to start, aborting test\n");
608 		ret = 0;
609 		goto out;
610 	}
611 
612 	if (munlock(map + page_size, page_size)) {
613 		perror("munlock()");
614 		goto out;
615 	}
616 
617 	if (get_vm_area((unsigned long)map, &page1) ||
618 	    get_vm_area((unsigned long)map + page_size, &page2) ||
619 	    get_vm_area((unsigned long)map + page_size * 2, &page3)) {
620 		printf("couldn't find mapping in /proc/self/maps\n");
621 		goto out;
622 	}
623 
624 	/* All three VMAs should be different */
625 	if (page1.start == page2.start || page2.start == page3.start) {
626 		printf("failed to split VMA for munlock\n");
627 		goto out;
628 	}
629 
630 	/* Now unlock the first and third page and check the VMAs again */
631 	if (munlock(map, page_size * 3)) {
632 		perror("munlock()");
633 		goto out;
634 	}
635 
636 	if (get_vm_area((unsigned long)map, &page1) ||
637 	    get_vm_area((unsigned long)map + page_size, &page2) ||
638 	    get_vm_area((unsigned long)map + page_size * 2, &page3)) {
639 		printf("couldn't find mapping in /proc/self/maps\n");
640 		goto out;
641 	}
642 
643 	/* Now all three VMAs should be the same */
644 	if (page1.start != page2.start || page2.start != page3.start) {
645 		printf("failed to merge VMAs after munlock\n");
646 		goto out;
647 	}
648 
649 	ret = 0;
650 out:
651 	munmap(map, 3 * page_size);
652 	return ret;
653 }
654 
test_mlockall(int (test_function)(bool call_mlock))655 static int test_mlockall(int (test_function)(bool call_mlock))
656 {
657 	int ret = 1;
658 
659 	if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) {
660 		perror("mlockall");
661 		return ret;
662 	}
663 
664 	ret = test_function(false);
665 	munlockall();
666 	return ret;
667 }
668 
main(int argc,char ** argv)669 int main(int argc, char **argv)
670 {
671 	int ret = 0;
672 	ret += test_mlock_lock();
673 	ret += test_mlock_onfault();
674 	ret += test_munlockall();
675 	ret += test_lock_onfault_of_present();
676 	ret += test_vma_management(true);
677 	ret += test_mlockall(test_vma_management);
678 	return ret;
679 }
680