1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (c) 2019 SUSE LLC 4f08c3bdfSopenharmony_ci * Author: Christian Amann <camann@suse.com> 5f08c3bdfSopenharmony_ci * 6f08c3bdfSopenharmony_ci * Test userfaultfd 7f08c3bdfSopenharmony_ci * 8f08c3bdfSopenharmony_ci * Force a pagefault event and handle it using userfaultfd 9f08c3bdfSopenharmony_ci * from a different thread 10f08c3bdfSopenharmony_ci */ 11f08c3bdfSopenharmony_ci 12f08c3bdfSopenharmony_ci#include "config.h" 13f08c3bdfSopenharmony_ci#include "tst_test.h" 14f08c3bdfSopenharmony_ci 15f08c3bdfSopenharmony_ci#include <poll.h> 16f08c3bdfSopenharmony_ci 17f08c3bdfSopenharmony_ci#include "tst_safe_macros.h" 18f08c3bdfSopenharmony_ci#include "tst_safe_pthread.h" 19f08c3bdfSopenharmony_ci#include "lapi/userfaultfd.h" 20f08c3bdfSopenharmony_ci 21f08c3bdfSopenharmony_cistatic int page_size; 22f08c3bdfSopenharmony_cistatic char *page; 23f08c3bdfSopenharmony_cistatic void *copy_page; 24f08c3bdfSopenharmony_cistatic int uffd; 25f08c3bdfSopenharmony_ci 26f08c3bdfSopenharmony_cistatic int sys_userfaultfd(int flags) 27f08c3bdfSopenharmony_ci{ 28f08c3bdfSopenharmony_ci return tst_syscall(__NR_userfaultfd, flags); 29f08c3bdfSopenharmony_ci} 30f08c3bdfSopenharmony_ci 31f08c3bdfSopenharmony_cistatic void set_pages(void) 32f08c3bdfSopenharmony_ci{ 33f08c3bdfSopenharmony_ci page_size = sysconf(_SC_PAGE_SIZE); 34f08c3bdfSopenharmony_ci page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, 35f08c3bdfSopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 36f08c3bdfSopenharmony_ci copy_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE, 37f08c3bdfSopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 38f08c3bdfSopenharmony_ci} 39f08c3bdfSopenharmony_ci 40f08c3bdfSopenharmony_cistatic void handle_thread(void) 41f08c3bdfSopenharmony_ci{ 42f08c3bdfSopenharmony_ci static struct uffd_msg msg; 43f08c3bdfSopenharmony_ci struct uffdio_copy uffdio_copy; 44f08c3bdfSopenharmony_ci 45f08c3bdfSopenharmony_ci struct pollfd pollfd; 46f08c3bdfSopenharmony_ci int nready; 47f08c3bdfSopenharmony_ci 48f08c3bdfSopenharmony_ci pollfd.fd = uffd; 49f08c3bdfSopenharmony_ci pollfd.events = POLLIN; 50f08c3bdfSopenharmony_ci nready = poll(&pollfd, 1, -1); 51f08c3bdfSopenharmony_ci if (nready == -1) 52f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, 53f08c3bdfSopenharmony_ci "Error on poll"); 54f08c3bdfSopenharmony_ci 55f08c3bdfSopenharmony_ci SAFE_READ(1, uffd, &msg, sizeof(msg)); 56f08c3bdfSopenharmony_ci 57f08c3bdfSopenharmony_ci if (msg.event != UFFD_EVENT_PAGEFAULT) 58f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, 59f08c3bdfSopenharmony_ci "Received unexpected UFFD_EVENT"); 60f08c3bdfSopenharmony_ci 61f08c3bdfSopenharmony_ci memset(copy_page, 'X', page_size); 62f08c3bdfSopenharmony_ci 63f08c3bdfSopenharmony_ci uffdio_copy.src = (unsigned long) copy_page; 64f08c3bdfSopenharmony_ci 65f08c3bdfSopenharmony_ci uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address 66f08c3bdfSopenharmony_ci & ~(page_size - 1); 67f08c3bdfSopenharmony_ci uffdio_copy.len = page_size; 68f08c3bdfSopenharmony_ci uffdio_copy.mode = 0; 69f08c3bdfSopenharmony_ci uffdio_copy.copy = 0; 70f08c3bdfSopenharmony_ci SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy); 71f08c3bdfSopenharmony_ci 72f08c3bdfSopenharmony_ci close(uffd); 73f08c3bdfSopenharmony_ci} 74f08c3bdfSopenharmony_ci 75f08c3bdfSopenharmony_cistatic void run(void) 76f08c3bdfSopenharmony_ci{ 77f08c3bdfSopenharmony_ci pthread_t thr; 78f08c3bdfSopenharmony_ci struct uffdio_api uffdio_api; 79f08c3bdfSopenharmony_ci struct uffdio_register uffdio_register; 80f08c3bdfSopenharmony_ci 81f08c3bdfSopenharmony_ci set_pages(); 82f08c3bdfSopenharmony_ci 83f08c3bdfSopenharmony_ci TEST(sys_userfaultfd(O_CLOEXEC | O_NONBLOCK)); 84f08c3bdfSopenharmony_ci 85f08c3bdfSopenharmony_ci if (TST_RET == -1) { 86f08c3bdfSopenharmony_ci if (TST_ERR == EPERM) { 87f08c3bdfSopenharmony_ci tst_res(TCONF, "Hint: check /proc/sys/vm/unprivileged_userfaultfd"); 88f08c3bdfSopenharmony_ci tst_brk(TCONF | TTERRNO, 89f08c3bdfSopenharmony_ci "userfaultfd() requires CAP_SYS_PTRACE on this system"); 90f08c3bdfSopenharmony_ci } else 91f08c3bdfSopenharmony_ci tst_brk(TBROK | TTERRNO, 92f08c3bdfSopenharmony_ci "Could not create userfault file descriptor"); 93f08c3bdfSopenharmony_ci } 94f08c3bdfSopenharmony_ci 95f08c3bdfSopenharmony_ci uffd = TST_RET; 96f08c3bdfSopenharmony_ci uffdio_api.api = UFFD_API; 97f08c3bdfSopenharmony_ci uffdio_api.features = 0; 98f08c3bdfSopenharmony_ci SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api); 99f08c3bdfSopenharmony_ci 100f08c3bdfSopenharmony_ci uffdio_register.range.start = (unsigned long) page; 101f08c3bdfSopenharmony_ci uffdio_register.range.len = page_size; 102f08c3bdfSopenharmony_ci uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; 103f08c3bdfSopenharmony_ci 104f08c3bdfSopenharmony_ci SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register); 105f08c3bdfSopenharmony_ci 106f08c3bdfSopenharmony_ci SAFE_PTHREAD_CREATE(&thr, NULL, 107f08c3bdfSopenharmony_ci (void * (*)(void *)) handle_thread, NULL); 108f08c3bdfSopenharmony_ci 109f08c3bdfSopenharmony_ci char c = page[0xf]; 110f08c3bdfSopenharmony_ci 111f08c3bdfSopenharmony_ci if (c == 'X') 112f08c3bdfSopenharmony_ci tst_res(TPASS, "Pagefault handled!"); 113f08c3bdfSopenharmony_ci else 114f08c3bdfSopenharmony_ci tst_res(TFAIL, "Pagefault not handled!"); 115f08c3bdfSopenharmony_ci 116f08c3bdfSopenharmony_ci SAFE_PTHREAD_JOIN(thr, NULL); 117f08c3bdfSopenharmony_ci} 118f08c3bdfSopenharmony_ci 119f08c3bdfSopenharmony_cistatic struct tst_test test = { 120f08c3bdfSopenharmony_ci .test_all = run, 121f08c3bdfSopenharmony_ci .min_kver = "4.3", 122f08c3bdfSopenharmony_ci}; 123