Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions runtest/syscalls
Original file line number Diff line number Diff line change
Expand Up @@ -1770,6 +1770,11 @@ umount2_01 umount2_01
umount2_02 umount2_02

userfaultfd01 userfaultfd01
userfaultfd02 userfaultfd02
userfaultfd03 userfaultfd03
userfaultfd04 userfaultfd04
userfaultfd05 userfaultfd05
userfaultfd06 userfaultfd06

ustat01 ustat01
ustat02 ustat02
Expand Down
5 changes: 5 additions & 0 deletions testcases/kernel/syscalls/userfaultfd/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
/userfaultfd01
/userfaultfd02
/userfaultfd03
/userfaultfd04
/userfaultfd05
/userfaultfd06
5 changes: 5 additions & 0 deletions testcases/kernel/syscalls/userfaultfd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ include $(top_srcdir)/include/mk/testcases.mk
include $(top_srcdir)/include/mk/generic_leaf_target.mk

userfaultfd01: CFLAGS += -pthread
userfaultfd02: CFLAGS += -pthread
userfaultfd03: CFLAGS += -pthread
userfaultfd04: CFLAGS += -pthread
userfaultfd05: CFLAGS += -pthread
userfaultfd06: CFLAGS += -pthread
119 changes: 119 additions & 0 deletions testcases/kernel/syscalls/userfaultfd/userfaultfd02.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2025 SUSE LLC
* Author: Christian Amann <[email protected]>
* Author: Ricardo Branco <[email protected]>
*/

/*\
* Force a pagefault event and handle it using :man2:`userfaultfd`
* from a different thread using UFFDIO_MOVE
*/

#include "config.h"
#include <poll.h>
#include "tst_test.h"
#include "tst_safe_macros.h"
#include "tst_safe_pthread.h"
#include "lapi/userfaultfd.h"

static int page_size;
static char *page;
static void *move_page;
static int uffd;

static int sys_userfaultfd(int flags)
{
return tst_syscall(__NR_userfaultfd, flags);
}

static void set_pages(void)
{
page_size = sysconf(_SC_PAGE_SIZE);
page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
move_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}

static void handle_thread(void)
{
static struct uffd_msg msg;
struct uffdio_move uffdio_move;

struct pollfd pollfd;
int nready;

pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
if (nready == -1)
tst_brk(TBROK | TERRNO, "Error on poll");

SAFE_READ(1, uffd, &msg, sizeof(msg));

if (msg.event != UFFD_EVENT_PAGEFAULT)
tst_brk(TBROK | TERRNO, "Received unexpected UFFD_EVENT");

memset(move_page, 'X', page_size);

uffdio_move.src = (unsigned long) move_page;

uffdio_move.dst = (unsigned long) msg.arg.pagefault.address
& ~(page_size - 1);
uffdio_move.len = page_size;
uffdio_move.mode = 0;
uffdio_move.move = 0;
SAFE_IOCTL(uffd, UFFDIO_MOVE, &uffdio_move);

close(uffd);
}

static void run(void)
{
pthread_t thr;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;

set_pages();

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);

uffdio_register.range.start = (unsigned long) page;
uffdio_register.range.len = page_size;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;

SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);

SAFE_PTHREAD_CREATE(&thr, NULL,
(void * (*)(void *)) handle_thread, NULL);

char c = page[0xf];

if (c == 'X')
tst_res(TPASS, "Pagefault handled via UFFDIO_MOVE");
else
tst_res(TFAIL, "Pagefault not handled via UFFDIO_MOVE");

SAFE_PTHREAD_JOIN(thr, NULL);
}

static struct tst_test test = {
.test_all = run,
.min_kver = "6.8",
};
114 changes: 114 additions & 0 deletions testcases/kernel/syscalls/userfaultfd/userfaultfd03.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2025 SUSE LLC
* Author: Christian Amann <[email protected]>
* Author: Ricardo Branco <[email protected]>
*/

/*\
* Force a pagefault event and handle it using :man2:`userfaultfd`
* from a different thread opening uffd with UFFD_USER_MODE_ONLY.
*/

#include "config.h"
#include <poll.h>
#include "tst_test.h"
#include "tst_safe_macros.h"
#include "tst_safe_pthread.h"
#include "lapi/userfaultfd.h"

static int page_size;
static char *page;
static void *copy_page;
static int uffd;

