Skip to content

Commit 5844101

Browse files
haoluo1022Alexei Starovoitov
authored andcommitted
bpf: Reject programs that try to load __percpu memory.
With the introduction of the btf_type_tag "percpu", we can add a MEM_PERCPU to identify those pointers that point to percpu memory. The ability of differetiating percpu pointers from regular memory pointers have two benefits: 1. It forbids unexpected use of percpu pointers, such as direct loads. In kernel, there are special functions used for accessing percpu memory. Directly loading percpu memory is meaningless. We already have BPF helpers like bpf_per_cpu_ptr() and bpf_this_cpu_ptr() that wrap the kernel percpu functions. So we can now convert percpu pointers into regular pointers in a safe way. 2. Previously, bpf_per_cpu_ptr() and bpf_this_cpu_ptr() only work on PTR_TO_PERCPU_BTF_ID, a special reg_type which describes static percpu variables in kernel (we rely on pahole to encode them into vmlinux BTF). Now, since we can identify __percpu tagged pointers, we can also identify dynamically allocated percpu memory as well. It means we can use bpf_xxx_cpu_ptr() on dynamic percpu memory. This would be very convenient when accessing fields like "cgroup->rstat_cpu". Signed-off-by: Hao Luo <haoluo@google.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Yonghong Song <yhs@fb.com> Link: https://lore.kernel.org/bpf/20220304191657.981240-4-haoluo@google.com
1 parent 9216c91 commit 5844101

File tree

3 files changed

+30
-13
lines changed

3 files changed

+30
-13
lines changed

include/linux/bpf.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,15 @@ enum bpf_type_flag {
334334
/* MEM is in user address space. */
335335
MEM_USER = BIT(3 + BPF_BASE_TYPE_BITS),
336336

337-
__BPF_TYPE_LAST_FLAG = MEM_USER,
337+
/* MEM is a percpu memory. MEM_PERCPU tags PTR_TO_BTF_ID. When tagged
338+
* with MEM_PERCPU, PTR_TO_BTF_ID _cannot_ be directly accessed. In
339+
* order to drop this tag, it must be passed into bpf_per_cpu_ptr()
340+
* or bpf_this_cpu_ptr(), which will return the pointer corresponding
341+
* to the specified cpu.
342+
*/
343+
MEM_PERCPU = BIT(4 + BPF_BASE_TYPE_BITS),
344+
345+
__BPF_TYPE_LAST_FLAG = MEM_PERCPU,
338346
};
339347

340348
/* Max number of base types. */
@@ -516,7 +524,6 @@ enum bpf_reg_type {
516524
*/
517525
PTR_TO_MEM, /* reg points to valid memory region */
518526
PTR_TO_BUF, /* reg points to a read/write buffer */
519-
PTR_TO_PERCPU_BTF_ID, /* reg points to a percpu kernel variable */
520527
PTR_TO_FUNC, /* reg points to a bpf program function */
521528
__BPF_REG_TYPE_MAX,
522529

kernel/bpf/btf.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5057,6 +5057,8 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
50575057
tag_value = __btf_name_by_offset(btf, t->name_off);
50585058
if (strcmp(tag_value, "user") == 0)
50595059
info->reg_type |= MEM_USER;
5060+
if (strcmp(tag_value, "percpu") == 0)
5061+
info->reg_type |= MEM_PERCPU;
50605062
}
50615063

50625064
/* skip modifiers */
@@ -5285,12 +5287,16 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
52855287
return -EACCES;
52865288
}
52875289

5288-
/* check __user tag */
5290+
/* check type tag */
52895291
t = btf_type_by_id(btf, mtype->type);
52905292
if (btf_type_is_type_tag(t)) {
52915293
tag_value = __btf_name_by_offset(btf, t->name_off);
5294+
/* check __user tag */
52925295
if (strcmp(tag_value, "user") == 0)
52935296
tmp_flag = MEM_USER;
5297+
/* check __percpu tag */
5298+
if (strcmp(tag_value, "percpu") == 0)
5299+
tmp_flag = MEM_PERCPU;
52945300
}
52955301

52965302
stype = btf_type_skip_modifiers(btf, mtype->type, &id);

kernel/bpf/verifier.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -554,16 +554,14 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
554554
[PTR_TO_TP_BUFFER] = "tp_buffer",
555555
[PTR_TO_XDP_SOCK] = "xdp_sock",
556556
[PTR_TO_BTF_ID] = "ptr_",
557-
[PTR_TO_PERCPU_BTF_ID] = "percpu_ptr_",
558557
[PTR_TO_MEM] = "mem",
559558
[PTR_TO_BUF] = "buf",
560559
[PTR_TO_FUNC] = "func",
561560
[PTR_TO_MAP_KEY] = "map_key",
562561
};
563562

