* [LTP] [PATCH v2] userfaultfd: Add test using UFFDIO_POISON
@ 2026-02-06 15:52 Ricardo Branco
2026-02-18 10:49 ` Cyril Hrubis
0 siblings, 1 reply; 3+ messages in thread
From: Ricardo Branco @ 2026-02-06 15:52 UTC (permalink / raw)
To: ltp
Signed-off-by: Ricardo Branco <rbranco@suse.de>
---
configure.ac | 1 +
include/lapi/userfaultfd.h | 13 ++
.../kernel/syscalls/userfaultfd/.gitignore | 1 +
.../kernel/syscalls/userfaultfd/Makefile | 1 +
.../syscalls/userfaultfd/userfaultfd06.c | 141 ++++++++++++++++++
5 files changed, 157 insertions(+)
create mode 100644 testcases/kernel/syscalls/userfaultfd/userfaultfd06.c
diff --git a/configure.ac b/configure.ac
index 7fa614dcb..94bcbcc98 100644
--- a/configure.ac
+++ b/configure.ac
@@ -278,6 +278,7 @@ AC_CHECK_TYPES([struct sockaddr_vm],,,[
])
AC_CHECK_TYPES([struct uffdio_move],,,[#include <linux/userfaultfd.h>])
+AC_CHECK_TYPES([struct uffdio_poison],,,[#include <linux/userfaultfd.h>])
AC_CHECK_TYPES([struct uffdio_writeprotect],,,[#include <linux/userfaultfd.h>])
# Tools knobs
diff --git a/include/lapi/userfaultfd.h b/include/lapi/userfaultfd.h
index 0c9e34c84..aab3890b7 100644
--- a/include/lapi/userfaultfd.h
+++ b/include/lapi/userfaultfd.h
@@ -233,6 +233,19 @@ struct uffdio_writeprotect {
};
#endif /* HAVE_STRUCT_UFFDIO_WRITEPROTECT */
+#ifndef HAVE_STRUCT_UFFDIO_POISON
+#define UFFD_FEATURE_POISON (1<<14)
+#define _UFFDIO_POISON (0x08)
+#define UFFDIO_POISON _IOWR(UFFDIO, _UFFDIO_POISON, \
+ struct uffdio_poison)
+struct uffdio_poison {
+ struct uffdio_range range;
+#define UFFDIO_POISON_MODE_DONTWAKE ((__u64)1<<0)
+ __u64 mode;
+ __s64 updated;
+};
+#endif /* HAVE_STRUCT_UFFDIO_POISON */
+
#define SAFE_USERFAULTFD(flags, retry) \
safe_userfaultfd(__FILE__, __LINE__, (flags), (retry))
diff --git a/testcases/kernel/syscalls/userfaultfd/.gitignore b/testcases/kernel/syscalls/userfaultfd/.gitignore
index fb2ae243b..bc32fdf3b 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 96650a65a..3252e47df 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 000000000..213ba77d7
--- /dev/null
+++ b/testcases/kernel/syscalls/userfaultfd/userfaultfd06.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 SUSE LLC
+ * Author: Ricardo Branco <rbranco@suse.com>
+ */
+
+/*\
+ * Force a pagefault event and handle it using :manpage:`userfaultfd(2)`
+ * from a different thread testing UFFDIO_POISON.
+ */
+
+#include "config.h"
+#include <poll.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <unistd.h>
+#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 int uffd;
+static volatile int poison_fault_seen;
+static volatile int sigbus_seen;
+static sigjmp_buf jmpbuf;
+static pthread_barrier_t barrier;
+
+static void sigbus_handler(int sig)
+{
+ if (sig == SIGBUS) {
+ sigbus_seen = 1;
+ siglongjmp(jmpbuf, 1);
+ }
+}
+
+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 reset_pages(void)
+{
+ if (page) {
+ SAFE_MUNMAP(page, page_size);
+ page = NULL;
+ }
+}
+
+static void *handle_thread(void)
+{
+ static struct uffd_msg msg;
+ struct uffdio_poison uffdio_poison = {};
+ struct pollfd pollfd;
+ int nready;
+
+ SAFE_PTHREAD_BARRIER_WAIT(&barrier);
+
+ 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 %d", msg.event);
+
+ poison_fault_seen = 1;
+
+ /* Poison the page that triggered the fault */
+ uffdio_poison.range.start = msg.arg.pagefault.address & ~(page_size - 1);
+ uffdio_poison.range.len = page_size;
+
+ SAFE_IOCTL(uffd, UFFDIO_POISON, &uffdio_poison);
+
+ close(uffd);
+ return NULL;
+}
+
+static void run(void)
+{
+ pthread_t thr;
+ struct uffdio_api uffdio_api = {};
+ struct uffdio_register uffdio_register;
+ struct sigaction sa = {};
+ volatile char dummy;
+
+ sa.sa_handler = sigbus_handler;
+ sigemptyset(&sa.sa_mask);
+ SAFE_SIGACTION(SIGBUS, &sa, NULL);
+
+ set_pages();
+
+ uffd = SAFE_USERFAULTFD(O_CLOEXEC | O_NONBLOCK, false);
+
+ uffdio_api.api = UFFD_API;
+ uffdio_api.features = UFFD_FEATURE_POISON;
+
+ 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_BARRIER_INIT(&barrier, NULL, 2);
+ SAFE_PTHREAD_CREATE(&thr, NULL, (void *) handle_thread, NULL);
+
+ SAFE_PTHREAD_BARRIER_WAIT(&barrier);
+
+ /* Try to read from the page: should trigger fault, get poisoned, then SIGBUS */
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ dummy = page[0];
+ (void)dummy;
+ }
+
+ SAFE_PTHREAD_JOIN(thr, NULL);
+ SAFE_PTHREAD_BARRIER_DESTROY(&barrier);
+ reset_pages();
+
+ if (poison_fault_seen && sigbus_seen) {
+ tst_res(TPASS, "POISON successfully triggered SIGBUS");
+ } else if (poison_fault_seen && !sigbus_seen) {
+ tst_res(TFAIL, "POISON fault seen but no SIGBUS received");
+ } else if (!poison_fault_seen && sigbus_seen) {
+ tst_res(TFAIL, "SIGBUS received but no poison fault seen");
+ } else {
+ tst_res(TFAIL, "No poison fault or SIGBUS observed");
+ }
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .min_kver = "6.6",
+};
--
2.53.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [LTP] [PATCH v2] userfaultfd: Add test using UFFDIO_POISON
2026-02-06 15:52 [LTP] [PATCH v2] userfaultfd: Add test using UFFDIO_POISON Ricardo Branco
@ 2026-02-18 10:49 ` Cyril Hrubis
2026-02-18 13:51 ` Ricardo Branco
0 siblings, 1 reply; 3+ messages in thread
From: Cyril Hrubis @ 2026-02-18 10:49 UTC (permalink / raw)
To: Ricardo Branco; +Cc: ltp
Hi!
> +static void *handle_thread(void)
> +{
> + static struct uffd_msg msg;
> + struct uffdio_poison uffdio_poison = {};
> + struct pollfd pollfd;
> + int nready;
> +
> + SAFE_PTHREAD_BARRIER_WAIT(&barrier);
I still do not understand why we need the barrier here. The thread that
tries to access the memory should block until this thread processes the
event sicne the usefaultfd range was registered before the memory is
accessed.
Also the same pattern is present in rest of the usefaultfd tests, so
either we need this barrier in all of them, or it shouldn't be needed
here.
And the test seems to work just fine if I remove the barrier code.
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [LTP] [PATCH v2] userfaultfd: Add test using UFFDIO_POISON
2026-02-18 10:49 ` Cyril Hrubis
@ 2026-02-18 13:51 ` Ricardo Branco
0 siblings, 0 replies; 3+ messages in thread
From: Ricardo Branco @ 2026-02-18 13:51 UTC (permalink / raw)
To: Cyril Hrubis; +Cc: ltp
On 2/18/26 11:49 AM, Cyril Hrubis wrote:
> I still do not understand why we need the barrier here. The thread that
> tries to access the memory should block until this thread processes the
> event sicne the usefaultfd range was registered before the memory is
> accessed.
>
> Also the same pattern is present in rest of the usefaultfd tests, so
> either we need this barrier in all of them, or it shouldn't be needed
> here.
>
> And the test seems to work just fine if I remove the barrier code.
I submitted a v3 without the barrier code. Seems safe.
Best,
R
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-02-18 13:51 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-06 15:52 [LTP] [PATCH v2] userfaultfd: Add test using UFFDIO_POISON Ricardo Branco
2026-02-18 10:49 ` Cyril Hrubis
2026-02-18 13:51 ` Ricardo Branco
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox