All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/5] userfaultfd: add /dev/userfaultfd for fine grained access control
@ 2022-08-08 17:56 Axel Rasmussen
  2022-08-08 17:56 ` [PATCH v5 1/5] selftests: vm: add hugetlb_shared userfaultfd test to run_vmtests.sh Axel Rasmussen
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: Axel Rasmussen @ 2022-08-08 17:56 UTC (permalink / raw)
  To: Alexander Viro, Andrew Morton, Dave Hansen, Dmitry V . Levin,
	Gleb Fotengauer-Malinovskiy, Hugh Dickins, Jan Kara,
	Jonathan Corbet, Mel Gorman, Mike Kravetz, Mike Rapoport,
	Nadav Amit, Peter Xu, Shuah Khan, Suren Baghdasaryan,
	Vlastimil Babka, zhangyi
  Cc: Axel Rasmussen, linux-doc, linux-fsdevel, linux-kernel,
	linux-kselftest, linux-mm, linux-security-module

This series is based on torvalds/master.

The series is split up like so:
- Patch 1 is a simple fixup which we should take in any case (even by itself).
- Patches 2-5 add the feature, configurable selftest support, and docs.

Why not ...?
============

- Why not /proc/[pid]/userfaultfd? Two main points (additional discussion [1]):

    - /proc/[pid]/* files are all owned by the user/group of the process, and
      they don't really support chmod/chown. So, without extending procfs it
      doesn't solve the problem this series is trying to solve.

    - The main argument *for* this was to support creating UFFDs for remote
      processes. But, that use case clearly calls for CAP_SYS_PTRACE, so to
      support this we could just use the UFFD syscall as-is.

- Why not use a syscall? Access to syscalls is generally controlled by
  capabilities. We don't have a capability which is used for userfaultfd access
  without also granting more / other permissions as well, and adding a new
  capability was rejected [2].

    - It's possible a LSM could be used to control access instead, but I have
      some concerns. I don't think this approach would be as easy to use,
      particularly if we were to try to solve this with something heavyweight
      like SELinux. Maybe we could pursue adding a new LSM specifically for
      this user case, but it may be too narrow of a case to justify that.

Changelog
=========

v4->v5:
  - Call userfaultfd_syscall_allowed() directly in the syscall, so we don't
    have to plumb a flag into new_userfaultfd(). [Nadav]
  - Refactored run_vmtests.sh to loop over UFFD test mods. [Nadav]
  - Reworded cover letter.
  - Picked up some Acked-by's.

v3->v4:
  - Picked up an Acked-by on 5/5.
  - Updated cover letter to cover "why not ...".
  - Refactored userfaultfd_allowed() into userfaultfd_syscall_allowed(). [Peter]
  - Removed obsolete comment from a previous version. [Peter]
  - Refactored userfaultfd_open() in selftest. [Peter]
  - Reworded admin-guide documentation. [Mike, Peter]
  - Squashed 2 commits adding /dev/userfaultfd to selftest and making selftest
    configurable. [Peter]
  - Added "syscall" test modifier (the default behavior) to selftest. [Peter]

v2->v3:
  - Rebased onto linux-next/akpm-base, in order to be based on top of the
    run_vmtests.sh refactor which was merged previously.
  - Picked up some Reviewed-by's.
  - Fixed ioctl definition (_IO instead of _IOWR), and stopped using
    compat_ptr_ioctl since it is unneeded for ioctls which don't take a pointer.
  - Removed the "handle_kernel_faults" bool, simplifying the code. The result is
    logically equivalent, but simpler.
  - Fixed userfaultfd selftest so it returns KSFT_SKIP appropriately.
  - Reworded documentation per Shuah's feedback on v2.
  - Improved example usage for userfaultfd selftest.

v1->v2:
  - Add documentation update.
  - Test *both* userfaultfd(2) and /dev/userfaultfd via the selftest.

[1]: https://patchwork.kernel.org/project/linux-mm/cover/20220719195628.3415852-1-axelrasmussen@google.com/
[2]: https://lore.kernel.org/lkml/686276b9-4530-2045-6bd8-170e5943abe4@schaufler-ca.com/T/

Axel Rasmussen (5):
  selftests: vm: add hugetlb_shared userfaultfd test to run_vmtests.sh
  userfaultfd: add /dev/userfaultfd for fine grained access control
  userfaultfd: selftests: modify selftest to use /dev/userfaultfd
  userfaultfd: update documentation to describe /dev/userfaultfd
  selftests: vm: add /dev/userfaultfd test cases to run_vmtests.sh

 Documentation/admin-guide/mm/userfaultfd.rst | 41 ++++++++++-
 Documentation/admin-guide/sysctl/vm.rst      |  3 +
 fs/userfaultfd.c                             | 73 +++++++++++++++-----
 include/uapi/linux/userfaultfd.h             |  4 ++
 tools/testing/selftests/vm/run_vmtests.sh    | 15 ++--
 tools/testing/selftests/vm/userfaultfd.c     | 69 +++++++++++++++---
 6 files changed, 172 insertions(+), 33 deletions(-)

--
2.37.1.559.g78731f0fdb-goog


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v5 1/5] selftests: vm: add hugetlb_shared userfaultfd test to run_vmtests.sh
  2022-08-08 17:56 [PATCH v5 0/5] userfaultfd: add /dev/userfaultfd for fine grained access control Axel Rasmussen
@ 2022-08-08 17:56 ` Axel Rasmussen
  2022-08-08 17:56 ` [PATCH v5 2/5] userfaultfd: add /dev/userfaultfd for fine grained access control Axel Rasmussen
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Axel Rasmussen @ 2022-08-08 17:56 UTC (permalink / raw)
  To: Alexander Viro, Andrew Morton, Dave Hansen, Dmitry V . Levin,
	Gleb Fotengauer-Malinovskiy, Hugh Dickins, Jan Kara,
	Jonathan Corbet, Mel Gorman, Mike Kravetz, Mike Rapoport,
	Nadav Amit, Peter Xu, Shuah Khan, Suren Baghdasaryan,
	Vlastimil Babka, zhangyi
  Cc: Axel Rasmussen, linux-doc, linux-fsdevel, linux-kernel,
	linux-kselftest, linux-mm, linux-security-module, Shuah Khan

This not being included was just a simple oversight. There are certain
features (like minor fault support) which are only enabled on shared
mappings, so without including hugetlb_shared we actually lose a
significant amount of test coverage.

Reviewed-by: Shuah Khan <skhan@linuxfoundation.org>
Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>
---
 tools/testing/selftests/vm/run_vmtests.sh | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/vm/run_vmtests.sh b/tools/testing/selftests/vm/run_vmtests.sh
index de86983b8a0f..b8e7f6f38d64 100755
--- a/tools/testing/selftests/vm/run_vmtests.sh
+++ b/tools/testing/selftests/vm/run_vmtests.sh
@@ -121,9 +121,11 @@ run_test ./gup_test -a
 run_test ./gup_test -ct -F 0x1 0 19 0x1000
 
 run_test ./userfaultfd anon 20 16
-# Test requires source and destination huge pages.  Size of source
-# (half_ufd_size_MB) is passed as argument to test.
+# Hugetlb tests require source and destination huge pages. Pass in half the
+# size ($half_ufd_size_MB), which is used for *each*.
 run_test ./userfaultfd hugetlb "$half_ufd_size_MB" 32
+run_test ./userfaultfd hugetlb_shared "$half_ufd_size_MB" 32 "$mnt"/uffd-test
+rm -f "$mnt"/uffd-test
 run_test ./userfaultfd shmem 20 16
 
 #cleanup
-- 
2.37.1.559.g78731f0fdb-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 2/5] userfaultfd: add /dev/userfaultfd for fine grained access control
  2022-08-08 17:56 [PATCH v5 0/5] userfaultfd: add /dev/userfaultfd for fine grained access control Axel Rasmussen
  2022-08-08 17:56 ` [PATCH v5 1/5] selftests: vm: add hugetlb_shared userfaultfd test to run_vmtests.sh Axel Rasmussen
