Skip to content

Commit c1fb414

Browse files
committed
bpf: Introduce MEM_RDONLY flag
jira VULN-136 cve-pre CVE-2022-0500 commit-author Hao Luo <[email protected]> commit 20b2aff 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: Hao Luo <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/[email protected] (cherry picked from commit 20b2aff) Signed-off-by: Brett Mastbergen <[email protected]>
1 parent 5f27b3f commit c1fb414

File tree

6 files changed

+60
-43
lines changed

6 files changed

+60
-43
lines changed

include/linux/bpf.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,10 @@ enum bpf_type_flag {
293293
/* PTR may be NULL. */
294294
PTR_MAYBE_NULL = BIT(0 + BPF_BASE_TYPE_BITS),
295295

296-
__BPF_TYPE_LAST_FLAG = PTR_MAYBE_NULL,
296+
/* MEM is read-only. */
297+
MEM_RDONLY = BIT(1 + BPF_BASE_TYPE_BITS),
298+
299+
__BPF_TYPE_LAST_FLAG = MEM_RDONLY,
297300
};
298301

299302
/* Max number of base types. */
@@ -473,8 +476,7 @@ enum bpf_reg_type {
473476
* an explicit null check is required for this struct.
474477
*/
475478
PTR_TO_MEM, /* reg points to valid memory region */
476-
PTR_TO_RDONLY_BUF, /* reg points to a readonly buffer */
477-
PTR_TO_RDWR_BUF, /* reg points to a read/write buffer */
479+
PTR_TO_BUF, /* reg points to a read/write buffer */
478480
PTR_TO_PERCPU_BTF_ID, /* reg points to a percpu kernel variable */
479481
PTR_TO_FUNC, /* reg points to a bpf program function */
480482
__BPF_REG_TYPE_MAX,

kernel/bpf/btf.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4757,8 +4757,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
47574757

47584758
type = base_type(ctx_arg_info->reg_type);
47594759
flag = type_flag(ctx_arg_info->reg_type);
4760-
if (ctx_arg_info->offset == off &&
4761-
(type == PTR_TO_RDWR_BUF || type == PTR_TO_RDONLY_BUF) &&
4760+
if (ctx_arg_info->offset == off && type == PTR_TO_BUF &&
47624761
(flag & PTR_MAYBE_NULL)) {
47634762
info->reg_type = ctx_arg_info->reg_type;
47644763
return true;

kernel/bpf/map_iter.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,9 @@ static const struct bpf_iter_reg bpf_map_elem_reg_info = {
174174
.ctx_arg_info_size = 2,
175175
.ctx_arg_info = {
176176
{ offsetof(struct bpf_iter__bpf_map_elem, key),
177-
PTR_TO_RDONLY_BUF | PTR_MAYBE_NULL },
177+
PTR_TO_BUF | PTR_MAYBE_NULL | MEM_RDONLY },
178178
{ offsetof(struct bpf_iter__bpf_map_elem, value),
179-
PTR_TO_RDWR_BUF | PTR_MAYBE_NULL },
179+
PTR_TO_BUF | PTR_MAYBE_NULL },
180180
},
181181
};
182182

kernel/bpf/verifier.c

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,11 @@ static bool reg_type_may_be_refcounted_or_null(enum bpf_reg_type type)
459459
base_type(type) == PTR_TO_MEM;
460460
}
461461

462+
static bool type_is_rdonly_mem(u32 type)
463+
{
464+
return type & MEM_RDONLY;
465+
}
466+
462467
static bool arg_type_may_be_refcounted(enum bpf_arg_type type)
463468
{
464469
return type == ARG_PTR_TO_SOCK_COMMON;
@@ -534,7 +539,7 @@ static bool is_cmpxchg_insn(const struct bpf_insn *insn)
534539
static const char *reg_type_str(struct bpf_verifier_env *env,
535540
enum bpf_reg_type type)
536541
{
537-
char postfix[16] = {0};
542+
char postfix[16] = {0}, prefix[16] = {0};
538543
static const char * const str[] = {
539544
[NOT_INIT] = "?",
540545
[SCALAR_VALUE] = "inv",
@@ -554,8 +559,7 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
554559
[PTR_TO_BTF_ID] = "ptr_",
555560
[PTR_TO_PERCPU_BTF_ID] = "percpu_ptr_",
556561
[PTR_TO_MEM] = "mem",
557-
[PTR_TO_RDONLY_BUF] = "rdonly_buf",
558-
[PTR_TO_RDWR_BUF] = "rdwr_buf",
562+
[PTR_TO_BUF] = "buf",
559563
[PTR_TO_FUNC] = "func",
560564
[PTR_TO_MAP_KEY] = "map_key",
561565
};
@@ -568,8 +572,11 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
568572
strncpy(postfix, "_or_null", 16);
569573
}
570574

571-
snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s",
572-
str[base_type(type)], postfix);
575+
if (type & MEM_RDONLY)
576+
strncpy(prefix, "rdonly_", 16);
577+
578+
snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s",
579+
prefix, str[base_type(type)], postfix);
573580
return env->type_str_buf;
574581
}
575582

