Skip to content

Commit 7c8ce4f

Browse files
Xu KuohaiAlexei Starovoitov
authored andcommitted
bpf: Add kernel symbol for struct_ops trampoline
Without kernel symbols for struct_ops trampoline, the unwinder may produce unexpected stacktraces. For example, the x86 ORC and FP unwinders check if an IP is in kernel text by verifying the presence of the IP's kernel symbol. When a struct_ops trampoline address is encountered, the unwinder stops due to the absence of symbol, resulting in an incomplete stacktrace that consists only of direct and indirect child functions called from the trampoline. The arm64 unwinder is another example. While the arm64 unwinder can proceed across a struct_ops trampoline address, the corresponding symbol name is displayed as "unknown", which is confusing. Thus, add kernel symbol for struct_ops trampoline. The name is bpf__<struct_ops_name>_<member_name>, where <struct_ops_name> is the type name of the struct_ops, and <member_name> is the name of the member that the trampoline is linked to. Below is a comparison of stacktraces captured on x86 by perf record, before and after this patch. Before: ffffffff8116545d __lock_acquire+0xad ([kernel.kallsyms]) ffffffff81167fcc lock_acquire+0xcc ([kernel.kallsyms]) ffffffff813088f4 __bpf_prog_enter+0x34 ([kernel.kallsyms]) After: ffffffff811656bd __lock_acquire+0x30d ([kernel.kallsyms]) ffffffff81167fcc lock_acquire+0xcc ([kernel.kallsyms]) ffffffff81309024 __bpf_prog_enter+0x34 ([kernel.kallsyms]) ffffffffc000d7e9 bpf__tcp_congestion_ops_cong_avoid+0x3e ([kernel.kallsyms]) ffffffff81f250a5 tcp_ack+0x10d5 ([kernel.kallsyms]) ffffffff81f27c66 tcp_rcv_established+0x3b6 ([kernel.kallsyms]) ffffffff81f3ad03 tcp_v4_do_rcv+0x193 ([kernel.kallsyms]) ffffffff81d65a18 __release_sock+0xd8 ([kernel.kallsyms]) ffffffff81d65af4 release_sock+0x34 ([kernel.kallsyms]) ffffffff81f15c4b tcp_sendmsg+0x3b ([kernel.kallsyms]) ffffffff81f663d7 inet_sendmsg+0x47 ([kernel.kallsyms]) ffffffff81d5ab40 sock_write_iter+0x160 ([kernel.kallsyms]) ffffffff8149c67b vfs_write+0x3fb ([kernel.kallsyms]) ffffffff8149caf6 ksys_write+0xc6 ([kernel.kallsyms]) ffffffff8149cb5d __x64_sys_write+0x1d ([kernel.kallsyms]) ffffffff81009200 x64_sys_call+0x1d30 ([kernel.kallsyms]) ffffffff82232d28 do_syscall_64+0x68 ([kernel.kallsyms]) ffffffff8240012f entry_SYSCALL_64_after_hwframe+0x76 ([kernel.kallsyms]) Fixes: 85d33df ("bpf: Introduce BPF_MAP_TYPE_STRUCT_OPS") Signed-off-by: Xu Kuohai <[email protected]> Acked-by: Yonghong Song <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 821a3fa commit 7c8ce4f

File tree

4 files changed

+89
-5
lines changed

4 files changed

+89
-5
lines changed

include/linux/bpf.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1402,7 +1402,8 @@ int arch_prepare_bpf_dispatcher(void *image, void *buf, s64 *funcs, int num_func
14021402
void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from,
14031403
struct bpf_prog *to);
14041404
/* Called only from JIT-enabled code, so there's no need for stubs. */
1405-
void bpf_image_ksym_add(void *data, unsigned int size, struct bpf_ksym *ksym);
1405+
void bpf_image_ksym_init(void *data, unsigned int size, struct bpf_ksym *ksym);
1406+
void bpf_image_ksym_add(struct bpf_ksym *ksym);
14061407
void bpf_image_ksym_del(struct bpf_ksym *ksym);
14071408
void bpf_ksym_add(struct bpf_ksym *ksym);
14081409
void bpf_ksym_del(struct bpf_ksym *ksym);

