Skip to content

Commit 9da6afe

Browse files
userfaultfd: Add test using UFFDIO_WRITEPROTECT
Signed-off-by: Ricardo Branco <[email protected]>
1 parent 720cfe2 commit 9da6afe

File tree

4 files changed

+145
-0
lines changed

4 files changed

+145
-0
lines changed

runtest/syscalls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,6 +1774,7 @@ userfaultfd02 userfaultfd02
17741774
userfaultfd03 userfaultfd03
17751775
userfaultfd04 userfaultfd04
17761776
userfaultfd05 userfaultfd05
1777+
userfaultfd06 userfaultfd06
17771778

17781779
ustat01 ustat01
17791780
ustat02 ustat02

testcases/kernel/syscalls/userfaultfd/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
/userfaultfd03
44
/userfaultfd04
55
/userfaultfd05
6+
/userfaultfd06

testcases/kernel/syscalls/userfaultfd/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ userfaultfd02: CFLAGS += -pthread
1616
userfaultfd03: CFLAGS += -pthread
1717
userfaultfd04: CFLAGS += -pthread
1818
userfaultfd05: CFLAGS += -pthread
19+
userfaultfd06: CFLAGS += -pthread
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (c) 2025 SUSE LLC
4+
* Author: Christian Amann <[email protected]>
5+
* Author: Ricardo Branco <[email protected]>
6+
*/
7+
8+
/*\
9+
* Force a pagefault event and handle it using :man2:`userfaultfd`
10+
* from a different thread testing UFFDIO_WRITEPROTECT_MODE_WP
11+
*/
12+
13+
#include "config.h"
14+
#include <poll.h>
15+
#include "tst_test.h"
16+
#include "tst_safe_macros.h"
17+
#include "tst_safe_pthread.h"
18+
#include "lapi/userfaultfd.h"
19+
20+
static int page_size;
21+
static char *page;
22+
static int uffd;
23+
static volatile int wp_fault_seen;
24+
25+
static int sys_userfaultfd(int flags)
26+
{
27+
return tst_syscall(__NR_userfaultfd, flags);
28+
}
29+
30+
static void set_pages(void)
31+
{
32+
page_size = sysconf(_SC_PAGE_SIZE);
33+
page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
34+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
35+
36+
memset(page, 0, page_size);
37+
}
38+
39+
static void handle_thread(void)
40+
{
41+
static struct uffd_msg msg;
42+
struct uffdio_writeprotect uffdio_writeprotect;
43+
44+
struct pollfd pollfd;
45+
int nready;
46+
47+
pollfd.fd = uffd;
48+
pollfd.events = POLLIN;
49+
nready = poll(&pollfd, 1, -1);
50+
if (nready == -1)
51+
tst_brk(TBROK | TERRNO, "Error on poll");
52+
53+
SAFE_READ(1, uffd, &msg, sizeof(msg));
54+
55+
if (msg.event != UFFD_EVENT_PAGEFAULT)
56+
tst_brk(TBROK | TERRNO, "Received unexpected UFFD_EVENT");
57+
58+
if (!(msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP) ||
59+
!(msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE)) {
60+
tst_brk(TBROK,
61+
"Expected WP+WRITE fault but flags=%lx",
62+
(unsigned long)msg.arg.pagefault.flags);
63+
}
64+
65+
/* While the WP fault is pending, the write must NOT be visible. */
66+
if (page[0xf] != '\0')
67+
tst_brk(TFAIL,
68+
"Write became visible while page was write-protected!");
69+
70+
wp_fault_seen = 1;
71+
72+
/* Resolve the fault by clearing WP so the writer can resume. */
73+
uffdio_writeprotect.range.start = msg.arg.pagefault.address & ~(page_size - 1);
74+
uffdio_writeprotect.range.len = page_size;
75+
uffdio_writeprotect.mode = 0; /* clear write-protect */
76+
77+
SAFE_IOCTL(uffd, UFFDIO_WRITEPROTECT, &uffdio_writeprotect);
78+
79+
close(uffd);
80+
}
81+
82+
static void run(void)
83+
{
84+
pthread_t thr;
85+
struct uffdio_api uffdio_api;
86+
struct uffdio_register uffdio_register;
87+
struct uffdio_writeprotect uffdio_writeprotect;
88+
89+
set_pages();
90+
91+
TEST(sys_userfaultfd(O_CLOEXEC | O_NONBLOCK));
92+
93+
if (TST_RET == -1) {
94+
if (TST_ERR == EPERM) {
95+
tst_res(TCONF, "Hint: check /proc/sys/vm/unprivileged_userfaultfd");
96+
tst_brk(TCONF | TTERRNO,
97+
"userfaultfd() requires CAP_SYS_PTRACE on this system");
98+
} else
99+
tst_brk(TBROK | TTERRNO,
100+
"Could not create userfault file descriptor");
101+
}
102+
103+
uffd = TST_RET;
104+
uffdio_api.api = UFFD_API;
105+
uffdio_api.features = UFFD_FEATURE_PAGEFAULT_FLAG_WP;
106+
SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api);
107+
108+
if (!(uffdio_api.features & UFFD_FEATURE_PAGEFAULT_FLAG_WP)) {
109+
tst_brk(TCONF, "UFFD write-protect unsupported");
110+
return;
111+
}
112+
113+
uffdio_register.range.start = (unsigned long) page;
114+
uffdio_register.range.len = page_size;
115+
uffdio_register.mode = UFFDIO_REGISTER_MODE_WP;
116+
117+
SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
118+
119+
uffdio_writeprotect.range.start = (unsigned long)page;
120+
uffdio_writeprotect.range.len = page_size;
121+
uffdio_writeprotect.mode = UFFDIO_WRITEPROTECT_MODE_WP;
122+
123+
SAFE_IOCTL(uffd, UFFDIO_WRITEPROTECT, &uffdio_writeprotect);
124+
125+
SAFE_PTHREAD_CREATE(&thr, NULL,
126+
(void * (*)(void *)) handle_thread, NULL);
127+
128+
/* Try to write */
129+
page[0xf] = 'W';
130+
131+
SAFE_PTHREAD_JOIN(thr, NULL);
132+
133+
if (wp_fault_seen)
134+
tst_res(TPASS, "WRITEPROTECT pagefault handled!");
135+
else
136+
tst_res(TFAIL, "No WRITEPROTECT pagefault observed");
137+
}
138+
139+
static struct tst_test test = {
140+
.test_all = run,
141+
.min_kver = "5.7",
142+
};

0 commit comments

Comments
 (0)