@ 2022-08-08 17:56 ` Axel Rasmussen
  2022-08-11  6:37   ` Mike Rapoport
  2022-08-08 17:56 ` [PATCH v5 3/5] userfaultfd: selftests: modify selftest to use /dev/userfaultfd Axel Rasmussen
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 10+ messages in thread
From: Axel Rasmussen @ 2022-08-08 17:56 UTC (permalink / raw)
  To: Alexander Viro, Andrew Morton, Dave Hansen, Dmitry V . Levin,
	Gleb Fotengauer-Malinovskiy, Hugh Dickins, Jan Kara,
	Jonathan Corbet, Mel Gorman, Mike Kravetz, Mike Rapoport,
	Nadav Amit, Peter Xu, Shuah Khan, Suren Baghdasaryan,
	Vlastimil Babka, zhangyi
  Cc: Axel Rasmussen, linux-doc, linux-fsdevel, linux-kernel,
	linux-kselftest, linux-mm, linux-security-module

Historically, it has been shown that intercepting kernel faults with
userfaultfd (thereby forcing the kernel to wait for an arbitrary amount
of time) can be exploited, or at least can make some kinds of exploits
easier. So, in 37cd0575b8 "userfaultfd: add UFFD_USER_MODE_ONLY" we
changed things so, in order for kernel faults to be handled by
userfaultfd, either the process needs CAP_SYS_PTRACE, or this sysctl
must be configured so that any unprivileged user can do it.

In a typical implementation of a hypervisor with live migration (take
QEMU/KVM as one such example), we do indeed need to be able to handle
kernel faults. But, both options above are less than ideal:

- Toggling the sysctl increases attack surface by allowing any
  unprivileged user to do it.

- Granting the live migration process CAP_SYS_PTRACE gives it this
  ability, but *also* the ability to "observe and control the
  execution of another process [...], and examine and change [its]
  memory and registers" (from ptrace(2)). This isn't something we need
  or want to be able to do, so granting this permission violates the
  "principle of least privilege".

This is all a long winded way to say: we want a more fine-grained way to
grant access to userfaultfd, without granting other additional
permissions at the same time.

To achieve this, add a /dev/userfaultfd misc device. This device
provides an alternative to the userfaultfd(2) syscall for the creation
of new userfaultfds. The idea is, any userfaultfds created this way will
be able to handle kernel faults, without the caller having any special
capabilities. Access to this mechanism is instead restricted using e.g.
standard filesystem permissions.

Acked-by: Nadav Amit <namit@vmware.com>
Acked-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>
---
 fs/userfaultfd.c                 | 73 +++++++++++++++++++++++++-------
 include/uapi/linux/userfaultfd.h |  4 ++
 2 files changed, 61 insertions(+), 16 deletions(-)

diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index 1c44bf75f916..698e768d5c3d 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -30,6 +30,7 @@
 #include <linux/security.h>
 #include <linux/hugetlb.h>
 #include <linux/swapops.h>
+#include <linux/miscdevice.h>
 
 int sysctl_unprivileged_userfaultfd __read_mostly;
 
@@ -415,13 +416,8 @@ vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned long reason)
 
 	if (ctx->features & UFFD_FEATURE_SIGBUS)
 		goto out;
-	if ((vmf->flags & FAULT_FLAG_USER) == 0 &&
-	    ctx->flags & UFFD_USER_MODE_ONLY) {
-		printk_once(KERN_WARNING "uffd: Set unprivileged_userfaultfd "
-			"sysctl knob to 1 if kernel faults must be handled "
-			"without obtaining CAP_SYS_PTRACE capability\n");
+	if (!(vmf->flags & FAULT_FLAG_USER) && (ctx->flags & UFFD_USER_MODE_ONLY))
 		goto out;
-	}
 
 	/*
 	 * If it's already released don't get it. This avoids to loop
@@ -2052,20 +2048,11 @@ static void init_once_userfaultfd_ctx(void *mem)
 	seqcount_spinlock_init(&ctx->refile_seq, &ctx->fault_pending_wqh.lock);
 }
 
-SYSCALL_DEFINE1(userfaultfd, int, flags)
+static int new_userfaultfd(int flags)
 {
 	struct userfaultfd_ctx *ctx;
 	int fd;
 
-	if (!sysctl_unprivileged_userfaultfd &&
-	    (flags & UFFD_USER_MODE_ONLY) == 0 &&
-	    !capable(CAP_SYS_PTRACE)) {
-		printk_once(KERN_WARNING "uffd: Set unprivileged_userfaultfd "
-			"sysctl knob to 1 if kernel faults must be handled "
-			"without obtaining CAP_SYS_PTRACE capability\n");
-		return -EPERM;
-	}
-
 	BUG_ON(!current->mm);
 
 	/* Check the UFFD_* constants for consistency.  */
@@ -2098,8 +2085,62 @@ SYSCALL_DEFINE1(userfaultfd, int, flags)
 	return fd;
 }
 
+static inline bool userfaultfd_syscall_allowed(int flags)
+{
+	/* Userspace-only page faults are always allowed */
+	if (flags & UFFD_USER_MODE_ONLY)
+		return true;
+
+	/*
+	 * The user is requesting a userfaultfd which can handle kernel faults.
+	 * Privileged users are always allowed to do this.
+	 */
+	if (capable(CAP_SYS_PTRACE))
+		return true;
+
+	/* Otherwise, access to kernel fault handling is sysctl controlled. */
+	return sysctl_unprivileged_userfaultfd;
+}
+
+SYSCALL_DEFINE1(userfaultfd, int, flags)
+{
+	if (!userfaultfd_syscall_allowed(flags))
+		return -EPERM;
+
+	return new_userfaultfd(flags);
+}
+
+static int userfaultfd_dev_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static long userfaultfd_dev_ioctl(struct file *file, unsigned int cmd, unsigned long flags)
+{
+	if (cmd != USERFAULTFD_IOC_NEW)
+		return -EINVAL;
+
+	return new_userfaultfd(flags);
+}
+
+static const struct file_operations userfaultfd_dev_fops = {
+	.open = userfaultfd_dev_open,
+	.unlocked_ioctl = userfaultfd_dev_ioctl,
+	.compat_ioctl = userfaultfd_dev_ioctl,
+	.owner = THIS_MODULE,
+	.llseek = noop_llseek,
+};
+
+static struct miscdevice userfaultfd_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "userfaultfd",
+	.fops = &userfaultfd_dev_fops
+};
+
 static int __init userfaultfd_init(void)
 {
+	WARN_ON(misc_register(&userfaultfd_misc));
+
 	userfaultfd_ctx_cachep = kmem_cache_create("userfaultfd_ctx_cache",
 						sizeof(struct userfaultfd_ctx),
 						0,
diff --git a/include/uapi/linux/userfaultfd.h b/include/uapi/linux/userfaultfd.h
index 7d32b1e797fb..005e5e306266 100644
--- a/include/uapi/linux/userfaultfd.h
+++ b/include/uapi/linux/userfaultfd.h
@@ -12,6 +12,10 @@
 
 #include <linux/types.h>
 
+/* ioctls for /dev/userfaultfd */
+#define USERFAULTFD_IOC 0xAA
+#define USERFAULTFD_IOC_NEW _IO(USERFAULTFD_IOC, 0x00)
+
 /*
  * If the UFFDIO_API is upgraded someday, the UFFDIO_UNREGISTER and
  * UFFDIO_WAKE ioctls should be defined as _IOW and not as _IOR.  In
-- 
2.37.1.559.g78731f0fdb-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 3/5] userfaultfd: selftests: modify selftest to use /dev/userfaultfd
  2022-08-08 17:56 [PATCH v5 0/5] userfaultfd: add /dev/userfaultfd for fine grained access control Axel Rasmussen
  2022-08-08 17:56 ` [PATCH v5 1/5] selftests: vm: add hugetlb_shared userfaultfd test to run_vmtests.sh Axel Rasmussen
  2022-08-08 17:56 ` [PATCH v5 2/5] userfaultfd: add /dev/userfaultfd for fine grained access control Axel Rasmussen
@ 2022-08-08 17:56 ` Axel Rasmussen
  2022-08-11  6:38   ` Mike Rapoport
  2022-08-08 17:56 ` [PATCH v5 4/5] userfaultfd: update documentation to describe /dev/userfaultfd Axel Rasmussen
  2022-08-08 17:56 ` [PATCH v5 5/5] selftests: vm: add /dev/userfaultfd test cases to run_vmtests.sh Axel Rasmussen
  4 siblings, 1 reply; 10+ messages in thread
From: Axel Rasmussen @ 2022-08-08 17:56 UTC (permalink / raw)
  To: Alexander Viro, Andrew Morton, Dave Hansen, Dmitry V . Levin,
	Gleb Fotengauer-Malinovskiy, Hugh Dickins, Jan Kara,
	Jonathan Corbet, Mel Gorman, Mike Kravetz, Mike Rapoport,
	Nadav Amit, Peter Xu, Shuah Khan, Suren Baghdasaryan,
	Vlastimil Babka, zhangyi
  Cc: Axel Rasmussen, linux-doc, linux-fsdevel, linux-kernel,
	linux-kselftest, linux-mm, linux-security-module

We clearly want to ensure both userfaultfd(2) and /dev/userfaultfd keep
working into the future, so just run the test twice, using each
interface.

Instead of always testing both userfaultfd(2) and /dev/userfaultfd,
let the user choose which to test.

As with other test features, change the behavior based on a new
command line flag. Introduce the idea of "test mods", which are
generic (not specific to a test type) modifications to the behavior of
the test. This is sort of borrowed from this RFC patch series [1], but
simplified a bit.

The benefit is, in "typical" configurations this test is somewhat slow
(say, 30sec or something). Testing both clearly doubles it, so it may
not always be desirable, as users are likely to use one or the other,
but never both, in the "real world".

[1]: https://patchwork.kernel.org/project/linux-mm/patch/20201129004548.1619714-14-namit@vmware.com/

Acked-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>
---
 tools/testing/selftests/vm/userfaultfd.c | 69 ++++++++++++++++++++----
 1 file changed, 60 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c
index 7c3f1b0ab468..cae72867c173 100644
--- a/tools/testing/selftests/vm/userfaultfd.c
+++ b/tools/testing/selftests/vm/userfaultfd.c
@@ -77,6 +77,11 @@ static int bounces;
 #define TEST_SHMEM	3
 static int test_type;
 
+#define UFFD_FLAGS	(O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY)
+
+/* test using /dev/userfaultfd, instead of userfaultfd(2) */
+static bool test_dev_userfaultfd;
+
 /* exercise the test_uffdio_*_eexist every ALARM_INTERVAL_SECS */
 #define ALARM_INTERVAL_SECS 10
 static volatile bool test_uffdio_copy_eexist = true;
@@ -125,6 +130,8 @@ struct uffd_stats {
 const char *examples =
     "# Run anonymous memory test on 100MiB region with 99999 bounces:\n"
     "./userfaultfd anon 100 99999\n\n"
+    "# Run the same anonymous memory test, but using /dev/userfaultfd:\n"
+    "./userfaultfd anon:dev 100 99999\n\n"
     "# Run share memory test on 1GiB region with 99 bounces:\n"
     "./userfaultfd shmem 1000 99\n\n"
     "# Run hugetlb memory test on 256MiB region with 50 bounces:\n"
@@ -141,6 +148,14 @@ static void usage(void)
 		"[hugetlbfs_file]\n\n");
 	fprintf(stderr, "Supported <test type>: anon, hugetlb, "
 		"hugetlb_shared, shmem\n\n");
+	fprintf(stderr, "'Test mods' can be joined to the test type string with a ':'. "
+		"Supported mods:\n");
+	fprintf(stderr, "\tsyscall - Use userfaultfd(2) (default)\n");
+	fprintf(stderr, "\tdev - Use /dev/userfaultfd instead of userfaultfd(2)\n");
+	fprintf(stderr, "\nExample test mod usage:\n");
+	fprintf(stderr, "# Run anonymous memory test with /dev/userfaultfd:\n");
+	fprintf(stderr, "./userfaultfd anon:dev 100 99999\n\n");
+
 	fprintf(stderr, "Examples:\n\n");
 	fprintf(stderr, "%s", examples);
 	exit(1);
@@ -154,12 +169,14 @@ static void usage(void)
 			ret, __LINE__);				\
 	} while (0)
 
-#define err(fmt, ...)				\
+#define errexit(exitcode, fmt, ...)		\
 	do {					\
 		_err(fmt, ##__VA_ARGS__);	\
-		exit(1);			\
+		exit(exitcode);			\
 	} while (0)
 
+#define err(fmt, ...) errexit(1, fmt, ##__VA_ARGS__)
+
 static void uffd_stats_reset(struct uffd_stats *uffd_stats,
 			     unsigned long n_cpus)
 {
@@ -383,13 +400,29 @@ static void assert_expected_ioctls_present(uint64_t mode, uint64_t ioctls)
 	}
 }
 
+static int __userfaultfd_open_dev(void)
+{
+	int fd, _uffd = -1;
+
+	fd = open("/dev/userfaultfd", O_RDWR | O_CLOEXEC);
+	if (fd < 0)
+		return -1;
+
+	_uffd = ioctl(fd, USERFAULTFD_IOC_NEW, UFFD_FLAGS);
+	close(fd);
+	return _uffd;
+}
+
 static void userfaultfd_open(uint64_t *features)
 {
 	struct uffdio_api uffdio_api;
 
-	uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY);
+	if (test_dev_userfaultfd)
+		uffd = __userfaultfd_open_dev();
+	else
+		uffd = syscall(__NR_userfaultfd, UFFD_FLAGS);
 	if (uffd < 0)
-		err("userfaultfd syscall not available in this kernel");
+		errexit(KSFT_SKIP, "creating userfaultfd failed");
 	uffd_flags = fcntl(uffd, F_GETFD, NULL);
 
 	uffdio_api.api = UFFD_API;
@@ -1584,8 +1617,6 @@ unsigned long default_huge_page_size(void)
 
 static void set_test_type(const char *type)
 {
-	uint64_t features = UFFD_API_FEATURES;
-
 	if (!strcmp(type, "anon")) {
 		test_type = TEST_ANON;
 		uffd_test_ops = &anon_uffd_test_ops;
@@ -1603,9 +1634,29 @@ static void set_test_type(const char *type)
 		test_type = TEST_SHMEM;
 		uffd_test_ops = &shmem_uffd_test_ops;
 		test_uffdio_minor = true;
-	} else {
-		err("Unknown test type: %s", type);
 	}
+}
+
+static void parse_test_type_arg(const char *raw_type)
+{
+	char *buf = strdup(raw_type);
+	uint64_t features = UFFD_API_FEATURES;
+
+	while (buf) {
+		const char *token = strsep(&buf, ":");
+
+		if (!test_type)
+			set_test_type(token);
+		else if (!strcmp(token, "dev"))
+			test_dev_userfaultfd = true;
+		else if (!strcmp(token, "syscall"))
+			test_dev_userfaultfd = false;
+		else
+			err("unrecognized test mod '%s'", token);
+	}
+
+	if (!test_type)
+		err("failed to parse test type argument: '%s'", raw_type);
 
 	if (test_type == TEST_HUGETLB)
 		page_size = default_huge_page_size();
@@ -1653,7 +1704,7 @@ int main(int argc, char **argv)
 		err("failed to arm SIGALRM");
 	alarm(ALARM_INTERVAL_SECS);
 
-	set_test_type(argv[1]);
+	parse_test_type_arg(argv[1]);
 
 	nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
 	nr_pages_per_cpu = atol(argv[2]) * 1024*1024 / page_size /
-- 
2.37.1.559.g78731f0fdb-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 4/5] userfaultfd: update documentation to describe /dev/userfaultfd
  2022-08-08 17:56 [PATCH v5 0/5] userfaultfd: add /dev/userfaultfd for fine grained access control Axel Rasmussen
                   ` (2 preceding siblings ...)
  2022-08-08 17:56 ` [PATCH v5 3/5] userfaultfd: selftests: modify selftest to use /dev/userfaultfd Axel Rasmussen
@ 2022-08-08 17:56 ` Axel Rasmussen
  2022-08-11  6:49   ` Mike Rapoport
  2022-08-08 17:56 ` [PATCH v5 5/5] selftests: vm: add /dev/userfaultfd test cases to run_vmtests.sh Axel Rasmussen
  4 siblings, 1 reply; 10+ messages in thread