564563
if (type & PTR_MAYBE_NULL) {
565-
if (base_type(type) == PTR_TO_BTF_ID ||
566-
base_type(type) == PTR_TO_PERCPU_BTF_ID)
564+
if (base_type(type) == PTR_TO_BTF_ID)
567565
strncpy(postfix, "or_null_", 16);
568566
else
569567
strncpy(postfix, "_or_null", 16);
@@ -575,6 +573,8 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
575573
strncpy(prefix, "alloc_", 32);
576574
if (type & MEM_USER)
577575
strncpy(prefix, "user_", 32);
576+
if (type & MEM_PERCPU)
577+
strncpy(prefix, "percpu_", 32);
578578

579579
snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s",
580580
prefix, str[base_type(type)], postfix);
@@ -697,8 +697,7 @@ static void print_verifier_state(struct bpf_verifier_env *env,
697697
const char *sep = "";
698698

699699
verbose(env, "%s", reg_type_str(env, t));
700-
if (base_type(t) == PTR_TO_BTF_ID ||
701-
base_type(t) == PTR_TO_PERCPU_BTF_ID)
700+
if (base_type(t) == PTR_TO_BTF_ID)
702701
verbose(env, "%s", kernel_type_name(reg->btf, reg->btf_id));
703702
verbose(env, "(");
704703
/*
@@ -2783,7 +2782,6 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
27832782
case PTR_TO_XDP_SOCK:
27842783
case PTR_TO_BTF_ID:
27852784
case PTR_TO_BUF:
2786-
case PTR_TO_PERCPU_BTF_ID:
27872785
case PTR_TO_MEM:
27882786
case PTR_TO_FUNC:
27892787
case PTR_TO_MAP_KEY:
@@ -4203,6 +4201,13 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
42034201
return -EACCES;
42044202
}
42054203

4204+
if (reg->type & MEM_PERCPU) {
4205+
verbose(env,
4206+
"R%d is ptr_%s access percpu memory: off=%d\n",
4207+
regno, tname, off);
4208+
return -EACCES;
4209+
}
4210+
42064211
if (env->ops->btf_struct_access) {
42074212
ret = env->ops->btf_struct_access(&env->log, reg->btf, t,
42084213
off, size, atype, &btf_id, &flag);
@@ -4809,7 +4814,7 @@ static int check_stack_range_initialized(
48094814
}
48104815

48114816
if (is_spilled_reg(&state->stack[spi]) &&
4812-
state->stack[spi].spilled_ptr.type == PTR_TO_BTF_ID)
4817+
base_type(state->stack[spi].spilled_ptr.type) == PTR_TO_BTF_ID)
48134818
goto mark;
48144819

48154820
if (is_spilled_reg(&state->stack[spi]) &&
@@ -5265,7 +5270,7 @@ static const struct bpf_reg_types alloc_mem_types = { .types = { PTR_TO_MEM | ME
52655270
static const struct bpf_reg_types const_map_ptr_types = { .types = { CONST_PTR_TO_MAP } };
52665271
static const struct bpf_reg_types btf_ptr_types = { .types = { PTR_TO_BTF_ID } };
52675272
static const struct bpf_reg_types spin_lock_types = { .types = { PTR_TO_MAP_VALUE } };
5268-
static const struct bpf_reg_types percpu_btf_ptr_types = { .types = { PTR_TO_PERCPU_BTF_ID } };
5273+
static const struct bpf_reg_types percpu_btf_ptr_types = { .types = { PTR_TO_BTF_ID | MEM_PERCPU } };
52695274
static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } };
52705275
static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } };
52715276
static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } };
@@ -9677,7 +9682,6 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn)
96779682
dst_reg->mem_size = aux->btf_var.mem_size;
96789683
break;
96799684
case PTR_TO_BTF_ID:
9680-
case PTR_TO_PERCPU_BTF_ID:
96819685
dst_reg->btf = aux->btf_var.btf;
96829686
dst_reg->btf_id = aux->btf_var.btf_id;
96839687
break;
@@ -11877,7 +11881,7 @@ static int check_pseudo_btf_id(struct bpf_verifier_env *env,
1187711881
type = t->type;
1187811882
t = btf_type_skip_modifiers(btf, type, NULL);
1187911883
if (percpu) {
11880-
aux->btf_var.reg_type = PTR_TO_PERCPU_BTF_ID;
11884+
aux->btf_var.reg_type = PTR_TO_BTF_ID | MEM_PERCPU;
1188111885
aux->btf_var.btf = btf;
1188211886
aux->btf_var.btf_id = type;
1188311887
} else if (!btf_type_is_struct(t)) {

0 commit comments

Comments
 (0)