David Hildenbrand
42b2af2c9b
mm/userfaultfd: propagate uffd-wp bit when PTE-mapping the huge zeropage
...
Currently, we'd lose the userfaultfd-wp marker when PTE-mapping a huge
zeropage, resulting in the next write faults in the PMD range not
triggering uffd-wp events.
Various actions (partial MADV_DONTNEED, partial mremap, partial munmap,
partial mprotect) could trigger this. However, most importantly,
un-protecting a single sub-page from the userfaultfd-wp handler when
processing a uffd-wp event will PTE-map the shared huge zeropage and lose
the uffd-wp bit for the remainder of the PMD.
Let's properly propagate the uffd-wp bit to the PMDs.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <linux/userfaultfd.h>
static size_t pagesize;
static int uffd;
static volatile bool uffd_triggered;
#define barrier() __asm__ __volatile__("": : :"memory")
static void uffd_wp_range(char *start, size_t size, bool wp)
{
struct uffdio_writeprotect uffd_writeprotect;
uffd_writeprotect.range.start = (unsigned long) start;
uffd_writeprotect.range.len = size;
if (wp) {
uffd_writeprotect.mode = UFFDIO_WRITEPROTECT_MODE_WP;
} else {
uffd_writeprotect.mode = 0;
}
if (ioctl(uffd, UFFDIO_WRITEPROTECT, &uffd_writeprotect)) {
fprintf(stderr, "UFFDIO_WRITEPROTECT failed: %d\n", errno);
exit(1);
}
}
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);
}
/* un-protect the single page. */
uffd_triggered = true;
uffd_wp_range((char *)(uintptr_t)msg.arg.pagefault.address,
pagesize, false);
}
return arg;
}
static int setup_uffd(char *map, size_t size)
{
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
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 = size;
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(void)
{
const size_t size = 4 * 1024 * 1024ull;
char *map, *cur;
pagesize = getpagesize();
map = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
if (map == MAP_FAILED) {
fprintf(stderr, "mmap() failed\n");
return -errno;
}
if (madvise(map, size, MADV_HUGEPAGE)) {
fprintf(stderr, "MADV_HUGEPAGE failed\n");
return -errno;
}
if (setup_uffd(map, size))
return 1;
/* Read the whole range, populating zeropages. */
madvise(map, size, MADV_POPULATE_READ);
/* Write-protect the whole range. */
uffd_wp_range(map, size, true);
/* Make sure uffd-wp triggers on each page. */
for (cur = map; cur < map + size; cur += pagesize) {
uffd_triggered = false;
barrier();
/* Trigger a write fault. */
*cur = 1;
barrier();
if (!uffd_triggered) {
printf("FAIL: uffd-wp did not trigger\n");
return 1;
}
}
printf("PASS: uffd-wp triggered\n");
return 0;
}
Link: https://lkml.kernel.org/r/20230302175423.589164-1-david@redhat.com
Fixes: e06f1e1dd499 ("userfaultfd: wp: enabled write protection in userfaultfd API")
Signed-off-by: David Hildenbrand <david@redhat.com>
Acked-by: Peter Xu <peterx@redhat.com>
Cc: Mike Rapoport <rppt@linux.vnet.ibm.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Jerome Glisse <jglisse@redhat.com>
Cc: Shaohua Li <shli@fb.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-03-07 17:04:53 -08:00
..
2023-02-27 17:00:14 -08:00
2023-03-02 21:54:22 -08:00
2022-12-14 12:20:00 -08:00
2023-02-20 12:46:16 -08:00
2022-11-30 15:59:06 -08:00
2022-08-02 12:34:03 -04:00
2022-08-28 14:02:45 -07:00
2022-09-11 20:25:50 -07:00
2023-02-02 22:33:24 -08:00
2023-02-23 17:09:35 -08:00
2023-02-02 22:33:11 -08:00
2023-02-09 16:51:40 -08:00
2022-03-22 15:57:11 -07:00
2023-01-18 17:12:57 -08:00
2022-11-22 18:50:44 -08:00
2023-02-23 17:09:35 -08:00
2023-02-20 12:46:17 -08:00
2022-09-26 12:14:34 -07:00
2022-12-15 16:37:48 -08:00
2022-11-08 17:37:15 -08:00
2023-02-20 12:46:17 -08:00
2022-10-12 18:51:51 -07:00
2023-01-18 17:12:39 -08:00
2023-03-07 17:04:53 -08:00
2023-02-13 15:54:27 -08:00
2023-02-21 13:34:07 -08:00
2022-08-08 18:06:43 -07:00
2023-02-20 12:46:17 -08:00
2022-10-03 14:03:05 -07:00
2022-09-26 19:46:16 -07:00
2023-02-23 17:09:35 -08:00
2022-06-27 12:22:31 +01:00
2023-02-02 22:33:23 -08:00
2023-02-02 22:33:26 -08:00
2023-02-23 17:09:35 -08:00
2023-02-16 20:43:49 -08:00
2023-02-10 15:34:48 -08:00
2022-06-16 19:48:31 -07:00
2022-11-11 11:44:46 -08:00
2023-02-23 17:09:35 -08:00
2022-10-03 14:03:36 -07:00
2023-02-02 22:32:54 -08:00
2023-02-27 09:34:53 -08:00
2023-02-23 17:09:35 -08:00
2023-01-18 17:12:37 -08:00
2023-02-20 12:46:18 -08:00
2023-02-27 17:00:14 -08:00
2023-02-09 16:51:40 -08:00
2023-02-20 12:46:18 -08:00
2023-02-20 12:46:17 -08:00
2022-11-30 15:58:41 -08:00
2023-02-09 16:51:46 -08:00
2023-02-20 12:46:17 -08:00
2023-03-04 14:03:27 -08:00
2023-03-07 17:04:53 -08:00
2023-02-09 16:51:41 -08:00
2022-11-08 17:37:17 -08:00
2022-10-03 14:02:43 -07:00
2023-02-27 17:00:14 -08:00
2022-12-11 18:12:21 -08:00
2023-02-02 22:32:54 -08:00
2022-09-26 19:46:09 -07:00
2023-02-25 20:16:48 -08:00
2023-02-09 16:51:39 -08:00
2022-09-26 19:46:25 -07:00
2023-02-09 16:51:39 -08:00
2023-02-02 22:32:54 -08:00
2023-02-23 17:09:35 -08:00
2022-09-11 20:26:01 -07:00
2023-02-02 22:33:22 -08:00
2023-01-18 17:12:52 -08:00
2023-02-23 17:09:35 -08:00
2022-10-28 13:37:22 -07:00
2023-02-16 20:43:49 -08:00
2023-01-18 17:12:50 -08:00
2023-02-02 22:33:11 -08:00
2023-01-18 17:12:39 -08:00
2023-02-02 22:33:34 -08:00
2023-01-18 17:12:39 -08:00
2023-02-16 20:43:55 -08:00
2023-02-16 20:43:56 -08:00
2022-05-13 07:20:05 -07:00
2022-11-25 13:01:55 -05:00
2022-09-03 10:13:13 -07:00
2023-02-02 22:33:21 -08:00
2023-02-27 17:00:14 -08:00
2022-10-03 14:03:05 -07:00
2023-02-23 17:09:35 -08:00
2023-02-23 17:09:35 -08:00
2023-02-09 15:56:51 -08:00
2022-10-03 14:03:07 -07:00
2023-01-18 17:12:55 -08:00
2023-02-23 17:09:35 -08:00
2023-02-16 20:43:56 -08:00
2022-09-29 11:30:55 +02:00
2023-02-23 17:09:35 -08:00
2022-12-11 18:12:12 -08:00
2023-02-02 22:33:29 -08:00
2022-10-03 14:03:36 -07:00
2022-10-03 14:02:46 -07:00
2023-02-02 22:33:24 -08:00
2023-02-23 17:09:35 -08:00
2023-02-02 22:33:33 -08:00
2023-02-23 17:09:35 -08:00
2022-12-11 18:12:13 -08:00
2022-11-30 15:58:45 -08:00
2023-01-18 17:12:53 -08:00
2023-02-13 15:54:33 -08:00
2023-02-23 17:09:35 -08:00
2023-02-20 12:46:17 -08:00
2022-11-30 15:58:41 -08:00
2023-02-02 22:33:24 -08:00
2023-01-18 17:12:57 -08:00
2022-12-11 18:12:10 -08:00
2022-12-11 18:12:10 -08:00
2023-02-02 22:33:23 -08:00
2022-12-11 18:12:09 -08:00