From: Axel Rasmussen @ 2022-08-08 17:56 UTC (permalink / raw)
  To: Alexander Viro, Andrew Morton, Dave Hansen, Dmitry V . Levin,
	Gleb Fotengauer-Malinovskiy, Hugh Dickins, Jan Kara,
	Jonathan Corbet, Mel Gorman, Mike Kravetz, Mike Rapoport,
	Nadav Amit, Peter Xu, Shuah Khan, Suren Baghdasaryan,
	Vlastimil Babka, zhangyi
  Cc: Axel Rasmussen, linux-doc, linux-fsdevel, linux-kernel,
	linux-kselftest, linux-mm, linux-security-module

Explain the different ways to create a new userfaultfd, and how access
control works for each way.

Acked-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>
---
 Documentation/admin-guide/mm/userfaultfd.rst | 41 ++++++++++++++++++--
 Documentation/admin-guide/sysctl/vm.rst      |  3 ++
 2 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/Documentation/admin-guide/mm/userfaultfd.rst b/Documentation/admin-guide/mm/userfaultfd.rst
index 6528036093e1..a76c9dc1865b 100644
--- a/Documentation/admin-guide/mm/userfaultfd.rst
+++ b/Documentation/admin-guide/mm/userfaultfd.rst
@@ -17,7 +17,10 @@ of the ``PROT_NONE+SIGSEGV`` trick.
 Design
 ======
 
