tailcall in freplace 有 BUG?真的?假的!
最近,偶然发现在 freplace 里使用 tailcall 有 BUG。尽管,我的同事早就发现了有问题,但不确定是不是内核的问题。所以,我就花了点时间去确认了一下出错的地方。
BUG 分析
当给 freplace
里的 PROG_ARRAY bpf map 更新表项时,会触发 BUG:
1
2
|
# ./tailcall-in-freplace
2023/08/27 15:00:28 Failed to put tailcall: update: invalid argument
|
看看内核源代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
// KERNEL version:5.15
__sys_bpf() // ${KERNEL}/kernel/bpf/syscall.c
|-->map_update_elem()
|-->bpf_map_update_value()
|-->bpf_fd_array_map_update_elem() { // ${KERNEL}/kernel/bpf/arraymap.c
ufd = *(u32 *)value;
new_ptr = map->ops->map_fd_get_ptr(map, map_file, ufd);
if (IS_ERR(new_ptr))
return PTR_ERR(new_ptr);
}
/
/
/
|-->prog_fd_array_get_ptr() // ${KERNEL}/kernel/bpf/arraymap.c
|-->bpf_prog_array_compatible() { // ${KERNEL}/kernel/bpf/core.c
bool ret;
if (fp->kprobe_override)
return false;
spin_lock(&array->aux->owner.lock);
if (!array->aux->owner.type) {
/* There's no owner yet where we could check for
* compatibility.
*/
array->aux->owner.type = fp->type;
array->aux->owner.jited = fp->jited;
ret = true;
} else {
ret = array->aux->owner.type == fp->type &&
array->aux->owner.jited == fp->jited;
}
spin_unlock(&array->aux->owner.lock);
return ret;
}
|
怀疑是 bpf_prog_array_compatible()
检查失败了,怎么办?
用 retsnoop
trace 一下看看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
# retsnoop -e '*sys_bpf' -a 'bpf_fd_array_*' -a 'bpf_prog_array_compatible' -T
Receiving data...
15:22:51.303186 -> 15:22:51.303245 TID/PID 5275/5273 (tailcall-in-fre/tailcall-in-fre):
FUNCTION CALL TRACE RESULT DURATION
--------------------------------------- --------- --------
→ __x64_sys_bpf
→ __sys_bpf
→ bpf_fd_array_map_update_elem
↔ bpf_prog_array_compatible [false] 0.594us
← bpf_fd_array_map_update_elem [-EINVAL] 1.889us
← __sys_bpf [-EINVAL] 7.433us
← __x64_sys_bpf [-EINVAL] 8.730us
entry_SYSCALL_64_after_hwframe+0x61 (arch/x86/entry/entry_64.S:118:0)
__kretprobe_trampoline+0x0
__kretprobe_trampoline+0x0
__sys_bpf+0x88c (kernel/bpf/syscall.c:4602:9)
map_update_elem+0x1af (kernel/bpf/syscall.c:1163:8)
__kretprobe_trampoline+0x0
! 8us [-EINVAL] __x64_sys_bpf
! 7us [-EINVAL] __sys_bpf
! 1us [-EINVAL] bpf_fd_array_map_update_elem
|
噢,果真是 bpf_prog_array_compatible()
检查失败了。
再来用 drgn-bpf 看看 array->aux->owner.typ
是什么。
1
2
3
4
5
6
|
# drgn tools/bpf_inspect.py m -D
58: BPF_MAP_TYPE_PERF_EVENT_ARRAY events
59: BPF_MAP_TYPE_PERCPU_ARRAY socks
60: BPF_MAP_TYPE_ARRAY .rodata
61: BPF_MAP_TYPE_PROG_ARRAY progs
owner bpf prog: BPF_PROG_TYPE_EXT jited:True
|
果真,owner.type
有问题,是 BPF_PROG_TYPE_EXT
,而不是预期的 BPF_PROG_TYPE_KPROBE
。
而在 6.5.0-rc4+ 内核里:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# drgn tools/bpf_inspect.py m -D
131: BPF_MAP_TYPE_ARRAY .rodata
132: BPF_MAP_TYPE_PERCPU_ARRAY socks
133: BPF_MAP_TYPE_PERF_EVENT_ARRAY events
134: BPF_MAP_TYPE_PROG_ARRAY progs
owner bpf prog: BPF_PROG_TYPE_KPROBE jited:True
progs[0]: BPF_PROG_TYPE_KPROBE handle_new_connection bpf_prog_12e9e2b2c252d64a_handle_new_connection 0xffffffffc00fb63c
progs[1]: BPF_PROG_TYPE_KPROBE handle_new_connection bpf_prog_12e9e2b2c252d64a_handle_new_connection 0xffffffffc00fb63c
progs[2]: BPF_PROG_TYPE_KPROBE handle_new_connection bpf_prog_12e9e2b2c252d64a_handle_new_connection 0xffffffffc00fb63c
progs[3]: BPF_PROG_TYPE_KPROBE handle_new_connection bpf_prog_12e9e2b2c252d64a_handle_new_connection 0xffffffffc00fb63c
progs[4]: BPF_PROG_TYPE_KPROBE handle_new_connection bpf_prog_12e9e2b2c252d64a_handle_new_connection 0xffffffffc00fb63c
progs[5]: BPF_PROG_TYPE_KPROBE handle_new_connection bpf_prog_12e9e2b2c252d64a_handle_new_connection 0xffffffffc00fb63c
progs[6]: BPF_PROG_TYPE_KPROBE handle_new_connection bpf_prog_12e9e2b2c252d64a_handle_new_connection 0xffffffffc00fb63c
progs[7]: BPF_PROG_TYPE_KPROBE handle_new_connection bpf_prog_12e9e2b2c252d64a_handle_new_connection 0xffffffffc00fb63c
progs[8]: BPF_PROG_TYPE_KPROBE handle_new_connection bpf_prog_12e9e2b2c252d64a_handle_new_connection 0xffffffffc00fb63c
progs[9]: BPF_PROG_TYPE_KPROBE handle_new_connection bpf_prog_12e9e2b2c252d64a_handle_new_connection 0xffffffffc00fb63c
135: BPF_MAP_TYPE_ARRAY .rodata
|
tailcall
in freplace
demo 源代码:GitHub - Asphaltt/tailcall-in-freplace。
BUG 修复
最终,经过一番研究,发现该 BUG 在 6.2 内核里修复了:
一声叹息,就这么失去一个给内核提交 patch 的机会。
小结
一番折腾,弄了个 eBPF 加强版的 drgn:drgn-bpf,欢迎 star。