static int sys_userfaultfd(int flags)
{
return tst_syscall(__NR_userfaultfd, flags);
}

static void set_pages(void)
{
page_size = sysconf(_SC_PAGE_SIZE);
page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
copy_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}

static void handle_thread(void)
{
static struct uffd_msg msg;
struct uffdio_copy uffdio_copy;

struct pollfd pollfd;
int nready;

pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
if (nready == -1)
tst_brk(TBROK | TERRNO, "Error on poll");

SAFE_READ(1, uffd, &msg, sizeof(msg));

if (msg.event != UFFD_EVENT_PAGEFAULT)
tst_brk(TBROK | TERRNO, "Received unexpected UFFD_EVENT");

memset(copy_page, 'X', page_size);

uffdio_copy.src = (unsigned long) copy_page;

uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address
& ~(page_size - 1);
uffdio_copy.len = page_size;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);

close(uffd);
}

static void run(void)
{
pthread_t thr;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;

set_pages();

TEST(sys_userfaultfd(O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY));

if (TST_RET == -1) {
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);

uffdio_register.range.start = (unsigned long) page;
uffdio_register.range.len = page_size;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;

SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);

SAFE_PTHREAD_CREATE(&thr, NULL,
(void * (*)(void *)) handle_thread, NULL);

char c = page[0xf];

if (c == 'X')
tst_res(TPASS, "Pagefault handled in user-mode!");
else
tst_res(TFAIL, "Pagefault not handled in user-mode!");

SAFE_PTHREAD_JOIN(thr, NULL);
}

static struct tst_test test = {
.test_all = run,
.min_kver = "5.11",
};
125 changes: 125 additions & 0 deletions testcases/kernel/syscalls/userfaultfd/userfaultfd04.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2025 SUSE LLC
* Author: Christian Amann <[email protected]>
* Author: Ricardo Branco <[email protected]>
*/

/*\
* Force a pagefault event and handle it using :man2:`userfaultfd`
* from a different thread using /dev/userfaultfd instead of syscall.
*/

#include "config.h"
#include <poll.h>
#include "tst_test.h"
#include "tst_safe_macros.h"
#include "tst_safe_pthread.h"
#include "lapi/userfaultfd.h"

static int page_size;
static char *page;
static void *copy_page;
static int uffd;

static int open_userfaultfd(int flags)
{
int fd, fd2;

fd = SAFE_OPEN("/dev/userfaultfd", O_RDWR);

fd2 = SAFE_IOCTL(fd, USERFAULTFD_IOC_NEW, flags);

SAFE_CLOSE(fd);

return fd2;
}

static void set_pages(void)
{
page_size = sysconf(_SC_PAGE_SIZE);
page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
copy_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}

static void handle_thread(void)
{
static struct uffd_msg msg;
struct uffdio_copy uffdio_copy;

struct pollfd pollfd;
int nready;

pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
if (nready == -1)
tst_brk(TBROK | TERRNO, "Error on poll");

SAFE_READ(1, uffd, &msg, sizeof(msg));

if (msg.event != UFFD_EVENT_PAGEFAULT)
tst_brk(TBROK | TERRNO, "Received unexpected UFFD_EVENT");

memset(copy_page, 'X', page_size);

uffdio_copy.src = (unsigned long) copy_page;

uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address
& ~(page_size - 1);
uffdio_copy.len = page_size;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);

close(uffd);
}

static void run(void)
{
pthread_t thr;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;

set_pages();

TEST(open_userfaultfd(O_CLOEXEC | O_NONBLOCK));

if (TST_RET == -1) {
if (TST_ERR == EPERM) {
tst_brk(TCONF, "Hint: check /dev/userfaultfd permissions");
} 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);

uffdio_register.range.start = (unsigned long) page;
uffdio_register.range.len = page_size;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;

SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);

SAFE_PTHREAD_CREATE(&thr, NULL,
(void * (*)(void *)) handle_thread, NULL);

char c = page[0xf];

if (c == 'X')
tst_res(TPASS, "Pagefault handled via /dev/userfaultfd");
else
tst_res(TFAIL, "Pagefault not handled via /dev/userfaultfd");

SAFE_PTHREAD_JOIN(thr, NULL);
}

static struct tst_test test = {
.test_all = run,
.min_kver = "5.11",
};
Loading