-Userfaults are delivered and resolved through the ``userfaultfd`` syscall.
+Userspace creates a new userfaultfd, initializes it, and registers one or more
+regions of virtual memory with it. Then, any page faults which occur within the
+region(s) result in a message being delivered to the userfaultfd, notifying
+userspace of the fault.
 
 The ``userfaultfd`` (aside from registering and unregistering virtual
 memory ranges) provides two primary functionalities:
@@ -34,12 +37,11 @@ The real advantage of userfaults if compared to regular virtual memory
 management of mremap/mprotect is that the userfaults in all their
 operations never involve heavyweight structures like vmas (in fact the
 ``userfaultfd`` runtime load never takes the mmap_lock for writing).
-
 Vmas are not suitable for page- (or hugepage) granular fault tracking
 when dealing with virtual address spaces that could span
 Terabytes. Too many vmas would be needed for that.
 
-The ``userfaultfd`` once opened by invoking the syscall, can also be
+The ``userfaultfd``, once created, can also be
 passed using unix domain sockets to a manager process, so the same
 manager process could handle the userfaults of a multitude of
 different processes without them being aware about what is going on
@@ -50,6 +52,39 @@ is a corner case that would currently return ``-EBUSY``).
 API
 ===
 
+Creating a userfaultfd
+----------------------
+
+There are two ways to create a new userfaultfd, each of which provide ways to
+restrict access to this functionality (since historically userfaultfds which
+handle kernel page faults have been a useful tool for exploiting the kernel).
+
+The first way, supported since userfaultfd was introduced, is the
+userfaultfd(2) syscall. Access to this is controlled in several ways:
+
+- Any user can always create a userfaultfd which traps userspace page faults
+  only. Such a userfaultfd can be created using the userfaultfd(2) syscall
+  with the flag UFFD_USER_MODE_ONLY.
+
+- In order to also trap kernel page faults for the address space, then either
+  the process needs the CAP_SYS_PTRACE capability, or the system must have
+  vm.unprivileged_userfaultfd set to 1. By default, vm.unprivileged_userfaultfd
+  is set to 0.
+
+The second way, added to the kernel more recently, is by opening and issuing a
+USERFAULTFD_IOC_NEW ioctl to /dev/userfaultfd. This method yields equivalent
+userfaultfds to the userfaultfd(2) syscall.
+
+Unlike userfaultfd(2), access to /dev/userfaultfd is controlled via normal
+filesystem permissions (user/group/mode), which gives fine grained access to
+userfaultfd specifically, without also granting other unrelated privileges at
+the same time (as e.g. granting CAP_SYS_PTRACE would do). Users who have access
+to /dev/userfaultfd can always create userfaultfds that trap kernel page faults;
+vm.unprivileged_userfaultfd is not considered.
+
+Initializing a userfaultfd
+--------------------------
+
 When first opened the ``userfaultfd`` must be enabled invoking the
 ``UFFDIO_API`` ioctl specifying a ``uffdio_api.api`` value set to ``UFFD_API`` (or
 a later API version) which will specify the ``read/POLLIN`` protocol
diff --git a/Documentation/admin-guide/sysctl/vm.rst b/Documentation/admin-guide/sysctl/vm.rst
index f74f722ad702..b3e40b42e1b3 100644
--- a/Documentation/admin-guide/sysctl/vm.rst
+++ b/Documentation/admin-guide/sysctl/vm.rst
@@ -927,6 +927,9 @@ calls without any restrictions.
 
 The default value is 0.
 
+Another way to control permissions for userfaultfd is to use
+/dev/userfaultfd instead of userfaultfd(2). See
+Documentation/admin-guide/mm/userfaultfd.rst.
 
 user_reserve_kbytes
 ===================
-- 
2.37.1.559.g78731f0fdb-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 5/5] selftests: vm: add /dev/userfaultfd test cases to run_vmtests.sh
  2022-08-08 17:56 [PATCH v5 0/5] userfaultfd: add /dev/userfaultfd for fine grained access control Axel Rasmussen
                   ` (3 preceding siblings ...)
  2022-08-08 17:56 ` [PATCH v5 4/5] userfaultfd: update documentation to describe /dev/userfaultfd Axel Rasmussen
