具有 void 返回类型的 eBPF 函数:验证者问题

问题描述 投票:0回答:1

我运行了一个示例 ebpf 代码,以 void 作为返回类型,并在返回之前调用 bpf_printk。我原以为这段代码会被验证者拒绝。然而,我能够成功地将它加载到内核中,但它有点损坏了我的网络子系统,因为它与 xdp 相关,并且必须重新启动我的计算机才能使网络再次工作。这正常吗?

内核:Linux asus 6.5.0-28-generic #29~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Apr 4 14:39:20 UTC 2 x86_64 x86_64 x86_64 GNU/Linux 操作系统:Ubuntu 22.04.4 LTS

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

int counter = 0;

SEC("xdp")
void error_packet_count(void *ctx) {
    bpf_printk("%d", counter);
    counter++;
    return;
}

char LICENSE[] SEC("license") = "Dual BSD/GPL";
linux ebpf bpf xdp-bpf
1个回答
0
投票

TL;DR. 看起来编译器忽略了您的

void
返回值并添加了
exit
eBPF 指令。


我已经能够使用以下程序重现这一点,这里是在加载到内核后由 bpftool 转储的:

$ sudo bpftool prog dump xlated id 201
void error_packet_count(struct xdp_md * ctx):
; void error_packet_count(struct xdp_md *ctx) {
   0: (b7) r1 = 0
; ({ char _fmt[] = "Hello, World!\n"; bpf_trace_printk_(_fmt, sizeof(_fmt)); });
   1: (73) *(u8 *)(r10 -2) = r1
   2: (b7) r1 = 2593
   3: (6b) *(u16 *)(r10 -4) = r1
   4: (b7) r1 = 1684828783
   5: (63) *(u32 *)(r10 -8) = r1
   6: (18) r1 = 0x57202c6f6c6c6548
   8: (7b) *(u64 *)(r10 -16) = r1
   9: (bf) r1 = r10
; 
  10: (07) r1 += -16
; ({ char _fmt[] = "Hello, World!\n"; bpf_trace_printk_(_fmt, sizeof(_fmt)); });
  11: (b7) r2 = 15
  12: (85) call bpf_trace_printk#-108752
; }
  13: (95) exit

我们可以看到,尽管 C 程序有一个

void
返回值,但 BPF 程序以
exit
指令结束。看起来 编译器会自动添加
exit
指令


有趣的是,如果您删除对

bpf_trace_printk
的调用,它将失败并显示:

0: (95) exit
R0 !read_ok

这里,验证者抱怨 R0 未初始化。 R0 包含程序的返回代码,因此在程序结束时不应未初始化。

调用

bpf_trace_printk
可以避免此验证器错误,因为它最终会将
bpf_trace_printk
的返回值放入 R0 中。因此,无论
bpf_trace_printk
给你什么返回值都将是你的 BPF 程序的返回代码。

© www.soinside.com 2019 - 2024. All rights reserved.