Commit 74a7e76f authored by Hao Luo's avatar Hao Luo Committed by Zheng Zengkai
Browse files

bpf: Introduce MEM_RDONLY flag

mainline inclusion
from mainline-v5.17-rc1
commit 20b2aff4
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I4WRPV
CVE: CVE-2022-0500

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



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

This patch introduce a flag MEM_RDONLY to tag a reg value
pointing to read-only memory. It makes the following changes:

1. PTR_TO_RDWR_BUF -> PTR_TO_BUF
2. PTR_TO_RDONLY_BUF -> PTR_TO_BUF | MEM_RDONLY

Signed-off-by: default avatarHao Luo <haoluo@google.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20211217003152.48334-6-haoluo@google.com


Conflicts:
	kernel/bpf/verifier.c
Signed-off-by: default avatarPu Lehui <pulehui@huawei.com>
Reviewed-by: default avatarKuohai Xu <xukuohai@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parent 9dfcfd4d
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -268,7 +268,10 @@ enum bpf_type_flag {
	/* PTR may be NULL. */
	PTR_MAYBE_NULL		= BIT(0 + BPF_BASE_TYPE_BITS),

	__BPF_TYPE_LAST_FLAG	= PTR_MAYBE_NULL,
	/* MEM is read-only. */
	MEM_RDONLY		= BIT(1 + BPF_BASE_TYPE_BITS),

	__BPF_TYPE_LAST_FLAG	= MEM_RDONLY,
};

/* Max number of base types. */
@@ -443,8 +446,7 @@ enum bpf_reg_type {
	 * an explicit null check is required for this struct.
	 */
	PTR_TO_MEM,		 /* reg points to valid memory region */
	PTR_TO_RDONLY_BUF,	 /* reg points to a readonly buffer */
	PTR_TO_RDWR_BUF,	 /* reg points to a read/write buffer */
	PTR_TO_BUF,		 /* reg points to a read/write buffer */
	PTR_TO_PERCPU_BTF_ID,	 /* reg points to a percpu kernel variable */
	__BPF_REG_TYPE_MAX,

+1 −2
Original line number Diff line number Diff line
@@ -4529,8 +4529,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,

		type = base_type(ctx_arg_info->reg_type);
		flag = type_flag(ctx_arg_info->reg_type);
		if (ctx_arg_info->offset == off &&
		    (type == PTR_TO_RDWR_BUF || type == PTR_TO_RDONLY_BUF) &&
		if (ctx_arg_info->offset == off && type == PTR_TO_BUF &&
		    (flag & PTR_MAYBE_NULL)) {
			info->reg_type = ctx_arg_info->reg_type;
			return true;
+2 −2
Original line number Diff line number Diff line
@@ -174,9 +174,9 @@ static const struct bpf_iter_reg bpf_map_elem_reg_info = {
	.ctx_arg_info_size	= 2,
	.ctx_arg_info		= {
		{ offsetof(struct bpf_iter__bpf_map_elem, key),
		  PTR_TO_RDONLY_BUF | PTR_MAYBE_NULL },
		  PTR_TO_BUF | PTR_MAYBE_NULL | MEM_RDONLY },
		{ offsetof(struct bpf_iter__bpf_map_elem, value),
		  PTR_TO_RDWR_BUF | PTR_MAYBE_NULL },
		  PTR_TO_BUF | PTR_MAYBE_NULL },
	},
};

+49 −34
Original line number Diff line number Diff line
@@ -417,6 +417,11 @@ static bool reg_type_may_be_refcounted_or_null(enum bpf_reg_type type)
	       base_type(type) == PTR_TO_MEM;
}

static bool type_is_rdonly_mem(u32 type)
{
	return type & MEM_RDONLY;
}

static bool arg_type_may_be_refcounted(enum bpf_arg_type type)
{
	return type == ARG_PTR_TO_SOCK_COMMON;
@@ -485,7 +490,7 @@ static bool is_ptr_cast_function(enum bpf_func_id func_id)
static const char *reg_type_str(struct bpf_verifier_env *env,
		enum bpf_reg_type type)
{
	char postfix[16] = {0};
	char postfix[16] = {0}, prefix[16] = {0};
	static const char * const str[] = {
		[NOT_INIT]		= "?",
		[SCALAR_VALUE]		= "inv",
@@ -505,8 +510,7 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
		[PTR_TO_BTF_ID]		= "ptr_",
		[PTR_TO_PERCPU_BTF_ID]	= "percpu_ptr_",
		[PTR_TO_MEM]		= "mem",
		[PTR_TO_RDONLY_BUF]	= "rdonly_buf",
		[PTR_TO_RDWR_BUF]	= "rdwr_buf",
		[PTR_TO_BUF]		= "buf",
	};

	if (type & PTR_MAYBE_NULL) {
@@ -517,8 +521,11 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
			strncpy(postfix, "_or_null", 16);
	}

	snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s",
		 str[base_type(type)], postfix);
	if (type & MEM_RDONLY)
		strncpy(prefix, "rdonly_", 16);

	snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s",
		 prefix, str[base_type(type)], postfix);
	return env->type_str_buf;
}