@ 2022-08-08 17:56 ` Axel Rasmussen
  4 siblings, 0 replies; 10+ messages in thread
From: Axel Rasmussen @ 2022-08-08 17:56 UTC (permalink / raw)
  To: Alexander Viro, Andrew Morton, Dave Hansen, Dmitry V . Levin,
	Gleb Fotengauer-Malinovskiy, Hugh Dickins, Jan Kara,
	Jonathan Corbet, Mel Gorman, Mike Kravetz, Mike Rapoport,
	Nadav Amit, Peter Xu, Shuah Khan, Suren Baghdasaryan,
	Vlastimil Babka, zhangyi
  Cc: Axel Rasmussen, linux-doc, linux-fsdevel, linux-kernel,
	linux-kselftest, linux-mm, linux-security-module, Shuah Khan

This new mode was recently added to the userfaultfd selftest. We want to
exercise both userfaultfd(2) as well as /dev/userfaultfd, so add both
test cases to the script.

Reviewed-by: Shuah Khan <skhan@linuxfoundation.org>
Acked-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>
---
 tools/testing/selftests/vm/run_vmtests.sh | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/tools/testing/selftests/vm/run_vmtests.sh b/tools/testing/selftests/vm/run_vmtests.sh
index b8e7f6f38d64..e780e76c26b8 100755
--- a/tools/testing/selftests/vm/run_vmtests.sh
+++ b/tools/testing/selftests/vm/run_vmtests.sh
@@ -120,13 +120,16 @@ run_test ./gup_test -a
 # Dump pages 0, 19, and 4096, using pin_user_pages:
 run_test ./gup_test -ct -F 0x1 0 19 0x1000
 
-run_test ./userfaultfd anon 20 16
-# Hugetlb tests require source and destination huge pages. Pass in half the
-# size ($half_ufd_size_MB), which is used for *each*.
-run_test ./userfaultfd hugetlb "$half_ufd_size_MB" 32
-run_test ./userfaultfd hugetlb_shared "$half_ufd_size_MB" 32 "$mnt"/uffd-test
-rm -f "$mnt"/uffd-test
-run_test ./userfaultfd shmem 20 16
+uffd_mods=("" ":dev")
+for mod in "${uffd_mods[@]}"; do
+	run_test ./userfaultfd anon${mod} 20 16
+	# Hugetlb tests require source and destination huge pages. Pass in half
+	# the size ($half_ufd_size_MB), which is used for *each*.
+	run_test ./userfaultfd hugetlb${mod} "$half_ufd_size_MB" 32
+	run_test ./userfaultfd hugetlb_shared${mod} "$half_ufd_size_MB" 32 "$mnt"/uffd-test
+	rm -f "$mnt"/uffd-test
+	run_test ./userfaultfd shmem${mod} 20 16
+done
 
 #cleanup
 umount "$mnt"
-- 
2.37.1.559.g78731f0fdb-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/5] userfaultfd: add /dev/userfaultfd for fine grained access control
  2022-08-08 17:56 ` [PATCH v5 2/5] userfaultfd: add /dev/userfaultfd for fine grained access control Axel Rasmussen
@ 2022-08-11  6:37   ` Mike Rapoport
  0 siblings, 0 replies; 10+ messages in thread
From: Mike Rapoport @ 2022-08-11  6:37 UTC (permalink / raw)
  To: Axel Rasmussen
  Cc: Alexander Viro, Andrew Morton, Dave Hansen, Dmitry V . Levin,
	Gleb Fotengauer-Malinovskiy, Hugh Dickins, Jan Kara,
	Jonathan Corbet, Mel Gorman, Mike Kravetz, Nadav Amit, Peter Xu,
	Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, zhangyi,
	linux-doc, linux-fsdevel, linux-kernel, linux-kselftest,
	linux-mm, linux-security-module

On Mon, Aug 08, 2022 at 10:56:11AM -0700, Axel Rasmussen wrote:
> Historically, it has been shown that intercepting kernel faults with
> userfaultfd (thereby forcing the kernel to wait for an arbitrary amount
> of time) can be exploited, or at least can make some kinds of exploits
> easier. So, in 37cd0575b8 "userfaultfd: add UFFD_USER_MODE_ONLY" we
> changed things so, in order for kernel faults to be handled by
> userfaultfd, either the process needs CAP_SYS_PTRACE, or this sysctl
> must be configured so that any unprivileged user can do it.
> 
> In a typical implementation of a hypervisor with live migration (take
> QEMU/KVM as one such example), we do indeed need to be able to handle
> kernel faults. But, both options above are less than ideal:
> 
> - Toggling the sysctl increases attack surface by allowing any
>   unprivileged user to do it.
> 
> - Granting the live migration process CAP_SYS_PTRACE gives it this
>   ability, but *also* the ability to "observe and control the
>   execution of another process [...], and examine and change [its]
>   memory and registers" (from ptrace(2)). This isn't something we need
>   or want to be able to do, so granting this permission violates the
>   "principle of least privilege".
> 
> This is all a long winded way to say: we want a more fine-grained way to
> grant access to userfaultfd, without granting other additional
> permissions at the same time.
> 
> To achieve this, add a /dev/userfaultfd misc device. This device
> provides an alternative to the userfaultfd(2) syscall for the creation
> of new userfaultfds. The idea is, any userfaultfds created this way will
> be able to handle kernel faults, without the caller having any special
> capabilities. Access to this mechanism is instead restricted using e.g.
> standard filesystem permissions.
> 
> Acked-by: Nadav Amit <namit@vmware.com>
> Acked-by: Peter Xu <peterx@redhat.com>
> Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>

Acked-by: Mike Rapoport <rppt@linux.ibm.com>

> ---
>  fs/userfaultfd.c                 | 73 +++++++++++++++++++++++++-------
>  include/uapi/linux/userfaultfd.h |  4 ++
>  2 files changed, 61 insertions(+), 16 deletions(-)

-- 
Sincerely yours,
Mike.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 3/5] userfaultfd: selftests: modify selftest to use /dev/userfaultfd
  2022-08-08 17:56 ` [PATCH v5 3/5] userfaultfd: selftests: modify selftest to use /dev/userfaultfd Axel Rasmussen
@ 2022-08-11  6:38   ` Mike Rapoport
  2022-08-17 19:53     ` Axel Rasmussen
  0 siblings, 1 reply; 10+ messages in thread
From: Mike Rapoport @ 2022-08-11  6:38 UTC (permalink / raw)
  To: Axel Rasmussen
  Cc: Alexander Viro, Andrew Morton, Dave Hansen, Dmitry V . Levin,
	Gleb Fotengauer-Malinovskiy, Hugh Dickins, Jan Kara,
	Jonathan Corbet, Mel Gorman, Mike Kravetz, Nadav Amit, Peter Xu,
	Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, zhangyi,
	linux-doc, linux-fsdevel, linux-kernel, linux-kselftest,
	linux-mm, linux-security-module

On Mon, Aug 08, 2022 at 10:56:12AM -0700, Axel Rasmussen wrote:
> We clearly want to ensure both userfaultfd(2) and /dev/userfaultfd keep
> working into the future, so just run the test twice, using each
> interface.
> 
> Instead of always testing both userfaultfd(2) and /dev/userfaultfd,
> let the user choose which to test.
> 
> As with other test features, change the behavior based on a new
> command line flag. Introduce the idea of "test mods", which are
> generic (not specific to a test type) modifications to the behavior of
> the test. This is sort of borrowed from this RFC patch series [1], but
> simplified a bit.
> 
> The benefit is, in "typical" configurations this test is somewhat slow
> (say, 30sec or something). Testing both clearly doubles it, so it may
> not always be desirable, as users are likely to use one or the other,
> but never both, in the "real world".
> 
> [1]: https://patchwork.kernel.org/project/linux-mm/patch/20201129004548.1619714-14-namit@vmware.com/
> 
> Acked-by: Peter Xu <peterx@redhat.com>
> Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>

With a few nits below

Acked-by: Mike Rapoport <rppt@linux.ibm.com>

