1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2019 SUSE LLC
4 * Author: Christian Amann <camann@suse.com>
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
21static int page_size;
22static char *page;
23static void *copy_page;
24static int uffd;
25
26static int sys_userfaultfd(int flags)
27{
28	return tst_syscall(__NR_userfaultfd, flags);
29}
30
31static 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
40static 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
75static 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
119static struct tst_test test = {
120	.test_all = run,
121	.min_kver = "4.3",
122};
123