xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2019 SUSE LLC
4  * Author: Christian Amann <[email protected]>
5  *
6  * Test userfaultfd
7  *
8  * Force a pagefault event and handle it using userfaultfd
9  * from a different thread
10  */
11 
12 #include "config.h"
13 #include "tst_test.h"
14 
15 #include <poll.h>
16 
17 #include "tst_safe_macros.h"
18 #include "tst_safe_pthread.h"
19 #include "lapi/userfaultfd.h"
20 
21 static int page_size;
22 static char *page;
23 static void *copy_page;
24 static int uffd;
25 
sys_userfaultfd(int flags)26 static int sys_userfaultfd(int flags)
27 {
28 	return tst_syscall(__NR_userfaultfd, flags);
29 }
30 
set_pages(void)31 static void set_pages(void)
32 {
33 	page_size = sysconf(_SC_PAGE_SIZE);
34 	page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
35 			MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
36 	copy_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
37 			MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
38 }
39 
handle_thread(void)40 static void handle_thread(void)
41 {
42 	static struct uffd_msg msg;
43 	struct uffdio_copy uffdio_copy;
44 
45 	struct pollfd pollfd;
46 	int nready;
47 
48 	pollfd.fd = uffd;
49 	pollfd.events = POLLIN;
50 	nready = poll(&pollfd, 1, -1);
51 	if (nready == -1)
52 		tst_brk(TBROK | TERRNO,
53 				"Error on poll");
54 
55 	SAFE_READ(1, uffd, &msg, sizeof(msg));
56 
57 	if (msg.event != UFFD_EVENT_PAGEFAULT)
58 		tst_brk(TBROK | TERRNO,
59 				"Received unexpected UFFD_EVENT");
60 
61 	memset(copy_page, 'X', page_size);
62 
63 	uffdio_copy.src = (unsigned long) copy_page;
64 
65 	uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address
66 			& ~(page_size - 1);
67 	uffdio_copy.len = page_size;
68 	uffdio_copy.mode = 0;
69 	uffdio_copy.copy = 0;
70 	SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);
71 
72 	close(uffd);
73 }
74 
run(void)75 static void run(void)
76 {
77 	pthread_t thr;
78 	struct uffdio_api uffdio_api;
79 	struct uffdio_register uffdio_register;
80 
81 	set_pages();
82 
83 	TEST(sys_userfaultfd(O_CLOEXEC | O_NONBLOCK));
84 
85 	if (TST_RET == -1) {
86 		if (TST_ERR == EPERM) {
87 			tst_res(TCONF, "Hint: check /proc/sys/vm/unprivileged_userfaultfd");
88 			tst_brk(TCONF | TTERRNO,
89 				"userfaultfd() requires CAP_SYS_PTRACE on this system");
90 		} else
91 			tst_brk(TBROK | TTERRNO,
92 				"Could not create userfault file descriptor");
93 	}
94 
95 	uffd = TST_RET;
96 	uffdio_api.api = UFFD_API;
97 	uffdio_api.features = 0;
98 	SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api);
99 
100 	uffdio_register.range.start = (unsigned long) page;
101 	uffdio_register.range.len = page_size;
102 	uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
103 
104 	SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
105 
106 	SAFE_PTHREAD_CREATE(&thr, NULL,
107 			(void * (*)(void *)) handle_thread, NULL);
108 
109 	char c = page[0xf];
110 
111 	if (c == 'X')
112 		tst_res(TPASS, "Pagefault handled!");
113 	else
114 		tst_res(TFAIL, "Pagefault not handled!");
115 
116 	SAFE_PTHREAD_JOIN(thr, NULL);
117 }
118 
119 static struct tst_test test = {
120 	.test_all = run,
121 	.min_kver = "4.3",
122 };
123