kernel/bpf/bpf_struct_ops.c

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ struct bpf_struct_ops_map {
3131
* (in kvalue.data).
3232
*/
3333
struct bpf_link **links;
34+
/* ksyms for bpf trampolines */
35+
struct bpf_ksym **ksyms;
3436
u32 funcs_cnt;
3537
u32 image_pages_cnt;
3638
/* image_pages is an array of pages that has all the trampolines
@@ -585,6 +587,49 @@ int bpf_struct_ops_prepare_trampoline(struct bpf_tramp_links *tlinks,
585587
return 0;
586588
}
587589

590+
static void bpf_struct_ops_ksym_init(const char *tname, const char *mname,
591+
void *image, unsigned int size,
592+
struct bpf_ksym *ksym)
593+
{
594+
snprintf(ksym->name, KSYM_NAME_LEN, "bpf__%s_%s", tname, mname);
595+
INIT_LIST_HEAD_RCU(&ksym->lnode);
596+
bpf_image_ksym_init(image, size, ksym);
597+
}
598+
599+
static void bpf_struct_ops_map_add_ksyms(struct bpf_struct_ops_map *st_map)
600+
{
601+
u32 i;
602+
603+
for (i = 0; i < st_map->funcs_cnt; i++) {
604+
if (!st_map->ksyms[i])
605+
break;
606+
bpf_image_ksym_add(st_map->ksyms[i]);
607+
}
608+
}
609+
610+
static void bpf_struct_ops_map_del_ksyms(struct bpf_struct_ops_map *st_map)
611+
{
612+
u32 i;
613+
614+
for (i = 0; i < st_map->funcs_cnt; i++) {
615+
if (!st_map->ksyms[i])
616+
break;
617+
bpf_image_ksym_del(st_map->ksyms[i]);
618+
}
619+
}
620+
621+
static void bpf_struct_ops_map_free_ksyms(struct bpf_struct_ops_map *st_map)
622+
{
623+
u32 i;
624+
625+
for (i = 0; i < st_map->funcs_cnt; i++) {
626+
if (!st_map->ksyms[i])
627+
break;
628+
kfree(st_map->ksyms[i]);
629+
st_map->ksyms[i] = NULL;
630+
}
631+
}
632+
588633
static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
589634
void *value, u64 flags)
590635
{
@@ -601,6 +646,8 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
601646
u32 i, trampoline_start, image_off = 0;
602647
void *cur_image = NULL, *image = NULL;
603648
struct bpf_link **plink;
649+
struct bpf_ksym **pksym;
650+
const char *tname, *mname;
604651

605652
if (flags)
606653
return -EINVAL;
@@ -640,14 +687,18 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
640687
kdata = &kvalue->data;
641688

642689
plink = st_map->links;
690+
pksym = st_map->ksyms;
691+
tname = btf_name_by_offset(st_map->btf, t->name_off);
643692
module_type = btf_type_by_id(btf_vmlinux, st_ops_ids[IDX_MODULE_ID]);
644693
for_each_member(i, t, member) {
645694
const struct btf_type *mtype, *ptype;
646695
struct bpf_prog *prog;
647696
struct bpf_tramp_link *link;
697+
struct bpf_ksym *ksym;
648698
u32 moff;
649699

650700
moff = __btf_member_bit_offset(t, member) / 8;
701+
mname = btf_name_by_offset(st_map->btf, member->name_off);
651702
ptype = btf_type_resolve_ptr(st_map->btf, member->type, NULL);
652703
if (ptype == module_type) {
653704
if (*(void **)(udata + moff))
@@ -717,6 +768,13 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
717768
&bpf_struct_ops_link_lops, prog);
718769
*plink++ = &link->link;
719770

771+
ksym = kzalloc(sizeof(*ksym), GFP_USER);
772+
if (!ksym) {
773+
err = -ENOMEM;
774+
goto reset_unlock;
775+
}
776+
*pksym++ = ksym;
777+
720778
trampoline_start = image_off;
721779
err = bpf_struct_ops_prepare_trampoline(tlinks, link,
722780
&st_ops->func_models[i],
@@ -736,6 +794,12 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
736794

737795
/* put prog_id to udata */
738796
*(unsigned long *)(udata + moff) = prog->aux->id;
797+
798+
/* init ksym for this trampoline */
799+
bpf_struct_ops_ksym_init(tname, mname,
800+
image + trampoline_start,
801+
image_off - trampoline_start,
802+
ksym);
739803
}
740804

