Skip to content

Commit f27a6fa

Browse files
author
Alexei Starovoitov
committed
Merge branch 'introduce dummy BPF STRUCT_OPS'
Hou Tao says: ==================== Hi, Currently the test of BPF STRUCT_OPS depends on the specific bpf implementation (e.g, tcp_congestion_ops), but it can not cover all basic functionalities (e.g, return value handling), so introduce a dummy BPF STRUCT_OPS for test purpose. Instead of loading a userspace-implemeted bpf_dummy_ops map into kernel and calling the specific function by writing to sysfs provided by bpf_testmode.ko, only loading bpf_dummy_ops related prog into kernel and calling these prog by bpf_prog_test_run(). The latter is more flexible and has no dependency on extra kernel module. Now the return value handling is supported by test_1(...) ops, and passing multiple arguments is supported by test_2(...) ops. If more is needed, test_x(...) ops can be added afterwards. Comments are always welcome. Regards, Hou Change Log: v4: * add Acked-by tags in patch 1~4 * patch 2: remove unncessary comments and update commit message accordingly * patch 4: remove unnecessary nr checking in dummy_ops_init_args() v3: https://www.spinics.net/lists/bpf/msg48303.html * rebase on bpf-next * address comments for Martin, mainly include: merge patch 3 & patch 4 in v2, fix names of btf ctx access check helpers, handle CONFIG_NET, fix leak in dummy_ops_init_args(), and simplify bpf_dummy_init() * patch 4: use a loop to check args in test_dummy_multiple_args() v2: https://www.spinics.net/lists/bpf/msg47948.html * rebase on bpf-next * add test_2(...) ops to test the passing of multiple arguments * a new patch (patch #2) is added to factor out ctx access helpers * address comments from Martin & Andrii v1: https://www.spinics.net/lists/bpf/msg46787.html RFC: https://www.spinics.net/lists/bpf/msg46117.html ==================== Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 36e70b9 + 31122b2 commit f27a6fa

File tree

9 files changed

+439
-32
lines changed

9 files changed

+439
-32
lines changed

include/linux/bpf.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,10 @@ bool bpf_struct_ops_get(const void *kdata);
10001000
void bpf_struct_ops_put(const void *kdata);
10011001
int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,
10021002
void *value);
1003+
int bpf_struct_ops_prepare_trampoline(struct bpf_tramp_progs *tprogs,
1004+
struct bpf_prog *prog,
1005+
const struct btf_func_model *model,
1006+
void *image, void *image_end);
10031007
static inline bool bpf_try_module_get(const void *data, struct module *owner)
10041008
{
10051009
if (owner == BPF_MODULE_OWNER)
@@ -1014,6 +1018,22 @@ static inline void bpf_module_put(const void *data, struct module *owner)
10141018
else
10151019
module_put(owner);
10161020
}
1021+
1022+
#ifdef CONFIG_NET
1023+
/* Define it here to avoid the use of forward declaration */
1024+
struct bpf_dummy_ops_state {
1025+
int val;
1026+
};
1027+
1028+
struct bpf_dummy_ops {
1029+
int (*test_1)(struct bpf_dummy_ops_state *cb);
1030+
int (*test_2)(struct bpf_dummy_ops_state *cb, int a1, unsigned short a2,
1031+
char a3, unsigned long a4);
1032+
};
1033+
1034+
int bpf_struct_ops_test_run(struct bpf_prog *prog, const union bpf_attr *kattr,
1035+
union bpf_attr __user *uattr);
1036+
#endif
10171037
#else
10181038
static inline const struct bpf_struct_ops *bpf_struct_ops_find(u32 type_id)
10191039
{
@@ -1646,6 +1666,29 @@ bool bpf_prog_test_check_kfunc_call(u32 kfunc_id, struct module *owner);
16461666
bool btf_ctx_access(int off, int size, enum bpf_access_type type,
16471667
const struct bpf_prog *prog,
16481668
struct bpf_insn_access_aux *info);
1669+
1670+
static inline bool bpf_tracing_ctx_access(int off, int size,
1671+
enum bpf_access_type type)
1672+
{
1673+
if (off < 0 || off >= sizeof(__u64) * MAX_BPF_FUNC_ARGS)
1674+
return false;
1675+
if (type != BPF_READ)
1676+
return false;
1677+
if (off % size != 0)
1678+
return false;
1679+
return true;
1680+
}
1681+
1682+
static inline bool bpf_tracing_btf_ctx_access(int off, int size,
1683+
enum bpf_access_type type,
1684+
const struct bpf_prog *prog,
1685+
struct bpf_insn_access_aux *info)
1686+
{
1687+
if (!bpf_tracing_ctx_access(off, size, type))
1688+
return false;
1689+
return btf_ctx_access(off, size, type, prog, info);
1690+
}
1691+
16491692
int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf,
16501693
const struct btf_type *t, int off, int size,
16511694
enum bpf_access_type atype,

kernel/bpf/bpf_struct_ops.c

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ const struct bpf_verifier_ops bpf_struct_ops_verifier_ops = {
9393
};
9494

9595
const struct bpf_prog_ops bpf_struct_ops_prog_ops = {
96+
#ifdef CONFIG_NET
97+
.test_run = bpf_struct_ops_test_run,
98+
#endif
9699
};
97100

98101
static const struct btf_type *module_type;
@@ -312,6 +315,20 @@ static int check_zero_holes(const struct btf_type *t, void *data)
312315
return 0;
313316
}
314317