> ---
>  tools/testing/selftests/vm/userfaultfd.c | 69 ++++++++++++++++++++----
>  1 file changed, 60 insertions(+), 9 deletions(-)
> 
> diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c
> index 7c3f1b0ab468..cae72867c173 100644
> --- a/tools/testing/selftests/vm/userfaultfd.c
> +++ b/tools/testing/selftests/vm/userfaultfd.c
> @@ -77,6 +77,11 @@ static int bounces;
>  #define TEST_SHMEM	3
>  static int test_type;
>  
> +#define UFFD_FLAGS	(O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY)
> +
> +/* test using /dev/userfaultfd, instead of userfaultfd(2) */
> +static bool test_dev_userfaultfd;
> +
>  /* exercise the test_uffdio_*_eexist every ALARM_INTERVAL_SECS */
>  #define ALARM_INTERVAL_SECS 10
>  static volatile bool test_uffdio_copy_eexist = true;
> @@ -125,6 +130,8 @@ struct uffd_stats {
>  const char *examples =
>      "# Run anonymous memory test on 100MiB region with 99999 bounces:\n"
>      "./userfaultfd anon 100 99999\n\n"
> +    "# Run the same anonymous memory test, but using /dev/userfaultfd:\n"
> +    "./userfaultfd anon:dev 100 99999\n\n"
>      "# Run share memory test on 1GiB region with 99 bounces:\n"
>      "./userfaultfd shmem 1000 99\n\n"
>      "# Run hugetlb memory test on 256MiB region with 50 bounces:\n"
> @@ -141,6 +148,14 @@ static void usage(void)
>  		"[hugetlbfs_file]\n\n");
>  	fprintf(stderr, "Supported <test type>: anon, hugetlb, "
>  		"hugetlb_shared, shmem\n\n");
> +	fprintf(stderr, "'Test mods' can be joined to the test type string with a ':'. "
> +		"Supported mods:\n");
> +	fprintf(stderr, "\tsyscall - Use userfaultfd(2) (default)\n");
> +	fprintf(stderr, "\tdev - Use /dev/userfaultfd instead of userfaultfd(2)\n");
> +	fprintf(stderr, "\nExample test mod usage:\n");
> +	fprintf(stderr, "# Run anonymous memory test with /dev/userfaultfd:\n");
> +	fprintf(stderr, "./userfaultfd anon:dev 100 99999\n\n");
> +
>  	fprintf(stderr, "Examples:\n\n");
>  	fprintf(stderr, "%s", examples);
>  	exit(1);
> @@ -154,12 +169,14 @@ static void usage(void)
>  			ret, __LINE__);				\
>  	} while (0)
>  
> -#define err(fmt, ...)				\
> +#define errexit(exitcode, fmt, ...)		\
>  	do {					\
>  		_err(fmt, ##__VA_ARGS__);	\
> -		exit(1);			\
> +		exit(exitcode);			\
>  	} while (0)
>  
> +#define err(fmt, ...) errexit(1, fmt, ##__VA_ARGS__)
> +
>  static void uffd_stats_reset(struct uffd_stats *uffd_stats,
>  			     unsigned long n_cpus)
>  {
> @@ -383,13 +400,29 @@ static void assert_expected_ioctls_present(uint64_t mode, uint64_t ioctls)
>  	}
>  }
>  
> +static int __userfaultfd_open_dev(void)
> +{
> +	int fd, _uffd = -1;

Nit: the initialization here is not necessary, _uffd is always set from
ioctl() return value.

> +
> +	fd = open("/dev/userfaultfd", O_RDWR | O_CLOEXEC);
> +	if (fd < 0)
> +		return -1;
> +
> +	_uffd = ioctl(fd, USERFAULTFD_IOC_NEW, UFFD_FLAGS);
> +	close(fd);
> +	return _uffd;
> +}
> +
>  static void userfaultfd_open(uint64_t *features)
>  {
>  	struct uffdio_api uffdio_api;
>  
> -	uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY);
> +	if (test_dev_userfaultfd)
> +		uffd = __userfaultfd_open_dev();
> +	else
> +		uffd = syscall(__NR_userfaultfd, UFFD_FLAGS);
>  	if (uffd < 0)
> -		err("userfaultfd syscall not available in this kernel");
> +		errexit(KSFT_SKIP, "creating userfaultfd failed");

I'm not sure if this should be KSFT_SKIP. If creation of uffd failed
because anything except ENOSYS I'd consider the test failing. 

>  	uffd_flags = fcntl(uffd, F_GETFD, NULL);
>  
>  	uffdio_api.api = UFFD_API;
> @@ -1584,8 +1617,6 @@ unsigned long default_huge_page_size(void)
>  
>  static void set_test_type(const char *type)
>  {
> -	uint64_t features = UFFD_API_FEATURES;
> -
>  	if (!strcmp(type, "anon")) {
>  		test_type = TEST_ANON;
>  		uffd_test_ops = &anon_uffd_test_ops;
> @@ -1603,9 +1634,29 @@ static void set_test_type(const char *type)
>  		test_type = TEST_SHMEM;
>  		uffd_test_ops = &shmem_uffd_test_ops;
>  		test_uffdio_minor = true;
> -	} else {
> -		err("Unknown test type: %s", type);
>  	}
> +}
> +
> +static void parse_test_type_arg(const char *raw_type)
> +{
> +	char *buf = strdup(raw_type);
> +	uint64_t features = UFFD_API_FEATURES;
> +
> +	while (buf) {
> +		const char *token = strsep(&buf, ":");
> +
> +		if (!test_type)
> +			set_test_type(token);
> +		else if (!strcmp(token, "dev"))
> +			test_dev_userfaultfd = true;
> +		else if (!strcmp(token, "syscall"))
> +			test_dev_userfaultfd = false;
> +		else
> +			err("unrecognized test mod '%s'", token);
> +	}
> +
> +	if (!test_type)
> +		err("failed to parse test type argument: '%s'", raw_type);
>  
>  	if (test_type == TEST_HUGETLB)
>  		page_size = default_huge_page_size();
> @@ -1653,7 +1704,7 @@ int main(int argc, char **argv)
>  		err("failed to arm SIGALRM");
>  	alarm(ALARM_INTERVAL_SECS);
>  
> -	set_test_type(argv[1]);
> +	parse_test_type_arg(argv[1]);
>  
>  	nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
>  	nr_pages_per_cpu = atol(argv[2]) * 1024*1024 / page_size /
> -- 
> 2.37.1.559.g78731f0fdb-goog
> 

-- 
Sincerely yours,
Mike.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 4/5] userfaultfd: update documentation to describe /dev/userfaultfd
  2022-08-08 17:56 ` [PATCH v5 4/5] userfaultfd: update documentation to describe /dev/userfaultfd Axel Rasmussen
@ 2022-08-11  6:49   ` Mike Rapoport
  0 siblings, 0 replies; 10+ messages in thread
From: Mike Rapoport @ 2022-08-11  6:49 UTC (permalink / raw)
  To: Axel Rasmussen
  Cc: Alexander Viro, Andrew Morton, Dave Hansen, Dmitry V . Levin,
	Gleb Fotengauer-Malinovskiy, Hugh Dickins, Jan Kara,
	Jonathan Corbet, Mel Gorman, Mike Kravetz, Nadav Amit, Peter Xu,
	Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, zhangyi,
	linux-doc, linux-fsdevel, linux-kernel, linux-kselftest,
	linux-mm, linux-security-module

