* [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; 18+ 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] 18+ 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; 18+ 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] 18+ 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; 18+ 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] 18+ 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; 18+ 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] 18+ 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; 18+ 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] 18+ 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; 18+ 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] 18+ 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; 18+ 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] 18+ 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; 18+ 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] 18+ 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; 18+ 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] 18+ 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; 18+ 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] 18+ 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 2026-04-10 2:31 ` [LTP] [PATCH v6] " Wei Gao via ltp 1 sibling, 2 replies; 18+ 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] 18+ 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 2026-04-10 2:31 ` [LTP] [PATCH v6] " Wei Gao via ltp 1 sibling, 0 replies; 18+ 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] 18+ messages in thread
* [LTP] [PATCH v6] 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 @ 2026-04-10 2:31 ` Wei Gao via ltp 2026-04-17 6:53 ` [LTP] [PATCH v7] mremap07.c: New test for mremap() " Wei Gao via ltp 1 sibling, 1 reply; 18+ messages in thread From: Wei Gao via ltp @ 2026-04-10 2:31 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: https://github.com/linux-test-project/ltp/issues/1168 Signed-off-by: Wei Gao <wegao@suse.com> --- v5->v6: - Refined the test description header - Added a missing return NULL to the fault_handler_thread - Replaced a manual strcmp and tst_res conditional block with the TST_EXP_EQ_STR macro 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 812f17d8b..211aaa8ad 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 d72fceb5e..b9a9ce5fe 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -927,6 +927,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..0510fdfe1 --- /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. + * + * 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. + */ + +#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"); + + return NULL; +} + +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); + + TST_EXP_EQ_STR(fault_addr, test_string); + + 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] 18+ messages in thread
* [LTP] [PATCH v7] mremap07.c: New test for mremap() with MREMAP_DONTUNMAP 2026-04-10 2:31 ` [LTP] [PATCH v6] " Wei Gao via ltp @ 2026-04-17 6:53 ` Wei Gao via ltp 2026-04-17 7:55 ` [LTP] " linuxtestproject.agent 2026-04-17 12:27 ` [LTP] [PATCH v8] " Wei Gao via ltp 0 siblings, 2 replies; 18+ messages in thread From: Wei Gao via ltp @ 2026-04-17 6:53 UTC (permalink / raw) To: ltp This test verifies the mremap() syscall with the MREMAP_DONTUNMAP flag. It uses userfaultfd to verify that accessing the old memory region correctly triggers a page fault after the mapping has been moved. MREMAP_DONTUNMAP behavior with userfaultfd was not covered by existing mremap tests. This test provides coverage for the feature requested in the linked issue. Fixes: https://github.com/linux-test-project/ltp/issues/1168 Signed-off-by: Wei Gao <wegao@suse.com> --- v6->v7: - Update commit description - Update year of copyright 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 812f17d8b..211aaa8ad 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 d72fceb5e..b9a9ce5fe 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -927,6 +927,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..102136322 --- /dev/null +++ b/testcases/kernel/syscalls/mremap/mremap07.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 Wei Gao <wegao@suse.com> + */ + +/*\ + * LTP test case for mremap() with MREMAP_DONTUNMAP and userfaultfd. + * + * 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. + */ + +#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"); + + return NULL; +} + +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); + + TST_EXP_EQ_STR(fault_addr, test_string); + + 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] 18+ messages in thread
* Re: [LTP] mremap07.c: New test for mremap() with MREMAP_DONTUNMAP 2026-04-17 6:53 ` [LTP] [PATCH v7] mremap07.c: New test for mremap() " Wei Gao via ltp @ 2026-04-17 7:55 ` linuxtestproject.agent 2026-04-17 8:00 ` Andrea Cervesato via ltp 2026-04-17 12:27 ` [LTP] [PATCH v8] " Wei Gao via ltp 1 sibling, 1 reply; 18+ messages in thread From: linuxtestproject.agent @ 2026-04-17 7:55 UTC (permalink / raw) To: Wei Gao; +Cc: ltp Hi Wei, On 2026-04-17, Wei Gao wrote: > mremap07.c: New test for mremap() with MREMAP_DONTUNMAP > Fixes: https://github.com/linux-test-project/ltp/issues/1168 Use Closes: for GitHub issue references. Fixes: is reserved for commit SHA references in the form: Fixes: <hash> ("<subject>"). [...] > +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); > +} SAFE_MUNMAP does not clear the pointer. If setup() fails on a subsequent iteration before SAFE_MMAP reassigns fault_addr, cleanup() will double-munmap the stale pointer. Add fault_addr = NULL; after the SAFE_MUNMAP call. --- Note: Our agent completed the review of the patch. The full review can be found at: https://github.com/linux-test-project/ltp-agent/actions/runs/24554018117 The agent can sometimes produce false positives although often its findings are genuine. If you find issues with the review, please comment this email or ignore the suggestions. Regards, LTP AI Reviewer -- Mailing list info: https://lists.linux.it/listinfo/ltp ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [LTP] mremap07.c: New test for mremap() with MREMAP_DONTUNMAP 2026-04-17 7:55 ` [LTP] " linuxtestproject.agent @ 2026-04-17 8:00 ` Andrea Cervesato via ltp 0 siblings, 0 replies; 18+ messages in thread From: Andrea Cervesato via ltp @ 2026-04-17 8:00 UTC (permalink / raw) To: linuxtestproject.agent; +Cc: ltp > SAFE_MUNMAP does not clear the pointer. If setup() fails on a subsequent > iteration before SAFE_MMAP reassigns fault_addr, cleanup() will > double-munmap the stale pointer. Add fault_addr = NULL; after the > SAFE_MUNMAP call. The SAFE_MUNMAP() call already set the pointer to NULL, it didn't catch this error. I will fix it in the agent config. -- 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] 18+ messages in thread
* [LTP] [PATCH v8] mremap07.c: New test for mremap() with MREMAP_DONTUNMAP 2026-04-17 6:53 ` [LTP] [PATCH v7] mremap07.c: New test for mremap() " Wei Gao via ltp 2026-04-17 7:55 ` [LTP] " linuxtestproject.agent @ 2026-04-17 12:27 ` Wei Gao via ltp 2026-04-17 13:26 ` [LTP] " linuxtestproject.agent 1 sibling, 1 reply; 18+ messages in thread From: Wei Gao via ltp @ 2026-04-17 12:27 UTC (permalink / raw) To: ltp This test verifies the mremap() syscall with the MREMAP_DONTUNMAP flag. It uses userfaultfd to verify that accessing the old memory region correctly triggers a page fault after the mapping has been moved. MREMAP_DONTUNMAP behavior with userfaultfd was not covered by existing mremap tests. This test provides coverage for the feature requested in the linked issue. Closes: https://github.com/linux-test-project/ltp/issues/1168 Signed-off-by: Wei Gao <wegao@suse.com> --- v7 -> v8: - Change Fixes: tag to Closes: for GitHub issue reference. 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 812f17d8b..211aaa8ad 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 d72fceb5e..b9a9ce5fe 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -927,6 +927,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..102136322 --- /dev/null +++ b/testcases/kernel/syscalls/mremap/mremap07.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 Wei Gao <wegao@suse.com> + */ + +/*\ + * LTP test case for mremap() with MREMAP_DONTUNMAP and userfaultfd. + * + * 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. + */ + +#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"); + + return NULL; +} + +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); + + TST_EXP_EQ_STR(fault_addr, test_string); + + 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] 18+ messages in thread
* Re: [LTP] mremap07.c: New test for mremap() with MREMAP_DONTUNMAP 2026-04-17 12:27 ` [LTP] [PATCH v8] " Wei Gao via ltp @ 2026-04-17 13:26 ` linuxtestproject.agent 0 siblings, 0 replies; 18+ messages in thread From: linuxtestproject.agent @ 2026-04-17 13:26 UTC (permalink / raw) To: Wei Gao; +Cc: ltp, LTP AI Reviewer Hi Wei, On 2026-04-17, Wei Gao wrote: > mremap07.c: New test for mremap() with MREMAP_DONTUNMAP > +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; > + > + int nready = poll(&pollfd, 1, -1); Nit: declare all variables at the top of the block, before any statements (struct pollfd and int nready should move above TST_CHECKPOINT_WAIT(0)). [...] Reviewed-by: LTP AI Reviewer <ltp-ai@noreply.github.com> --- Note: Our agent completed the review of the patch. The full review can be found at: <review_url> The agent can sometimes produce false positives although often its findings are genuine. If you find issues with the review, please comment this email or ignore the suggestions. Regards, LTP AI Reviewer -- Mailing list info: https://lists.linux.it/listinfo/ltp ^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-04-17 13:27 UTC | newest] Thread overview: 18+ 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 2026-04-10 2:31 ` [LTP] [PATCH v6] " Wei Gao via ltp 2026-04-17 6:53 ` [LTP] [PATCH v7] mremap07.c: New test for mremap() " Wei Gao via ltp 2026-04-17 7:55 ` [LTP] " linuxtestproject.agent 2026-04-17 8:00 ` Andrea Cervesato via ltp 2026-04-17 12:27 ` [LTP] [PATCH v8] " Wei Gao via ltp 2026-04-17 13:26 ` [LTP] " linuxtestproject.agent
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox