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