On Mon, Aug 08, 2022 at 10:56:13AM -0700, Axel Rasmussen wrote:
> Explain the different ways to create a new userfaultfd, and how access
> control works for each way.
> 
> Acked-by: Peter Xu <peterx@redhat.com>
> Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>
> ---
>  Documentation/admin-guide/mm/userfaultfd.rst | 41 ++++++++++++++++++--
>  Documentation/admin-guide/sysctl/vm.rst      |  3 ++
>  2 files changed, 41 insertions(+), 3 deletions(-)
> 
> diff --git a/Documentation/admin-guide/mm/userfaultfd.rst b/Documentation/admin-guide/mm/userfaultfd.rst
> index 6528036093e1..a76c9dc1865b 100644
> --- a/Documentation/admin-guide/mm/userfaultfd.rst
> +++ b/Documentation/admin-guide/mm/userfaultfd.rst
> @@ -17,7 +17,10 @@ of the ``PROT_NONE+SIGSEGV`` trick.
>  Design
>  ======
>  
> -Userfaults are delivered and resolved through the ``userfaultfd`` syscall.
> +Userspace creates a new userfaultfd, initializes it, and registers one or more
> +regions of virtual memory with it. Then, any page faults which occur within the
> +region(s) result in a message being delivered to the userfaultfd, notifying
> +userspace of the fault.
>  
>  The ``userfaultfd`` (aside from registering and unregistering virtual
>  memory ranges) provides two primary functionalities:
> @@ -34,12 +37,11 @@ The real advantage of userfaults if compared to regular virtual memory
>  management of mremap/mprotect is that the userfaults in all their
>  operations never involve heavyweight structures like vmas (in fact the
>  ``userfaultfd`` runtime load never takes the mmap_lock for writing).
> -
>  Vmas are not suitable for page- (or hugepage) granular fault tracking
>  when dealing with virtual address spaces that could span
>  Terabytes. Too many vmas would be needed for that.
>  
> -The ``userfaultfd`` once opened by invoking the syscall, can also be
> +The ``userfaultfd``, once created, can also be
>  passed using unix domain sockets to a manager process, so the same
>  manager process could handle the userfaults of a multitude of
>  different processes without them being aware about what is going on
> @@ -50,6 +52,39 @@ is a corner case that would currently return ``-EBUSY``).
>  API
>  ===
>  
> +Creating a userfaultfd
> +----------------------
> +
> +There are two ways to create a new userfaultfd, each of which provide ways to
> +restrict access to this functionality (since historically userfaultfds which
> +handle kernel page faults have been a useful tool for exploiting the kernel).
> +
> +The first way, supported since userfaultfd was introduced, is the
> +userfaultfd(2) syscall. Access to this is controlled in several ways:
> +
> +- Any user can always create a userfaultfd which traps userspace page faults
> +  only. Such a userfaultfd can be created using the userfaultfd(2) syscall
> +  with the flag UFFD_USER_MODE_ONLY.
> +
> +- In order to also trap kernel page faults for the address space, then either

                                    I think "then" is excessive here ^

> +  the process needs the CAP_SYS_PTRACE capability, or the system must have
> +  vm.unprivileged_userfaultfd set to 1. By default, vm.unprivileged_userfaultfd
> +  is set to 0.
> +
> +The second way, added to the kernel more recently, is by opening and issuing a

Maybe:

..., is by opening /dev/userfaultfd and issuing USERFAULTFD_IOC_NEW ioctl
to it.

> +USERFAULTFD_IOC_NEW ioctl to /dev/userfaultfd. This method yields equivalent
> +userfaultfds to the userfaultfd(2) syscall.
> +
> +Unlike userfaultfd(2), access to /dev/userfaultfd is controlled via normal
> +filesystem permissions (user/group/mode), which gives fine grained access to
> +userfaultfd specifically, without also granting other unrelated privileges at
> +the same time (as e.g. granting CAP_SYS_PTRACE would do). Users who have access
> +to /dev/userfaultfd can always create userfaultfds that trap kernel page faults;
> +vm.unprivileged_userfaultfd is not considered.
> +
> +Initializing a userfaultfd
> +--------------------------
> +
>  When first opened the ``userfaultfd`` must be enabled invoking the
>  ``UFFDIO_API`` ioctl specifying a ``uffdio_api.api`` value set to ``UFFD_API`` (or
>  a later API version) which will specify the ``read/POLLIN`` protocol
> diff --git a/Documentation/admin-guide/sysctl/vm.rst b/Documentation/admin-guide/sysctl/vm.rst
> index f74f722ad702..b3e40b42e1b3 100644
> --- a/Documentation/admin-guide/sysctl/vm.rst
> +++ b/Documentation/admin-guide/sysctl/vm.rst
> @@ -927,6 +927,9 @@ calls without any restrictions.
>  
>  The default value is 0.
>  
> +Another way to control permissions for userfaultfd is to use
> +/dev/userfaultfd instead of userfaultfd(2). See
> +Documentation/admin-guide/mm/userfaultfd.rst.
>  
>  user_reserve_kbytes
>  ===================
> -- 
> 2.37.1.559.g78731f0fdb-goog
> 

-- 
Sincerely yours,
Mike.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 3/5] userfaultfd: selftests: modify selftest to use /dev/userfaultfd
  2022-08-11  6:38   ` Mike Rapoport
@ 2022-08-17 19:53     ` Axel Rasmussen
  0 siblings, 0 replies; 10+ messages in thread
From: Axel Rasmussen @ 2022-08-17 19:53 UTC (permalink / raw)
  To: Mike Rapoport
  Cc: Alexander Viro, Andrew Morton, Dave Hansen, Dmitry V . Levin,
	Gleb Fotengauer-Malinovskiy, Hugh Dickins, Jan Kara,
	Jonathan Corbet, Mel Gorman, Mike Kravetz, Nadav Amit, Peter Xu,
	Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, zhangyi,
	linux-doc, linux-fsdevel, LKML, Linuxkselftest, Linux MM,
	linux-security-module

On Wed, Aug 10, 2022 at 11:38 PM Mike Rapoport <rppt@kernel.org> wrote:
>
> On Mon, Aug 08, 2022 at 10:56:12AM -0700, Axel Rasmussen wrote:
> > We clearly want to ensure both userfaultfd(2) and /dev/userfaultfd keep
> > working into the future, so just run the test twice, using each
> > interface.
> >
> > Instead of always testing both userfaultfd(2) and /dev/userfaultfd,
> > let the user choose which to test.
> >
> > As with other test features, change the behavior based on a new
> > command line flag. Introduce the idea of "test mods", which are
> > generic (not specific to a test type) modifications to the behavior of
> > the test. This is sort of borrowed from this RFC patch series [1], but
> > simplified a bit.
> >
> > The benefit is, in "typical" configurations this test is somewhat slow
> > (say, 30sec or something). Testing both clearly doubles it, so it may
> > not always be desirable, as users are likely to use one or the other,
> > but never both, in the "real world".
> >
> > [1]: https://patchwork.kernel.org/project/linux-mm/patch/20201129004548.1619714-14-namit@vmware.com/
> >
> > Acked-by: Peter Xu <peterx@redhat.com>
> > Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>
>
> With a few nits below
>
> Acked-by: Mike Rapoport <rppt@linux.ibm.com>

Thanks for reviewing, Mike!

