David Hildenbrand
b2747b690c
mm/userfaultfd: propagate uffd-wp bit when PTE-mapping the huge zeropage
...
commit 42b2af2c9b7eede8ef21d0943f84d135e21a32a3 upstream.
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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-03-22 13:30:04 +01:00
..
2023-02-01 08:23:20 +01:00
2021-12-14 11:32:37 +01:00
2020-07-10 13:54:00 -07:00
2020-08-12 10:57:57 -07:00
2020-09-01 09:19:43 +02:00
2023-01-14 10:16:27 +01:00
2022-02-08 18:30:35 +01:00
2020-10-13 18:38:29 -07:00
2020-10-13 18:38:32 -07:00
2020-10-13 18:38:29 -07:00
2022-11-25 17:45:56 +01:00
2022-12-08 11:24:00 +01:00
2020-08-14 19:56:56 -07:00
2020-10-18 09:27:09 -07:00
2023-02-15 17:22:21 +01:00
2020-10-16 11:11:18 -07:00
2022-01-27 10:54:36 +01:00
2023-03-22 13:30:04 +01:00
2021-03-30 14:31:54 +02:00
2023-02-15 17:22:21 +01:00
2020-10-16 11:11:16 -07:00
2020-12-30 11:53:54 +01:00
2021-06-30 08:47:27 -04:00
2020-08-07 11:33:26 -07:00
2020-12-06 10:19:07 -08:00
2020-06-14 01:57:21 +09:00
2023-01-24 07:20:01 +01:00
2022-09-15 11:32:02 +02:00
2021-05-19 10:13:07 +02:00
2020-12-06 10:19:07 -08:00
2022-11-25 17:45:53 +01:00
2022-10-05 10:38:40 +02:00
2020-10-13 18:38:27 -07:00
2023-02-22 12:55:56 +01:00
2023-03-11 16:40:04 +01:00
2022-03-08 19:09:36 +01:00
2023-02-15 17:22:21 +01:00
2023-02-15 17:22:21 +01:00
2022-11-03 23:57:50 +09:00
2023-02-15 17:22:21 +01:00
2020-10-13 18:38:34 -07:00
2022-11-16 09:57:17 +01:00
2023-02-15 17:22:21 +01:00
2020-10-13 18:38:29 -07:00
2020-09-19 13:13:38 -07:00
2020-08-07 11:33:26 -07:00
2022-10-26 13:25:11 +02:00
2022-12-14 11:31:55 +01:00
2022-04-27 13:53:54 +02:00
2022-05-15 20:00:09 +02:00
2022-02-23 12:00:57 +01:00
2022-08-21 15:15:21 +02:00
2020-06-09 09:39:14 -07:00
2020-10-18 09:27:10 -07:00
2022-04-27 13:53:54 +02:00
2023-02-15 17:22:27 +01:00
2020-10-13 18:38:30 -07:00
2020-04-07 10:43:40 -07:00
2020-06-08 11:05:55 -07:00
2022-04-20 09:23:25 +02:00
2020-10-16 11:11:19 -07:00
2020-10-16 11:11:19 -07:00
2020-10-16 11:11:17 -07:00
2020-10-16 11:11:19 -07:00
2020-06-09 09:39:13 -07:00
2021-06-30 08:47:29 -04:00
2021-01-12 20:18:22 +01:00
2022-09-08 11:11:38 +02:00
2021-04-14 08:42:03 +02:00
2020-08-12 10:57:55 -07:00
2021-04-14 08:42:03 +02:00
2020-08-12 10:57:55 -07:00
2021-04-14 08:42:03 +02:00
2020-08-07 11:33:26 -07:00
2021-06-30 08:47:26 -04:00
2021-01-19 18:27:21 +01:00
2022-09-08 11:11:38 +02:00
2020-10-17 13:49:08 -06:00
2022-09-05 10:28:56 +02:00
2020-08-21 09:52:53 -07:00
2022-01-27 10:53:44 +01:00
2020-10-16 11:11:19 -07:00
2020-08-07 11:33:29 -07:00
2021-06-23 14:42:54 +02:00
2021-05-14 09:50:45 +02:00
2021-11-26 10:39:19 +01:00
2020-08-07 11:33:24 -07:00
2022-09-28 11:10:28 +02:00
2020-08-07 11:33:27 -07:00
2021-05-14 09:50:45 +02:00
2020-06-03 20:09:48 -07:00
2020-10-13 18:38:30 -07:00
2022-03-23 09:13:27 +01:00
2020-10-13 18:38:33 -07:00
2023-02-15 17:22:20 +01:00
2021-06-30 08:47:27 -04:00
2022-04-08 14:40:43 +02:00
2022-05-15 20:00:09 +02:00
2022-08-21 15:15:21 +02:00
2020-06-10 19:14:18 -07:00
2021-01-19 18:27:21 +01:00
2022-12-02 17:40:04 +01:00
2022-05-15 20:00:09 +02:00
2020-10-20 14:39:37 -07:00
2021-07-14 16:56:51 +02:00
2020-10-13 18:38:34 -07:00
2020-08-12 10:57:58 -07:00
2022-06-06 08:42:43 +02:00
2020-04-07 10:43:41 -07:00