Skip to content

Commit 838e969

Browse files
yonghong-songAlexei Starovoitov
authored andcommitted
bpf: Introduce bpf_func_info
This patch added interface to load a program with the following additional information: . prog_btf_fd . func_info, func_info_rec_size and func_info_cnt where func_info will provide function range and type_id corresponding to each function. The func_info_rec_size is introduced in the UAPI to specify struct bpf_func_info size passed from user space. This intends to make bpf_func_info structure growable in the future. If the kernel gets a different bpf_func_info size from userspace, it will try to handle user request with part of bpf_func_info it can understand. In this patch, kernel can understand struct bpf_func_info { __u32 insn_offset; __u32 type_id; }; If user passed a bpf func_info record size of 16 bytes, the kernel can still handle part of records with the above definition. If verifier agrees with function range provided by the user, the bpf_prog ksym for each function will use the func name provided in the type_id, which is supposed to provide better encoding as it is not limited by 16 bytes program name limitation and this is better for bpf program which contains multiple subprograms. The bpf_prog_info interface is also extended to return btf_id, func_info, func_info_rec_size and func_info_cnt to userspace, so userspace can print out the function prototype for each xlated function. The insn_offset in the returned func_info corresponds to the insn offset for xlated functions. With other jit related fields in bpf_prog_info, userspace can also print out function prototypes for each jited function. Signed-off-by: Yonghong Song <yhs@fb.com> Signed-off-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 78a2540 commit 838e969

File tree

8 files changed

+209
-8
lines changed

8 files changed

+209
-8
lines changed

include/linux/bpf.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ struct bpf_prog_aux {
316316
void *security;
317317
#endif
318318
struct bpf_prog_offload *offload;
319+
struct btf *btf;
320+
u32 type_id; /* type id for this prog/func */
319321
union {
320322
struct work_struct work;
321323
struct rcu_head rcu;
@@ -527,7 +529,8 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size)
527529
}
528530

529531
/* verify correctness of eBPF program */
530-
int bpf_check(struct bpf_prog **fp, union bpf_attr *attr);
532+
int bpf_check(struct bpf_prog **fp, union bpf_attr *attr,
533+
union bpf_attr __user *uattr);
531534
void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
532535

533536
/* Map specifics */

include/linux/bpf_verifier.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
204204
struct bpf_subprog_info {
205205
u32 start; /* insn idx of function entry point */
206206
u16 stack_depth; /* max. stack depth used by this function */
207+
u32 type_id; /* btf type_id for this subprog */
207208
};
208209

209210
/* single container for all structs

include/linux/btf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,7 @@ void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
4646
struct seq_file *m);
4747
int btf_get_fd_by_id(u32 id);
4848
u32 btf_id(const struct btf *btf);
49+
const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id);
50+
const char *btf_name_by_offset(const struct btf *btf, u32 offset);
4951

5052
#endif

include/uapi/linux/bpf.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,10 @@ union bpf_attr {
338338
* (context accesses, allowed helpers, etc).
339339
*/
340340
__u32 expected_attach_type;
341+
__u32 prog_btf_fd; /* fd pointing to BTF type data */
342+
__u32 func_info_rec_size; /* userspace bpf_func_info size */
343+
__aligned_u64 func_info; /* func info */
344+
__u32 func_info_cnt; /* number of bpf_func_info records */
341345
};
342346

343347
struct { /* anonymous struct used by BPF_OBJ_* commands */
@@ -2638,6 +2642,10 @@ struct bpf_prog_info {
26382642
__u32 nr_jited_func_lens;
26392643
__aligned_u64 jited_ksyms;
26402644
__aligned_u64 jited_func_lens;
2645+
__u32 btf_id;
2646+
__u32 func_info_rec_size;
2647+
__aligned_u64 func_info;
2648+
__u32 func_info_cnt;
26412649
} __attribute__((aligned(8)));
26422650

26432651
struct bpf_map_info {
@@ -2949,4 +2957,9 @@ struct bpf_flow_keys {
29492957
};
29502958
};
29512959

2960+
struct bpf_func_info {
2961+
__u32 insn_offset;
2962+
__u32 type_id;
2963+
};
2964+
29522965
#endif /* _UAPI__LINUX_BPF_H__ */

kernel/bpf/btf.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ static bool btf_name_valid_identifier(const struct btf *btf, u32 offset)
474474
return !*src;
475475
}
476476

