public inbox for ltp@lists.linux.it
 help / color / mirror / Atom feed
* [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