@@ -2493,8 +2500,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
24932500
case PTR_TO_TCP_SOCK:
24942501
case PTR_TO_XDP_SOCK:
24952502
case PTR_TO_BTF_ID:
2496-
case PTR_TO_RDONLY_BUF:
2497-
case PTR_TO_RDWR_BUF:
2503+
case PTR_TO_BUF:
24982504
case PTR_TO_PERCPU_BTF_ID:
24992505
case PTR_TO_MEM:
25002506
case PTR_TO_FUNC:
@@ -4187,22 +4193,28 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
41874193
} else if (reg->type == CONST_PTR_TO_MAP) {
41884194
err = check_ptr_to_map_access(env, regs, regno, off, size, t,
41894195
value_regno);
4190-
} else if (reg->type == PTR_TO_RDONLY_BUF) {
4191-
if (t == BPF_WRITE) {
4192-
verbose(env, "R%d cannot write into %s\n",
4193-
regno, reg_type_str(env, reg->type));
4194-
return -EACCES;
4196+
} else if (base_type(reg->type) == PTR_TO_BUF) {
4197+
bool rdonly_mem = type_is_rdonly_mem(reg->type);
4198+
const char *buf_info;
4199+
u32 *max_access;
4200+
4201+
if (rdonly_mem) {
4202+
if (t == BPF_WRITE) {
4203+
verbose(env, "R%d cannot write into %s\n",
4204+
regno, reg_type_str(env, reg->type));
4205+
return -EACCES;
4206+
}
4207+
buf_info = "rdonly";
4208+
max_access = &env->prog->aux->max_rdonly_access;
4209+
} else {
4210+
buf_info = "rdwr";
4211+
max_access = &env->prog->aux->max_rdwr_access;
41954212
}
4213+
41964214
err = check_buffer_access(env, reg, regno, off, size, false,
4197-
"rdonly",
4198-
&env->prog->aux->max_rdonly_access);
4199-
if (!err && value_regno >= 0)
4200-
mark_reg_unknown(env, regs, value_regno);
4201-
} else if (reg->type == PTR_TO_RDWR_BUF) {
4202-
err = check_buffer_access(env, reg, regno, off, size, false,
4203-
"rdwr",
4204-
&env->prog->aux->max_rdwr_access);
4205-
if (!err && t == BPF_READ && value_regno >= 0)
4215+
buf_info, max_access);
4216+
4217+
if (!err && value_regno >= 0 && (rdonly_mem || t == BPF_READ))
42064218
mark_reg_unknown(env, regs, value_regno);
42074219
} else {
42084220
verbose(env, "R%d invalid mem access '%s'\n", regno,
@@ -4450,8 +4462,10 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
44504462
struct bpf_call_arg_meta *meta)
44514463
{
44524464
struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
4465+
const char *buf_info;
4466+
u32 *max_access;
44534467

4454-
switch (reg->type) {
4468+
switch (base_type(reg->type)) {
44554469
case PTR_TO_PACKET:
44564470
case PTR_TO_PACKET_META:
44574471
return check_packet_access(env, regno, reg->off, access_size,
@@ -4470,18 +4484,20 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
44704484
return check_mem_region_access(env, regno, reg->off,
44714485
access_size, reg->mem_size,
44724486
zero_size_allowed);
4473-
case PTR_TO_RDONLY_BUF:
4474-
if (meta && meta->raw_mode)
4475-
return -EACCES;
4476-
return check_buffer_access(env, reg, regno, reg->off,
4477-
access_size, zero_size_allowed,
4478-
"rdonly",
4479-
&env->prog->aux->max_rdonly_access);
4480-
case PTR_TO_RDWR_BUF:
4487+
case PTR_TO_BUF:
4488+
if (type_is_rdonly_mem(reg->type)) {
4489+
if (meta && meta->raw_mode)
4490+
return -EACCES;
4491+
4492+
buf_info = "rdonly";
4493+
max_access = &env->prog->aux->max_rdonly_access;
4494+
} else {
4495+
buf_info = "rdwr";
4496+
max_access = &env->prog->aux->max_rdwr_access;
4497+
}
44814498
return check_buffer_access(env, reg, regno, reg->off,
44824499
access_size, zero_size_allowed,
4483-
"rdwr",
4484-
&env->prog->aux->max_rdwr_access);
4500+
buf_info, max_access);
44854501
case PTR_TO_STACK:
44864502
return check_stack_range_initialized(
44874503
env,
@@ -4709,8 +4725,8 @@ static const struct bpf_reg_types mem_types = {
47094725
PTR_TO_MAP_KEY,
47104726
PTR_TO_MAP_VALUE,
47114727
PTR_TO_MEM,
4712-
PTR_TO_RDONLY_BUF,
4713-
PTR_TO_RDWR_BUF,
4728+
PTR_TO_BUF,
4729+
PTR_TO_BUF | MEM_RDONLY,
47144730
},
47154731
};
47164732

net/core/bpf_sk_storage.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -929,7 +929,7 @@ static struct bpf_iter_reg bpf_sk_storage_map_reg_info = {
929929
{ offsetof(struct bpf_iter__bpf_sk_storage_map, sk),
930930
PTR_TO_BTF_ID_OR_NULL },
931931
{ offsetof(struct bpf_iter__bpf_sk_storage_map, value),
932-
PTR_TO_RDWR_BUF | PTR_MAYBE_NULL },
932+
PTR_TO_BUF | PTR_MAYBE_NULL },
933933
},
934934
.seq_info = &iter_seq_info,
935935
};

net/core/sock_map.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1586,7 +1586,7 @@ static struct bpf_iter_reg sock_map_iter_reg = {
15861586
.ctx_arg_info_size = 2,
15871587
.ctx_arg_info = {
15881588
{ offsetof(struct bpf_iter__sockmap, key),
1589-
PTR_TO_RDONLY_BUF | PTR_MAYBE_NULL },
1589+
PTR_TO_BUF | PTR_MAYBE_NULL | MEM_RDONLY },
15901590
{ offsetof(struct bpf_iter__sockmap, sk),
15911591
PTR_TO_BTF_ID_OR_NULL },
15921592
},

0 commit comments

Comments
 (0)