477-
static const char *btf_name_by_offset(const struct btf *btf, u32 offset)
477+
const char *btf_name_by_offset(const struct btf *btf, u32 offset)
478478
{
479479
if (!offset)
480480
return "(anon)";
@@ -484,7 +484,7 @@ static const char *btf_name_by_offset(const struct btf *btf, u32 offset)
484484
return "(invalid-name-offset)";
485485
}
486486

487-
static const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id)
487+
const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id)
488488
{
489489
if (type_id > btf->nr_types)
490490
return NULL;

kernel/bpf/core.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121
* Kris Katterjohn - Added many additional checks in bpf_check_classic()
2222
*/
2323

24+
#include <uapi/linux/btf.h>
2425
#include <linux/filter.h>
2526
#include <linux/skbuff.h>
2627
#include <linux/vmalloc.h>
2728
#include <linux/random.h>
2829
#include <linux/moduleloader.h>
2930
#include <linux/bpf.h>
31+
#include <linux/btf.h>
3032
#include <linux/frame.h>
3133
#include <linux/rbtree_latch.h>
3234
#include <linux/kallsyms.h>
@@ -390,6 +392,8 @@ bpf_get_prog_addr_region(const struct bpf_prog *prog,
390392
static void bpf_get_prog_name(const struct bpf_prog *prog, char *sym)
391393
{
392394
const char *end = sym + KSYM_NAME_LEN;
395+
const struct btf_type *type;
396+
const char *func_name;
393397

394398
BUILD_BUG_ON(sizeof("bpf_prog_") +
395399
sizeof(prog->tag) * 2 +
@@ -404,6 +408,15 @@ static void bpf_get_prog_name(const struct bpf_prog *prog, char *sym)
404408

405409
sym += snprintf(sym, KSYM_NAME_LEN, "bpf_prog_");
406410
sym = bin2hex(sym, prog->tag, sizeof(prog->tag));
411+
412+
/* prog->aux->name will be ignored if full btf name is available */
413+
if (prog->aux->btf) {
414+
type = btf_type_by_id(prog->aux->btf, prog->aux->type_id);
415+
func_name = btf_name_by_offset(prog->aux->btf, type->name_off);
416+
snprintf(sym, (size_t)(end - sym), "_%s", func_name);
417+
return;
418+
}
419+
407420
if (prog->aux->name[0])
408421
snprintf(sym, (size_t)(end - sym), "_%s", prog->aux->name);
409422
else

kernel/bpf/syscall.c

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,7 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
12131213
/* bpf_prog_free_id() must be called first */
12141214
bpf_prog_free_id(prog, do_idr_lock);
12151215
bpf_prog_kallsyms_del_all(prog);
1216+
btf_put(prog->aux->btf);
12161217

12171218
call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
12181219
}
@@ -1437,9 +1438,9 @@ bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type,
14371438
}
14381439

14391440
/* last field in 'union bpf_attr' used by this command */
1440-
#define BPF_PROG_LOAD_LAST_FIELD expected_attach_type
1441+
#define BPF_PROG_LOAD_LAST_FIELD func_info_cnt
14411442

1442-
static int bpf_prog_load(union bpf_attr *attr)
1443+
static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
14431444
{
14441445
enum bpf_prog_type type = attr->prog_type;
14451446
struct bpf_prog *prog;
@@ -1525,7 +1526,7 @@ static int bpf_prog_load(union bpf_attr *attr)
15251526
goto free_prog;
15261527

15271528
/* run eBPF verifier */
1528-
err = bpf_check(&prog, attr);
1529+
err = bpf_check(&prog, attr, uattr);
15291530
if (err < 0)
15301531
goto free_used_maps;
15311532

@@ -2079,6 +2080,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
20792080
info.xlated_prog_len = 0;
20802081
info.nr_jited_ksyms = 0;
20812082
info.nr_jited_func_lens = 0;
2083+
info.func_info_cnt = 0;
20822084
goto done;
20832085
}
20842086

@@ -2216,6 +2218,55 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
22162218
}
22172219
}
22182220