>
> > ---
> >  tools/testing/selftests/vm/userfaultfd.c | 69 ++++++++++++++++++++----
> >  1 file changed, 60 insertions(+), 9 deletions(-)
> >
> > diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c
> > index 7c3f1b0ab468..cae72867c173 100644
> > --- a/tools/testing/selftests/vm/userfaultfd.c
> > +++ b/tools/testing/selftests/vm/userfaultfd.c
> > @@ -77,6 +77,11 @@ static int bounces;
> >  #define TEST_SHMEM   3
> >  static int test_type;
> >
> > +#define UFFD_FLAGS   (O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY)
> > +
> > +/* test using /dev/userfaultfd, instead of userfaultfd(2) */
> > +static bool test_dev_userfaultfd;
> > +
> >  /* exercise the test_uffdio_*_eexist every ALARM_INTERVAL_SECS */
> >  #define ALARM_INTERVAL_SECS 10
> >  static volatile bool test_uffdio_copy_eexist = true;
> > @@ -125,6 +130,8 @@ struct uffd_stats {
> >  const char *examples =
> >      "# Run anonymous memory test on 100MiB region with 99999 bounces:\n"
> >      "./userfaultfd anon 100 99999\n\n"
> > +    "# Run the same anonymous memory test, but using /dev/userfaultfd:\n"
> > +    "./userfaultfd anon:dev 100 99999\n\n"
> >      "# Run share memory test on 1GiB region with 99 bounces:\n"
> >      "./userfaultfd shmem 1000 99\n\n"
> >      "# Run hugetlb memory test on 256MiB region with 50 bounces:\n"
> > @@ -141,6 +148,14 @@ static void usage(void)
> >               "[hugetlbfs_file]\n\n");
> >       fprintf(stderr, "Supported <test type>: anon, hugetlb, "
> >               "hugetlb_shared, shmem\n\n");
> > +     fprintf(stderr, "'Test mods' can be joined to the test type string with a ':'. "
> > +             "Supported mods:\n");
> > +     fprintf(stderr, "\tsyscall - Use userfaultfd(2) (default)\n");
> > +     fprintf(stderr, "\tdev - Use /dev/userfaultfd instead of userfaultfd(2)\n");
> > +     fprintf(stderr, "\nExample test mod usage:\n");
> > +     fprintf(stderr, "# Run anonymous memory test with /dev/userfaultfd:\n");
> > +     fprintf(stderr, "./userfaultfd anon:dev 100 99999\n\n");
> > +
> >       fprintf(stderr, "Examples:\n\n");
> >       fprintf(stderr, "%s", examples);
> >       exit(1);
> > @@ -154,12 +169,14 @@ static void usage(void)
> >                       ret, __LINE__);                         \
> >       } while (0)
> >
> > -#define err(fmt, ...)                                \
> > +#define errexit(exitcode, fmt, ...)          \
> >       do {                                    \
> >               _err(fmt, ##__VA_ARGS__);       \
> > -             exit(1);                        \
> > +             exit(exitcode);                 \
> >       } while (0)
> >
> > +#define err(fmt, ...) errexit(1, fmt, ##__VA_ARGS__)
> > +
> >  static void uffd_stats_reset(struct uffd_stats *uffd_stats,
> >                            unsigned long n_cpus)
> >  {
> > @@ -383,13 +400,29 @@ static void assert_expected_ioctls_present(uint64_t mode, uint64_t ioctls)
> >       }
> >  }
> >
> > +static int __userfaultfd_open_dev(void)
> > +{
> > +     int fd, _uffd = -1;
>
> Nit: the initialization here is not necessary, _uffd is always set from
> ioctl() return value.

Agreed.

>
> > +
> > +     fd = open("/dev/userfaultfd", O_RDWR | O_CLOEXEC);
> > +     if (fd < 0)
> > +             return -1;
> > +
> > +     _uffd = ioctl(fd, USERFAULTFD_IOC_NEW, UFFD_FLAGS);
> > +     close(fd);
> > +     return _uffd;
> > +}
> > +
> >  static void userfaultfd_open(uint64_t *features)
> >  {
> >       struct uffdio_api uffdio_api;
> >
> > -     uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY);
> > +     if (test_dev_userfaultfd)
> > +             uffd = __userfaultfd_open_dev();
> > +     else
> > +             uffd = syscall(__NR_userfaultfd, UFFD_FLAGS);
> >       if (uffd < 0)
> > -             err("userfaultfd syscall not available in this kernel");
> > +             errexit(KSFT_SKIP, "creating userfaultfd failed");
>
> I'm not sure if this should be KSFT_SKIP. If creation of uffd failed
> because anything except ENOSYS I'd consider the test failing.

Agreed, but looking at it I think it's more complicated.

In __userfaultfd_open_dev, I think:
- If we fail to open /dev/userfaultfd for any reason, that's a KSFT_SKIP
- If USERFAULTFD_IOC_NEW returns ENOTTY that's a KSFT_SKIP, otherwise
a real test failure.

But then for syscall(__NR_userfaultfd, ...), I think ENOSYS is a
KSFT_SKIP, but any other errno is a real test failure.

I'll send an updated version which has those semantics. I think to do
so we need to move some of the error handling into
__userfaultfd_open_dev, instead of having its caller do it.

>
> >       uffd_flags = fcntl(uffd, F_GETFD, NULL);
> >
> >       uffdio_api.api = UFFD_API;
> > @@ -1584,8 +1617,6 @@ unsigned long default_huge_page_size(void)
> >
> >  static void set_test_type(const char *type)
> >  {
> > -     uint64_t features = UFFD_API_FEATURES;
> > -
> >       if (!strcmp(type, "anon")) {
> >               test_type = TEST_ANON;
> >               uffd_test_ops = &anon_uffd_test_ops;
> > @@ -1603,9 +1634,29 @@ static void set_test_type(const char *type)
> >               test_type = TEST_SHMEM;
> >               uffd_test_ops = &shmem_uffd_test_ops;
> >               test_uffdio_minor = true;
> > -     } else {
> > -             err("Unknown test type: %s", type);
> >       }
> > +}
> > +
> > +static void parse_test_type_arg(const char *raw_type)
> > +{
> > +     char *buf = strdup(raw_type);
> > +     uint64_t features = UFFD_API_FEATURES;
> > +
> > +     while (buf) {
> > +             const char *token = strsep(&buf, ":");
> > +
> > +             if (!test_type)
> > +                     set_test_type(token);
> > +             else if (!strcmp(token, "dev"))
> > +                     test_dev_userfaultfd = true;
> > +             else if (!strcmp(token, "syscall"))
> > +                     test_dev_userfaultfd = false;
> > +             else
> > +                     err("unrecognized test mod '%s'", token);
> > +     }
> > +
> > +     if (!test_type)
> > +             err("failed to parse test type argument: '%s'", raw_type);
> >
> >       if (test_type == TEST_HUGETLB)
> >               page_size = default_huge_page_size();
> > @@ -1653,7 +1704,7 @@ int main(int argc, char **argv)
> >               err("failed to arm SIGALRM");
> >       alarm(ALARM_INTERVAL_SECS);
> >
> > -     set_test_type(argv[1]);
> > +     parse_test_type_arg(argv[1]);
> >
> >       nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
> >       nr_pages_per_cpu = atol(argv[2]) * 1024*1024 / page_size /
> > --
> > 2.37.1.559.g78731f0fdb-goog
> >
>
> --
> Sincerely yours,
> Mike.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2022-08-17 19:54 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-08 17:56 [PATCH v5 0/5] userfaultfd: add /dev/userfaultfd for fine grained access control Axel Rasmussen
2022-08-08 17:56 ` [PATCH v5 1/5] selftests: vm: add hugetlb_shared userfaultfd test to run_vmtests.sh Axel Rasmussen
2022-08-08 17:56 ` [PATCH v5 2/5] userfaultfd: add /dev/userfaultfd for fine grained access control Axel Rasmussen
2022-08-11  6:37   ` Mike Rapoport
2022-08-08 17:56 ` [PATCH v5 3/5] userfaultfd: selftests: modify selftest to use /dev/userfaultfd Axel Rasmussen
2022-08-11  6:38   ` Mike Rapoport
2022-08-17 19:53     ` Axel Rasmussen
2022-08-08 17:56 ` [PATCH v5 4/5] userfaultfd: update documentation to describe /dev/userfaultfd Axel Rasmussen
2022-08-11  6:49   ` Mike Rapoport
2022-08-08 17:56 ` [PATCH v5 5/5] selftests: vm: add /dev/userfaultfd test cases to run_vmtests.sh Axel Rasmussen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.