#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include size_t pagesize; int uffd; static void *uffd_thread_fn(void *arg) { static struct uffd_msg msg; ssize_t nread; while (1) { struct pollfd pollfd; int nready; pollfd.fd = uffd; pollfd.events = POLLIN; nready = poll(&pollfd, 1, -1); if (nready == -1) { fprintf(stderr, "poll() failed: %d\n", errno); exit(1); } nread = read(uffd, &msg, sizeof(msg)); if (nread <= 0) continue; if (msg.event != UFFD_EVENT_PAGEFAULT || !(msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP)) { printf("FAIL: wrong uffd-wp event fired\n"); exit(1); } printf("PASS: uffd-wp fired\n"); exit(0); } } static int setup_uffd(char *map) { struct uffdio_api uffdio_api; struct uffdio_register uffdio_register; struct uffdio_range uffd_range; pthread_t thread; uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY); if (uffd < 0) { fprintf(stderr, "syscall() failed: %d\n", errno); return -errno; } uffdio_api.api = UFFD_API; uffdio_api.features = UFFD_FEATURE_PAGEFAULT_FLAG_WP; if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) { fprintf(stderr, "UFFDIO_API failed: %d\n", errno); return -errno; } if (!(uffdio_api.features & UFFD_FEATURE_PAGEFAULT_FLAG_WP)) { fprintf(stderr, "UFFD_FEATURE_WRITEPROTECT missing\n"); return -ENOSYS; } uffdio_register.range.start = (unsigned long) map; uffdio_register.range.len = pagesize; uffdio_register.mode = UFFDIO_REGISTER_MODE_WP; if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) < 0) { fprintf(stderr, "UFFDIO_REGISTER failed: %d\n", errno); return -errno; } pthread_create(&thread, NULL, uffd_thread_fn, NULL); return 0; } int main(int argc, char **argv) { struct uffdio_writeprotect uffd_writeprotect; char *map; int fd; pagesize = getpagesize(); fd = memfd_create("test", 0); if (fd < 0) { fprintf(stderr, "memfd_create() failed\n"); return -errno; } if (ftruncate(fd, pagesize)) { fprintf(stderr, "ftruncate() failed\n"); return -errno; } /* Start out without write protection. */ map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { fprintf(stderr, "mmap() failed\n"); return -errno; } if (setup_uffd(map)) return 1; /* Populate a page ... */ memset(map, 0, pagesize); /* ... and write-protect it using uffd-wp. */ uffd_writeprotect.range.start = (unsigned long) map; uffd_writeprotect.range.len = pagesize; uffd_writeprotect.mode = UFFDIO_WRITEPROTECT_MODE_WP; if (ioctl(uffd, UFFDIO_WRITEPROTECT, &uffd_writeprotect)) { fprintf(stderr, "UFFDIO_WRITEPROTECT failed: %d\n", errno); return -errno; } /* Write-protect the whole mapping temporarily. */ mprotect(map, pagesize, PROT_READ); mprotect(map, pagesize, PROT_READ|PROT_WRITE); /* Test if uffd-wp fires. */ memset(map, 1, pagesize); printf("FAIL: uffd-wp did not fire\n"); return 1; }