@@ -2200,8 +2207,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
	case PTR_TO_TCP_SOCK:
	case PTR_TO_XDP_SOCK:
	case PTR_TO_BTF_ID:
	case PTR_TO_RDONLY_BUF:
	case PTR_TO_RDWR_BUF:
	case PTR_TO_BUF:
	case PTR_TO_PERCPU_BTF_ID:
	case PTR_TO_MEM:
		return true;
@@ -3885,22 +3891,27 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
	} else if (reg->type == CONST_PTR_TO_MAP) {
		err = check_ptr_to_map_access(env, regs, regno, off, size, t,
					      value_regno);
	} else if (reg->type == PTR_TO_RDONLY_BUF) {
	} else if (base_type(reg->type) == PTR_TO_BUF) {
		bool rdonly_mem = type_is_rdonly_mem(reg->type);
		const char *buf_info;
		u32 *max_access;

		if (rdonly_mem) {
			if (t == BPF_WRITE) {
				verbose(env, "R%d cannot write into %s\n",
						regno, reg_type_str(env, reg->type));
				return -EACCES;
			}
			buf_info = "rdonly";
			max_access = &env->prog->aux->max_rdonly_access;
		} else {
			buf_info = "rdwr";
			max_access = &env->prog->aux->max_rdwr_access;
		}

		err = check_buffer_access(env, reg, regno, off, size, false,
					  "rdonly",
					  &env->prog->aux->max_rdonly_access);
		if (!err && value_regno >= 0)
			mark_reg_unknown(env, regs, value_regno);
	} else if (reg->type == PTR_TO_RDWR_BUF) {
		err = check_buffer_access(env, reg, regno, off, size, false,
					  "rdwr",
					  &env->prog->aux->max_rdwr_access);
		if (!err && t == BPF_READ && value_regno >= 0)
					  buf_info, max_access);
		if (!err && value_regno >= 0 && (rdonly_mem || t == BPF_READ))
			mark_reg_unknown(env, regs, value_regno);
	} else {
		verbose(env, "R%d invalid mem access '%s'\n", regno,
@@ -4103,8 +4114,10 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
				   struct bpf_call_arg_meta *meta)
{
	struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
	const char *buf_info;
	u32 *max_access;

	switch (reg->type) {
	switch (base_type(reg->type)) {
	case PTR_TO_PACKET:
	case PTR_TO_PACKET_META:
		return check_packet_access(env, regno, reg->off, access_size,
@@ -4120,18 +4133,20 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
		return check_mem_region_access(env, regno, reg->off,
					       access_size, reg->mem_size,
					       zero_size_allowed);
	case PTR_TO_RDONLY_BUF:
	case PTR_TO_BUF:
		if (type_is_rdonly_mem(reg->type)) {
			if (meta && meta->raw_mode)
				return -EACCES;

			buf_info = "rdonly";
			max_access = &env->prog->aux->max_rdonly_access;
		} else {
			buf_info = "rdwr";
			max_access = &env->prog->aux->max_rdwr_access;
		}
		return check_buffer_access(env, reg, regno, reg->off,
					   access_size, zero_size_allowed,
					   "rdonly",
					   &env->prog->aux->max_rdonly_access);
	case PTR_TO_RDWR_BUF:
		return check_buffer_access(env, reg, regno, reg->off,
					   access_size, zero_size_allowed,
					   "rdwr",
					   &env->prog->aux->max_rdwr_access);
					   buf_info, max_access);
	case PTR_TO_STACK:
		return check_stack_range_initialized(
				env,
@@ -4334,8 +4349,8 @@ static const struct bpf_reg_types mem_types = {
		PTR_TO_PACKET_META,
		PTR_TO_MAP_VALUE,
		PTR_TO_MEM,
		PTR_TO_RDONLY_BUF,
		PTR_TO_RDWR_BUF,
		PTR_TO_BUF,
		PTR_TO_BUF | MEM_RDONLY,
	},
};

+1 −1
Original line number Diff line number Diff line
@@ -858,7 +858,7 @@ static struct bpf_iter_reg bpf_sk_storage_map_reg_info = {
		{ offsetof(struct bpf_iter__bpf_sk_storage_map, sk),
		  PTR_TO_BTF_ID_OR_NULL },
		{ offsetof(struct bpf_iter__bpf_sk_storage_map, value),
		  PTR_TO_RDWR_BUF | PTR_MAYBE_NULL },
		  PTR_TO_BUF | PTR_MAYBE_NULL },
	},
	.seq_info		= &iter_seq_info,
};
Loading