2221+
if (prog->aux->btf) {
2222+
u32 ucnt, urec_size;
2223+
2224+
info.btf_id = btf_id(prog->aux->btf);
2225+
2226+
ucnt = info.func_info_cnt;
2227+
info.func_info_cnt = prog->aux->func_cnt ? : 1;
2228+
urec_size = info.func_info_rec_size;
2229+
info.func_info_rec_size = sizeof(struct bpf_func_info);
2230+
if (ucnt) {
2231+
/* expect passed-in urec_size is what the kernel expects */
2232+
if (urec_size != info.func_info_rec_size)
2233+
return -EINVAL;
2234+
2235+
if (bpf_dump_raw_ok()) {
2236+
struct bpf_func_info kern_finfo;
2237+
char __user *user_finfo;
2238+
u32 i, insn_offset;
2239+
2240+
user_finfo = u64_to_user_ptr(info.func_info);
2241+
if (prog->aux->func_cnt) {
2242+
ucnt = min_t(u32, info.func_info_cnt, ucnt);
2243+
insn_offset = 0;
2244+
for (i = 0; i < ucnt; i++) {
2245+
kern_finfo.insn_offset = insn_offset;
2246+
kern_finfo.type_id = prog->aux->func[i]->aux->type_id;
2247+
if (copy_to_user(user_finfo, &kern_finfo,
2248+
sizeof(kern_finfo)))
2249+
return -EFAULT;
2250+
2251+
/* func[i]->len holds the prog len */
2252+
insn_offset += prog->aux->func[i]->len;
2253+
user_finfo += urec_size;
2254+
}
2255+
} else {
2256+
kern_finfo.insn_offset = 0;
2257+
kern_finfo.type_id = prog->aux->type_id;
2258+
if (copy_to_user(user_finfo, &kern_finfo,
2259+
sizeof(kern_finfo)))
2260+
return -EFAULT;
2261+
}
2262+
} else {
2263+
info.func_info_cnt = 0;
2264+
}
2265+
}
2266+
} else {
2267+
info.func_info_cnt = 0;
2268+
}
2269+
22192270
done:
22202271
if (copy_to_user(uinfo, &info, info_len) ||
22212272
put_user(info_len, &uattr->info.info_len))
@@ -2501,7 +2552,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
25012552
err = map_get_next_key(&attr);
25022553
break;
25032554
case BPF_PROG_LOAD:
2504-
err = bpf_prog_load(&attr);
2555+
err = bpf_prog_load(&attr, uattr);
25052556
break;
25062557
case BPF_OBJ_PIN:
25072558
err = bpf_obj_pin(&attr);

kernel/bpf/verifier.c

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212
* General Public License for more details.
1313
*/
14+
#include <uapi/linux/btf.h>
1415
#include <linux/kernel.h>
1516
#include <linux/types.h>
1617
#include <linux/slab.h>
1718
#include <linux/bpf.h>
19+
#include <linux/btf.h>
1820
#include <linux/bpf_verifier.h>
1921
#include <linux/filter.h>
2022
#include <net/netlink.h>
@@ -4639,6 +4641,114 @@ static int check_cfg(struct bpf_verifier_env *env)
46394641
return ret;
46404642
}
46414643