318+
int bpf_struct_ops_prepare_trampoline(struct bpf_tramp_progs *tprogs,
319+
struct bpf_prog *prog,
320+
const struct btf_func_model *model,
321+
void *image, void *image_end)
322+
{
323+
u32 flags;
324+
325+
tprogs[BPF_TRAMP_FENTRY].progs[0] = prog;
326+
tprogs[BPF_TRAMP_FENTRY].nr_progs = 1;
327+
flags = model->ret_size > 0 ? BPF_TRAMP_F_RET_FENTRY_RET : 0;
328+
return arch_prepare_bpf_trampoline(NULL, image, image_end,
329+
model, flags, tprogs, NULL);
330+
}
331+
315332
static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
316333
void *value, u64 flags)
317334
{
@@ -323,7 +340,7 @@ static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
323340
struct bpf_tramp_progs *tprogs = NULL;
324341
void *udata, *kdata;
325342
int prog_fd, err = 0;
326-
void *image;
343+
void *image, *image_end;
327344
u32 i;
328345

329346
if (flags)
@@ -363,12 +380,12 @@ static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
363380
udata = &uvalue->data;
364381
kdata = &kvalue->data;
365382
image = st_map->image;
383+
image_end = st_map->image + PAGE_SIZE;
366384

367385
for_each_member(i, t, member) {
368386
const struct btf_type *mtype, *ptype;
369387
struct bpf_prog *prog;
370388
u32 moff;
371-
u32 flags;
372389

373390
moff = btf_member_bit_offset(t, member) / 8;
374391
ptype = btf_type_resolve_ptr(btf_vmlinux, member->type, NULL);
@@ -430,14 +447,9 @@ static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
430447
goto reset_unlock;
431448
}
432449

433-
tprogs[BPF_TRAMP_FENTRY].progs[0] = prog;
434-
tprogs[BPF_TRAMP_FENTRY].nr_progs = 1;
435-
flags = st_ops->func_models[i].ret_size > 0 ?
436-
BPF_TRAMP_F_RET_FENTRY_RET : 0;
437-
err = arch_prepare_bpf_trampoline(NULL, image,
438-
st_map->image + PAGE_SIZE,
439-
&st_ops->func_models[i],
440-
flags, tprogs, NULL);
450+
err = bpf_struct_ops_prepare_trampoline(tprogs, prog,
451+
&st_ops->func_models[i],
452+
image, image_end);
441453
if (err < 0)
442454
goto reset_unlock;
443455

kernel/bpf/bpf_struct_ops_types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
/* internal file - do not include directly */
33

44
#ifdef CONFIG_BPF_JIT
5+
#ifdef CONFIG_NET
6+
BPF_STRUCT_OPS_TYPE(bpf_dummy_ops)
7+
#endif
58
#ifdef CONFIG_INET
69
#include <net/tcp.h>
710
BPF_STRUCT_OPS_TYPE(tcp_congestion_ops)

kernel/trace/bpf_trace.c

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,27 +1646,15 @@ static bool raw_tp_prog_is_valid_access(int off, int size,
16461646
const struct bpf_prog *prog,
16471647
struct bpf_insn_access_aux *info)
16481648
{
1649-
if (off < 0 || off >= sizeof(__u64) * MAX_BPF_FUNC_ARGS)
1650-
return false;
1651-
if (type != BPF_READ)
1652-
return false;
1653-
if (off % size != 0)
1654-
return false;
1655-
return true;
1649+
return bpf_tracing_ctx_access(off, size, type);
16561650
}
16571651

16581652
static bool tracing_prog_is_valid_access(int off, int size,
16591653
enum bpf_access_type type,
16601654
const struct bpf_prog *prog,
16611655
struct bpf_insn_access_aux *info)
16621656
{
1663-
if (off < 0 || off >= sizeof(__u64) * MAX_BPF_FUNC_ARGS)
1664-
return false;
1665-
if (type != BPF_READ)
1666-
return false;
1667-
if (off % size != 0)
1668-
return false;
1669-
return btf_ctx_access(off, size, type, prog, info);
1657+
return bpf_tracing_btf_ctx_access(off, size, type, prog, info);
16701658
}
16711659

16721660
int __weak bpf_prog_test_run_tracing(struct bpf_prog *prog,

net/bpf/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
obj-$(CONFIG_BPF_SYSCALL) := test_run.o
3+
ifeq ($(CONFIG_BPF_JIT),y)
4+
obj-$(CONFIG_BPF_SYSCALL) += bpf_dummy_struct_ops.o
5+
endif

net/bpf/bpf_dummy_struct_ops.c

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2021. Huawei Technologies Co., Ltd
4+
*/
5+
#include <linux/kernel.h>
6+
#include <linux/bpf_verifier.h>
7+
#include <linux/bpf.h>
8+
#include <linux/btf.h>
9+
10+
extern struct bpf_struct_ops bpf_bpf_dummy_ops;
11+
12+
/* A common type for test_N with return value in bpf_dummy_ops */
13+
typedef int (*dummy_ops_test_ret_fn)(struct bpf_dummy_ops_state *state, ...);
14+
15+
struct bpf_dummy_ops_test_args {
16+
u64 args[MAX_BPF_FUNC_ARGS];
17+
struct bpf_dummy_ops_state state;
18+
};
19+
20+
static struct bpf_dummy_ops_test_args *
21+
dummy_ops_init_args(const union bpf_attr *kattr, unsigned int nr)
22+
{
23+
__u32 size_in;
24+
struct bpf_dummy_ops_test_args *args;
25+
void __user *ctx_in;
26+
void __user *u_state;
27+
28+
size_in = kattr->test.ctx_size_in;
29+
if (size_in != sizeof(u64) * nr)
30+
return ERR_PTR(-EINVAL);
31+
32+
args = kzalloc(sizeof(*args), GFP_KERNEL);
33+
if (!args)
34+
return ERR_PTR(-ENOMEM);
35+
36+
ctx_in = u64_to_user_ptr(kattr->test.ctx_in);
37+
if (copy_from_user(args->args, ctx_in, size_in))
38+
goto out;
39+
40+
/* args[0] is 0 means state argument of test_N will be NULL */
41+
u_state = u64_to_user_ptr(args->args[0]);
42+
if (u_state && copy_from_user(&args->state, u_state,
43+
sizeof(args->state)))
44+
goto out;
45+
46+
return args;
47+
out:
48+
kfree(args);
49+
return ERR_PTR(-EFAULT);
50+
}
51+
52+
static int dummy_ops_copy_args(struct bpf_dummy_ops_test_args *args)
53+
{
54+
void __user *u_state;
55+
56+
u_state = u64_to_user_ptr(args->args[0]);
57+
if (u_state && copy_to_user(u_state, &args->state, sizeof(args->state)))
58+
return -EFAULT;
59+
60+
return 0;
61+
}
62+
63+
static int dummy_ops_call_op(void *image, struct bpf_dummy_ops_test_args *args)
64+
{
65+
dummy_ops_test_ret_fn test = (void *)image;
66+
struct bpf_dummy_ops_state *state = NULL;
67+
68+
/* state needs to be NULL if args[0] is 0 */
69+
if (args->args[0])
70+
state = &args->state;
71+
return test(state, args->args[1], args->args[2],
72+
args->args[3], args->args[4]);
73+
}
74+
75+
int bpf_struct_ops_test_run(struct bpf_prog *prog, const union bpf_attr *kattr,
76+
union bpf_attr __user *uattr)
77+
{
78+
const struct bpf_struct_ops *st_ops = &bpf_bpf_dummy_ops;
79+
const struct btf_type *func_proto;
80+
struct bpf_dummy_ops_test_args *args;
81+
struct bpf_tramp_progs *tprogs;
82+
void *image = NULL;
83+
unsigned int op_idx;
84+
int prog_ret;
85+
int err;
86+
87+
if (prog->aux->attach_btf_id != st_ops->type_id)
88+
return -EOPNOTSUPP;
89+
90+
func_proto = prog->aux->attach_func_proto;
91+
args = dummy_ops_init_args(kattr, btf_type_vlen(func_proto));
92+
if (IS_ERR(args))
93+
return PTR_ERR(args);
94+
95+
tprogs = kcalloc(BPF_TRAMP_MAX, sizeof(*tprogs), GFP_KERNEL);
96+
if (!tprogs) {
97+
err = -ENOMEM;
98+
goto out;
99+
}
100+
101+
image = bpf_jit_alloc_exec(PAGE_SIZE);
102+
if (!image) {
103+
err = -ENOMEM;
104+
goto out;
105+
}
106+
set_vm_flush_reset_perms(image);
107+
108+
op_idx = prog->expected_attach_type;
109+
err = bpf_struct_ops_prepare_trampoline(tprogs, prog,
110+
&st_ops->func_models[op_idx],
111+
image, image + PAGE_SIZE);
112+
if (err < 0)
113+
goto out;
114+
115+
set_memory_ro((long)image, 1);
116+
set_memory_x((long)image, 1);
117+
prog_ret = dummy_ops_call_op(image, args);
118+
119+
err = dummy_ops_copy_args(args);
120+
if (err)
121+
goto out;
122+
if (put_user(prog_ret, &uattr->test.retval))
123+
err = -EFAULT;
124+
out:
125+
kfree(args);
126+
bpf_jit_free_exec(image);
127+
kfree(tprogs);
128+
return err;
129+
}
130+
131+
static int bpf_dummy_init(struct btf *btf)
132+
{
133+
return 0;
134+
}
135+
136+
static bool bpf_dummy_ops_is_valid_access(int off, int size,
137+
enum bpf_access_type type,
138+
const struct bpf_prog *prog,
139+
struct bpf_insn_access_aux *info)
140+
{
141+
return bpf_tracing_btf_ctx_access(off, size, type, prog, info);
142+
}
143+
144+
static int bpf_dummy_ops_btf_struct_access(struct bpf_verifier_log *log,
145+
const struct btf *btf,
146+
const struct btf_type *t, int off,
147+
int size, enum bpf_access_type atype,
148+
u32 *next_btf_id)
149+
{
150+
const struct btf_type *state;
151+
s32 type_id;
152+
int err;
153+
154+
type_id = btf_find_by_name_kind(btf, "bpf_dummy_ops_state",
155+
BTF_KIND_STRUCT);
156+
if (type_id < 0)
157+
return -EINVAL;
158+
159+
state = btf_type_by_id(btf, type_id);
160+
if (t != state) {
161+
bpf_log(log, "only access to bpf_dummy_ops_state is supported\n");
162+
return -EACCES;
163+
}
164+
165+
err = btf_struct_access(log, btf, t, off, size, atype, next_btf_id);
166+
if (err < 0)
167+
return err;
168+
169+
return atype == BPF_READ ? err : NOT_INIT;
170+
}
171+
172+
static const struct bpf_verifier_ops bpf_dummy_verifier_ops = {
173+
.is_valid_access = bpf_dummy_ops_is_valid_access,
174+
.btf_struct_access = bpf_dummy_ops_btf_struct_access,
175+
};
176+
177+
static int bpf_dummy_init_member(const struct btf_type *t,
178+
const struct btf_member *member,
179+
void *kdata, const void *udata)
180+
{
181+
return -EOPNOTSUPP;
182+
}
183+
184+
static int bpf_dummy_reg(void *kdata)
185+
{
186+
return -EOPNOTSUPP;
187+
}
188+
189+
static void bpf_dummy_unreg(void *kdata)
190+
{
191+
}
192+
193+
struct bpf_struct_ops bpf_bpf_dummy_ops = {
194+
.verifier_ops = &bpf_dummy_verifier_ops,
195+
.init = bpf_dummy_init,
196+
.init_member = bpf_dummy_init_member,
197+
.reg = bpf_dummy_reg,
198+
.unreg = bpf_dummy_unreg,
199+
.name = "bpf_dummy_ops",
200+
};

net/ipv4/bpf_tcp_ca.c

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,7 @@ static bool bpf_tcp_ca_is_valid_access(int off, int size,
8181
const struct bpf_prog *prog,
8282
struct bpf_insn_access_aux *info)
8383
{
84-
if (off < 0 || off >= sizeof(__u64) * MAX_BPF_FUNC_ARGS)
85-
return false;
86-
if (type != BPF_READ)
87-
return false;
88-
if (off % size != 0)
89-
return false;
90-
91-
if (!btf_ctx_access(off, size, type, prog, info))
84+
if (!bpf_tracing_btf_ctx_access(off, size, type, prog, info))
9285
return false;
9386

9487
if (info->reg_type == PTR_TO_BTF_ID && info->btf_id == sock_id)

0 commit comments

Comments
 (0)