* [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
* 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 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 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 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
* [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