From: Masami Hiramatsu <mhiramat@kernel.org>
To: Steven Rostedt <rostedt@goodmis.org>,
Linus Torvalds <torvalds@linux-foundation.org>,
Shuah Khan <shuah@kernel.org>
Cc: mhiramat@kernel.org, linux-kernel@vger.kernel.org,
Andy Lutomirski <luto@amacapital.net>,
Ingo Molnar <mingo@kernel.org>,
Andrew Morton <akpm@linux-foundation.org>,
Changbin Du <changbin.du@gmail.com>, Jann Horn <jannh@google.com>,
Kees Cook <keescook@chromium.org>,
Andy Lutomirski <luto@kernel.org>,
Alexei Starovoitov <alexei.starovoitov@gmail.com>,
Nadav Amit <namit@vmware.com>,
Peter Zijlstra <peterz@infradead.org>,
Joel Fernandes <joel@joelfernandes.org>,
yhs@fb.com
Subject: [PATCH v5 3/6] uaccess: Add non-pagefault user-space read functions
Date: Fri, 1 Mar 2019 01:03:54 +0900 [thread overview]
Message-ID: <155136983467.2968.13980231890937828195.stgit@devbox> (raw)
In-Reply-To: <155136974478.2968.3105123100519786079.stgit@devbox>
Add probe_user_read(), strncpy_from_unsafe_user() and
strnlen_unsafe_user() which allows caller to access user-space
in IRQ context.
Current probe_kernel_read() and strncpy_from_unsafe() are
not available for user-space memory, because it sets
KERNEL_DS while accessing data. On some arch, user address
space and kernel address space can be co-exist, but others
can not. In that case, setting KERNEL_DS means given
address is treated as a kernel address space.
Also strnlen_user() is only available from user context since
it can sleep if pagefault is enabled.
To access user-space memory without pagefault, we need
these new functions which sets USER_DS while accessing
the data.
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
---
Changes in v5:
- Simplify probe_user_read() (Thanks, Peter!)
- Add strnlen_unsafe_user()
Changes in v3:
- Use user_access_ok() for probe_user_read().
Changes in v2:
- Simplify strncpy_from_unsafe_user() using strncpy_from_user()
according to Linus's suggestion.
- Simplify probe_user_read() not using intermediate function.
---
include/linux/uaccess.h | 14 +++++
mm/maccess.c | 122 +++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 130 insertions(+), 6 deletions(-)
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 1afd9dfabe67..5be7f9adb418 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -258,6 +258,17 @@ extern long probe_kernel_read(void *dst, const void *src, size_t size);
extern long __probe_kernel_read(void *dst, const void *src, size_t size);
/*
+ * probe_user_read(): safely attempt to read from a location in user space
+ * @dst: pointer to the buffer that shall take the data
+ * @src: address to read from
+ * @size: size of the data chunk
+ *
+ * Safely read from address @src to the buffer at @dst. If a kernel fault
+ * happens, handle that and return -EFAULT.
+ */
+extern long probe_user_read(void *dst, const void __user *src, size_t size);
+
+/*
* probe_kernel_write(): safely attempt to write to a location
* @dst: address to write to
* @src: pointer to the data that shall be written
@@ -270,6 +281,9 @@ extern long notrace probe_kernel_write(void *dst, const void *src, size_t size);
extern long notrace __probe_kernel_write(void *dst, const void *src, size_t size);
extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
+extern long strncpy_from_unsafe_user(char *dst, const void __user *unsafe_addr,
+ long count);
+extern long strnlen_unsafe_user(const void __user *unsafe_addr, long count);
/**
* probe_kernel_address(): safely attempt to read from a location
diff --git a/mm/maccess.c b/mm/maccess.c
index ec00be51a24f..d1b2ec78d9ef 100644
--- a/mm/maccess.c
+++ b/mm/maccess.c
@@ -5,8 +5,20 @@
#include <linux/mm.h>
#include <linux/uaccess.h>
+static __always_inline long
+probe_read_common(void *dst, const void __user *src, size_t size)
+{
+ long ret;
+
+ pagefault_disable();
+ ret = __copy_from_user_inatomic(dst, src, size);
+ pagefault_enable();
+
+ return ret ? -EFAULT : 0;
+}
+
/**
- * probe_kernel_read(): safely attempt to read from a location
+ * probe_kernel_read(): safely attempt to read from a kernel-space location
* @dst: pointer to the buffer that shall take the data
* @src: address to read from
* @size: size of the data chunk
@@ -29,17 +41,45 @@ long __probe_kernel_read(void *dst, const void *src, size_t size)
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);
- pagefault_disable();
- ret = __copy_from_user_inatomic(dst,
- (__force const void __user *)src, size);
- pagefault_enable();
+ ret = probe_read_common(dst, (__force const void __user *)src, size);
set_fs(old_fs);
- return ret ? -EFAULT : 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(probe_kernel_read);
/**
+ * probe_user_read(): safely attempt to read from a user-space location
+ * @dst: pointer to the buffer that shall take the data
+ * @src: address to read from. This must be a user address.
+ * @size: size of the data chunk
+ *
+ * Safely read from user address @src to the buffer at @dst. If a kernel fault
+ * happens, handle that and return -EFAULT.
+ */
+
+long __weak probe_user_read(void *dst, const void __user *src, size_t size)
+ __attribute__((alias("__probe_user_read")));
+
+long __probe_user_read(void *dst, const void __user *src, size_t size)
+{
+ long ret = -EFAULT;
+ mm_segment_t old_fs = get_fs();
+
+ /*
+ * Since this can be called in IRQ context, we carefully set the
+ * USER_DS and use user_access_ok() which checks segment setting
+ * instead of task context.
+ */
+ set_fs(USER_DS);
+ if (user_access_ok(src, size))
+ ret = probe_read_common(dst, src, size);
+ set_fs(old_fs);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(probe_user_read);
+
+/**
* probe_kernel_write(): safely attempt to write to a location
* @dst: address to write to
* @src: pointer to the data that shall be written
@@ -66,6 +106,7 @@ long __probe_kernel_write(void *dst, const void *src, size_t size)
}
EXPORT_SYMBOL_GPL(probe_kernel_write);
+
/**
* strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
* @dst: Destination address, in kernel space. This buffer must be at
@@ -105,3 +146,72 @@ long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
return ret ? -EFAULT : src - unsafe_addr;
}
+
+/**
+ * strncpy_from_unsafe_user: - Copy a NUL terminated string from unsafe user
+ * address.
+ * @dst: Destination address, in kernel space. This buffer must be at
+ * least @count bytes long.
+ * @unsafe_addr: Unsafe user address.
+ * @count: Maximum number of bytes to copy, including the trailing NUL.
+ *
+ * Copies a NUL-terminated string from unsafe user address to kernel buffer.
+ *
+ * On success, returns the length of the string INCLUDING the trailing NUL.
+ *
+ * If access fails, returns -EFAULT (some data may have been copied
+ * and the trailing NUL added).
+ *
+ * If @count is smaller than the length of the string, copies @count-1 bytes,
+ * sets the last byte of @dst buffer to NUL and returns @count.
+ */
+long strncpy_from_unsafe_user(char *dst, const void __user *unsafe_addr,
+ long count)
+{
+ mm_segment_t old_fs = get_fs();
+ long ret;
+
+ if (unlikely(count <= 0))
+ return 0;
+
+ set_fs(USER_DS);
+ pagefault_disable();
+ ret = strncpy_from_user(dst, unsafe_addr, count);
+ pagefault_enable();
+ set_fs(old_fs);
+ if (ret >= count) {
+ ret = count;
+ dst[ret - 1] = '\0';
+ } else if (ret > 0)
+ ret++;
+ return ret;
+}
+
+/**
+ * strnlen_unsafe_user: - Get the size of a user string INCLUDING final NUL.
+ * @unsafe_addr: The string to measure.
+ * @count: Maximum count (including NUL character)
+ *
+ * Get the size of a NUL-terminated string in user space without pagefault.
+ *
+ * Returns the size of the string INCLUDING the terminating NUL.
+ *
+ * If the string is too long, returns a number larger than @count. User
+ * has to check the return value against "> count".
+ * On exception (or invalid count), returns 0.
+ *
+ * Unlike strnlen_user, this can be used from IRQ handler etc. because
+ * it disables pagefaults.
+ */
+long strnlen_unsafe_user(const void __user *unsafe_addr, long count)
+{
+ mm_segment_t old_fs = get_fs();
+ int ret;
+
+ set_fs(USER_DS);
+ pagefault_disable();
+ ret = strnlen_user(unsafe_addr, count);
+ pagefault_enable();
+ set_fs(old_fs);
+ return ret;
+}
next prev parent reply other threads:[~2019-02-28 16:04 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-02-28 16:02 [PATCH v5 0/6] tracing/probes: uaccess: Add support user-space access Masami Hiramatsu
2019-02-28 16:02 ` [PATCH v5 1/6] uaccess: Add user_access_ok() Masami Hiramatsu
2019-02-28 16:03 ` [PATCH v5 2/6] uaccess: Use user_access_ok() in user_access_begin() Masami Hiramatsu
2019-03-03 17:39 ` [uaccess] 780464aed0: WARNING:at_arch/x86/include/asm/uaccess.h:#strnlen_user/0x kernel test robot
2019-03-03 17:39 ` kernel test robot
2019-03-03 19:53 ` Linus Torvalds
2019-03-03 19:53 ` Linus Torvalds
2019-03-04 1:14 ` Masami Hiramatsu
2019-03-04 1:14 ` Masami Hiramatsu
2019-03-04 2:37 ` Linus Torvalds
2019-03-04 2:37 ` Linus Torvalds
2019-03-04 9:06 ` Masami Hiramatsu
2019-03-04 9:06 ` Masami Hiramatsu
2019-03-04 15:16 ` Masami Hiramatsu
2019-03-04 15:16 ` Masami Hiramatsu
2019-03-04 15:58 ` Jann Horn
2019-03-04 15:58 ` Jann Horn
2019-03-04 18:59 ` Linus Torvalds
2019-03-04 18:59 ` Linus Torvalds
2019-03-05 2:36 ` Masami Hiramatsu
2019-03-05 2:36 ` Masami Hiramatsu
2019-03-05 8:22 ` Masami Hiramatsu
2019-03-05 8:22 ` Masami Hiramatsu
2019-03-05 9:01 ` Masami Hiramatsu
2019-03-05 9:01 ` Masami Hiramatsu
2019-03-05 9:07 ` Peter Zijlstra
2019-03-05 9:07 ` Peter Zijlstra
2019-03-05 13:58 ` Masami Hiramatsu
2019-03-05 13:58 ` Masami Hiramatsu
2019-03-05 14:53 ` Peter Zijlstra
2019-03-05 14:53 ` Peter Zijlstra
2019-03-05 15:18 ` Masami Hiramatsu
2019-03-05 15:18 ` Masami Hiramatsu
2019-03-04 3:20 ` Rong Chen
2019-03-04 3:20 ` [LKP] " Rong Chen
2019-02-28 16:03 ` Masami Hiramatsu [this message]
2019-02-28 22:49 ` [PATCH v5 3/6] uaccess: Add non-pagefault user-space read functions Yonghong Song
2019-03-01 2:29 ` Masami Hiramatsu
2019-03-01 6:30 ` Yonghong Song
2019-02-28 16:04 ` [PATCH v5 4/6] tracing/probe: Add ustring type for user-space string Masami Hiramatsu
2019-02-28 16:04 ` [PATCH v5 5/6] tracing/probe: Support user-space dereference Masami Hiramatsu
2019-02-28 16:05 ` [PATCH v5 6/6] selftests/ftrace: Add user-memory access syntax testcase Masami Hiramatsu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=155136983467.2968.13980231890937828195.stgit@devbox \
--to=mhiramat@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=alexei.starovoitov@gmail.com \
--cc=changbin.du@gmail.com \
--cc=jannh@google.com \
--cc=joel@joelfernandes.org \
--cc=keescook@chromium.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luto@amacapital.net \
--cc=luto@kernel.org \
--cc=mingo@kernel.org \
--cc=namit@vmware.com \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
--cc=shuah@kernel.org \
--cc=torvalds@linux-foundation.org \
--cc=yhs@fb.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.