userfaultfd(2) System Calls Manual userfaultfd(2) userfaultfd - (libc -lc) #include /* O_* */ #include /* SYS_* */ #include /* UFFD_* */ #include int syscall(SYS_userfaultfd, int flags); : glibc userfaultfd() syscall(2). userfaultfd() userfaultfd . userfaultfd ioctl(2). userfaultfd read(2) userfaultfd. userfaultfd flags userfaultfd fcntl(2). OR flags userfaultfd(): O_CLOEXEC (close-on-exec) userfaultfd . O_CLOEXEC open(2). O_NONBLOCK userfaultfd. O_NONBLOCK open(2). UFFD_USER_MODE_ONLY userfaultfd 5.11. userfaultfd . userfaultfd SIGBUS. userfaultfd . Userfaultfd : UFFDIO_REGISTER_MODE_MISSING ( 4.10) UFFDIO_REGISTER_MODE_MISSING . ioctl UFFDIO_COPY UFFDIO_ZEROPAGE. UFFDIO_REGISTER_MODE_MINOR ( 5.13) UFFDIO_REGISTER_MODE_MINOR . . ioctl UFFDIO_CONTINUE. UFFDIO_REGISTER_MODE_WP ( 5.7) UFFDIO_REGISTER_MODE_WP . ioctl UFFDIO_WRITEPROTECT. . 4.14 userfaultfd . UFFD_FEATURE_THREAD_ID userfaultfd. . userfaultfd . userfaultfd userfaultfd. ioctl_userfaultfd(2). . . . userfaultfd . 4.11 userfaultfd . fork(2) userfaultfd userfaultfd ( UFFD_EVENT_FORK ) userfault userfaultfd . userfaultfd read(2). userfaultfd UFFDIO_COPY . userfaultfd . 5.7 userfaultfd . UFFD_FEATURE_PAGEFAULT_FLAG_WP . userfaultfd userfaultfd . . " Userfaultfd". Userfaultfd userfaultfd userfaultfd() UFFDIO_API ioctl(2). API . ioctl(2) ( EINVAL). UFFDIO_API UFFDIO_REGISTER ioctl(2). UFFDIO_REGISTER . ioctl(2) ( UFFDIO_COPY UFFDIO_ZEROPAGE UFFDIO_CONTINUE) . 4.14 UFFD_FEATURE_SIGBUS UFFDIO_API ioctl(2) . SIGBUS . userfaultfd userfaultfd. userfaultfd . . UFFD_FEATURE_SIGBUS fork(2) UFFD_FEATURE_FORK. ioctl(2) ioctl_userfaultfd(2). 4.11 UFFDIO_API. 4.11 userfaultfd . 4.11 userfaultfd hugetlbfs . Userfaultfd ( 5.7) 5.7 userfaultfd . ioctl UFFDIO_API UFFD_FEATURE_PAGEFAULT_FLAG_WP . 5.19 shmem hugetlbfs. UFFD_FEATURE_WP_HUGETLBFS_SHMEM. userfaultfd ioctl UFFDIO_REGISTER UFFDIO_REGISTER_MODE_WP. . UFFDIO_REGISTER UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP. UFFDIO_REGISTER_MODE_WP . . ioctl UFFDIO_REGISTER UFFDIO_REGISTER_MODE_WP ioctl UFFDIO_WRITEPROTECT uffdio_writeprotect.mode UFFDIO_WRITEPROTECT_MODE_WP. uffd_msg.pagefault.flags UFFD_PAGEFAULT_FLAG_WP. : UFFD_PAGEFAULT_FLAG_WRITE UFFD_PAGEFAULT_FLAG_WP. ioctl UFFDIO_WRITEPROTECT uffd_msg.pagefault.flags UFFDIO_WRITEPROTECT_MODE_WP . Userfaultfd ( 5.13) 5.13 userfaultfd . ( ) . ioctl UFFDIO_API : UFFD_FEATURE_MINOR_HUGETLBFS 5.13 UFFD_FEATURE_MINOR_SHMEM 5.14. userfaultfd ioctl UFFDIO_REGISTER UFFD_REGISTER_MODE_MINOR. uffd_msg.pagefault.flags UFFD_PAGEFAULT_FLAG_MINOR. . userfaultfd ( shmem hugetlbfs ). "" ioctl UFFDIO_CONTINUE ( ) . hugetlbfs ( 5.13) shmem ( 5.14). userfaultfd read(2) userfaultfd uffd_msg userfaultfd : struct uffd_msg { __u8 event; /* Type of event */ ... union { struct { __u64 flags; /* Flags describing fault */ __u64 address; /* Faulting address */ union { __u32 ptid; /* Thread ID of the fault */ } feat; } pagefault; struct { /* Since Linux 4.11 */ __u32 ufd; /* Userfault file descriptor of the child process */ } fork; struct { /* Since Linux 4.11 */ __u64 from; /* Old address of remapped area */ __u64 to; /* New address of remapped area */ __u64 len; /* Original mapping size */ } remap; struct { /* Since Linux 4.11 */ __u64 start; /* Start address of removed area */ __u64 end; /* End address of removed area */ } remove; ... } arg; /* Padding fields omitted */ } __packed; (buffer) read(2) . read(2) uffd_msg read(2) EINVAL. uffd_msg : event . arg (union) . - (page-fault) UFFDIO_API ioctl(2). event: UFFD_EVENT_PAGEFAULT ( 4.3) -. - pagefault. UFFD_EVENT_FORK ( 4.11) fork(2) ( clone(2) CLONE_VM). fork. UFFD_EVENT_REMAP ( 4.11) mremap(2). remap. UFFD_EVENT_REMOVE ( 4.11) madvise(2) MADV_DONTNEED MADV_REMOVE. remove. UFFD_EVENT_UNMAP ( 4.11) munmap(2) mmap(2) mremap(2). remove. pagefault.address . pagefault.flags . UFFD_EVENT_PAGEFAULT : UFFD_PAGEFAULT_FLAG_WP . UFFD_PAGEFAULT_FLAG_MINOR . UFFD_PAGEFAULT_FLAG_WRITE . UFFD_PAGEFAULT_FLAG_WP UFFD_PAGEFAULT_FLAG_MINOR . pagefault.feat.pid . fork.ufd userfault fork(2). remap.from mremap(2). remap.to mremap(2). remap.len mremap(2). remove.start madvise(2) remove.end madvise(2) read(2) userfaultfd : EINVAL userfaultfd UFFDIO_API ioctl(2) O_NONBLOCK userfaultfd poll(2) select(2) epoll(7). . O_NONBLOCK poll(2) () POLLERR select(2) . userfaultfd() userfaultfd. -1 errno . EINVAL flags. EMFILE ENFILE . ENOMEM . EPERM ( Linux 5.2) ( CAP_SYS_PTRACE ) /proc/sys/vm/unprivileged_userfaultfd 0. . 4.3. hugetlbfs Linux 4.11 userfaultfd SIGSEGV mmap(2). / () Linux . UFFD_FEATURE_EVENT_FORK fork(2) userfaultfd . UFFD_EVENT_FORK userfaultfd. userfaultfd. mmap(2). userfaultfd. userfaultfd UFFDIO_REGISTER ioctl(2). . . userfaultfd. userfaultfd. poll(2) . UFFD_EVENT_PAGEFAULT UFFDIO_COPY ioctl(2). : $ ./userfaultfd_demo 3; Address returned by mmap() = 0x7fd30106c000 fault_handler_thread(): poll() returns: nready = 1; POLLIN = 1; POLLERR = 0 UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106c00f (uffdio_copy.copy returned 4096) Read address 0x7fd30106c00f in main(): A Read address 0x7fd30106c40f in main(): A Read address 0x7fd30106c80f in main(): A Read address 0x7fd30106cc0f in main(): A fault_handler_thread(): poll() returns: nready = 1; POLLIN = 1; POLLERR = 0 UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106d00f (uffdio_copy.copy returned 4096) Read address 0x7fd30106d00f in main(): B Read address 0x7fd30106d40f in main(): B Read address 0x7fd30106d80f in main(): B Read address 0x7fd30106dc0f in main(): B fault_handler_thread(): poll() returns: nready = 1; POLLIN = 1; POLLERR = 0 UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106e00f (uffdio_copy.copy returned 4096) Read address 0x7fd30106e00f in main(): C Read address 0x7fd30106e40f in main(): C Read address 0x7fd30106e80f in main(): C Read address 0x7fd30106ec0f in main(): C /* userfaultfd_demo.c Licensed under the GNU General Public License version 2 or later. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int page_size; static void * fault_handler_thread(void *arg) { long uffd; /* userfaultfd file descriptor */ static int fault_cnt = 0; /* Number of faults so far handled */ static char *page = NULL; static struct uffd_msg msg; /* Data read from userfaultfd */ uffd = (long) arg; /* Create a page that will be copied into the faulting region. */ if (page == NULL) { page = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (page == MAP_FAILED) err(EXIT_FAILURE, "mmap"); } /* Loop, handling incoming events on the userfaultfd file descriptor. */ for (;;) { int nready; ssize_t nread; struct pollfd pollfd; struct uffdio_copy uffdio_copy; /* See what poll() tells us about the userfaultfd. */ pollfd.fd = uffd; pollfd.events = POLLIN; nready = poll(&pollfd, 1, -1); if (nready == -1) err(EXIT_FAILURE, "poll"); printf("\nfault_handler_thread():\n"); printf(" poll() returns: nready = %d; " "POLLIN = %d; POLLERR = %d\n", nready, (pollfd.revents & POLLIN) != 0, (pollfd.revents & POLLERR) != 0); /* Read an event from the userfaultfd. */ nread = read(uffd, &msg, sizeof(msg)); if (nread == 0) { printf("EOF on userfaultfd!\n"); exit(EXIT_FAILURE); } if (nread == -1) err(EXIT_FAILURE, "read"); /* We expect only one kind of event; verify that assumption. */ if (msg.event != UFFD_EVENT_PAGEFAULT) { fprintf(stderr, "Unexpected event on userfaultfd\n"); exit(EXIT_FAILURE); } /* Display info about the page-fault event. */ printf(" UFFD_EVENT_PAGEFAULT event: "); printf("flags = %w64x; ", msg.arg.pagefault.flags); printf("address = %w64x\n", msg.arg.pagefault.address); /* Copy the page pointed to by 'page' into the faulting region. Vary the contents that are copied in, so that it is more obvious that each fault is handled separately. */ memset(page, 'A' + fault_cnt % 20, page_size); fault_cnt++; uffdio_copy.src = (unsigned long) page; /* We need to handle page faults in units of pages(!). So, round faulting address down to page boundary. */ 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; if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err(EXIT_FAILURE, "ioctl-UFFDIO_COPY"); printf(" (uffdio_copy.copy returned %w64d)\n", uffdio_copy.copy); } } int main(int argc, char *argv[]) { int s; char *addr; /* Start of region handled by userfaultfd */ long uffd; /* userfaultfd file descriptor */ size_t size, i; /* Size of region handled by userfaultfd */ pthread_t thr; /* ID of thread that handles page faults */ struct uffdio_api uffdio_api; struct uffdio_register uffdio_register; if (argc != 2) { fprintf(stderr, "Usage: %s num-pages\n", argv[0]); exit(EXIT_FAILURE); } page_size = sysconf(_SC_PAGE_SIZE); size = strtoull(argv[1], NULL, 0) * page_size; /* Create and enable userfaultfd object. */ uffd = syscall(SYS_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd == -1) err(EXIT_FAILURE, "userfaultfd"); /* NOTE: Two-step feature handshake is not needed here, since this example doesn't require any specific features. Programs that *do* should call UFFDIO_API twice: once with `features = 0` to detect features supported by this kernel, and again with the subset of features the program actually wants to enable. */ uffdio_api.api = UFFD_API; uffdio_api.features = 0; if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) err(EXIT_FAILURE, "ioctl-UFFDIO_API"); /* Create a private anonymous mapping. The memory will be demand-zero paged--that is, not yet allocated. When we actually touch the memory, it will be allocated via the userfaultfd. */ addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (addr == MAP_FAILED) err(EXIT_FAILURE, "mmap"); printf("Address returned by mmap() = %p\n", addr); /* Register the memory range of the mapping we just created for handling by the userfaultfd object. In mode, we request to track missing pages (i.e., pages that have not yet been faulted in). */ uffdio_register.range.start = (unsigned long) addr; uffdio_register.range.len = size; uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) err(EXIT_FAILURE, "ioctl-UFFDIO_REGISTER"); /* Create a thread that will process the userfaultfd events. */ s = pthread_create(&thr, NULL, fault_handler_thread, (void *) uffd); if (s != 0) { errc(EXIT_FAILURE, s, "pthread_create"); } /* Main thread now touches memory in the mapping, touching locations 1024 bytes apart. This will trigger userfaultfd events for all pages in the region. */ i = 0xf; /* Ensure that faulting address is not on a page boundary, in order to test that we correctly handle that case in fault_handling_thread(). */ while (i < size) { char c; c = addr[i]; printf("Read address %p in %s(): ", addr + i, __func__); printf("%c\n", c); i += 1024; usleep(100000); /* Slow things down a little */ } exit(EXIT_SUCCESS); } fcntl(2), ioctl(2), ioctl_userfaultfd(2), madvise(2), mmap(2) Documentation/admin-guide/mm/userfaultfd.rst 3 . . : . 6.18 16 2026 userfaultfd(2)