提交 bceb8062 编写于 作者: H Hao Luo 提交者: Zheng Zengkai

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: NHao Luo <haoluo@google.com>
Signed-off-by: NAlexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20211217003152.48334-6-haoluo@google.com
Conflicts:
	kernel/bpf/verifier.c
Signed-off-by: NPu Lehui <pulehui@huawei.com>
Reviewed-by: NKuohai Xu <xukuohai@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 3dae5870
...@@ -268,7 +268,10 @@ enum bpf_type_flag { ...@@ -268,7 +268,10 @@ enum bpf_type_flag {
/* PTR may be NULL. */ /* PTR may be NULL. */
PTR_MAYBE_NULL = BIT(0 + BPF_BASE_TYPE_BITS), 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. */ /* Max number of base types. */
...@@ -443,8 +446,7 @@ enum bpf_reg_type { ...@@ -443,8 +446,7 @@ enum bpf_reg_type {
* an explicit null check is required for this struct. * an explicit null check is required for this struct.
*/ */
PTR_TO_MEM, /* reg points to valid memory region */ PTR_TO_MEM, /* reg points to valid memory region */
PTR_TO_RDONLY_BUF, /* reg points to a readonly buffer */ PTR_TO_BUF, /* reg points to a read/write buffer */
PTR_TO_RDWR_BUF, /* reg points to a read/write buffer */
PTR_TO_PERCPU_BTF_ID, /* reg points to a percpu kernel variable */ PTR_TO_PERCPU_BTF_ID, /* reg points to a percpu kernel variable */
__BPF_REG_TYPE_MAX, __BPF_REG_TYPE_MAX,
......
...@@ -4529,8 +4529,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, ...@@ -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); type = base_type(ctx_arg_info->reg_type);
flag = type_flag(ctx_arg_info->reg_type); flag = type_flag(ctx_arg_info->reg_type);
if (ctx_arg_info->offset == off && if (ctx_arg_info->offset == off && type == PTR_TO_BUF &&
(type == PTR_TO_RDWR_BUF || type == PTR_TO_RDONLY_BUF) &&
(flag & PTR_MAYBE_NULL)) { (flag & PTR_MAYBE_NULL)) {
info->reg_type = ctx_arg_info->reg_type; info->reg_type = ctx_arg_info->reg_type;
return true; return true;
......
...@@ -174,9 +174,9 @@ static const struct bpf_iter_reg bpf_map_elem_reg_info = { ...@@ -174,9 +174,9 @@ static const struct bpf_iter_reg bpf_map_elem_reg_info = {
.ctx_arg_info_size = 2, .ctx_arg_info_size = 2,
.ctx_arg_info = { .ctx_arg_info = {
{ offsetof(struct bpf_iter__bpf_map_elem, key), { 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), { offsetof(struct bpf_iter__bpf_map_elem, value),
PTR_TO_RDWR_BUF | PTR_MAYBE_NULL }, PTR_TO_BUF | PTR_MAYBE_NULL },
}, },
}; };
......
...@@ -417,6 +417,11 @@ static bool reg_type_may_be_refcounted_or_null(enum bpf_reg_type type) ...@@ -417,6 +417,11 @@ static bool reg_type_may_be_refcounted_or_null(enum bpf_reg_type type)
base_type(type) == PTR_TO_MEM; 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) static bool arg_type_may_be_refcounted(enum bpf_arg_type type)
{ {
return type == ARG_PTR_TO_SOCK_COMMON; return type == ARG_PTR_TO_SOCK_COMMON;
...@@ -485,7 +490,7 @@ static bool is_ptr_cast_function(enum bpf_func_id func_id) ...@@ -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, static const char *reg_type_str(struct bpf_verifier_env *env,
enum bpf_reg_type type) enum bpf_reg_type type)
{ {
char postfix[16] = {0}; char postfix[16] = {0}, prefix[16] = {0};
static const char * const str[] = { static const char * const str[] = {
[NOT_INIT] = "?", [NOT_INIT] = "?",
[SCALAR_VALUE] = "inv", [SCALAR_VALUE] = "inv",
...@@ -505,8 +510,7 @@ static const char *reg_type_str(struct bpf_verifier_env *env, ...@@ -505,8 +510,7 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
[PTR_TO_BTF_ID] = "ptr_", [PTR_TO_BTF_ID] = "ptr_",
[PTR_TO_PERCPU_BTF_ID] = "percpu_ptr_", [PTR_TO_PERCPU_BTF_ID] = "percpu_ptr_",
[PTR_TO_MEM] = "mem", [PTR_TO_MEM] = "mem",
[PTR_TO_RDONLY_BUF] = "rdonly_buf", [PTR_TO_BUF] = "buf",
[PTR_TO_RDWR_BUF] = "rdwr_buf",
}; };
if (type & PTR_MAYBE_NULL) { if (type & PTR_MAYBE_NULL) {
...@@ -517,8 +521,11 @@ static const char *reg_type_str(struct bpf_verifier_env *env, ...@@ -517,8 +521,11 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
strncpy(postfix, "_or_null", 16); strncpy(postfix, "_or_null", 16);
} }
snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s", if (type & MEM_RDONLY)
str[base_type(type)], postfix); 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; return env->type_str_buf;
} }
...@@ -2200,8 +2207,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type) ...@@ -2200,8 +2207,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
case PTR_TO_TCP_SOCK: case PTR_TO_TCP_SOCK:
case PTR_TO_XDP_SOCK: case PTR_TO_XDP_SOCK:
case PTR_TO_BTF_ID: case PTR_TO_BTF_ID:
case PTR_TO_RDONLY_BUF: case PTR_TO_BUF:
case PTR_TO_RDWR_BUF:
case PTR_TO_PERCPU_BTF_ID: case PTR_TO_PERCPU_BTF_ID:
case PTR_TO_MEM: case PTR_TO_MEM:
return true; return true;
...@@ -3885,22 +3891,27 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn ...@@ -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) { } else if (reg->type == CONST_PTR_TO_MAP) {
err = check_ptr_to_map_access(env, regs, regno, off, size, t, err = check_ptr_to_map_access(env, regs, regno, off, size, t,
value_regno); value_regno);
} else if (reg->type == PTR_TO_RDONLY_BUF) { } else if (base_type(reg->type) == PTR_TO_BUF) {
if (t == BPF_WRITE) { bool rdonly_mem = type_is_rdonly_mem(reg->type);
verbose(env, "R%d cannot write into %s\n", const char *buf_info;
regno, reg_type_str(env, reg->type)); u32 *max_access;
return -EACCES;
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, err = check_buffer_access(env, reg, regno, off, size, false,
"rdonly", buf_info, max_access);
&env->prog->aux->max_rdonly_access); if (!err && value_regno >= 0 && (rdonly_mem || t == BPF_READ))
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)
mark_reg_unknown(env, regs, value_regno); mark_reg_unknown(env, regs, value_regno);
} else { } else {
verbose(env, "R%d invalid mem access '%s'\n", regno, 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, ...@@ -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_call_arg_meta *meta)
{ {
struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno]; 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:
case PTR_TO_PACKET_META: case PTR_TO_PACKET_META:
return check_packet_access(env, regno, reg->off, access_size, 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, ...@@ -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, return check_mem_region_access(env, regno, reg->off,
access_size, reg->mem_size, access_size, reg->mem_size,
zero_size_allowed); zero_size_allowed);
case PTR_TO_RDONLY_BUF: case PTR_TO_BUF:
if (meta && meta->raw_mode) if (type_is_rdonly_mem(reg->type)) {
return -EACCES; if (meta && meta->raw_mode)
return check_buffer_access(env, reg, regno, reg->off, return -EACCES;
access_size, zero_size_allowed,
"rdonly", buf_info = "rdonly";
&env->prog->aux->max_rdonly_access); max_access = &env->prog->aux->max_rdonly_access;
case PTR_TO_RDWR_BUF: } else {
buf_info = "rdwr";
max_access = &env->prog->aux->max_rdwr_access;
}
return check_buffer_access(env, reg, regno, reg->off, return check_buffer_access(env, reg, regno, reg->off,
access_size, zero_size_allowed, access_size, zero_size_allowed,
"rdwr", buf_info, max_access);
&env->prog->aux->max_rdwr_access);
case PTR_TO_STACK: case PTR_TO_STACK:
return check_stack_range_initialized( return check_stack_range_initialized(
env, env,
...@@ -4334,8 +4349,8 @@ static const struct bpf_reg_types mem_types = { ...@@ -4334,8 +4349,8 @@ static const struct bpf_reg_types mem_types = {
PTR_TO_PACKET_META, PTR_TO_PACKET_META,
PTR_TO_MAP_VALUE, PTR_TO_MAP_VALUE,
PTR_TO_MEM, PTR_TO_MEM,
PTR_TO_RDONLY_BUF, PTR_TO_BUF,
PTR_TO_RDWR_BUF, PTR_TO_BUF | MEM_RDONLY,
}, },
}; };
......
...@@ -858,7 +858,7 @@ static struct bpf_iter_reg bpf_sk_storage_map_reg_info = { ...@@ -858,7 +858,7 @@ static struct bpf_iter_reg bpf_sk_storage_map_reg_info = {
{ offsetof(struct bpf_iter__bpf_sk_storage_map, sk), { offsetof(struct bpf_iter__bpf_sk_storage_map, sk),
PTR_TO_BTF_ID_OR_NULL }, PTR_TO_BTF_ID_OR_NULL },
{ offsetof(struct bpf_iter__bpf_sk_storage_map, value), { 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, .seq_info = &iter_seq_info,
}; };
......
...@@ -1608,7 +1608,7 @@ static struct bpf_iter_reg sock_map_iter_reg = { ...@@ -1608,7 +1608,7 @@ static struct bpf_iter_reg sock_map_iter_reg = {
.ctx_arg_info_size = 2, .ctx_arg_info_size = 2,
.ctx_arg_info = { .ctx_arg_info = {
{ offsetof(struct bpf_iter__sockmap, key), { offsetof(struct bpf_iter__sockmap, key),
PTR_TO_RDONLY_BUF | PTR_MAYBE_NULL }, PTR_TO_BUF | PTR_MAYBE_NULL | MEM_RDONLY },
{ offsetof(struct bpf_iter__sockmap, sk), { offsetof(struct bpf_iter__sockmap, sk),
PTR_TO_BTF_ID_OR_NULL }, PTR_TO_BTF_ID_OR_NULL },
}, },
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册