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

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.de


Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 19c8d8ac
Loading
Loading
Loading
Loading
+67 −42
Original line number Diff line number Diff line
@@ -136,17 +136,23 @@ static const struct bpf_func_proto bpf_override_return_proto = {
};
#endif

BPF_CALL_3(bpf_probe_read_user, void *, dst, u32, size,
	   const void __user *, unsafe_ptr)
static __always_inline int
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))
		memset(dst, 0, size);

	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 = {
	.func		= bpf_probe_read_user,
	.gpl_only	= true,
@@ -156,17 +162,24 @@ const struct bpf_func_proto bpf_probe_read_user_proto = {
	.arg3_type	= ARG_ANYTHING,
};

BPF_CALL_3(bpf_probe_read_user_str, void *, dst, u32, size,
	   const void __user *, unsafe_ptr)
static __always_inline int
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))
		memset(dst, 0, size);

	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 = {
	.func		= bpf_probe_read_user_str,
	.gpl_only	= true,
@@ -177,17 +190,17 @@ const struct bpf_func_proto bpf_probe_read_user_str_proto = {
};

static __always_inline int
bpf_probe_read_kernel_common(void *dst, u32 size, const void *unsafe_ptr,
			     const bool compat)
bpf_probe_read_kernel_common(void *dst, u32 size, const void *unsafe_ptr)
{
	int ret = security_locked_down(LOCKDOWN_BPF_READ);

	if (unlikely(ret < 0))
		goto out;
	ret = compat ? probe_kernel_read(dst, unsafe_ptr, size) :
	      probe_kernel_read_strict(dst, unsafe_ptr, size);
		goto fail;
	ret = probe_kernel_read_strict(dst, unsafe_ptr, size);
	if (unlikely(ret < 0))
out:
		goto fail;
	return ret;
fail:
	memset(dst, 0, size);
	return ret;
}
@@ -195,7 +208,7 @@ out:
BPF_CALL_3(bpf_probe_read_kernel, void *, dst, u32, size,
	   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 = {
@@ -207,42 +220,29 @@ const struct bpf_func_proto bpf_probe_read_kernel_proto = {
	.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
bpf_probe_read_kernel_str_common(void *dst, u32 size, const void *unsafe_ptr,
				 const bool compat)
bpf_probe_read_kernel_str_common(void *dst, u32 size, const void *unsafe_ptr)
{
	int ret = security_locked_down(LOCKDOWN_BPF_READ);

	if (unlikely(ret < 0))
		goto out;
		goto fail;

	/*
	 * The strncpy_from_unsafe_*() call will likely not fill the entire
	 * buffer, but that's okay in this circumstance as we're probing
	 * The strncpy_from_kernel_nofault() call will likely not fill the
	 * entire buffer, but that's okay in this circumstance as we're probing
	 * arbitrary memory anyway similar to bpf_probe_read_*() and might
	 * as well probe the stack. Thus, memory is explicitly cleared
	 * only in error case, so that improper users ignoring return
	 * code altogether don't copy garbage; otherwise length of string
	 * is returned that can be used for bpf_perf_event_output() et al.
	 */
	ret = compat ? strncpy_from_unsafe(dst, unsafe_ptr, size) :
	      strncpy_from_kernel_nofault(dst, unsafe_ptr, size);
	ret = strncpy_from_kernel_nofault(dst, unsafe_ptr, size);
	if (unlikely(ret < 0))
out:
		goto fail;

	return 0;
fail:
	memset(dst, 0, size);
	return ret;
}
@@ -250,7 +250,7 @@ out:
BPF_CALL_3(bpf_probe_read_kernel_str, void *, dst, u32, size,
	   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 = {
@@ -262,10 +262,34 @@ const struct bpf_func_proto bpf_probe_read_kernel_str_proto = {
	.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,
	   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 = {
@@ -276,6 +300,7 @@ static const struct bpf_func_proto bpf_probe_read_compat_str_proto = {
	.arg2_type	= ARG_CONST_SIZE_OR_ZERO,
	.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,
	   u32, size)