Skip to content

Commit be8704f

Browse files
Alexei Starovoitovborkmann
Alexei Starovoitov
authored andcommitted
bpf: Introduce dynamic program extensions
Introduce dynamic program extensions. The users can load additional BPF functions and replace global functions in previously loaded BPF programs while these programs are executing. Global functions are verified individually by the verifier based on their types only. Hence the global function in the new program which types match older function can safely replace that corresponding function. This new function/program is called 'an extension' of old program. At load time the verifier uses (attach_prog_fd, attach_btf_id) pair to identify the function to be replaced. The BPF program type is derived from the target program into extension program. Technically bpf_verifier_ops is copied from target program. The BPF_PROG_TYPE_EXT program type is a placeholder. It has empty verifier_ops. The extension program can call the same bpf helper functions as target program. Single BPF_PROG_TYPE_EXT type is used to extend XDP, SKB and all other program types. The verifier allows only one level of replacement. Meaning that the extension program cannot recursively extend an extension. That also means that the maximum stack size is increasing from 512 to 1024 bytes and maximum function nesting level from 8 to 16. The programs don't always consume that much. The stack usage is determined by the number of on-stack variables used by the program. The verifier could have enforced 512 limit for combined original plus extension program, but it makes for difficult user experience. The main use case for extensions is to provide generic mechanism to plug external programs into policy program or function call chaining. BPF trampoline is used to track both fentry/fexit and program extensions because both are using the same nop slot at the beginning of every BPF function. Attaching fentry/fexit to a function that was replaced is not allowed. The opposite is true as well. Replacing a function that currently being analyzed with fentry/fexit is not allowed. The executable page allocated by BPF trampoline is not used by program extensions. This inefficiency will be optimized in future patches. Function by function verification of global function supports scalars and pointer to context only. Hence program extensions are supported for such class of global functions only. In the future the verifier will be extended with support to pointers to structures, arrays with sizes, etc. Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: John Fastabend <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Acked-by: Toke Høiland-Jørgensen <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 2a67a6c commit be8704f

File tree

8 files changed

+283
-28
lines changed

8 files changed

+283
-28
lines changed

include/linux/bpf.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,8 @@ void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start);
465465
enum bpf_tramp_prog_type {
466466
BPF_TRAMP_FENTRY,
467467
BPF_TRAMP_FEXIT,
468-
BPF_TRAMP_MAX
468+
BPF_TRAMP_MAX,
469+
BPF_TRAMP_REPLACE, /* more than MAX */
469470
};
470471

471472
struct bpf_trampoline {
@@ -480,6 +481,11 @@ struct bpf_trampoline {
480481
void *addr;
481482
bool ftrace_managed;
482483
} func;
484+
/* if !NULL this is BPF_PROG_TYPE_EXT program that extends another BPF
485+
* program by replacing one of its functions. func.addr is the address
486+
* of the function it replaced.
487+
*/
488+
struct bpf_prog *extension_prog;
483489
/* list of BPF programs using this trampoline */
484490
struct hlist_head progs_hlist[BPF_TRAMP_MAX];
485491
/* Number of attached programs. A counter per kind. */
@@ -1107,6 +1113,8 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
11071113
struct bpf_reg_state *regs);
11081114
int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
11091115
struct bpf_reg_state *reg);
1116+
int btf_check_type_match(struct bpf_verifier_env *env, struct bpf_prog *prog,
1117+
struct btf *btf, const struct btf_type *t);
11101118

11111119
struct bpf_prog *bpf_prog_by_id(u32 id);
11121120

include/linux/bpf_types.h

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_SK_REUSEPORT, sk_reuseport,
6868
#if defined(CONFIG_BPF_JIT)
6969
BPF_PROG_TYPE(BPF_PROG_TYPE_STRUCT_OPS, bpf_struct_ops,
7070
void *, void *)
71+
BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension,
72+
void *, void *)
7173
#endif
7274

7375
BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)

include/linux/btf.h

+5
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ static inline u16 btf_type_vlen(const struct btf_type *t)
107107
return BTF_INFO_VLEN(t->info);
108108
}
109109

