From 3b6189bb114e0989022befed93edf1241ad3b9ff Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Sun, 7 Dec 2025 12:48:19 +0100 Subject: [PATCH 1/7] userfaultfd: Add test for UFFDIO_MOVE Signed-off-by: Ricardo Branco --- runtest/syscalls | 1 + .../kernel/syscalls/userfaultfd/.gitignore | 1 + .../kernel/syscalls/userfaultfd/Makefile | 1 + .../syscalls/userfaultfd/userfaultfd02.c | 119 ++++++++++++++++++ 4 files changed, 122 insertions(+) create mode 100644 testcases/kernel/syscalls/userfaultfd/userfaultfd02.c diff --git a/runtest/syscalls b/runtest/syscalls index a1ef7548b58..fba2c912bf4 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -1770,6 +1770,7 @@ umount2_01 umount2_01 umount2_02 umount2_02 userfaultfd01 userfaultfd01 +userfaultfd02 userfaultfd02 ustat01 ustat01 ustat02 ustat02 diff --git a/testcases/kernel/syscalls/userfaultfd/.gitignore b/testcases/kernel/syscalls/userfaultfd/.gitignore index d819a2a7c82..33084b0fdbf 100644 --- a/testcases/kernel/syscalls/userfaultfd/.gitignore +++ b/testcases/kernel/syscalls/userfaultfd/.gitignore @@ -1 +1,2 @@ /userfaultfd01 +/userfaultfd02 diff --git a/testcases/kernel/syscalls/userfaultfd/Makefile b/testcases/kernel/syscalls/userfaultfd/Makefile index 31ddc4a4217..d8e388d9e89 100644 --- a/testcases/kernel/syscalls/userfaultfd/Makefile +++ b/testcases/kernel/syscalls/userfaultfd/Makefile @@ -12,3 +12,4 @@ include $(top_srcdir)/include/mk/testcases.mk include $(top_srcdir)/include/mk/generic_leaf_target.mk userfaultfd01: CFLAGS += -pthread +userfaultfd02: CFLAGS += -pthread diff --git a/testcases/kernel/syscalls/userfaultfd/userfaultfd02.c b/testcases/kernel/syscalls/userfaultfd/userfaultfd02.c new file mode 100644 index 00000000000..61e02472177 --- /dev/null +++ b/testcases/kernel/syscalls/userfaultfd/userfaultfd02.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 SUSE LLC + * Author: Christian Amann + * Author: Ricardo Branco + */ + +/*\ + * Force a pagefault event and handle it using :man2:`userfaultfd` + * from a different thread using UFFDIO_MOVE + */ + +#include "config.h" +#include +#include "tst_test.h" +#include "tst_safe_macros.h" +#include "tst_safe_pthread.h" +#include "lapi/userfaultfd.h" + +static int page_size; +static char *page; +static void *move_page; +static int uffd; + +static int sys_userfaultfd(int flags) +{ + return tst_syscall(__NR_userfaultfd, flags); +} + +static void set_pages(void) +{ + page_size = sysconf(_SC_PAGE_SIZE); + page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + move_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +} + +static void *handle_thread(void) +{ + static struct uffd_msg msg; + struct uffdio_move uffdio_move; + + struct pollfd pollfd; + int nready; + + pollfd.fd = uffd; + pollfd.events = POLLIN; + nready = poll(&pollfd, 1, -1); + if (nready == -1) + tst_brk(TBROK | TERRNO, "Error on poll"); + + SAFE_READ(1, uffd, &msg, sizeof(msg)); + + if (msg.event != UFFD_EVENT_PAGEFAULT) + tst_brk(TBROK | TERRNO, "Received unexpected UFFD_EVENT"); + + memset(move_page, 'X', page_size); + + uffdio_move.src = (unsigned long) move_page; + + uffdio_move.dst = (unsigned long) msg.arg.pagefault.address + & ~(page_size - 1); + uffdio_move.len = page_size; + uffdio_move.mode = 0; + uffdio_move.move = 0; + SAFE_IOCTL(uffd, UFFDIO_MOVE, &uffdio_move); + + close(uffd); + return NULL; +} + +static void run(void) +{ + pthread_t thr; + struct uffdio_api uffdio_api; + struct uffdio_register uffdio_register; + + set_pages(); + + TEST(sys_userfaultfd(O_CLOEXEC | O_NONBLOCK)); + + if (TST_RET == -1) { + if (TST_ERR == EPERM) { + tst_res(TCONF, "Hint: check /proc/sys/vm/unprivileged_userfaultfd"); + tst_brk(TCONF | TTERRNO, + "userfaultfd() requires CAP_SYS_PTRACE on this system"); + } else + tst_brk(TBROK | TTERRNO, + "Could not create userfault file descriptor"); + } + + uffd = TST_RET; + uffdio_api.api = UFFD_API; + uffdio_api.features = 0; + SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api); + + uffdio_register.range.start = (unsigned long) page; + uffdio_register.range.len = page_size; + uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; + + SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register); + + SAFE_PTHREAD_CREATE(&thr, NULL, (void *) handle_thread, NULL); + + char c = page[0xf]; + + if (c == 'X') + tst_res(TPASS, "Pagefault handled via UFFDIO_MOVE"); + else + tst_res(TFAIL, "Pagefault not handled via UFFDIO_MOVE"); + + SAFE_PTHREAD_JOIN(thr, NULL); +} + +static struct tst_test test = { + .test_all = run, + .min_kver = "6.8", +}; From dce50b984e9101b0f1087a601ba7b68d181882bd Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Fri, 19 Dec 2025 11:24:44 +0100 Subject: [PATCH 2/7] userfaultfd: Minor fixes for userfaultfd01.c Signed-off-by: Ricardo Branco --- testcases/kernel/syscalls/userfaultfd/userfaultfd01.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c b/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c index cf2e927d551..6aee2c38adc 100644 --- a/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c +++ b/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c @@ -35,7 +35,7 @@ static void set_pages(void) MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } -static void handle_thread(void) +static void *handle_thread(void) { static struct uffd_msg msg; struct uffdio_copy uffdio_copy; @@ -66,6 +66,7 @@ static void handle_thread(void) SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy); close(uffd); + return NULL; } static void run(void) @@ -99,8 +100,7 @@ static void run(void) SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register); - SAFE_PTHREAD_CREATE(&thr, NULL, - (void * (*)(void *)) handle_thread, NULL); + SAFE_PTHREAD_CREATE(&thr, NULL, (void *) handle_thread, NULL); char c = page[0xf]; From 784a17f3e3f48ec26efa80ba80bb9a623c81fc8f Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Fri, 19 Dec 2025 15:26:36 +0100 Subject: [PATCH 3/7] userfaultfd: Put sys_userfaultfd in separate header Signed-off-by: Ricardo Branco --- .../syscalls/userfaultfd/tst_userfaultfd.h | 23 +++++++++++++++++++ .../syscalls/userfaultfd/userfaultfd01.c | 16 +------------ 2 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 testcases/kernel/syscalls/userfaultfd/tst_userfaultfd.h diff --git a/testcases/kernel/syscalls/userfaultfd/tst_userfaultfd.h b/testcases/kernel/syscalls/userfaultfd/tst_userfaultfd.h new file mode 100644 index 00000000000..39db4b34790 --- /dev/null +++ b/testcases/kernel/syscalls/userfaultfd/tst_userfaultfd.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 SUSE LLC + * Author: Christian Amann + * Author: Ricardo Branco + */ + +static inline int sys_userfaultfd(int flags) +{ + TEST(tst_syscall(__NR_userfaultfd, flags)); + + if (TST_RET == -1) { + if (TST_ERR == EPERM) { + tst_res(TCONF, "Hint: check /proc/sys/vm/unprivileged_userfaultfd"); + tst_brk(TCONF | TTERRNO, + "userfaultfd() requires CAP_SYS_PTRACE on this system"); + } else + tst_brk(TBROK | TTERRNO, + "Could not create userfault file descriptor"); + } + + return TST_RET; +} diff --git a/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c b/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c index 6aee2c38adc..a9b2a583fb7 100644 --- a/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c +++ b/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c @@ -15,17 +15,13 @@ #include "tst_safe_macros.h" #include "tst_safe_pthread.h" #include "lapi/userfaultfd.h" +#include "tst_userfaultfd.h" static int page_size; static char *page; static void *copy_page; static int uffd; -static int sys_userfaultfd(int flags) -{ - return tst_syscall(__NR_userfaultfd, flags); -} - static void set_pages(void) { page_size = sysconf(_SC_PAGE_SIZE); @@ -79,16 +75,6 @@ static void run(void) TEST(sys_userfaultfd(O_CLOEXEC | O_NONBLOCK)); - if (TST_RET == -1) { - if (TST_ERR == EPERM) { - tst_res(TCONF, "Hint: check /proc/sys/vm/unprivileged_userfaultfd"); - tst_brk(TCONF | TTERRNO, - "userfaultfd() requires CAP_SYS_PTRACE on this system"); - } else - tst_brk(TBROK | TTERRNO, - "Could not create userfault file descriptor"); - } - uffd = TST_RET; uffdio_api.api = UFFD_API; uffdio_api.features = 0; From 3a4ce370d3c9cd0dedd8d5e7a37dd2549e973c0c Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Sun, 7 Dec 2025 12:57:50 +0100 Subject: [PATCH 4/7] userfaultfd: Add test using UFFD_USER_MODE_ONLY Signed-off-by: Ricardo Branco --- runtest/syscalls | 1 + .../kernel/syscalls/userfaultfd/.gitignore | 1 + .../kernel/syscalls/userfaultfd/Makefile | 1 + .../syscalls/userfaultfd/userfaultfd02.c | 16 +-- .../syscalls/userfaultfd/userfaultfd03.c | 114 ++++++++++++++++++ 5 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 testcases/kernel/syscalls/userfaultfd/userfaultfd03.c diff --git a/runtest/syscalls b/runtest/syscalls index fba2c912bf4..dcfdd0c24ae 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -1771,6 +1771,7 @@ umount2_02 umount2_02 userfaultfd01 userfaultfd01 userfaultfd02 userfaultfd02 +userfaultfd03 userfaultfd03 ustat01 ustat01 ustat02 ustat02 diff --git a/testcases/kernel/syscalls/userfaultfd/.gitignore b/testcases/kernel/syscalls/userfaultfd/.gitignore index 33084b0fdbf..4cd297cc1ea 100644 --- a/testcases/kernel/syscalls/userfaultfd/.gitignore +++ b/testcases/kernel/syscalls/userfaultfd/.gitignore @@ -1,2 +1,3 @@ /userfaultfd01 /userfaultfd02 +/userfaultfd03 diff --git a/testcases/kernel/syscalls/userfaultfd/Makefile b/testcases/kernel/syscalls/userfaultfd/Makefile index d8e388d9e89..8677001fcc1 100644 --- a/testcases/kernel/syscalls/userfaultfd/Makefile +++ b/testcases/kernel/syscalls/userfaultfd/Makefile @@ -13,3 +13,4 @@ include $(top_srcdir)/include/mk/generic_leaf_target.mk userfaultfd01: CFLAGS += -pthread userfaultfd02: CFLAGS += -pthread +userfaultfd03: CFLAGS += -pthread diff --git a/testcases/kernel/syscalls/userfaultfd/userfaultfd02.c b/testcases/kernel/syscalls/userfaultfd/userfaultfd02.c index 61e02472177..dcad43bf46c 100644 --- a/testcases/kernel/syscalls/userfaultfd/userfaultfd02.c +++ b/testcases/kernel/syscalls/userfaultfd/userfaultfd02.c @@ -16,17 +16,13 @@ #include "tst_safe_macros.h" #include "tst_safe_pthread.h" #include "lapi/userfaultfd.h" +#include "tst_userfaultfd.h" static int page_size; static char *page; static void *move_page; static int uffd; -static int sys_userfaultfd(int flags) -{ - return tst_syscall(__NR_userfaultfd, flags); -} - static void set_pages(void) { page_size = sysconf(_SC_PAGE_SIZE); @@ -80,16 +76,6 @@ static void run(void) TEST(sys_userfaultfd(O_CLOEXEC | O_NONBLOCK)); - if (TST_RET == -1) { - if (TST_ERR == EPERM) { - tst_res(TCONF, "Hint: check /proc/sys/vm/unprivileged_userfaultfd"); - tst_brk(TCONF | TTERRNO, - "userfaultfd() requires CAP_SYS_PTRACE on this system"); - } else - tst_brk(TBROK | TTERRNO, - "Could not create userfault file descriptor"); - } - uffd = TST_RET; uffdio_api.api = UFFD_API; uffdio_api.features = 0; diff --git a/testcases/kernel/syscalls/userfaultfd/userfaultfd03.c b/testcases/kernel/syscalls/userfaultfd/userfaultfd03.c new file mode 100644 index 00000000000..b69874b7a89 --- /dev/null +++ b/testcases/kernel/syscalls/userfaultfd/userfaultfd03.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 SUSE LLC + * Author: Christian Amann + * Author: Ricardo Branco + */ + +/*\ + * Force a pagefault event and handle it using :man2:`userfaultfd` + * from a different thread opening uffd with UFFD_USER_MODE_ONLY. + */ + +#include "config.h" +#include +#include "tst_test.h" +#include "tst_safe_macros.h" +#include "tst_safe_pthread.h" +#include "lapi/userfaultfd.h" + +static int page_size; +static char *page; +static void *copy_page; +static int uffd; + +static int sys_userfaultfd(int flags) +{ + return tst_syscall(__NR_userfaultfd, flags); +} + +static void set_pages(void) +{ + page_size = sysconf(_SC_PAGE_SIZE); + page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + copy_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +} + +static void *handle_thread(void) +{ + static struct uffd_msg msg; + struct uffdio_copy uffdio_copy; + + struct pollfd pollfd; + int nready; + + pollfd.fd = uffd; + pollfd.events = POLLIN; + nready = poll(&pollfd, 1, -1); + if (nready == -1) + tst_brk(TBROK | TERRNO, "Error on poll"); + + SAFE_READ(1, uffd, &msg, sizeof(msg)); + + if (msg.event != UFFD_EVENT_PAGEFAULT) + tst_brk(TBROK | TERRNO, "Received unexpected UFFD_EVENT"); + + memset(copy_page, 'X', page_size); + + uffdio_copy.src = (unsigned long) copy_page; + + uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address + & ~(page_size - 1); + uffdio_copy.len = page_size; + uffdio_copy.mode = 0; + uffdio_copy.copy = 0; + SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy); + + close(uffd); + return NULL; +} + +static void run(void) +{ + pthread_t thr; + struct uffdio_api uffdio_api; + struct uffdio_register uffdio_register; + + set_pages(); + + TEST(sys_userfaultfd(O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY)); + + if (TST_RET == -1) { + tst_brk(TBROK | TTERRNO, + "Could not create userfault file descriptor"); + } + + uffd = TST_RET; + uffdio_api.api = UFFD_API; + uffdio_api.features = 0; + SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api); + + uffdio_register.range.start = (unsigned long) page; + uffdio_register.range.len = page_size; + uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; + + SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register); + + SAFE_PTHREAD_CREATE(&thr, NULL, (void *) handle_thread, NULL); + + char c = page[0xf]; + + if (c == 'X') + tst_res(TPASS, "Pagefault handled in user-mode!"); + else + tst_res(TFAIL, "Pagefault not handled in user-mode!"); + + SAFE_PTHREAD_JOIN(thr, NULL); +} + +static struct tst_test test = { + .test_all = run, + .min_kver = "5.11", +}; From 7f08651fc79a0883daca3ccbdb05e0c96b1c8bfd Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Sun, 7 Dec 2025 13:14:34 +0100 Subject: [PATCH 5/7] userfaultfd: Add test using /dev/userfaultfd instead of syscall Signed-off-by: Ricardo Branco --- runtest/syscalls | 1 + .../kernel/syscalls/userfaultfd/.gitignore | 1 + .../kernel/syscalls/userfaultfd/Makefile | 1 + .../syscalls/userfaultfd/userfaultfd04.c | 126 ++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 testcases/kernel/syscalls/userfaultfd/userfaultfd04.c diff --git a/runtest/syscalls b/runtest/syscalls index dcfdd0c24ae..ce323f401a4 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -1772,6 +1772,7 @@ umount2_02 umount2_02 userfaultfd01 userfaultfd01 userfaultfd02 userfaultfd02 userfaultfd03 userfaultfd03 +userfaultfd04 userfaultfd04 ustat01 ustat01 ustat02 ustat02 diff --git a/testcases/kernel/syscalls/userfaultfd/.gitignore b/testcases/kernel/syscalls/userfaultfd/.gitignore index 4cd297cc1ea..71af08effa5 100644 --- a/testcases/kernel/syscalls/userfaultfd/.gitignore +++ b/testcases/kernel/syscalls/userfaultfd/.gitignore @@ -1,3 +1,4 @@ /userfaultfd01 /userfaultfd02 /userfaultfd03 +/userfaultfd04 diff --git a/testcases/kernel/syscalls/userfaultfd/Makefile b/testcases/kernel/syscalls/userfaultfd/Makefile index 8677001fcc1..f1dc6e4167a 100644 --- a/testcases/kernel/syscalls/userfaultfd/Makefile +++ b/testcases/kernel/syscalls/userfaultfd/Makefile @@ -14,3 +14,4 @@ include $(top_srcdir)/include/mk/generic_leaf_target.mk userfaultfd01: CFLAGS += -pthread userfaultfd02: CFLAGS += -pthread userfaultfd03: CFLAGS += -pthread +userfaultfd04: CFLAGS += -pthread diff --git a/testcases/kernel/syscalls/userfaultfd/userfaultfd04.c b/testcases/kernel/syscalls/userfaultfd/userfaultfd04.c new file mode 100644 index 00000000000..d982252d9a9 --- /dev/null +++ b/testcases/kernel/syscalls/userfaultfd/userfaultfd04.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 SUSE LLC + * Author: Christian Amann + * Author: Ricardo Branco + */ + +/*\ + * Force a pagefault event and handle it using :man2:`userfaultfd` + * from a different thread using /dev/userfaultfd instead of syscall, + * using USERFAULTFD_IOC_NEW ioctl to create the uffd & UFFDIO_COPY. + */ + +#include "config.h" +#include +#include "tst_test.h" +#include "tst_safe_macros.h" +#include "tst_safe_pthread.h" +#include "lapi/userfaultfd.h" + +static int page_size; +static char *page; +static void *copy_page; +static int uffd; + +static int open_userfaultfd(int flags) +{ + int fd, fd2; + + fd = SAFE_OPEN("/dev/userfaultfd", O_RDWR); + + fd2 = SAFE_IOCTL(fd, USERFAULTFD_IOC_NEW, flags); + + SAFE_CLOSE(fd); + + return fd2; +} + +static void set_pages(void) +{ + page_size = sysconf(_SC_PAGE_SIZE); + page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + copy_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +} + +static void *handle_thread(void) +{ + static struct uffd_msg msg; + struct uffdio_copy uffdio_copy; + + struct pollfd pollfd; + int nready; + + pollfd.fd = uffd; + pollfd.events = POLLIN; + nready = poll(&pollfd, 1, -1); + if (nready == -1) + tst_brk(TBROK | TERRNO, "Error on poll"); + + SAFE_READ(1, uffd, &msg, sizeof(msg)); + + if (msg.event != UFFD_EVENT_PAGEFAULT) + tst_brk(TBROK | TERRNO, "Received unexpected UFFD_EVENT"); + + memset(copy_page, 'X', page_size); + + uffdio_copy.src = (unsigned long) copy_page; + + uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address + & ~(page_size - 1); + uffdio_copy.len = page_size; + uffdio_copy.mode = 0; + uffdio_copy.copy = 0; + SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy); + + close(uffd); + return NULL; +} + +static void run(void) +{ + pthread_t thr; + struct uffdio_api uffdio_api; + struct uffdio_register uffdio_register; + + set_pages(); + + TEST(open_userfaultfd(O_CLOEXEC | O_NONBLOCK)); + + if (TST_RET == -1) { + if (TST_ERR == EPERM) { + tst_brk(TCONF, "Hint: check /dev/userfaultfd permissions"); + } else + tst_brk(TBROK | TTERRNO, + "Could not create userfault file descriptor"); + } + + uffd = TST_RET; + uffdio_api.api = UFFD_API; + uffdio_api.features = 0; + SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api); + + uffdio_register.range.start = (unsigned long) page; + uffdio_register.range.len = page_size; + uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; + + SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register); + + SAFE_PTHREAD_CREATE(&thr, NULL, (void *) handle_thread, NULL); + + char c = page[0xf]; + + if (c == 'X') + tst_res(TPASS, "Pagefault handled via /dev/userfaultfd"); + else + tst_res(TFAIL, "Pagefault not handled via /dev/userfaultfd"); + + SAFE_PTHREAD_JOIN(thr, NULL); +} + +static struct tst_test test = { + .test_all = run, + .min_kver = "5.11", +}; From cee17c055675ce777888030200bef798b1b878f1 Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Sun, 7 Dec 2025 14:02:32 +0100 Subject: [PATCH 6/7] userfaultfd: Add test using UFFDIO_ZEROPAGE Signed-off-by: Ricardo Branco --- runtest/syscalls | 1 + .../kernel/syscalls/userfaultfd/.gitignore | 1 + .../kernel/syscalls/userfaultfd/Makefile | 1 + .../syscalls/userfaultfd/userfaultfd05.c | 100 ++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 testcases/kernel/syscalls/userfaultfd/userfaultfd05.c diff --git a/runtest/syscalls b/runtest/syscalls index ce323f401a4..5b7a9fc64ef 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -1773,6 +1773,7 @@ userfaultfd01 userfaultfd01 userfaultfd02 userfaultfd02 userfaultfd03 userfaultfd03 userfaultfd04 userfaultfd04 +userfaultfd05 userfaultfd05 ustat01 ustat01 ustat02 ustat02 diff --git a/testcases/kernel/syscalls/userfaultfd/.gitignore b/testcases/kernel/syscalls/userfaultfd/.gitignore index 71af08effa5..fb2ae243b16 100644 --- a/testcases/kernel/syscalls/userfaultfd/.gitignore +++ b/testcases/kernel/syscalls/userfaultfd/.gitignore @@ -2,3 +2,4 @@ /userfaultfd02 /userfaultfd03 /userfaultfd04 +/userfaultfd05 diff --git a/testcases/kernel/syscalls/userfaultfd/Makefile b/testcases/kernel/syscalls/userfaultfd/Makefile index f1dc6e4167a..96650a65add 100644 --- a/testcases/kernel/syscalls/userfaultfd/Makefile +++ b/testcases/kernel/syscalls/userfaultfd/Makefile @@ -15,3 +15,4 @@ userfaultfd01: CFLAGS += -pthread userfaultfd02: CFLAGS += -pthread userfaultfd03: CFLAGS += -pthread userfaultfd04: CFLAGS += -pthread +userfaultfd05: CFLAGS += -pthread diff --git a/testcases/kernel/syscalls/userfaultfd/userfaultfd05.c b/testcases/kernel/syscalls/userfaultfd/userfaultfd05.c new file mode 100644 index 00000000000..3dd29f9cf22 --- /dev/null +++ b/testcases/kernel/syscalls/userfaultfd/userfaultfd05.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 SUSE LLC + * Author: Christian Amann + * Author: Ricardo Branco + */ + +/*\ + * Force a pagefault event and handle it using :man2:`userfaultfd` + * from a different thread using UFFDIO_ZEROPAGE + */ + +#include "config.h" +#include +#include "tst_test.h" +#include "tst_safe_macros.h" +#include "tst_safe_pthread.h" +#include "lapi/userfaultfd.h" +#include "tst_userfaultfd.h" + +static int page_size; +static char *page; +static int uffd; + +static void set_pages(void) +{ + page_size = sysconf(_SC_PAGE_SIZE); + page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +} + +static void *handle_thread(void) +{ + static struct uffd_msg msg; + struct uffdio_zeropage uffdio_zeropage; + + struct pollfd pollfd; + int nready; + + pollfd.fd = uffd; + pollfd.events = POLLIN; + nready = poll(&pollfd, 1, -1); + if (nready == -1) + tst_brk(TBROK | TERRNO, "Error on poll"); + + SAFE_READ(1, uffd, &msg, sizeof(msg)); + + if (msg.event != UFFD_EVENT_PAGEFAULT) + tst_brk(TBROK | TERRNO, "Received unexpected UFFD_EVENT"); + + uffdio_zeropage.range.start = msg.arg.pagefault.address + & ~(page_size - 1); + uffdio_zeropage.range.len = page_size; + uffdio_zeropage.mode = 0; + + SAFE_IOCTL(uffd, UFFDIO_ZEROPAGE, &uffdio_zeropage); + + close(uffd); + return NULL; +} + +static void run(void) +{ + pthread_t thr; + struct uffdio_api uffdio_api; + struct uffdio_register uffdio_register; + + set_pages(); + + TEST(sys_userfaultfd(O_CLOEXEC | O_NONBLOCK)); + + uffd = TST_RET; + uffdio_api.api = UFFD_API; + uffdio_api.features = 0; + SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api); + + uffdio_register.range.start = (unsigned long) page; + uffdio_register.range.len = page_size; + uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; + + SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register); + + SAFE_PTHREAD_CREATE(&thr, NULL, (void *) handle_thread, NULL); + + for (int i = 0; i < page_size; i++) { + if (page[i] != 0) { + tst_res(TFAIL, "page[%d]=0x%x not zero", i, page[i]); + return; + } + } + + tst_res(TPASS, "Pagefault handled with UFFDIO_ZEROPAGE"); + + SAFE_PTHREAD_JOIN(thr, NULL); +} + +static struct tst_test test = { + .test_all = run, + .min_kver = "4.3", +}; From 05b2ba24e1d69616269959e1edf6e7b76182df38 Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Sun, 7 Dec 2025 14:58:59 +0100 Subject: [PATCH 7/7] userfaultfd: Add test using UFFDIO_WRITEPROTECT Signed-off-by: Ricardo Branco --- runtest/syscalls | 1 + .../kernel/syscalls/userfaultfd/.gitignore | 1 + .../kernel/syscalls/userfaultfd/Makefile | 1 + .../syscalls/userfaultfd/userfaultfd06.c | 128 ++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 testcases/kernel/syscalls/userfaultfd/userfaultfd06.c diff --git a/runtest/syscalls b/runtest/syscalls index 5b7a9fc64ef..0f7ec722bb4 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -1774,6 +1774,7 @@ userfaultfd02 userfaultfd02 userfaultfd03 userfaultfd03 userfaultfd04 userfaultfd04 userfaultfd05 userfaultfd05 +userfaultfd06 userfaultfd06 ustat01 ustat01 ustat02 ustat02 diff --git a/testcases/kernel/syscalls/userfaultfd/.gitignore b/testcases/kernel/syscalls/userfaultfd/.gitignore index fb2ae243b16..bc32fdf3ba8 100644 --- a/testcases/kernel/syscalls/userfaultfd/.gitignore +++ b/testcases/kernel/syscalls/userfaultfd/.gitignore @@ -3,3 +3,4 @@ /userfaultfd03 /userfaultfd04 /userfaultfd05 +/userfaultfd06 diff --git a/testcases/kernel/syscalls/userfaultfd/Makefile b/testcases/kernel/syscalls/userfaultfd/Makefile index 96650a65add..3252e47dfe3 100644 --- a/testcases/kernel/syscalls/userfaultfd/Makefile +++ b/testcases/kernel/syscalls/userfaultfd/Makefile @@ -16,3 +16,4 @@ userfaultfd02: CFLAGS += -pthread userfaultfd03: CFLAGS += -pthread userfaultfd04: CFLAGS += -pthread userfaultfd05: CFLAGS += -pthread +userfaultfd06: CFLAGS += -pthread diff --git a/testcases/kernel/syscalls/userfaultfd/userfaultfd06.c b/testcases/kernel/syscalls/userfaultfd/userfaultfd06.c new file mode 100644 index 00000000000..a44e1b1c09d --- /dev/null +++ b/testcases/kernel/syscalls/userfaultfd/userfaultfd06.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 SUSE LLC + * Author: Christian Amann + * Author: Ricardo Branco + */ + +/*\ + * Force a pagefault event and handle it using :man2:`userfaultfd` + * from a different thread testing UFFDIO_WRITEPROTECT_MODE_WP + */ + +#include "config.h" +#include +#include "tst_test.h" +#include "tst_safe_macros.h" +#include "tst_safe_pthread.h" +#include "lapi/userfaultfd.h" +#include "tst_userfaultfd.h" + +static int page_size; +static char *page; +static int uffd; +static volatile int wp_fault_seen; + +static void set_pages(void) +{ + page_size = sysconf(_SC_PAGE_SIZE); + page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + memset(page, 0, page_size); +} + +static void *handle_thread(void) +{ + static struct uffd_msg msg; + struct uffdio_writeprotect uffdio_writeprotect; + + struct pollfd pollfd; + int nready; + + pollfd.fd = uffd; + pollfd.events = POLLIN; + nready = poll(&pollfd, 1, -1); + if (nready == -1) + tst_brk(TBROK | TERRNO, "Error on poll"); + + SAFE_READ(1, uffd, &msg, sizeof(msg)); + + if (msg.event != UFFD_EVENT_PAGEFAULT) + tst_brk(TFAIL, "Received unexpected UFFD_EVENT"); + + if (!(msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP) || + !(msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE)) { + tst_brk(TFAIL, + "Expected WP+WRITE fault but flags=%lx", + (unsigned long)msg.arg.pagefault.flags); + } + + /* While the WP fault is pending, the write must NOT be visible. */ + if (page[0xf] != 0) + tst_brk(TFAIL, + "Write became visible while page was write-protected!"); + + wp_fault_seen = 1; + + /* Resolve the fault by clearing WP so the writer can resume. */ + uffdio_writeprotect.range.start = msg.arg.pagefault.address & ~(page_size - 1); + uffdio_writeprotect.range.len = page_size; + uffdio_writeprotect.mode = 0; /* clear write-protect */ + + SAFE_IOCTL(uffd, UFFDIO_WRITEPROTECT, &uffdio_writeprotect); + + close(uffd); + return NULL; +} + +static void run(void) +{ + pthread_t thr; + struct uffdio_api uffdio_api; + struct uffdio_register uffdio_register; + struct uffdio_writeprotect uffdio_writeprotect; + + set_pages(); + + TEST(sys_userfaultfd(O_CLOEXEC | O_NONBLOCK)); + + uffd = TST_RET; + uffdio_api.api = UFFD_API; + uffdio_api.features = UFFD_FEATURE_PAGEFAULT_FLAG_WP; + SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api); + + if (!(uffdio_api.features & UFFD_FEATURE_PAGEFAULT_FLAG_WP)) { + tst_brk(TCONF, "UFFD write-protect unsupported"); + return; + } + + uffdio_register.range.start = (unsigned long) page; + uffdio_register.range.len = page_size; + uffdio_register.mode = UFFDIO_REGISTER_MODE_WP; + + SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register); + + uffdio_writeprotect.range.start = (unsigned long)page; + uffdio_writeprotect.range.len = page_size; + uffdio_writeprotect.mode = UFFDIO_WRITEPROTECT_MODE_WP; + + SAFE_IOCTL(uffd, UFFDIO_WRITEPROTECT, &uffdio_writeprotect); + + SAFE_PTHREAD_CREATE(&thr, NULL, (void *) handle_thread, NULL); + + /* Try to write */ + page[0xf] = 'W'; + + SAFE_PTHREAD_JOIN(thr, NULL); + + if (wp_fault_seen) + tst_res(TPASS, "WRITEPROTECT pagefault handled!"); + else + tst_res(TFAIL, "No WRITEPROTECT pagefault observed"); +} + +static struct tst_test test = { + .test_all = run, + .min_kver = "5.7", +};