Skip to content

Commit c344b9f

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf: add __percpu tagging in vmlinux BTF'
Hao Luo says: ==================== This patchset is very much similar to Yonghong's patchset on adding __user tagging [1], where a "user" btf_type_tag was introduced to describe __user memory pointers. Similar approach can be applied on __percpu pointers. The __percpu attribute in kernel is used to identify pointers that point to memory allocated in percpu region. Normally, accessing __percpu memory requires using special functions like per_cpu_ptr() etc. Directly accessing __percpu pointer is meaningless. Currently vmlinux BTF does not have a way to differentiate a __percpu pointer from a regular pointer. So BPF programs are allowed to load __percpu memory directly, which is an incorrect behavior. With the previous work that encodes __user information in BTF, a nice framework has been set up to allow us to encode __percpu information in BTF and let the verifier to reject programs that try to directly access percpu pointer. Previously, there is a PTR_TO_PERCPU_BTF_ID reg type which is used to represent those percpu static variables in the kernel. Pahole is able to collect variables that are stored in ".data..percpu" section in the kernel image and emit BTF information for those variables. The bpf_per_cpu_ptr() and bpf_this_cpu_ptr() helper functions were added to access these variables. Now with __percpu information, we can tag those __percpu fields in a struct (such as cgroup->rstat_cpu) and allow the pair of bpf percpu helpers to access them as well. In addition to adding __percpu tagging, this patchset also fixes a harmless bug in the previous patch that introduced __user. Patch 01/04 is for that. Patch 02/04 adds the new attribute "percpu". Patch 03/04 adds MEM_PERCPU tag for PTR_TO_BTF_ID and replaces PTR_TO_PERCPU_BTF_ID with (BTF_ID | MEM_PERCPU). Patch 04/04 refactors the btf_tag test a bit and adds tests for percpu tag. Like [1], the minimal requirements for btf_type_tag is clang (>= clang14) and pahole (>= 1.23). [1] https://lore.kernel.org/bpf/[email protected]/t/ ==================== Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 401af75 + 50c6b8a commit c344b9f

File tree

7 files changed

+253
-44
lines changed

7 files changed

+253
-44
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

include/linux/compiler_types.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { }
3838
# define __user
3939
# endif
4040
# define __iomem
41-
# define __percpu
41+
# if defined(CONFIG_DEBUG_INFO_BTF) && defined(CONFIG_PAHOLE_HAS_BTF_TAG) && \
42+
__has_attribute(btf_type_tag)
43+
# define __percpu __attribute__((btf_type_tag("percpu")))
44+
# else
45+
# define __percpu
46+
# endif
4247
# define __rcu
4348
# define __chk_user_ptr(x) (void)0
4449
# define __chk_io_ptr(x) (void)0

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: 16 additions & 11 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);
@@ -4562,7 +4567,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
45624567
err = check_tp_buffer_access(env, reg, regno, off, size);
45634568
if (!err && t == BPF_READ && value_regno >= 0)
45644569
mark_reg_unknown(env, regs, value_regno);
4565-
} else if (reg->type == PTR_TO_BTF_ID) {
4570+
} else if (base_type(reg->type) == PTR_TO_BTF_ID &&
4571+
!type_may_be_null(reg->type)) {
45664572
err = check_ptr_to_btf_access(env, regs, regno, off, size, t,
45674573
value_regno);
45684574
} else if (reg->type == CONST_PTR_TO_MAP) {
@@ -4808,7 +4814,7 @@ static int check_stack_range_initialized(
48084814
}
48094815

48104816
if (is_spilled_reg(&state->stack[spi]) &&
4811-
state->stack[spi].spilled_ptr.type == PTR_TO_BTF_ID)
4817+
base_type(state->stack[spi].spilled_ptr.type) == PTR_TO_BTF_ID)
48124818
goto mark;
48134819

48144820
if (is_spilled_reg(&state->stack[spi]) &&
@@ -5264,7 +5270,7 @@ static const struct bpf_reg_types alloc_mem_types = { .types = { PTR_TO_MEM | ME
52645270
static const struct bpf_reg_types const_map_ptr_types = { .types = { CONST_PTR_TO_MAP } };
52655271
static const struct bpf_reg_types btf_ptr_types = { .types = { PTR_TO_BTF_ID } };
52665272
static const struct bpf_reg_types spin_lock_types = { .types = { PTR_TO_MAP_VALUE } };
5267-
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 } };
52685274
static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } };
52695275
static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } };
52705276
static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } };
@@ -9676,7 +9682,6 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn)
96769682
dst_reg->mem_size = aux->btf_var.mem_size;
96779683
break;
96789684
case PTR_TO_BTF_ID:
9679-
case PTR_TO_PERCPU_BTF_ID:
96809685
dst_reg->btf = aux->btf_var.btf;
96819686
dst_reg->btf_id = aux->btf_var.btf_id;
96829687
break;
@@ -11876,7 +11881,7 @@ static int check_pseudo_btf_id(struct bpf_verifier_env *env,
1187611881
type = t->type;
1187711882
t = btf_type_skip_modifiers(btf, type, NULL);
1187811883
if (percpu) {
11879-
aux->btf_var.reg_type = PTR_TO_PERCPU_BTF_ID;
11884+
aux->btf_var.reg_type = PTR_TO_BTF_ID | MEM_PERCPU;
1188011885
aux->btf_var.btf = btf;
1188111886
aux->btf_var.btf_id = type;
1188211887
} else if (!btf_type_is_struct(t)) {

tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ struct bpf_testmod_btf_type_tag_2 {
3333
struct bpf_testmod_btf_type_tag_1 __user *p;
3434
};
3535

36+
struct bpf_testmod_btf_type_tag_3 {
37+
struct bpf_testmod_btf_type_tag_1 __percpu *p;
38+
};
39+
3640
noinline int
3741
bpf_testmod_test_btf_type_tag_user_1(struct bpf_testmod_btf_type_tag_1 __user *arg) {
3842
BTF_TYPE_EMIT(func_proto_typedef);
@@ -46,6 +50,16 @@ bpf_testmod_test_btf_type_tag_user_2(struct bpf_testmod_btf_type_tag_2 *arg) {
4650
return arg->p->a;
4751
}
4852

53+
noinline int
54+
bpf_testmod_test_btf_type_tag_percpu_1(struct bpf_testmod_btf_type_tag_1 __percpu *arg) {
55+
return arg->a;
56+
}
57+
58+
noinline int
59+
bpf_testmod_test_btf_type_tag_percpu_2(struct bpf_testmod_btf_type_tag_3 *arg) {
60+
return arg->p->a;
61+
}
62+
4963
noinline int bpf_testmod_loop_test(int n)
5064
{
5165
int i, sum = 0;

0 commit comments

Comments
 (0)