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: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrew 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.de
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Christoph Hellwig 2020-06-08 21:34:40 -07:00 committed by Linus Torvalds
parent 19c8d8ac63
commit 8d92db5c04
1 changed files with 71 additions and 46 deletions

View File

@ -136,15 +136,21 @@ static const struct bpf_func_proto bpf_override_return_proto = {
}; };
#endif #endif
static __always_inline int
bpf_probe_read_user_common(void *dst, u32 size, const void __user *unsafe_ptr)
{
int ret;
ret = probe_user_read(dst, unsafe_ptr, size);
if (unlikely(ret < 0))
memset(dst, 0, size);
return ret;
}
BPF_CALL_3(bpf_probe_read_user, void *, dst, u32, size, BPF_CALL_3(bpf_probe_read_user, void *, dst, u32, size,
const void __user *, unsafe_ptr) const void __user *, unsafe_ptr)
{ {
int ret = probe_user_read(dst, unsafe_ptr, size); return bpf_probe_read_user_common(dst, size, unsafe_ptr);
if (unlikely(ret < 0))
memset(dst, 0, size);
return ret;
} }
const struct bpf_func_proto bpf_probe_read_user_proto = { const struct bpf_func_proto bpf_probe_read_user_proto = {
@ -156,15 +162,22 @@ const struct bpf_func_proto bpf_probe_read_user_proto = {
.arg3_type = ARG_ANYTHING, .arg3_type = ARG_ANYTHING,
}; };
static __always_inline int
bpf_probe_read_user_str_common(void *dst, u32 size,
const void __user *unsafe_ptr)
{
int ret;
ret = strncpy_from_user_nofault(dst, unsafe_ptr, size);
if (unlikely(ret < 0))
memset(dst, 0, size);
return ret;
}
BPF_CALL_3(bpf_probe_read_user_str, void *, dst, u32, size, BPF_CALL_3(bpf_probe_read_user_str, void *, dst, u32, size,
const void __user *, unsafe_ptr) const void __user *, unsafe_ptr)
{ {
int ret = strncpy_from_user_nofault(dst, unsafe_ptr, size); return bpf_probe_read_user_str_common(dst, size, unsafe_ptr);
if (unlikely(ret < 0))
memset(dst, 0, size);
return ret;
} }
const struct bpf_func_proto bpf_probe_read_user_str_proto = { const struct bpf_func_proto bpf_probe_read_user_str_proto = {
@ -177,17 +190,17 @@ 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;
return ret;
fail:
memset(dst, 0, size); memset(dst, 0, size);
return ret; return ret;
} }
@ -195,7 +208,7 @@ out:
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,42 +220,29 @@ 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;
return 0;
fail:
memset(dst, 0, size); memset(dst, 0, size);
return ret; return ret;
} }
@ -250,7 +250,7 @@ out:
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 = {
.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 = {
.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)