[RFC PATCH 07/10] uaccess: Fail user nofault helpers quietly when current->mm is NULL
From: James Houghton
Date: Mon Oct 26 2026 - 18:01:29 EST
The user nofault helpers (copy_{from,to}_user_nofault(),
strncpy_from_user_nofault() and strnlen_user_nofault()) are meant to be
callable from any context, e.g. from BPF programs, kprobes or perf, which
may run on behalf of a kernel thread or of a task that has already gone
through exit_mm(). In such a context, there is no user memory to access,
and the right answer is simply to fail.
Since the previous commit, the uaccess helpers they use warn when
current->mm is NULL, e.g. bpf_probe_read_user_str() from a BPF program
attached to a function called by a kernel thread would now trip the
WARN_ON_ONCE() in strncpy_from_user().
Check for a NULL current->mm up front in these helpers, and fail without
warning and without attempting the access.
On x86, copy_from_user_nofault() was already refusing such accesses, as
nmi_uaccess_okay() returns false when current->mm is not the loaded mm.
For the same reason, make copy_to_user_iter_nofault() use
should_fail_usercopy() directly, leaving the NULL mm check to
copy_to_user_nofault().
Signed-off-by: James Houghton <jthoughton@xxxxxxxxxx>
---
lib/iov_iter.c | 2 +-
mm/maccess.c | 17 +++++++++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 3d6ecb50d778..21e2ccf1e8f2 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -33,7 +33,7 @@ size_t copy_to_user_iter_nofault(void __user *iter_to, size_t progress,
{
ssize_t res;
- if (uaccess_should_fail())
+ if (should_fail_usercopy())
return len;
from += progress;
diff --git a/mm/maccess.c b/mm/maccess.c
index 486559d68858..678bb6165b39 100644
--- a/mm/maccess.c
+++ b/mm/maccess.c
@@ -121,6 +121,14 @@ long copy_from_user_nofault(void *dst, const void __user *src, size_t size)
{
long ret = -EFAULT;
+ /*
+ * The user nofault helpers may be called from any context, including
+ * from tasks that have no user memory to access (e.g. kernel threads).
+ * Fail quietly instead of tripping the warning in the uaccess helpers.
+ */
+ if (!current->mm)
+ return ret;
+
if (!__access_ok(src, size))
return ret;
@@ -150,6 +158,9 @@ long copy_to_user_nofault(void __user *dst, const void *src, size_t size)
{
long ret = -EFAULT;
+ if (!current->mm)
+ return ret;
+
if (access_ok(dst, size)) {
pagefault_disable();
ret = __copy_to_user_inatomic(dst, src, size);
@@ -188,6 +199,9 @@ long strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
if (unlikely(count <= 0))
return 0;
+ if (!current->mm)
+ return -EFAULT;
+
pagefault_disable();
ret = strncpy_from_user(dst, unsafe_addr, count);
pagefault_enable();
@@ -222,6 +236,9 @@ long strnlen_user_nofault(const void __user *unsafe_addr, long count)
{
int ret;
+ if (!current->mm)
+ return 0;
+
pagefault_disable();
ret = strnlen_user(unsafe_addr, count);
pagefault_enable();
--
2.56.0.307.g5fe7bd680c-goog