741805
if (st_ops->validate) {
@@ -784,13 +848,16 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
784848
*/
785849

786850
reset_unlock:
851+
bpf_struct_ops_map_free_ksyms(st_map);
787852
bpf_struct_ops_map_free_image(st_map);
788853
bpf_struct_ops_map_put_progs(st_map);
789854
memset(uvalue, 0, map->value_size);
790855
memset(kvalue, 0, map->value_size);
791856
unlock:
792857
kfree(tlinks);
793858
mutex_unlock(&st_map->lock);
859+
if (!err)
860+
bpf_struct_ops_map_add_ksyms(st_map);
794861
return err;
795862
}
796863

@@ -850,7 +917,10 @@ static void __bpf_struct_ops_map_free(struct bpf_map *map)
850917

851918
if (st_map->links)
852919
bpf_struct_ops_map_put_progs(st_map);
920+
if (st_map->ksyms)
921+
bpf_struct_ops_map_free_ksyms(st_map);
853922
bpf_map_area_free(st_map->links);
923+
bpf_map_area_free(st_map->ksyms);
854924
bpf_struct_ops_map_free_image(st_map);
855925
bpf_map_area_free(st_map->uvalue);
856926
bpf_map_area_free(st_map);
@@ -867,6 +937,8 @@ static void bpf_struct_ops_map_free(struct bpf_map *map)
867937
if (btf_is_module(st_map->btf))
868938
module_put(st_map->st_ops_desc->st_ops->owner);
869939

940+
bpf_struct_ops_map_del_ksyms(st_map);
941+
870942
/* The struct_ops's function may switch to another struct_ops.
871943
*
872944
* For example, bpf_tcp_cc_x->init() may switch to
@@ -979,7 +1051,11 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
9791051
st_map->links =
9801052
bpf_map_area_alloc(st_map->funcs_cnt * sizeof(struct bpf_link *),
9811053
NUMA_NO_NODE);
982-
if (!st_map->uvalue || !st_map->links) {
1054+
1055+
st_map->ksyms =
1056+
bpf_map_area_alloc(st_map->funcs_cnt * sizeof(struct bpf_ksym *),
1057+
NUMA_NO_NODE);
1058+
if (!st_map->uvalue || !st_map->links || !st_map->ksyms) {
9831059
ret = -ENOMEM;
9841060
goto errout_free;
9851061
}
@@ -1009,6 +1085,7 @@ static u64 bpf_struct_ops_map_mem_usage(const struct bpf_map *map)
10091085
vt->size - sizeof(struct bpf_struct_ops_value);
10101086
usage += vt->size;
10111087
usage += st_map->funcs_cnt * sizeof(struct bpf_link *);
1088+
usage += st_map->funcs_cnt * sizeof(struct bpf_ksym *);
10121089
usage += PAGE_SIZE;
10131090
return usage;
10141091
}

kernel/bpf/dispatcher.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from,
154154
d->image = NULL;
155155
goto out;
156156
}
157-
bpf_image_ksym_add(d->image, PAGE_SIZE, &d->ksym);
157+
bpf_image_ksym_init(d->image, PAGE_SIZE, &d->ksym);
158+
bpf_image_ksym_add(&d->ksym);
158159
}
159160

160161
prev_num_progs = d->num_progs;

kernel/bpf/trampoline.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,14 @@ bool bpf_prog_has_trampoline(const struct bpf_prog *prog)
115115
(ptype == BPF_PROG_TYPE_LSM && eatype == BPF_LSM_MAC);
116116
}
117117

118-
void bpf_image_ksym_add(void *data, unsigned int size, struct bpf_ksym *ksym)
118+
void bpf_image_ksym_init(void *data, unsigned int size, struct bpf_ksym *ksym)
119119
{
120120
ksym->start = (unsigned long) data;
121121
ksym->end = ksym->start + size;
122+
}
123+
124+
void bpf_image_ksym_add(struct bpf_ksym *ksym)
125+
{
122126
bpf_ksym_add(ksym);
123127
perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_BPF, ksym->start,
124128
PAGE_SIZE, false, ksym->name);
@@ -377,7 +381,8 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size)
377381
ksym = &im->ksym;
378382
INIT_LIST_HEAD_RCU(&ksym->lnode);
379383
snprintf(ksym->name, KSYM_NAME_LEN, "bpf_trampoline_%llu", key);
380-
bpf_image_ksym_add(image, size, ksym);
384+
bpf_image_ksym_init(image, size, ksym);
385+
bpf_image_ksym_add(ksym);
381386
return im;
382387

383388
out_free_image:

0 commit comments

Comments
 (0)