110+
static inline u16 btf_func_linkage(const struct btf_type *t)
111+
{
112+
return BTF_INFO_VLEN(t->info);
113+
}
114+
110115
static inline bool btf_type_kflag(const struct btf_type *t)
111116
{
112117
return BTF_INFO_KFLAG(t->info);

include/uapi/linux/bpf.h

+1
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ enum bpf_prog_type {
180180
BPF_PROG_TYPE_CGROUP_SOCKOPT,
181181
BPF_PROG_TYPE_TRACING,
182182
BPF_PROG_TYPE_STRUCT_OPS,
183+
BPF_PROG_TYPE_EXT,
183184
};
184185

185186
enum bpf_attach_type {

kernel/bpf/btf.c

+151-1
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,11 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
276276
[BTF_KIND_DATASEC] = "DATASEC",
277277
};
278278

279+
static const char *btf_type_str(const struct btf_type *t)
280+
{
281+
return btf_kind_str[BTF_INFO_KIND(t->info)];
282+
}
283+
279284
struct btf_kind_operations {
280285
s32 (*check_meta)(struct btf_verifier_env *env,
281286
const struct btf_type *t,
@@ -4115,6 +4120,148 @@ int btf_distill_func_proto(struct bpf_verifier_log *log,
41154120
return 0;
41164121
}
41174122

4123+
/* Compare BTFs of two functions assuming only scalars and pointers to context.
4124+
* t1 points to BTF_KIND_FUNC in btf1
4125+
* t2 points to BTF_KIND_FUNC in btf2
4126+
* Returns:
4127+
* EINVAL - function prototype mismatch
4128+
* EFAULT - verifier bug
4129+
* 0 - 99% match. The last 1% is validated by the verifier.
4130+
*/
4131+
int btf_check_func_type_match(struct bpf_verifier_log *log,
4132+
struct btf *btf1, const struct btf_type *t1,
4133+
struct btf *btf2, const struct btf_type *t2)
4134+
{
4135+
const struct btf_param *args1, *args2;
4136+
const char *fn1, *fn2, *s1, *s2;
4137+
u32 nargs1, nargs2, i;
4138+
4139+
fn1 = btf_name_by_offset(btf1, t1->name_off);
4140+
fn2 = btf_name_by_offset(btf2, t2->name_off);
4141+
4142+
if (btf_func_linkage(t1) != BTF_FUNC_GLOBAL) {
4143+
bpf_log(log, "%s() is not a global function\n", fn1);
4144+
return -EINVAL;
4145+
}
4146+
if (btf_func_linkage(t2) != BTF_FUNC_GLOBAL) {
4147+
bpf_log(log, "%s() is not a global function\n", fn2);
4148+
return -EINVAL;
4149+
}
4150+
4151+
t1 = btf_type_by_id(btf1, t1->type);
4152+
if (!t1 || !btf_type_is_func_proto(t1))
4153+
return -EFAULT;
4154+
t2 = btf_type_by_id(btf2, t2->type);
4155+
if (!t2 || !btf_type_is_func_proto(t2))
4156+
return -EFAULT;
4157+
4158+
args1 = (const struct btf_param *)(t1 + 1);
4159+
nargs1 = btf_type_vlen(t1);
4160+
args2 = (const struct btf_param *)(t2 + 1);
4161+
nargs2 = btf_type_vlen(t2);
4162+
4163+
if (nargs1 != nargs2) {
4164+
bpf_log(log, "%s() has %d args while %s() has %d args\n",
4165+
fn1, nargs1, fn2, nargs2);
4166+
return -EINVAL;
4167+
}
4168+
4169+
t1 = btf_type_skip_modifiers(btf1, t1->type, NULL);
4170+
t2 = btf_type_skip_modifiers(btf2, t2->type, NULL);
4171+
if (t1->info != t2->info) {
4172+
bpf_log(log,
4173+
"Return type %s of %s() doesn't match type %s of %s()\n",
4174+
btf_type_str(t1), fn1,
4175+
btf_type_str(t2), fn2);
4176+
return -EINVAL;
4177+
}
4178+
4179+
for (i = 0; i < nargs1; i++) {
4180+
t1 = btf_type_skip_modifiers(btf1, args1[i].type, NULL);
4181+
t2 = btf_type_skip_modifiers(btf2, args2[i].type, NULL);
4182+
4183+
if (t1->info != t2->info) {
4184+
bpf_log(log, "arg%d in %s() is %s while %s() has %s\n",
4185+
i, fn1, btf_type_str(t1),
4186+
fn2, btf_type_str(t2));
4187+
return -EINVAL;
4188+
}
4189+
if (btf_type_has_size(t1) && t1->size != t2->size) {
4190+
bpf_log(log,
4191+
"arg%d in %s() has size %d while %s() has %d\n",
4192+
i, fn1, t1->size,
4193+
fn2, t2->size);
4194+
return -EINVAL;
4195+
}
4196+
4197+
/* global functions are validated with scalars and pointers
4198+
* to context only. And only global functions can be replaced.
4199+
* Hence type check only those types.
4200+
*/
4201+
if (btf_type_is_int(t1) || btf_type_is_enum(t1))
4202+
continue;
4203+
if (!btf_type_is_ptr(t1)) {
4204+
bpf_log(log,
4205+
"arg%d in %s() has unrecognized type\n",
4206+
i, fn1);
4207+
return -EINVAL;
4208+
}
4209+
t1 = btf_type_skip_modifiers(btf1, t1->type, NULL);
4210+
t2 = btf_type_skip_modifiers(btf2, t2->type, NULL);
4211+
if (!btf_type_is_struct(t1)) {
4212+
bpf_log(log,
4213+
"arg%d in %s() is not a pointer to context\n",
4214+
i, fn1);
4215+
return -EINVAL;
4216+
}
4217+
if (!btf_type_is_struct(t2)) {
4218+
bpf_log(log,
4219+
"arg%d in %s() is not a pointer to context\n",
4220+
i, fn2);
4221+
return -EINVAL;
4222+
}
4223+
/* This is an optional check to make program writing easier.
4224+
* Compare names of structs and report an error to the user.
4225+
* btf_prepare_func_args() already checked that t2 struct
4226+
* is a context type. btf_prepare_func_args() will check
4227+
* later that t1 struct is a context type as well.
4228+
*/
4229+
s1 = btf_name_by_offset(btf1, t1->name_off);
4230+
s2 = btf_name_by_offset(btf2, t2->name_off);
4231+
if (strcmp(s1, s2)) {
4232+
bpf_log(log,
4233+
"arg%d %s(struct %s *) doesn't match %s(struct %s *)\n",
4234+
i, fn1, s1, fn2, s2);
4235+
return -EINVAL;
4236+
}
4237+
}
4238+
return 0;
4239+
}
4240+
4241+
/* Compare BTFs of given program with BTF of target program */
4242+
int btf_check_type_match(struct bpf_verifier_env *env, struct bpf_prog *prog,
4243+
struct btf *btf2, const struct btf_type *t2)
4244+
{
4245+
struct btf *btf1 = prog->aux->btf;
4246+
const struct btf_type *t1;
4247+
u32 btf_id = 0;
4248+
4249+
if (!prog->aux->func_info) {
4250+
bpf_log(&env->log, "Program extension requires BTF\n");
4251+
return -EINVAL;
4252+
}
4253+
4254+
btf_id = prog->aux->func_info[0].type_id;
4255+
if (!btf_id)
4256+
return -EFAULT;
4257+
4258+
t1 = btf_type_by_id(btf1, btf_id);
4259+
if (!t1 || !btf_type_is_func(t1))
4260+
return -EFAULT;
4261+
4262+
return btf_check_func_type_match(&env->log, btf1, t1, btf2, t2);
4263+
}
4264+
41184265
/* Compare BTF of a function with given bpf_reg_state.
41194266
* Returns:
41204267
* EFAULT - there is a verifier bug. Abort verification.
@@ -4224,6 +4371,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
42244371
{
42254372
struct bpf_verifier_log *log = &env->log;
42264373
struct bpf_prog *prog = env->prog;
4374+
enum bpf_prog_type prog_type = prog->type;
42274375
struct btf *btf = prog->aux->btf;
42284376
const struct btf_param *args;
42294377
const struct btf_type *t;
@@ -4261,6 +4409,8 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
42614409
bpf_log(log, "Verifier bug in function %s()\n", tname);
42624410
return -EFAULT;
42634411
}
4412+
if (prog_type == BPF_PROG_TYPE_EXT)
4413+
prog_type = prog->aux->linked_prog->type;
42644414

42654415
t = btf_type_by_id(btf, t->type);
42664416
if (!t || !btf_type_is_func_proto(t)) {
@@ -4296,7 +4446,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
42964446
continue;
42974447
}
42984448
if (btf_type_is_ptr(t) &&
4299-
btf_get_prog_ctx_type(log, btf, t, prog->type, i)) {
4449+
btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
43004450
reg[i + 1].type = PTR_TO_CTX;
43014451
continue;
43024452
}

kernel/bpf/syscall.c

+12-3
Original file line numberDiff line numberDiff line change
@@ -1932,13 +1932,15 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
19321932
switch (prog_type) {
19331933
case BPF_PROG_TYPE_TRACING:
19341934
case BPF_PROG_TYPE_STRUCT_OPS:
1935+
case BPF_PROG_TYPE_EXT:
19351936
break;
19361937
default:
19371938
return -EINVAL;
19381939
}
19391940
}
19401941

1941-
if (prog_fd && prog_type != BPF_PROG_TYPE_TRACING)
1942+
if (prog_fd && prog_type != BPF_PROG_TYPE_TRACING &&
1943+
prog_type != BPF_PROG_TYPE_EXT)
19421944
return -EINVAL;
19431945

19441946
switch (prog_type) {
@@ -1981,6 +1983,10 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
19811983
default:
19821984
return -EINVAL;
19831985
}
1986+
case BPF_PROG_TYPE_EXT:
1987+
if (expected_attach_type)
1988+
return -EINVAL;
1989+
/* fallthrough */
19841990
default:
19851991
return 0;
19861992
}
@@ -2183,7 +2189,8 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
21832189
int tr_fd, err;
21842190

21852191
if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
2186-
prog->expected_attach_type != BPF_TRACE_FEXIT) {
2192+
prog->expected_attach_type != BPF_TRACE_FEXIT &&
2193+
prog->type != BPF_PROG_TYPE_EXT) {
21872194
err = -EINVAL;
21882195
goto out_put_prog;
21892196
}
@@ -2250,12 +2257,14 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
22502257

22512258
if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT &&
22522259
prog->type != BPF_PROG_TYPE_TRACING &&
2260+
prog->type != BPF_PROG_TYPE_EXT &&
22532261
prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) {
22542262
err = -EINVAL;
22552263
goto out_put_prog;
22562264
}
22572265

