Commit 33e2b2e4 authored by Tengda Wu's avatar Tengda Wu
Browse files

bpf, lsm: Add check for BPF LSM return value

mainline inclusion
from mainline-v6.12-rc1
commit 5d99e198be279045e6ecefe220f5c52f8ce9bfd5
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IAYPJF
CVE: CVE-2024-47703

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5d99e198be279045e6ecefe220f5c52f8ce9bfd5



--------------------------------

A bpf prog returning a positive number attached to file_alloc_security
hook makes kernel panic.

This happens because file system can not filter out the positive number
returned by the LSM prog using IS_ERR, and misinterprets this positive
number as a file pointer.

Given that hook file_alloc_security never returned positive number
before the introduction of BPF LSM, and other BPF LSM hooks may
encounter similar issues, this patch adds LSM return value check
in verifier, to ensure no unexpected value is returned.

Fixes: 520b7aa0 ("bpf: lsm: Initialize the BPF LSM hooks")
Reported-by: default avatarXin Liu <liuxin350@huawei.com>
Signed-off-by: default avatarXu Kuohai <xukuohai@huawei.com>
Acked-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20240719110059.797546-3-xukuohai@huaweicloud.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>

Conflicts:
	include/linux/bpf.h
	include/linux/bpf_verifier.h
        include/linux/bpf_lsm.h
	kernel/bpf/bpf_lsm.c
        kernel/bpf/btf.c
        kernel/bpf/verifier.c
[This is because we did not backport 5f99f312bd3b, 61df7b828204,
8fa4ecd49b81, c871d0e00f0e and 69fd337a]
Signed-off-by: default avatarTengda Wu <wutengda2@huawei.com>
parent 170a6558
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -488,6 +488,7 @@ static_assert(__BPF_REG_TYPE_MAX <= BPF_BASE_TYPE_LIMIT);
 */
struct bpf_insn_access_aux {
	enum bpf_reg_type reg_type;
	bool is_retval; /* is accessing function return value ? */
	union {
		int ctx_field_size;
		u32 btf_id;
+8 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#define _LINUX_BPF_LSM_H

#include <linux/bpf.h>
#include <linux/bpf_verifier.h>
#include <linux/lsm_hooks.h>

#ifdef CONFIG_BPF_LSM
@@ -39,6 +40,8 @@ extern const struct bpf_func_proto bpf_inode_storage_get_proto;
extern const struct bpf_func_proto bpf_inode_storage_delete_proto;
void bpf_inode_storage_free(struct inode *inode);

int bpf_lsm_get_retval_range(const struct bpf_prog *prog,
			     struct bpf_retval_range *range);
#else /* !CONFIG_BPF_LSM */

static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
@@ -57,6 +60,11 @@ static inline void bpf_inode_storage_free(struct inode *inode)
{
}

static inline int bpf_lsm_get_retval_range(const struct bpf_prog *prog,
					   struct bpf_retval_range *range)
{
	return -EOPNOTSUPP;
}
#endif /* CONFIG_BPF_LSM */

#endif /* _LINUX_BPF_LSM_H */
+5 −0
Original line number Diff line number Diff line
@@ -177,6 +177,11 @@ struct bpf_reference_state {
	int insn_idx;
};

struct bpf_retval_range {
	s32 minval;
	s32 maxval;
};

/* state of the program:
 * type of all registers and stack info
 */
+32 −1
Original line number Diff line number Diff line
@@ -10,7 +10,6 @@
#include <linux/lsm_hooks.h>
#include <linux/bpf_lsm.h>
#include <linux/kallsyms.h>
#include <linux/bpf_verifier.h>
#include <net/bpf_sk_storage.h>
#include <linux/bpf_local_storage.h>
#include <linux/btf_ids.h>
@@ -101,3 +100,35 @@ const struct bpf_verifier_ops lsm_verifier_ops = {
	.get_func_proto = bpf_lsm_func_proto,
	.is_valid_access = btf_ctx_access,
};

/* hooks return 0 or 1 */
BTF_SET_START(bool_lsm_hooks)
#ifdef CONFIG_SECURITY_NETWORK_XFRM
BTF_ID(func, bpf_lsm_xfrm_state_pol_flow_match)
#endif
#ifdef CONFIG_AUDIT
BTF_ID(func, bpf_lsm_audit_rule_known)
#endif
BTF_SET_END(bool_lsm_hooks)

int bpf_lsm_get_retval_range(const struct bpf_prog *prog,
			     struct bpf_retval_range *retval_range)
{
	/* no return value range for void hooks */
	if (!prog->aux->attach_func_proto->type)
		return -EINVAL;

	if (btf_id_set_contains(&bool_lsm_hooks, prog->aux->attach_btf_id)) {
		retval_range->minval = 0;
		retval_range->maxval = 1;
	} else {
		/* All other available LSM hooks, except task_prctl, return 0
		 * on success and negative error code on failure.
		 * To keep things simple, we only allow bpf progs to return 0
		 * or negative errno for task_prctl too.
		 */
		retval_range->minval = -MAX_ERRNO;
		retval_range->maxval = 0;
	}
	return 0;
}
+3 −0
Original line number Diff line number Diff line
@@ -4472,6 +4472,9 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
	if (arg == nr_args) {
		switch (prog->expected_attach_type) {
		case BPF_LSM_MAC:
			/* mark we are accessing the return value */
			info->is_retval = true;
			fallthrough;
		case BPF_TRACE_FEXIT:
			/* When LSM programs are attached to void LSM hooks
			 * they use FEXIT trampolines and when attached to
Loading