Skip to content

Commit

Permalink
uaccess: Add non-pagefault user-space write function
Browse files Browse the repository at this point in the history
[ Upstream commit 1d1585c ]

Commit 3d70818 ("uaccess: Add non-pagefault user-space read functions")
missed to add probe write function, therefore factor out a probe_write_common()
helper with most logic of probe_kernel_write() except setting KERNEL_DS, and
add a new probe_user_write() helper so it can be used from BPF side.

Again, on some archs, the user address space and kernel address space can
co-exist and be overlapping, so in such case, setting KERNEL_DS would mean
that the given address is treated as being in kernel address space.

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Andrii Nakryiko <andriin@fb.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Link: https://lore.kernel.org/bpf/9df2542e68141bfa3addde631441ee45503856a8.1572649915.git.daniel@iogearbox.net
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
borkmann authored and gregkh committed Sep 12, 2020
1 parent 36fd2e4 commit f43434e
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 4 deletions.
12 changes: 12 additions & 0 deletions include/linux/uaccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@ extern long probe_user_read(void *dst, const void __user *src, size_t size);
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);

/*
* probe_user_write(): safely attempt to write to a location in user space
* @dst: address to write to
* @src: pointer to the data that shall be written
* @size: size of the data chunk
*
* Safely write to address @dst from the buffer at @src. If a kernel fault
* happens, handle that and return -EFAULT.
*/
extern long notrace probe_user_write(void __user *dst, const void *src, size_t size);
extern long notrace __probe_user_write(void __user *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);
Expand Down
45 changes: 41 additions & 4 deletions mm/maccess.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ probe_read_common(void *dst, const void __user *src, size_t size)
return ret ? -EFAULT : 0;
}

static __always_inline long
probe_write_common(void __user *dst, const void *src, size_t size)
{
long ret;

pagefault_disable();
ret = __copy_to_user_inatomic(dst, src, size);
pagefault_enable();

return ret ? -EFAULT : 0;
}

/**
* probe_kernel_read(): safely attempt to read from a kernel-space location
* @dst: pointer to the buffer that shall take the data
Expand Down Expand Up @@ -84,6 +96,7 @@ EXPORT_SYMBOL_GPL(probe_user_read);
* Safely write to address @dst from the buffer at @src. If a kernel fault
* happens, handle that and return -EFAULT.
*/

long __weak probe_kernel_write(void *dst, const void *src, size_t size)
__attribute__((alias("__probe_kernel_write")));

Expand All @@ -93,15 +106,39 @@ long __probe_kernel_write(void *dst, const void *src, size_t size)
mm_segment_t old_fs = get_fs();

set_fs(KERNEL_DS);
pagefault_disable();
ret = __copy_to_user_inatomic((__force void __user *)dst, src, size);
pagefault_enable();
ret = probe_write_common((__force void __user *)dst, src, size);
set_fs(old_fs);

return ret ? -EFAULT : 0;
return ret;
}
EXPORT_SYMBOL_GPL(probe_kernel_write);

/**
* probe_user_write(): safely attempt to write to a user-space location
* @dst: address to write to
* @src: pointer to the data that shall be written
* @size: size of the data chunk
*
* Safely write to address @dst from the buffer at @src. If a kernel fault
* happens, handle that and return -EFAULT.
*/

long __weak probe_user_write(void __user *dst, const void *src, size_t size)
__attribute__((alias("__probe_user_write")));

long __probe_user_write(void __user *dst, const void *src, size_t size)
{
long ret = -EFAULT;
mm_segment_t old_fs = get_fs();

set_fs(USER_DS);
if (access_ok(VERIFY_WRITE, dst, size))
ret = probe_write_common(dst, src, size);
set_fs(old_fs);

return ret;
}
EXPORT_SYMBOL_GPL(probe_user_write);

/**
* strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
Expand Down

0 comments on commit f43434e

Please sign in to comment.