2258-
if (prog->type == BPF_PROG_TYPE_TRACING) {
2266+
if (prog->type == BPF_PROG_TYPE_TRACING ||
2267+
prog->type == BPF_PROG_TYPE_EXT) {
22592268
if (attr->raw_tracepoint.name) {
22602269
/* The attach point for this category of programs
22612270
* should be specified via btf_id during program load.

kernel/bpf/trampoline.c

+38-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
#include <linux/filter.h>
66
#include <linux/ftrace.h>
77

8+
/* dummy _ops. The verifier will operate on target program's ops. */
9+
const struct bpf_verifier_ops bpf_extension_verifier_ops = {
10+
};
11+
const struct bpf_prog_ops bpf_extension_prog_ops = {
12+
};
13+
814
/* btf_vmlinux has ~22k attachable functions. 1k htab is enough. */
915
#define TRAMPOLINE_HASH_BITS 10
1016
#define TRAMPOLINE_TABLE_SIZE (1 << TRAMPOLINE_HASH_BITS)
@@ -194,8 +200,10 @@ static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(enum bpf_attach_type t)
194200
switch (t) {
195201
case BPF_TRACE_FENTRY:
196202
return BPF_TRAMP_FENTRY;
197-
default:
203+
case BPF_TRACE_FEXIT:
198204
return BPF_TRAMP_FEXIT;
205+
default:
206+
return BPF_TRAMP_REPLACE;
199207
}
200208
}
201209

@@ -204,12 +212,31 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog)
204212
enum bpf_tramp_prog_type kind;
205213
struct bpf_trampoline *tr;
206214
int err = 0;
215+
int cnt;
207216

208217
tr = prog->aux->trampoline;
209218
kind = bpf_attach_type_to_tramp(prog->expected_attach_type);
210219
mutex_lock(&tr->mutex);
211-
if (tr->progs_cnt[BPF_TRAMP_FENTRY] + tr->progs_cnt[BPF_TRAMP_FEXIT]
212-
>= BPF_MAX_TRAMP_PROGS) {
220+
if (tr->extension_prog) {
221+
/* cannot attach fentry/fexit if extension prog is attached.
222+
* cannot overwrite extension prog either.
223+
*/
224+
err = -EBUSY;
225+
goto out;
226+
}
227+
cnt = tr->progs_cnt[BPF_TRAMP_FENTRY] + tr->progs_cnt[BPF_TRAMP_FEXIT];
228+
if (kind == BPF_TRAMP_REPLACE) {
229+
/* Cannot attach extension if fentry/fexit are in use. */
230+
if (cnt) {
231+
err = -EBUSY;
232+
goto out;
233+
}
234+
tr->extension_prog = prog;
235+
err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_JUMP, NULL,
236+
prog->bpf_func);
237+
goto out;
238+
}
239+
if (cnt >= BPF_MAX_TRAMP_PROGS) {
213240
err = -E2BIG;
214241
goto out;
215242
}
@@ -240,9 +267,17 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog)
240267
tr = prog->aux->trampoline;
241268
kind = bpf_attach_type_to_tramp(prog->expected_attach_type);
242269
mutex_lock(&tr->mutex);
270+
if (kind == BPF_TRAMP_REPLACE) {
271+
WARN_ON_ONCE(!tr->extension_prog);
272+
err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_JUMP,
273+
tr->extension_prog->bpf_func, NULL);
274+
tr->extension_prog = NULL;
275+
goto out;
276+
}
243277
hlist_del(&prog->aux->tramp_hlist);
244278
tr->progs_cnt[kind]--;
245279
err = bpf_trampoline_update(prog->aux->trampoline);
280+
out:
246281
mutex_unlock(&tr->mutex);
247282
return err;
248283
}

0 commit comments

Comments
 (0)