4644+
/* The minimum supported BTF func info size */
4645+
#define MIN_BPF_FUNCINFO_SIZE 8
4646+
#define MAX_FUNCINFO_REC_SIZE 252
4647+
4648+
static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env,
4649+
union bpf_attr *attr, union bpf_attr __user *uattr)
4650+
{
4651+
u32 i, nfuncs, urec_size, min_size, prev_offset;
4652+
u32 krec_size = sizeof(struct bpf_func_info);
4653+
struct bpf_func_info krecord = {};
4654+
const struct btf_type *type;
4655+
void __user *urecord;
4656+
struct btf *btf;
4657+
int ret = 0;
4658+
4659+
nfuncs = attr->func_info_cnt;
4660+
if (!nfuncs)
4661+
return 0;
4662+
4663+
if (nfuncs != env->subprog_cnt) {
4664+
verbose(env, "number of funcs in func_info doesn't match number of subprogs\n");
4665+
return -EINVAL;
4666+
}
4667+
4668+
urec_size = attr->func_info_rec_size;
4669+
if (urec_size < MIN_BPF_FUNCINFO_SIZE ||
4670+
urec_size > MAX_FUNCINFO_REC_SIZE ||
4671+
urec_size % sizeof(u32)) {
4672+
verbose(env, "invalid func info rec size %u\n", urec_size);
4673+
return -EINVAL;
4674+
}
4675+
4676+
btf = btf_get_by_fd(attr->prog_btf_fd);
4677+
if (IS_ERR(btf)) {
4678+
verbose(env, "unable to get btf from fd\n");
4679+
return PTR_ERR(btf);
4680+
}
4681+
4682+
urecord = u64_to_user_ptr(attr->func_info);
4683+
min_size = min_t(u32, krec_size, urec_size);
4684+
4685+
for (i = 0; i < nfuncs; i++) {
4686+
ret = bpf_check_uarg_tail_zero(urecord, krec_size, urec_size);
4687+
if (ret) {
4688+
if (ret == -E2BIG) {
4689+
verbose(env, "nonzero tailing record in func info");
4690+
/* set the size kernel expects so loader can zero
4691+
* out the rest of the record.
4692+
*/
4693+
if (put_user(min_size, &uattr->func_info_rec_size))
4694+
ret = -EFAULT;
4695+
}
4696+
goto free_btf;
4697+
}
4698+
4699+
if (copy_from_user(&krecord, urecord, min_size)) {
4700+
ret = -EFAULT;
4701+
goto free_btf;
4702+
}
4703+
4704+
/* check insn_offset */
4705+
if (i == 0) {
4706+
if (krecord.insn_offset) {
4707+
verbose(env,
4708+
"nonzero insn_offset %u for the first func info record",
4709+
krecord.insn_offset);
4710+
ret = -EINVAL;
4711+
goto free_btf;
4712+
}
4713+
} else if (krecord.insn_offset <= prev_offset) {
4714+
verbose(env,
4715+
"same or smaller insn offset (%u) than previous func info record (%u)",
4716+
krecord.insn_offset, prev_offset);
4717+
ret = -EINVAL;
4718+
goto free_btf;
4719+
}
4720+
4721+
if (env->subprog_info[i].start != krecord.insn_offset) {
4722+
verbose(env, "func_info BTF section doesn't match subprog layout in BPF program\n");
4723+
ret = -EINVAL;
4724+
goto free_btf;
4725+
}
4726+
4727+
/* check type_id */
4728+
type = btf_type_by_id(btf, krecord.type_id);
4729+
if (!type || BTF_INFO_KIND(type->info) != BTF_KIND_FUNC) {
4730+
verbose(env, "invalid type id %d in func info",
4731+
krecord.type_id);
4732+
ret = -EINVAL;
4733+
goto free_btf;
4734+
}
4735+
4736+
if (i == 0)
4737+
prog->aux->type_id = krecord.type_id;
4738+
env->subprog_info[i].type_id = krecord.type_id;
4739+
4740+
prev_offset = krecord.insn_offset;
4741+
urecord += urec_size;
4742+
}
4743+
4744+
prog->aux->btf = btf;
4745+
return 0;
4746+
4747+
free_btf:
4748+
btf_put(btf);
4749+
return ret;
4750+
}
4751+
46424752
/* check %cur's range satisfies %old's */
46434753
static bool range_within(struct bpf_reg_state *old,
46444754
struct bpf_reg_state *cur)
@@ -5939,6 +6049,9 @@ static int jit_subprogs(struct bpf_verifier_env *env)
59396049
func[i]->aux->name[0] = 'F';
59406050
func[i]->aux->stack_depth = env->subprog_info[i].stack_depth;
59416051
func[i]->jit_requested = 1;
6052+
/* the btf will be freed only at prog->aux */
6053+
func[i]->aux->btf = prog->aux->btf;
6054+
func[i]->aux->type_id = env->subprog_info[i].type_id;
59426055
func[i] = bpf_int_jit_compile(func[i]);
59436056
if (!func[i]->jited) {
59446057
err = -ENOTSUPP;
@@ -6325,7 +6438,8 @@ static void free_states(struct bpf_verifier_env *env)
63256438
kfree(env->explored_states);
63266439
}
63276440

6328-
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
6441+
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
6442+
union bpf_attr __user *uattr)
63296443
{
63306444
struct bpf_verifier_env *env;
63316445
struct bpf_verifier_log *log;
@@ -6397,6 +6511,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
63976511
if (ret < 0)
63986512
goto skip_full_check;
63996513

6514+
ret = check_btf_func(env->prog, env, attr, uattr);
6515+
if (ret < 0)
6516+
goto skip_full_check;
6517+
64006518
ret = do_check(env);
64016519
if (env->cur_state) {
64026520
free_verifier_state(env->cur_state, true);

0 commit comments

Comments
 (0)