Commit 8d92db5c authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Linus Torvalds

bpf: rework the compat kernel probe handling

Instead of using the dangerous probe_kernel_read and strncpy_from_unsafe
helpers, rework the compat probes to check if an address is a kernel or
userspace one, and then use the low-level kernel or user probe helper
shared by the proper kernel and user probe helpers.  This slightly
changes behavior as the compat probe on a user address doesn't check
the lockdown flags, just as the pure user probes do.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/20200521152301.2587579-14-hch@lst.deSigned-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 19c8d8ac
...@@ -136,17 +136,23 @@ static const struct bpf_func_proto bpf_override_return_proto = { ...@@ -136,17 +136,23 @@ static const struct bpf_func_proto bpf_override_return_proto = {
}; };
#endif #endif
BPF_CALL_3(bpf_probe_read_user, void *, dst, u32, size, static __always_inline int
const void __user *, unsafe_ptr) bpf_probe_read_user_common(void *dst, u32 size, const void __user *unsafe_ptr)
{ {
int ret = probe_user_read(dst, unsafe_ptr, size); int ret;
ret = probe_user_read(dst, unsafe_ptr, size);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
memset(dst, 0, size); memset(dst, 0, size);
return ret; return ret;
} }
BPF_CALL_3(bpf_probe_read_user, void *, dst, u32, size,
const void __user *, unsafe_ptr)
{
return bpf_probe_read_user_common(dst, size, unsafe_ptr);
}
const struct bpf_func_proto bpf_probe_read_user_proto = { const struct bpf_func_proto bpf_probe_read_user_proto = {
.func = bpf_probe_read_user, .func = bpf_probe_read_user,
.gpl_only = true, .gpl_only = true,
...@@ -156,17 +162,24 @@ const struct bpf_func_proto bpf_probe_read_user_proto = { ...@@ -156,17 +162,24 @@ const struct bpf_func_proto bpf_probe_read_user_proto = {
.arg3_type = ARG_ANYTHING, .arg3_type = ARG_ANYTHING,
}; };
BPF_CALL_3(bpf_probe_read_user_str, void *, dst, u32, size, static __always_inline int
const void __user *, unsafe_ptr) bpf_probe_read_user_str_common(void *dst, u32 size,
const void __user *unsafe_ptr)
{ {
int ret = strncpy_from_user_nofault(dst, unsafe_ptr, size); int ret;
ret = strncpy_from_user_nofault(dst, unsafe_ptr, size);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
memset(dst, 0, size); memset(dst, 0, size);
return ret; return ret;
} }
BPF_CALL_3(bpf_probe_read_user_str, void *, dst, u32, size,
const void __user *, unsafe_ptr)
{
return bpf_probe_read_user_str_common(dst, size, unsafe_ptr);
}
const struct bpf_func_proto bpf_probe_read_user_str_proto = { const struct bpf_func_proto bpf_probe_read_user_str_proto = {
.func = bpf_probe_read_user_str, .func = bpf_probe_read_user_str,
.gpl_only = true, .gpl_only = true,
...@@ -177,25 +190,25 @@ const struct bpf_func_proto bpf_probe_read_user_str_proto = { ...@@ -177,25 +190,25 @@ const struct bpf_func_proto bpf_probe_read_user_str_proto = {
}; };
static __always_inline int static __always_inline int
bpf_probe_read_kernel_common(void *dst, u32 size, const void *unsafe_ptr, bpf_probe_read_kernel_common(void *dst, u32 size, const void *unsafe_ptr)
const bool compat)
{ {
int ret = security_locked_down(LOCKDOWN_BPF_READ); int ret = security_locked_down(LOCKDOWN_BPF_READ);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
goto out; goto fail;
ret = compat ? probe_kernel_read(dst, unsafe_ptr, size) : ret = probe_kernel_read_strict(dst, unsafe_ptr, size);
probe_kernel_read_strict(dst, unsafe_ptr, size);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
out: goto fail;
memset(dst, 0, size); return ret;
fail:
memset(dst, 0, size);
return ret; return ret;
} }
BPF_CALL_3(bpf_probe_read_kernel, void *, dst, u32, size, BPF_CALL_3(bpf_probe_read_kernel, void *, dst, u32, size,
const void *, unsafe_ptr) const void *, unsafe_ptr)
{ {
return bpf_probe_read_kernel_common(dst, size, unsafe_ptr, false); return bpf_probe_read_kernel_common(dst, size, unsafe_ptr);
} }
const struct bpf_func_proto bpf_probe_read_kernel_proto = { const struct bpf_func_proto bpf_probe_read_kernel_proto = {
...@@ -207,50 +220,37 @@ const struct bpf_func_proto bpf_probe_read_kernel_proto = { ...@@ -207,50 +220,37 @@ const struct bpf_func_proto bpf_probe_read_kernel_proto = {
.arg3_type = ARG_ANYTHING, .arg3_type = ARG_ANYTHING,
}; };
BPF_CALL_3(bpf_probe_read_compat, void *, dst, u32, size,
const void *, unsafe_ptr)
{
return bpf_probe_read_kernel_common(dst, size, unsafe_ptr, true);
}
static const struct bpf_func_proto bpf_probe_read_compat_proto = {
.func = bpf_probe_read_compat,
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
};
static __always_inline int static __always_inline int
bpf_probe_read_kernel_str_common(void *dst, u32 size, const void *unsafe_ptr, bpf_probe_read_kernel_str_common(void *dst, u32 size, const void *unsafe_ptr)
const bool compat)
{ {
int ret = security_locked_down(LOCKDOWN_BPF_READ); int ret = security_locked_down(LOCKDOWN_BPF_READ);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
goto out; goto fail;
/* /*
* The strncpy_from_unsafe_*() call will likely not fill the entire * The strncpy_from_kernel_nofault() call will likely not fill the
* buffer, but that's okay in this circumstance as we're probing * entire buffer, but that's okay in this circumstance as we're probing
* arbitrary memory anyway similar to bpf_probe_read_*() and might * arbitrary memory anyway similar to bpf_probe_read_*() and might
* as well probe the stack. Thus, memory is explicitly cleared * as well probe the stack. Thus, memory is explicitly cleared
* only in error case, so that improper users ignoring return * only in error case, so that improper users ignoring return
* code altogether don't copy garbage; otherwise length of string * code altogether don't copy garbage; otherwise length of string
* is returned that can be used for bpf_perf_event_output() et al. * is returned that can be used for bpf_perf_event_output() et al.
*/ */
ret = compat ? strncpy_from_unsafe(dst, unsafe_ptr, size) : ret = strncpy_from_kernel_nofault(dst, unsafe_ptr, size);
strncpy_from_kernel_nofault(dst, unsafe_ptr, size);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
out: goto fail;
memset(dst, 0, size);
return 0;
fail:
memset(dst, 0, size);
return ret; return ret;
} }
BPF_CALL_3(bpf_probe_read_kernel_str, void *, dst, u32, size, BPF_CALL_3(bpf_probe_read_kernel_str, void *, dst, u32, size,
const void *, unsafe_ptr) const void *, unsafe_ptr)
{ {
return bpf_probe_read_kernel_str_common(dst, size, unsafe_ptr, false); return bpf_probe_read_kernel_str_common(dst, size, unsafe_ptr);
} }
const struct bpf_func_proto bpf_probe_read_kernel_str_proto = { const struct bpf_func_proto bpf_probe_read_kernel_str_proto = {
...@@ -262,10 +262,34 @@ const struct bpf_func_proto bpf_probe_read_kernel_str_proto = { ...@@ -262,10 +262,34 @@ const struct bpf_func_proto bpf_probe_read_kernel_str_proto = {
.arg3_type = ARG_ANYTHING, .arg3_type = ARG_ANYTHING,
}; };
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
BPF_CALL_3(bpf_probe_read_compat, void *, dst, u32, size,
const void *, unsafe_ptr)
{
if ((unsigned long)unsafe_ptr < TASK_SIZE) {
return bpf_probe_read_user_common(dst, size,
(__force void __user *)unsafe_ptr);
}
return bpf_probe_read_kernel_common(dst, size, unsafe_ptr);
}
static const struct bpf_func_proto bpf_probe_read_compat_proto = {
.func = bpf_probe_read_compat,
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING,
};
BPF_CALL_3(bpf_probe_read_compat_str, void *, dst, u32, size, BPF_CALL_3(bpf_probe_read_compat_str, void *, dst, u32, size,
const void *, unsafe_ptr) const void *, unsafe_ptr)
{ {
return bpf_probe_read_kernel_str_common(dst, size, unsafe_ptr, true); if ((unsigned long)unsafe_ptr < TASK_SIZE) {
return bpf_probe_read_user_str_common(dst, size,
(__force void __user *)unsafe_ptr);
}
return bpf_probe_read_kernel_str_common(dst, size, unsafe_ptr);
} }
static const struct bpf_func_proto bpf_probe_read_compat_str_proto = { static const struct bpf_func_proto bpf_probe_read_compat_str_proto = {
...@@ -276,6 +300,7 @@ static const struct bpf_func_proto bpf_probe_read_compat_str_proto = { ...@@ -276,6 +300,7 @@ static const struct bpf_func_proto bpf_probe_read_compat_str_proto = {
.arg2_type = ARG_CONST_SIZE_OR_ZERO, .arg2_type = ARG_CONST_SIZE_OR_ZERO,
.arg3_type = ARG_ANYTHING, .arg3_type = ARG_ANYTHING,
}; };
#endif /* CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE */
BPF_CALL_3(bpf_probe_write_user, void __user *, unsafe_ptr, const void *, src, BPF_CALL_3(bpf_probe_write_user, void __user *, unsafe_ptr, const void *, src,
u32, size) u32, size)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment