* [LTP] [PATCH v1] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
@ 2025-08-27 23:02 Wei Gao via ltp
2025-10-15 3:15 ` [LTP] [PATCH v2] " Wei Gao via ltp
0 siblings, 1 reply; 12+ messages in thread
From: Wei Gao via ltp @ 2025-08-27 23:02 UTC (permalink / raw)
To: ltp
This case test mremap() with MREMAP_DONTUNMAP and use userfaultfd
verifies fault which triggered by accessing old memory region.
Fixes: #1168
Signed-off-by: Wei Gao <wegao@suse.com>
---
runtest/syscalls | 1 +
testcases/kernel/syscalls/mremap/.gitignore | 1 +
testcases/kernel/syscalls/mremap/mremap07.c | 154 ++++++++++++++++++++
3 files changed, 156 insertions(+)
create mode 100644 testcases/kernel/syscalls/mremap/mremap07.c
diff --git a/runtest/syscalls b/runtest/syscalls
index f15331da3..14ceb7696 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -916,6 +916,7 @@ mremap03 mremap03
mremap04 mremap04
mremap05 mremap05
mremap06 mremap06
+mremap07 mremap07
mseal01 mseal01
mseal02 mseal02
diff --git a/testcases/kernel/syscalls/mremap/.gitignore b/testcases/kernel/syscalls/mremap/.gitignore
index ec15a19cd..292899e03 100644
--- a/testcases/kernel/syscalls/mremap/.gitignore
+++ b/testcases/kernel/syscalls/mremap/.gitignore
@@ -4,3 +4,4 @@
/mremap04
/mremap05
/mremap06
+/mremap07
diff --git a/testcases/kernel/syscalls/mremap/mremap07.c b/testcases/kernel/syscalls/mremap/mremap07.c
new file mode 100644
index 000000000..084803775
--- /dev/null
+++ b/testcases/kernel/syscalls/mremap/mremap07.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Wei Gao <wegao@suse.com>
+ */
+
+/*\
+ * LTP test case for mremap() with MREMAP_DONTUNMAP and userfaultfd.
+ *
+ * This test verifies a fault is triggered on the old memory region
+ * and is then correctly handled by a userfaultfd handler.
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <pthread.h>
+
+#include "tst_test.h"
+#include "tst_safe_pthread.h"
+#include "lapi/userfaultfd.h"
+
+static int page_size;
+static int uffd;
+static char *fault_addr;
+static char *new_remap_addr;
+
+static const char *test_string = "Hello, world! This is a test string that fills up a page.";
+
+static int sys_userfaultfd(int flags)
+{
+ return tst_syscall(__NR_userfaultfd, flags);
+}
+
+static void fault_handler_thread(void)
+{
+ struct uffd_msg msg;
+ struct uffdio_copy uffdio_copy;
+
+ TST_CHECKPOINT_WAIT(0);
+
+ struct pollfd pollfd;
+
+ pollfd.fd = uffd;
+ pollfd.events = POLLIN;
+
+ int nready = poll(&pollfd, 1, -1);
+
+ if (nready <= 0)
+ tst_brk(TBROK | TERRNO, "Poll on uffd failed");
+
+ SAFE_READ(1, uffd, &msg, sizeof(msg));
+
+ if (msg.event != UFFD_EVENT_PAGEFAULT)
+ tst_brk(TBROK, "Received unexpected UFFD_EVENT: %d", msg.event);
+
+ if ((char *)msg.arg.pagefault.address != fault_addr)
+ tst_brk(TBROK, "Page fault on unexpected address: %p", (void *)msg.arg.pagefault.address);
+
+ tst_res(TINFO, "Userfaultfd handler caught a page fault at %p", (void *)msg.arg.pagefault.address);
+
+ uffdio_copy.src = (unsigned long)new_remap_addr;
+ uffdio_copy.dst = (unsigned long)fault_addr;
+ uffdio_copy.len = page_size;
+ uffdio_copy.mode = 0;
+ uffdio_copy.copy = 0;
+
+ SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);
+ tst_res(TPASS, "Userfaultfd handler successfully handled the fault.");
+}
+
+static void setup(void)
+{
+ page_size = getpagesize();
+ struct uffdio_api uffdio_api;
+ struct uffdio_register uffdio_register;
+
+ 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);
+
+ fault_addr = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ tst_res(TINFO, "Original mapping created at %p", (void *)fault_addr);
+
+ strcpy(fault_addr, "ABCD");
+
+ uffdio_register.range.start = (unsigned long)fault_addr;
+ uffdio_register.range.len = page_size;
+ uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+ SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
+}
+
+static void cleanup(void)
+{
+ if (new_remap_addr != NULL)
+ SAFE_MUNMAP(new_remap_addr, page_size);
+
+ if (fault_addr != NULL)
+ SAFE_MUNMAP(fault_addr, page_size);
+
+ if (uffd != -1)
+ SAFE_CLOSE(uffd);
+}
+
+static void run(void)
+{
+ pthread_t handler_thread;
+
+ SAFE_PTHREAD_CREATE(&handler_thread, NULL,
+ (void * (*)(void *))fault_handler_thread, NULL);
+
+ new_remap_addr = mremap(fault_addr, page_size, page_size, MREMAP_DONTUNMAP | MREMAP_MAYMOVE);
+ if (new_remap_addr == MAP_FAILED)
+ tst_brk(TBROK | TTERRNO, "mremap failed");
+
+ tst_res(TINFO, "New mapping created at %p", (void *)new_remap_addr);
+
+ strcpy(new_remap_addr, test_string);
+
+ TST_CHECKPOINT_WAKE(0);
+
+ tst_res(TINFO, "Main thread accessing old address %p to trigger fault. Access Content is \"%s\"",
+ (void *)fault_addr, fault_addr);
+
+ SAFE_PTHREAD_JOIN(handler_thread, NULL);
+
+ if (strcmp(fault_addr, test_string) != 0)
+ tst_res(TFAIL, "Verification failed: Content at old address is '%s', expected '%s'", fault_addr, test_string);
+ else
+ tst_res(TPASS, "Verification passed: Content at old address is correct after fault handling.");
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .needs_checkpoints = 1,
+ .cleanup = cleanup,
+ .needs_root = 1,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_USERFAULTFD=y",
+ NULL,
+ },
+ .min_kver = "5.7",
+};
--
2.43.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [LTP] [PATCH v2] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
2025-08-27 23:02 [LTP] [PATCH v1] mremap07.c: New case check mremap with MREMAP_DONTUNMAP Wei Gao via ltp
@ 2025-10-15 3:15 ` Wei Gao via ltp
2025-10-16 13:32 ` Petr Vorel
2025-10-30 5:40 ` [LTP] [PATCH v3] " Wei Gao via ltp
0 siblings, 2 replies; 12+ messages in thread
From: Wei Gao via ltp @ 2025-10-15 3:15 UTC (permalink / raw)
To: ltp
This case test mremap() with MREMAP_DONTUNMAP and use userfaultfd
verifies fault which triggered by accessing old memory region.
Fixes: #1168
Signed-off-by: Wei Gao <wegao@suse.com>
---
configure.ac | 1 +
runtest/syscalls | 1 +
testcases/kernel/syscalls/mremap/.gitignore | 1 +
testcases/kernel/syscalls/mremap/Makefile | 1 +
testcases/kernel/syscalls/mremap/mremap07.c | 159 ++++++++++++++++++++
5 files changed, 163 insertions(+)
create mode 100644 testcases/kernel/syscalls/mremap/mremap07.c
diff --git a/configure.ac b/configure.ac
index 0480f46ca..df842c9e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,6 +46,7 @@ AC_CHECK_DECLS([MADV_MERGEABLE],,,[#include <sys/mman.h>])
AC_CHECK_DECLS([NFTA_CHAIN_ID, NFTA_VERDICT_CHAIN_ID],,,[#include <linux/netfilter/nf_tables.h>])
AC_CHECK_DECLS([PR_CAPBSET_DROP, PR_CAPBSET_READ],,,[#include <sys/prctl.h>])
AC_CHECK_DECLS([SEM_STAT_ANY],,,[#include <sys/sem.h>])
+AC_CHECK_DECLS([MREMAP_DONTUNMAP],,,[#include <linux/mman.h>])
AC_CHECK_HEADERS_ONCE([ \
aio.h \
diff --git a/runtest/syscalls b/runtest/syscalls
index 4b284f279..6d9a4f1d2 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -920,6 +920,7 @@ mremap03 mremap03
mremap04 mremap04
mremap05 mremap05
mremap06 mremap06
+mremap07 mremap07
mseal01 mseal01
mseal02 mseal02
diff --git a/testcases/kernel/syscalls/mremap/.gitignore b/testcases/kernel/syscalls/mremap/.gitignore
index ec15a19cd..292899e03 100644
--- a/testcases/kernel/syscalls/mremap/.gitignore
+++ b/testcases/kernel/syscalls/mremap/.gitignore
@@ -4,3 +4,4 @@
/mremap04
/mremap05
/mremap06
+/mremap07
diff --git a/testcases/kernel/syscalls/mremap/Makefile b/testcases/kernel/syscalls/mremap/Makefile
index 9f5aca9ec..8811b887e 100644
--- a/testcases/kernel/syscalls/mremap/Makefile
+++ b/testcases/kernel/syscalls/mremap/Makefile
@@ -8,5 +8,6 @@ LTPLIBS = ipc
include $(top_srcdir)/include/mk/testcases.mk
mremap04: LTPLDLIBS = -lltpipc
+mremap07: LDLIBS += -lpthread
include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/mremap/mremap07.c b/testcases/kernel/syscalls/mremap/mremap07.c
new file mode 100644
index 000000000..db5e1dfd4
--- /dev/null
+++ b/testcases/kernel/syscalls/mremap/mremap07.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Wei Gao <wegao@suse.com>
+ */
+
+/*\
+ * LTP test case for mremap() with MREMAP_DONTUNMAP and userfaultfd.
+ *
+ * This test verifies a fault is triggered on the old memory region
+ * and is then correctly handled by a userfaultfd handler.
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <pthread.h>
+
+#include "tst_test.h"
+#include "tst_safe_pthread.h"
+#include "lapi/userfaultfd.h"
+
+static int page_size;
+static int uffd;
+static char *fault_addr;
+static char *new_remap_addr;
+
+static const char *test_string = "Hello, world! This is a test string that fills up a page.";
+
+static int sys_userfaultfd(int flags)
+{
+ return tst_syscall(__NR_userfaultfd, flags);
+}
+
+static void fault_handler_thread(void)
+{
+ struct uffd_msg msg;
+ struct uffdio_copy uffdio_copy;
+
+ TST_CHECKPOINT_WAIT(0);
+
+ struct pollfd pollfd;
+
+ pollfd.fd = uffd;
+ pollfd.events = POLLIN;
+
+ int nready = poll(&pollfd, 1, -1);
+
+ if (nready <= 0)
+ tst_brk(TBROK | TERRNO, "Poll on uffd failed");
+
+ SAFE_READ(1, uffd, &msg, sizeof(msg));
+
+ if (msg.event != UFFD_EVENT_PAGEFAULT)
+ tst_brk(TBROK, "Received unexpected UFFD_EVENT: %d", msg.event);
+
+ if ((char *)msg.arg.pagefault.address != fault_addr)
+ tst_brk(TBROK, "Page fault on unexpected address: %p", (void *)msg.arg.pagefault.address);
+
+ tst_res(TINFO, "Userfaultfd handler caught a page fault at %p", (void *)msg.arg.pagefault.address);
+
+ uffdio_copy.src = (unsigned long)new_remap_addr;
+ uffdio_copy.dst = (unsigned long)fault_addr;
+ uffdio_copy.len = page_size;
+ uffdio_copy.mode = 0;
+ uffdio_copy.copy = 0;
+
+ SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);
+ tst_res(TPASS, "Userfaultfd handler successfully handled the fault.");
+}
+
+static void setup(void)
+{
+ page_size = getpagesize();
+ struct uffdio_api uffdio_api;
+ struct uffdio_register uffdio_register;
+
+ 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);
+
+ fault_addr = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ tst_res(TINFO, "Original mapping created at %p", (void *)fault_addr);
+
+ strcpy(fault_addr, "ABCD");
+
+ uffdio_register.range.start = (unsigned long)fault_addr;
+ uffdio_register.range.len = page_size;
+ uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+ SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
+}
+
+static void cleanup(void)
+{
+ if (new_remap_addr != NULL)
+ SAFE_MUNMAP(new_remap_addr, page_size);
+
+ if (fault_addr != NULL)
+ SAFE_MUNMAP(fault_addr, page_size);
+
+ if (uffd != -1)
+ SAFE_CLOSE(uffd);
+}
+
+static void run(void)
+{
+ pthread_t handler_thread;
+
+ SAFE_PTHREAD_CREATE(&handler_thread, NULL,
+ (void * (*)(void *))fault_handler_thread, NULL);
+
+#if HAVE_DECL_MREMAP_DONTUNMAP
+ new_remap_addr = mremap(fault_addr, page_size, page_size, MREMAP_DONTUNMAP | MREMAP_MAYMOVE);
+#else
+ tst_brk(TCONF, "System not supported MREMAP_DONTUNMAP");
+#endif
+
+ if (new_remap_addr == MAP_FAILED)
+ tst_brk(TBROK | TTERRNO, "mremap failed");
+
+ tst_res(TINFO, "New mapping created at %p", (void *)new_remap_addr);
+
+ strcpy(new_remap_addr, test_string);
+
+ TST_CHECKPOINT_WAKE(0);
+
+ tst_res(TINFO, "Main thread accessing old address %p to trigger fault. Access Content is \"%s\"",
+ (void *)fault_addr, fault_addr);
+
+ SAFE_PTHREAD_JOIN(handler_thread, NULL);
+
+ if (strcmp(fault_addr, test_string) != 0)
+ tst_res(TFAIL, "Verification failed: Content at old address is '%s', expected '%s'", fault_addr, test_string);
+ else
+ tst_res(TPASS, "Verification passed: Content at old address is correct after fault handling.");
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .needs_checkpoints = 1,
+ .cleanup = cleanup,
+ .needs_root = 1,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_USERFAULTFD=y",
+ NULL,
+ },
+ .min_kver = "5.7",
+};
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH v2] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
2025-10-15 3:15 ` [LTP] [PATCH v2] " Wei Gao via ltp
@ 2025-10-16 13:32 ` Petr Vorel
2025-10-17 7:51 ` Wei Gao via ltp
2025-10-30 5:40 ` [LTP] [PATCH v3] " Wei Gao via ltp
1 sibling, 1 reply; 12+ messages in thread
From: Petr Vorel @ 2025-10-16 13:32 UTC (permalink / raw)
To: Wei Gao; +Cc: ltp
Hi Wei,
...
> +static void run(void)
> +{
> + pthread_t handler_thread;
> +
> + SAFE_PTHREAD_CREATE(&handler_thread, NULL,
> + (void * (*)(void *))fault_handler_thread, NULL);
> +
> +#if HAVE_DECL_MREMAP_DONTUNMAP
This is a compile check, whole test should be wrapped with #ifdef and fallback
using TST_TEST_TCONF() macro.
#include "config.h"
#include "tst_test.h"
...
#ifdef HAVE_DECL_MREMAP_DONTUNMAP
// other includes
#else
TST_TEST_TCONF("Missing MREMAP_DONTUNMAP in <linux/mman.h>");
#endif
This quits tests early.
And please use #ifdef.
Kind regards,
Petr
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH v2] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
2025-10-16 13:32 ` Petr Vorel
@ 2025-10-17 7:51 ` Wei Gao via ltp
2025-10-30 19:39 ` Petr Vorel
0 siblings, 1 reply; 12+ messages in thread
From: Wei Gao via ltp @ 2025-10-17 7:51 UTC (permalink / raw)
To: Petr Vorel; +Cc: ltp
On Thu, Oct 16, 2025 at 03:32:03PM +0200, Petr Vorel wrote:
> Hi Wei,
>
> ...
> > +static void run(void)
> > +{
> > + pthread_t handler_thread;
> > +
> > + SAFE_PTHREAD_CREATE(&handler_thread, NULL,
> > + (void * (*)(void *))fault_handler_thread, NULL);
> > +
> > +#if HAVE_DECL_MREMAP_DONTUNMAP
> This is a compile check, whole test should be wrapped with #ifdef and fallback
> using TST_TEST_TCONF() macro.
>
> #include "config.h"
> #include "tst_test.h"
> ...
> #ifdef HAVE_DECL_MREMAP_DONTUNMAP
> // other includes
>
> #else
> TST_TEST_TCONF("Missing MREMAP_DONTUNMAP in <linux/mman.h>");
> #endif
>
> This quits tests early.
Good.
> And please use #ifdef.
When MREMAP_DONTUNMAP can not be found config.h will contain:
#define HAVE_DECL_MREMAP_DONTUNMAP 0
So i suppose we should use #if instead of #ifdef otherwise compile issue
will happen on old platform.
If you agree then i will prepare new patch.
Thanks.
>
> Kind regards,
> Petr
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* [LTP] [PATCH v3] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
2025-10-15 3:15 ` [LTP] [PATCH v2] " Wei Gao via ltp
2025-10-16 13:32 ` Petr Vorel
@ 2025-10-30 5:40 ` Wei Gao via ltp
2025-10-30 20:07 ` Petr Vorel
2026-02-25 9:05 ` [LTP] [PATCH v4] " Wei Gao via ltp
1 sibling, 2 replies; 12+ messages in thread
From: Wei Gao via ltp @ 2025-10-30 5:40 UTC (permalink / raw)
To: ltp
This case test mremap() with MREMAP_DONTUNMAP and use userfaultfd
verifies fault which triggered by accessing old memory region.
Fixes: #1168
Signed-off-by: Wei Gao <wegao@suse.com>
---
configure.ac | 1 +
runtest/syscalls | 1 +
testcases/kernel/syscalls/mremap/.gitignore | 1 +
testcases/kernel/syscalls/mremap/Makefile | 1 +
testcases/kernel/syscalls/mremap/mremap07.c | 161 ++++++++++++++++++++
5 files changed, 165 insertions(+)
create mode 100644 testcases/kernel/syscalls/mremap/mremap07.c
diff --git a/configure.ac b/configure.ac
index 0480f46ca..df842c9e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,6 +46,7 @@ AC_CHECK_DECLS([MADV_MERGEABLE],,,[#include <sys/mman.h>])
AC_CHECK_DECLS([NFTA_CHAIN_ID, NFTA_VERDICT_CHAIN_ID],,,[#include <linux/netfilter/nf_tables.h>])
AC_CHECK_DECLS([PR_CAPBSET_DROP, PR_CAPBSET_READ],,,[#include <sys/prctl.h>])
AC_CHECK_DECLS([SEM_STAT_ANY],,,[#include <sys/sem.h>])
+AC_CHECK_DECLS([MREMAP_DONTUNMAP],,,[#include <linux/mman.h>])
AC_CHECK_HEADERS_ONCE([ \
aio.h \
diff --git a/runtest/syscalls b/runtest/syscalls
index 4b284f279..6d9a4f1d2 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -920,6 +920,7 @@ mremap03 mremap03
mremap04 mremap04
mremap05 mremap05
mremap06 mremap06
+mremap07 mremap07
mseal01 mseal01
mseal02 mseal02
diff --git a/testcases/kernel/syscalls/mremap/.gitignore b/testcases/kernel/syscalls/mremap/.gitignore
index ec15a19cd..292899e03 100644
--- a/testcases/kernel/syscalls/mremap/.gitignore
+++ b/testcases/kernel/syscalls/mremap/.gitignore
@@ -4,3 +4,4 @@
/mremap04
/mremap05
/mremap06
+/mremap07
diff --git a/testcases/kernel/syscalls/mremap/Makefile b/testcases/kernel/syscalls/mremap/Makefile
index 9f5aca9ec..8811b887e 100644
--- a/testcases/kernel/syscalls/mremap/Makefile
+++ b/testcases/kernel/syscalls/mremap/Makefile
@@ -8,5 +8,6 @@ LTPLIBS = ipc
include $(top_srcdir)/include/mk/testcases.mk
mremap04: LTPLDLIBS = -lltpipc
+mremap07: LDLIBS += -lpthread
include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/mremap/mremap07.c b/testcases/kernel/syscalls/mremap/mremap07.c
new file mode 100644
index 000000000..7acfff461
--- /dev/null
+++ b/testcases/kernel/syscalls/mremap/mremap07.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Wei Gao <wegao@suse.com>
+ */
+
+/*\
+ * LTP test case for mremap() with MREMAP_DONTUNMAP and userfaultfd.
+ *
+ * This test verifies a fault is triggered on the old memory region
+ * and is then correctly handled by a userfaultfd handler.
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <pthread.h>
+
+#include "tst_test.h"
+#include "tst_safe_pthread.h"
+#include "lapi/userfaultfd.h"
+
+#if HAVE_DECL_MREMAP_DONTUNMAP
+
+static int page_size;
+static int uffd;
+static char *fault_addr;
+static char *new_remap_addr;
+
+static const char *test_string = "Hello, world! This is a test string that fills up a page.";
+
+static int sys_userfaultfd(int flags)
+{
+ return tst_syscall(__NR_userfaultfd, flags);
+}
+
+static void fault_handler_thread(void)
+{
+ struct uffd_msg msg;
+ struct uffdio_copy uffdio_copy;
+
+ TST_CHECKPOINT_WAIT(0);
+
+ struct pollfd pollfd;
+
+ pollfd.fd = uffd;
+ pollfd.events = POLLIN;
+
+ int nready = poll(&pollfd, 1, -1);
+
+ if (nready <= 0)
+ tst_brk(TBROK | TERRNO, "Poll on uffd failed");
+
+ SAFE_READ(1, uffd, &msg, sizeof(msg));
+
+ if (msg.event != UFFD_EVENT_PAGEFAULT)
+ tst_brk(TBROK, "Received unexpected UFFD_EVENT: %d", msg.event);
+
+ if ((char *)msg.arg.pagefault.address != fault_addr)
+ tst_brk(TBROK, "Page fault on unexpected address: %p", (void *)msg.arg.pagefault.address);
+
+ tst_res(TINFO, "Userfaultfd handler caught a page fault at %p", (void *)msg.arg.pagefault.address);
+
+ uffdio_copy.src = (unsigned long)new_remap_addr;
+ uffdio_copy.dst = (unsigned long)fault_addr;
+ uffdio_copy.len = page_size;
+ uffdio_copy.mode = 0;
+ uffdio_copy.copy = 0;
+
+ SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);
+ tst_res(TPASS, "Userfaultfd handler successfully handled the fault.");
+}
+
+static void setup(void)
+{
+ page_size = getpagesize();
+ struct uffdio_api uffdio_api;
+ struct uffdio_register uffdio_register;
+
+ 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);
+
+ fault_addr = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ tst_res(TINFO, "Original mapping created at %p", (void *)fault_addr);
+
+ strcpy(fault_addr, "ABCD");
+
+ uffdio_register.range.start = (unsigned long)fault_addr;
+ uffdio_register.range.len = page_size;
+ uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+ SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
+}
+
+static void cleanup(void)
+{
+ if (new_remap_addr != NULL)
+ SAFE_MUNMAP(new_remap_addr, page_size);
+
+ if (fault_addr != NULL)
+ SAFE_MUNMAP(fault_addr, page_size);
+
+ if (uffd != -1)
+ SAFE_CLOSE(uffd);
+}
+
+static void run(void)
+{
+ pthread_t handler_thread;
+
+ SAFE_PTHREAD_CREATE(&handler_thread, NULL,
+ (void * (*)(void *))fault_handler_thread, NULL);
+
+ new_remap_addr = mremap(fault_addr, page_size, page_size, MREMAP_DONTUNMAP | MREMAP_MAYMOVE);
+
+ if (new_remap_addr == MAP_FAILED)
+ tst_brk(TBROK | TTERRNO, "mremap failed");
+
+ tst_res(TINFO, "New mapping created at %p", (void *)new_remap_addr);
+
+ strcpy(new_remap_addr, test_string);
+
+ TST_CHECKPOINT_WAKE(0);
+
+ tst_res(TINFO, "Main thread accessing old address %p to trigger fault. Access Content is \"%s\"",
+ (void *)fault_addr, fault_addr);
+
+ SAFE_PTHREAD_JOIN(handler_thread, NULL);
+
+ if (strcmp(fault_addr, test_string) != 0)
+ tst_res(TFAIL, "Verification failed: Content at old address is '%s', expected '%s'", fault_addr, test_string);
+ else
+ tst_res(TPASS, "Verification passed: Content at old address is correct after fault handling.");
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .needs_checkpoints = 1,
+ .cleanup = cleanup,
+ .needs_root = 1,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_USERFAULTFD=y",
+ NULL,
+ },
+ .min_kver = "5.7",
+};
+
+#else
+TST_TEST_TCONF("Missing MREMAP_DONTUNMAP in <linux/mman.h>");
+#endif
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH v2] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
2025-10-17 7:51 ` Wei Gao via ltp
@ 2025-10-30 19:39 ` Petr Vorel
2025-11-01 8:47 ` Wei Gao via ltp
0 siblings, 1 reply; 12+ messages in thread
From: Petr Vorel @ 2025-10-30 19:39 UTC (permalink / raw)
To: Wei Gao; +Cc: ltp
Hi Wei,
...
> > And please use #ifdef.
> When MREMAP_DONTUNMAP can not be found config.h will contain:
> #define HAVE_DECL_MREMAP_DONTUNMAP 0
> So i suppose we should use #if instead of #ifdef otherwise compile issue
> will happen on old platform.
Indeed declarations are really defined (instead of commented out undef).
I'm sorry for a wrong hint.
Kind regards,
Petr
> If you agree then i will prepare new patch.
> Thanks.
> > Kind regards,
> > Petr
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH v3] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
2025-10-30 5:40 ` [LTP] [PATCH v3] " Wei Gao via ltp
@ 2025-10-30 20:07 ` Petr Vorel
2026-02-25 9:05 ` [LTP] [PATCH v4] " Wei Gao via ltp
1 sibling, 0 replies; 12+ messages in thread
From: Petr Vorel @ 2025-10-30 20:07 UTC (permalink / raw)
To: Wei Gao; +Cc: ltp
Hi Wei,
> This case test mremap() with MREMAP_DONTUNMAP and use userfaultfd
> verifies fault which triggered by accessing old memory region.
nit: Having a changelog would help reviewing.
...
> +++ b/configure.ac
> @@ -46,6 +46,7 @@ AC_CHECK_DECLS([MADV_MERGEABLE],,,[#include <sys/mman.h>])
> AC_CHECK_DECLS([NFTA_CHAIN_ID, NFTA_VERDICT_CHAIN_ID],,,[#include <linux/netfilter/nf_tables.h>])
> AC_CHECK_DECLS([PR_CAPBSET_DROP, PR_CAPBSET_READ],,,[#include <sys/prctl.h>])
> AC_CHECK_DECLS([SEM_STAT_ANY],,,[#include <sys/sem.h>])
> +AC_CHECK_DECLS([MREMAP_DONTUNMAP],,,[#include <linux/mman.h>])
Obviously AC_CHECK_DECLS() does not require to define _GNU_SOURCE, interesting.
...
> +++ b/testcases/kernel/syscalls/mremap/mremap07.c
> @@ -0,0 +1,161 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2025 Wei Gao <wegao@suse.com>
> + */
> +
> +/*\
> + * LTP test case for mremap() with MREMAP_DONTUNMAP and userfaultfd.
> + *
> + * This test verifies a fault is triggered on the old memory region
> + * and is then correctly handled by a userfaultfd handler.
> + */
You should include config.h if you want to check for HAVE_DECL_MREMAP_DONTUNMAP:
#include "config.h"
This works just because some header already do it, but that can change in the
future.
> +#define _GNU_SOURCE
> +#include <poll.h>
> +#include <pthread.h>
> +
> +#include "tst_test.h"
> +#include "tst_safe_pthread.h"
> +#include "lapi/userfaultfd.h"
> +
> +#if HAVE_DECL_MREMAP_DONTUNMAP
Interesting, you don't include <linux/mman.h>, which should have
MREMAP_DONTUNMAP, but the check works as expected. But I would still prefer to
include <linux/mman.h>.
> +static int page_size;
> +static int uffd;
> +static char *fault_addr;
> +static char *new_remap_addr;
> +
> +static const char *test_string = "Hello, world! This is a test string that fills up a page.";
> +
> +static int sys_userfaultfd(int flags)
> +{
> + return tst_syscall(__NR_userfaultfd, flags);
> +}
This is copy pasted from userfaultfd01.c. I factored it out to
include/lapi/userfaultfd.h, could you please use it in the next version?
https://lore.kernel.org/ltp/20251030192543.761804-1-pvorel@suse.cz/
https://patchwork.ozlabs.org/project/ltp/patch/20251030192543.761804-1-pvorel@suse.cz/
> +static void fault_handler_thread(void)
> +{
> + struct uffd_msg msg;
> + struct uffdio_copy uffdio_copy;
> +
> + TST_CHECKPOINT_WAIT(0);
> +
> + struct pollfd pollfd;
> +
> + pollfd.fd = uffd;
> + pollfd.events = POLLIN;
> +
> + int nready = poll(&pollfd, 1, -1);
Interesting, we still don't have safe_poll().
> + if (nready <= 0)
man poll(2) says:
return value of zero indicates that the system call timed out before any file descriptors became ready.
userfaultfd01.c checks only for nready == -1, I'm not sure maybe it should also check for 0.
But if you also check for 0, maybe printing nready would be useful (OTOH TERRNO
prints SUCCESS(0)).
> + tst_brk(TBROK | TERRNO, "Poll on uffd failed");
Maybe just poll() failed?
> +
> + SAFE_READ(1, uffd, &msg, sizeof(msg));
> +
> + if (msg.event != UFFD_EVENT_PAGEFAULT)
> + tst_brk(TBROK, "Received unexpected UFFD_EVENT: %d", msg.event);
> +
> + if ((char *)msg.arg.pagefault.address != fault_addr)
> + tst_brk(TBROK, "Page fault on unexpected address: %p", (void *)msg.arg.pagefault.address);
> +
> + tst_res(TINFO, "Userfaultfd handler caught a page fault at %p", (void *)msg.arg.pagefault.address);
> +
> + uffdio_copy.src = (unsigned long)new_remap_addr;
> + uffdio_copy.dst = (unsigned long)fault_addr;
> + uffdio_copy.len = page_size;
> + uffdio_copy.mode = 0;
> + uffdio_copy.copy = 0;
Most of this code is the same as in userfaultfd01.c, but I don't see a way to
factor it out more than what I sent in my patch.
> +
> + SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);
> + tst_res(TPASS, "Userfaultfd handler successfully handled the fault.");
very nit: please avoid '.' at the end.
> +}
> +
> +static void setup(void)
> +{
> + page_size = getpagesize();
> + struct uffdio_api uffdio_api;
> + struct uffdio_register uffdio_register;
> +
> + 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");
> + }
> + }
This would be also replaced by my patch, only this would be used:
uffd = SAFE_USERFAULTFD(O_CLOEXEC | O_NONBLOCK, true);
> +
> + uffd = TST_RET;
> + uffdio_api.api = UFFD_API;
> + uffdio_api.features = 0;
> + SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api);
> +
> + fault_addr = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
nit: maybe split long line?
> +
> + tst_res(TINFO, "Original mapping created at %p", (void *)fault_addr);
> +
> + strcpy(fault_addr, "ABCD");
> +
> + uffdio_register.range.start = (unsigned long)fault_addr;
> + uffdio_register.range.len = page_size;
> + uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
> + SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
> +}
> +
> +static void cleanup(void)
> +{
> + if (new_remap_addr != NULL)
> + SAFE_MUNMAP(new_remap_addr, page_size);
> +
> + if (fault_addr != NULL)
> + SAFE_MUNMAP(fault_addr, page_size);
> +
> + if (uffd != -1)
> + SAFE_CLOSE(uffd);
> +}
> +
> +static void run(void)
> +{
> + pthread_t handler_thread;
> +
> + SAFE_PTHREAD_CREATE(&handler_thread, NULL,
> + (void * (*)(void *))fault_handler_thread, NULL);
> +
> + new_remap_addr = mremap(fault_addr, page_size, page_size, MREMAP_DONTUNMAP | MREMAP_MAYMOVE);
nit: long line.
> +
> + if (new_remap_addr == MAP_FAILED)
> + tst_brk(TBROK | TTERRNO, "mremap failed");
> +
> + tst_res(TINFO, "New mapping created at %p", (void *)new_remap_addr);
> +
> + strcpy(new_remap_addr, test_string);
> +
> + TST_CHECKPOINT_WAKE(0);
> +
> + tst_res(TINFO, "Main thread accessing old address %p to trigger fault. Access Content is \"%s\"",
> + (void *)fault_addr, fault_addr);
nit: could we remove "Access Content is \"%s\"" ? It's not important for
the result if it pass (you print it also on the failure) and line is too long.
> +
> + SAFE_PTHREAD_JOIN(handler_thread, NULL);
> +
> + if (strcmp(fault_addr, test_string) != 0)
> + tst_res(TFAIL, "Verification failed: Content at old address is '%s', expected '%s'", fault_addr, test_string);
Maybe s/at/of the/ ?
> + else
> + tst_res(TPASS, "Verification passed: Content at old address is correct after fault handling.");
nit: '.' at the end.
> +}
> +
> +static struct tst_test test = {
> + .test_all = run,
> + .setup = setup,
> + .needs_checkpoints = 1,
> + .cleanup = cleanup,
> + .needs_root = 1,
> + .needs_kconfigs = (const char *[]) {
> + "CONFIG_USERFAULTFD=y",
> + NULL,
> + },
> + .min_kver = "5.7",
I wonder if we check for MREMAP_DONTUNMAP whether we need to have also explicit
check for MREMAP_DONTUNMAP. But sure, it's safer to have runtime kernel check
and check that it was not compiled with an old headers.
Kind regards,
Petr
> +};
> +
> +#else
> +TST_TEST_TCONF("Missing MREMAP_DONTUNMAP in <linux/mman.h>");
> +#endif
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH v2] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
2025-10-30 19:39 ` Petr Vorel
@ 2025-11-01 8:47 ` Wei Gao via ltp
0 siblings, 0 replies; 12+ messages in thread
From: Wei Gao via ltp @ 2025-11-01 8:47 UTC (permalink / raw)
To: Petr Vorel; +Cc: ltp
On Thu, Oct 30, 2025 at 08:39:52PM +0100, Petr Vorel wrote:
> Hi Wei,
>
> ...
> > > And please use #ifdef.
> > When MREMAP_DONTUNMAP can not be found config.h will contain:
> > #define HAVE_DECL_MREMAP_DONTUNMAP 0
>
> > So i suppose we should use #if instead of #ifdef otherwise compile issue
> > will happen on old platform.
>
> Indeed declarations are really defined (instead of commented out undef).
> I'm sorry for a wrong hint.
No worry at all, thanks for confirming the correct way. That's help :)
>
> Kind regards,
> Petr
>
> > If you agree then i will prepare new patch.
> > Thanks.
>
> > > Kind regards,
> > > Petr
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* [LTP] [PATCH v4] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
2025-10-30 5:40 ` [LTP] [PATCH v3] " Wei Gao via ltp
2025-10-30 20:07 ` Petr Vorel
@ 2026-02-25 9:05 ` Wei Gao via ltp
2026-03-23 7:03 ` Andrea Cervesato via ltp
2026-03-25 1:15 ` [LTP] [PATCH v5] " Wei Gao via ltp
1 sibling, 2 replies; 12+ messages in thread
From: Wei Gao via ltp @ 2026-02-25 9:05 UTC (permalink / raw)
To: ltp
This case test mremap() with MREMAP_DONTUNMAP and use userfaultfd
verifies fault which triggered by accessing old memory region.
changelog v4->v3:
1) Add explicit volatile memory access to trigger page fault.
2) Use SAFE_USERFAULTFD and add config.h/linux/mman.h.
3) Logic: Distinguish poll() timeout from failure.
4) Style: Fix long lines.
Fixes: #1168
Signed-off-by: Wei Gao <wegao@suse.com>
---
configure.ac | 1 +
runtest/syscalls | 1 +
testcases/kernel/syscalls/mremap/.gitignore | 1 +
testcases/kernel/syscalls/mremap/Makefile | 1 +
testcases/kernel/syscalls/mremap/mremap07.c | 158 ++++++++++++++++++++
5 files changed, 162 insertions(+)
create mode 100644 testcases/kernel/syscalls/mremap/mremap07.c
diff --git a/configure.ac b/configure.ac
index 7fa614dcb..c836ef7f3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,6 +46,7 @@ AC_CHECK_DECLS([MADV_MERGEABLE],,,[#include <sys/mman.h>])
AC_CHECK_DECLS([NFTA_CHAIN_ID, NFTA_VERDICT_CHAIN_ID],,,[#include <linux/netfilter/nf_tables.h>])
AC_CHECK_DECLS([PR_CAPBSET_DROP, PR_CAPBSET_READ],,,[#include <sys/prctl.h>])
AC_CHECK_DECLS([SEM_STAT_ANY],,,[#include <sys/sem.h>])
+AC_CHECK_DECLS([MREMAP_DONTUNMAP],,,[#include <linux/mman.h>])
AC_CHECK_HEADERS_ONCE([ \
aio.h \
diff --git a/runtest/syscalls b/runtest/syscalls
index 2f629e4e4..9d7c6c32e 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -925,6 +925,7 @@ mremap03 mremap03
mremap04 mremap04
mremap05 mremap05
mremap06 mremap06
+mremap07 mremap07
mseal01 mseal01
mseal02 mseal02
diff --git a/testcases/kernel/syscalls/mremap/.gitignore b/testcases/kernel/syscalls/mremap/.gitignore
index ec15a19cd..292899e03 100644
--- a/testcases/kernel/syscalls/mremap/.gitignore
+++ b/testcases/kernel/syscalls/mremap/.gitignore
@@ -4,3 +4,4 @@
/mremap04
/mremap05
/mremap06
+/mremap07
diff --git a/testcases/kernel/syscalls/mremap/Makefile b/testcases/kernel/syscalls/mremap/Makefile
index 9f5aca9ec..8811b887e 100644
--- a/testcases/kernel/syscalls/mremap/Makefile
+++ b/testcases/kernel/syscalls/mremap/Makefile
@@ -8,5 +8,6 @@ LTPLIBS = ipc
include $(top_srcdir)/include/mk/testcases.mk
mremap04: LTPLDLIBS = -lltpipc
+mremap07: LDLIBS += -lpthread
include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/mremap/mremap07.c b/testcases/kernel/syscalls/mremap/mremap07.c
new file mode 100644
index 000000000..92dee4636
--- /dev/null
+++ b/testcases/kernel/syscalls/mremap/mremap07.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Wei Gao <wegao@suse.com>
+ */
+
+/*\
+ * LTP test case for mremap() with MREMAP_DONTUNMAP and userfaultfd.
+ *
+ * This test verifies a fault is triggered on the old memory region
+ * and is then correctly handled by a userfaultfd handler.
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <pthread.h>
+#include <linux/mman.h>
+
+#include "tst_test.h"
+#include "tst_safe_pthread.h"
+#include "lapi/userfaultfd.h"
+#include "config.h"
+
+#if HAVE_DECL_MREMAP_DONTUNMAP
+
+static int page_size;
+static int uffd;
+static char *fault_addr;
+static char *new_remap_addr;
+
+static const char *test_string = "Hello, world! This is a test string that fills up a page.";
+
+static void fault_handler_thread(void)
+{
+ struct uffd_msg msg;
+ struct uffdio_copy uffdio_copy;
+
+ TST_CHECKPOINT_WAIT(0);
+
+ struct pollfd pollfd;
+
+ pollfd.fd = uffd;
+ pollfd.events = POLLIN;
+
+ int nready = poll(&pollfd, 1, -1);
+
+ if (nready == -1)
+ tst_brk(TBROK | TERRNO, "poll() failed");
+
+ if (nready == 0)
+ tst_brk(TBROK, "poll() timed out unexpectedly");
+
+ SAFE_READ(1, uffd, &msg, sizeof(msg));
+
+ if (msg.event != UFFD_EVENT_PAGEFAULT)
+ tst_brk(TBROK, "Received unexpected UFFD_EVENT: %d", msg.event);
+
+ if ((char *)msg.arg.pagefault.address != fault_addr)
+ tst_brk(TBROK, "Page fault on unexpected address: %p", (void *)msg.arg.pagefault.address);
+
+ tst_res(TINFO, "Userfaultfd handler caught a page fault at %p", (void *)msg.arg.pagefault.address);
+
+ uffdio_copy.src = (unsigned long)new_remap_addr;
+ uffdio_copy.dst = (unsigned long)fault_addr;
+ uffdio_copy.len = page_size;
+ uffdio_copy.mode = 0;
+ uffdio_copy.copy = 0;
+
+ SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);
+ tst_res(TPASS, "Userfaultfd handler successfully handled the fault");
+}
+
+static void setup(void)
+{
+ page_size = getpagesize();
+ struct uffdio_api uffdio_api;
+ struct uffdio_register uffdio_register;
+
+ uffd = SAFE_USERFAULTFD(O_CLOEXEC | O_NONBLOCK, true);
+
+ uffdio_api.api = UFFD_API;
+ uffdio_api.features = 0;
+ SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api);
+
+ fault_addr = SAFE_MMAP(NULL, page_size,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ tst_res(TINFO, "Original mapping created at %p", (void *)fault_addr);
+
+ strcpy(fault_addr, "ABCD");
+
+ uffdio_register.range.start = (unsigned long)fault_addr;
+ uffdio_register.range.len = page_size;
+ uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+ SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
+}
+
+static void cleanup(void)
+{
+ if (new_remap_addr != NULL)
+ SAFE_MUNMAP(new_remap_addr, page_size);
+
+ if (fault_addr != NULL)
+ SAFE_MUNMAP(fault_addr, page_size);
+
+ if (uffd != -1)
+ SAFE_CLOSE(uffd);
+}
+
+static void run(void)
+{
+ pthread_t handler_thread;
+
+ SAFE_PTHREAD_CREATE(&handler_thread, NULL,
+ (void * (*)(void *))fault_handler_thread, NULL);
+
+ new_remap_addr = mremap(fault_addr, page_size, page_size,
+ MREMAP_DONTUNMAP | MREMAP_MAYMOVE);
+
+ if (new_remap_addr == MAP_FAILED)
+ tst_brk(TBROK | TTERRNO, "mremap failed");
+
+ tst_res(TINFO, "New mapping created at %p", (void *)new_remap_addr);
+
+ strcpy(new_remap_addr, test_string);
+
+ TST_CHECKPOINT_WAKE(0);
+
+ tst_res(TINFO, "Main thread accessing old address %p to trigger fault",
+ (void *)fault_addr);
+
+ (void)*(volatile char *)fault_addr;
+
+ SAFE_PTHREAD_JOIN(handler_thread, NULL);
+
+ if (strcmp(fault_addr, test_string) != 0)
+ tst_res(TFAIL, "Verification failed: Content of the old "
+ "address is '%s', expected '%s'", fault_addr, test_string);
+ else
+ tst_res(TPASS, "Verification passed: Content of the old "
+ "address is correct after fault handling");
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .needs_checkpoints = 1,
+ .cleanup = cleanup,
+ .needs_root = 1,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_USERFAULTFD=y",
+ NULL,
+ },
+ .min_kver = "5.7",
+};
+
+#else
+TST_TEST_TCONF("Missing MREMAP_DONTUNMAP in <linux/mman.h>");
+#endif
--
2.52.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH v4] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
2026-02-25 9:05 ` [LTP] [PATCH v4] " Wei Gao via ltp
@ 2026-03-23 7:03 ` Andrea Cervesato via ltp
2026-03-25 1:15 ` [LTP] [PATCH v5] " Wei Gao via ltp
1 sibling, 0 replies; 12+ messages in thread
From: Andrea Cervesato via ltp @ 2026-03-23 7:03 UTC (permalink / raw)
To: Wei Gao via ltp; +Cc: ltp
Hi Wei,
The overall structure looks good, but there are a few bugs that need
fixing before this can be merged.
> +static int uffd;
This is zero-initialized by the compiler, but fd 0 is a valid file
descriptor (stdin). cleanup() guards with `if (uffd != -1)`, so if
setup() fails before SAFE_USERFAULTFD assigns a real fd, cleanup() will
call SAFE_CLOSE(0) and close stdin. Initialize it to -1:
static int uffd = -1;
> +static void fault_handler_thread(void)
pthread start routines must have the signature `void *(*)(void *)`.
Casting a `void (*)(void)` function to that type and calling it through
the incompatible pointer is undefined behavior. Please use the correct
signature:
static void *fault_handler_thread(void *arg LTP_ATTRIBUTE_UNUSED)
{
...
return NULL;
}
> + if (new_remap_addr != NULL)
> + SAFE_MUNMAP(new_remap_addr, page_size);
> +
> + if (fault_addr != NULL)
> + SAFE_MUNMAP(fault_addr, page_size);
Kernel coding style: drop the `!= NULL`, write `if (new_remap_addr)`
and `if (fault_addr)`. checkpatch also flags these.
> + SAFE_PTHREAD_CREATE(&handler_thread, NULL,
> + (void * (*)(void *))fault_handler_thread, NULL);
> +
> + new_remap_addr = mremap(fault_addr, page_size, page_size,
> + MREMAP_DONTUNMAP | MREMAP_MAYMOVE);
> +
> + if (new_remap_addr == MAP_FAILED)
> + tst_brk(TBROK | TTERRNO, "mremap failed");
Two issues here:
1. TTERRNO prints TST_ERR, which is the errno captured by the TEST()
macro. mremap() is called directly, so TST_ERR is stale. Use TERRNO
(system errno) instead.
2. new_remap_addr is a static variable that is set here but only freed
in cleanup(). When the test is run with `-i N` for N > 1, each
iteration overwrites new_remap_addr with a fresh mremap result, and
the previous iteration's mapping is leaked. Add an explicit munmap at
the end of run() and reset the pointer:
SAFE_MUNMAP(new_remap_addr, page_size);
new_remap_addr = NULL;
> + .needs_root = 1,
This is not needed. SAFE_USERFAULTFD() is called with retry=true, which
automatically retries with UFFD_USER_MODE_ONLY when EPERM is returned.
Everything this test does (mmap/mremap, UFFDIO_REGISTER, UFFDIO_COPY on
user pages) works without root on kernels >= 5.11. On 5.7--5.10 the
retry will result in TCONF, which is the correct behavior. Please drop
.needs_root.
Regards,
--
Andrea Cervesato
SUSE QE Automation Engineer Linux
andrea.cervesato@suse.com
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* [LTP] [PATCH v5] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
2026-02-25 9:05 ` [LTP] [PATCH v4] " Wei Gao via ltp
2026-03-23 7:03 ` Andrea Cervesato via ltp
@ 2026-03-25 1:15 ` Wei Gao via ltp
2026-03-26 10:01 ` Andrea Cervesato via ltp
1 sibling, 1 reply; 12+ messages in thread
From: Wei Gao via ltp @ 2026-03-25 1:15 UTC (permalink / raw)
To: ltp
This case test mremap() with MREMAP_DONTUNMAP and use userfaultfd
verifies fault which triggered by accessing old memory region.
Fixes: #1168
Signed-off-by: Wei Gao <wegao@suse.com>
---
v4->v5:
1)Added NULL as the 5th argument to mremap(), otherwise mremap will return
EINVAL, latest kernel will check new address is aligned or not.
2)Initialized uffd to -1
3)Updated fault_handler_thread to return void *
4)Enhanced the cleanup() function to verify that pointers are not
MAP_FAILED
5)Switched from TTERRNO to TERRNO in the mremap check
6)Removed .needs_root = 1
configure.ac | 1 +
runtest/syscalls | 1 +
testcases/kernel/syscalls/mremap/.gitignore | 1 +
testcases/kernel/syscalls/mremap/Makefile | 1 +
testcases/kernel/syscalls/mremap/mremap07.c | 160 ++++++++++++++++++++
5 files changed, 164 insertions(+)
create mode 100644 testcases/kernel/syscalls/mremap/mremap07.c
diff --git a/configure.ac b/configure.ac
index 7fa614dcb..c836ef7f3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,6 +46,7 @@ AC_CHECK_DECLS([MADV_MERGEABLE],,,[#include <sys/mman.h>])
AC_CHECK_DECLS([NFTA_CHAIN_ID, NFTA_VERDICT_CHAIN_ID],,,[#include <linux/netfilter/nf_tables.h>])
AC_CHECK_DECLS([PR_CAPBSET_DROP, PR_CAPBSET_READ],,,[#include <sys/prctl.h>])
AC_CHECK_DECLS([SEM_STAT_ANY],,,[#include <sys/sem.h>])
+AC_CHECK_DECLS([MREMAP_DONTUNMAP],,,[#include <linux/mman.h>])
AC_CHECK_HEADERS_ONCE([ \
aio.h \
diff --git a/runtest/syscalls b/runtest/syscalls
index 2f629e4e4..9d7c6c32e 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -925,6 +925,7 @@ mremap03 mremap03
mremap04 mremap04
mremap05 mremap05
mremap06 mremap06
+mremap07 mremap07
mseal01 mseal01
mseal02 mseal02
diff --git a/testcases/kernel/syscalls/mremap/.gitignore b/testcases/kernel/syscalls/mremap/.gitignore
index ec15a19cd..292899e03 100644
--- a/testcases/kernel/syscalls/mremap/.gitignore
+++ b/testcases/kernel/syscalls/mremap/.gitignore
@@ -4,3 +4,4 @@
/mremap04
/mremap05
/mremap06
+/mremap07
diff --git a/testcases/kernel/syscalls/mremap/Makefile b/testcases/kernel/syscalls/mremap/Makefile
index 9f5aca9ec..8811b887e 100644
--- a/testcases/kernel/syscalls/mremap/Makefile
+++ b/testcases/kernel/syscalls/mremap/Makefile
@@ -8,5 +8,6 @@ LTPLIBS = ipc
include $(top_srcdir)/include/mk/testcases.mk
mremap04: LTPLDLIBS = -lltpipc
+mremap07: LDLIBS += -lpthread
include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/mremap/mremap07.c b/testcases/kernel/syscalls/mremap/mremap07.c
new file mode 100644
index 000000000..51c24b6fc
--- /dev/null
+++ b/testcases/kernel/syscalls/mremap/mremap07.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Wei Gao <wegao@suse.com>
+ */
+
+/*\
+ * LTP test case for mremap() with MREMAP_DONTUNMAP and userfaultfd.
+ *
+ * This test verifies a fault is triggered on the old memory region
+ * and is then correctly handled by a userfaultfd handler.
+ */
+
+#define _GNU_SOURCE
+#include <poll.h>
+#include <pthread.h>
+#include <linux/mman.h>
+
+#include "tst_test.h"
+#include "tst_safe_pthread.h"
+#include "lapi/userfaultfd.h"
+#include "config.h"
+
+#if HAVE_DECL_MREMAP_DONTUNMAP
+
+static int page_size;
+static int uffd = -1;
+static char *fault_addr;
+static char *new_remap_addr;
+
+static const char *test_string = "Hello, world! This is a test string that fills up a page.";
+
+static void *fault_handler_thread(void *arg LTP_ATTRIBUTE_UNUSED)
+{
+ struct uffd_msg msg;
+ struct uffdio_copy uffdio_copy;
+
+ TST_CHECKPOINT_WAIT(0);
+
+ struct pollfd pollfd;
+
+ pollfd.fd = uffd;
+ pollfd.events = POLLIN;
+
+ int nready = poll(&pollfd, 1, -1);
+
+ if (nready == -1)
+ tst_brk(TBROK | TERRNO, "poll() failed");
+
+ if (nready == 0)
+ tst_brk(TBROK, "poll() timed out unexpectedly");
+
+ SAFE_READ(1, uffd, &msg, sizeof(msg));
+
+ if (msg.event != UFFD_EVENT_PAGEFAULT)
+ tst_brk(TBROK, "Received unexpected UFFD_EVENT: %d", msg.event);
+
+ if ((char *)msg.arg.pagefault.address != fault_addr)
+ tst_brk(TBROK, "Page fault on unexpected address: %p", (void *)msg.arg.pagefault.address);
+
+ tst_res(TINFO, "Userfaultfd handler caught a page fault at %p", (void *)msg.arg.pagefault.address);
+
+ uffdio_copy.src = (unsigned long)new_remap_addr;
+ uffdio_copy.dst = (unsigned long)fault_addr;
+ uffdio_copy.len = page_size;
+ uffdio_copy.mode = 0;
+ uffdio_copy.copy = 0;
+
+ SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);
+ tst_res(TPASS, "Userfaultfd handler successfully handled the fault");
+}
+
+static void setup(void)
+{
+ page_size = getpagesize();
+ struct uffdio_api uffdio_api;
+ struct uffdio_register uffdio_register;
+
+ uffd = SAFE_USERFAULTFD(O_CLOEXEC | O_NONBLOCK, true);
+
+ uffdio_api.api = UFFD_API;
+ uffdio_api.features = 0;
+ SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api);
+
+ fault_addr = SAFE_MMAP(NULL, page_size,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ tst_res(TINFO, "Original mapping created at %p", (void *)fault_addr);
+
+ strcpy(fault_addr, "ABCD");
+
+ uffdio_register.range.start = (unsigned long)fault_addr;
+ uffdio_register.range.len = page_size;
+ uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+ SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
+}
+
+static void cleanup(void)
+{
+ if (new_remap_addr && new_remap_addr != MAP_FAILED)
+ SAFE_MUNMAP(new_remap_addr, page_size);
+
+ if (fault_addr && fault_addr != MAP_FAILED)
+ SAFE_MUNMAP(fault_addr, page_size);
+
+ if (uffd != -1)
+ SAFE_CLOSE(uffd);
+}
+
+static void run(void)
+{
+ pthread_t handler_thread;
+
+ SAFE_PTHREAD_CREATE(&handler_thread, NULL,
+ fault_handler_thread, NULL);
+
+ new_remap_addr = mremap(fault_addr, page_size, page_size,
+ MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
+
+ if (new_remap_addr == MAP_FAILED)
+ tst_brk(TBROK | TERRNO, "mremap failed");
+
+ tst_res(TINFO, "New mapping created at %p", (void *)new_remap_addr);
+
+ strcpy(new_remap_addr, test_string);
+
+ TST_CHECKPOINT_WAKE(0);
+
+ tst_res(TINFO, "Main thread accessing old address %p to trigger fault",
+ (void *)fault_addr);
+
+ (void)*(volatile char *)fault_addr;
+
+ SAFE_PTHREAD_JOIN(handler_thread, NULL);
+
+ if (strcmp(fault_addr, test_string) != 0)
+ tst_res(TFAIL, "Verification failed: Content of the old "
+ "address is '%s', expected '%s'", fault_addr, test_string);
+ else
+ tst_res(TPASS, "Verification passed: Content of the old "
+ "address is correct after fault handling");
+
+ SAFE_MUNMAP(new_remap_addr, page_size);
+ new_remap_addr = NULL;
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .needs_checkpoints = 1,
+ .cleanup = cleanup,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_USERFAULTFD=y",
+ NULL,
+ },
+ .min_kver = "5.7",
+};
+
+#else
+TST_TEST_TCONF("Missing MREMAP_DONTUNMAP in <linux/mman.h>");
+#endif
--
2.52.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH v5] mremap07.c: New case check mremap with MREMAP_DONTUNMAP
2026-03-25 1:15 ` [LTP] [PATCH v5] " Wei Gao via ltp
@ 2026-03-26 10:01 ` Andrea Cervesato via ltp
0 siblings, 0 replies; 12+ messages in thread
From: Andrea Cervesato via ltp @ 2026-03-26 10:01 UTC (permalink / raw)
To: Wei Gao; +Cc: ltp
Hi Wei,
we are almost there to merge. A few comments below..
> This case test mremap() with MREMAP_DONTUNMAP and use userfaultfd
> verifies fault which triggered by accessing old memory region.
English can be improved a bit here:
Test mremap() with MREMAP_DONTUNMAP and verify that accessing the old
memory region triggers a page fault, which is then correctly handled
by a userfaultfd handler.
> +static void *fault_handler_thread(void *arg LTP_ATTRIBUTE_UNUSED)
> +{
Missing return NULL; in this function.
> + if (strcmp(fault_addr, test_string) != 0)
> + tst_res(TFAIL, "Verification failed: Content of the old "
> + "address is '%s', expected '%s'", fault_addr, test_string);
> + else
> + tst_res(TPASS, "Verification passed: Content of the old "
> + "address is correct after fault handling");
> +
We have TST_EXP_EQ_STR(fault_addr, test_string).
The rest is ok.
Regards,
--
Andrea Cervesato
SUSE QE Automation Engineer Linux
andrea.cervesato@suse.com
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-03-26 10:01 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-27 23:02 [LTP] [PATCH v1] mremap07.c: New case check mremap with MREMAP_DONTUNMAP Wei Gao via ltp
2025-10-15 3:15 ` [LTP] [PATCH v2] " Wei Gao via ltp
2025-10-16 13:32 ` Petr Vorel
2025-10-17 7:51 ` Wei Gao via ltp
2025-10-30 19:39 ` Petr Vorel
2025-11-01 8:47 ` Wei Gao via ltp
2025-10-30 5:40 ` [LTP] [PATCH v3] " Wei Gao via ltp
2025-10-30 20:07 ` Petr Vorel
2026-02-25 9:05 ` [LTP] [PATCH v4] " Wei Gao via ltp
2026-03-23 7:03 ` Andrea Cervesato via ltp
2026-03-25 1:15 ` [LTP] [PATCH v5] " Wei Gao via ltp
2026-03-26 10:01 ` Andrea Cervesato via ltp
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox