$30 off During Our Annual Pro Sale. View Details »

waruiBPF

 waruiBPF

以下動画のスライドです。
https://youtu.be/UKynfPaLm0Q

Avatar for Satoru Takeuchi

Satoru Takeuchi PRO

December 08, 2025
Tweet

More Decks by Satoru Takeuchi

Other Decks in Technology

Transcript

  1. verifierのエントリポイント … static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32

    uattr_size) { … err = security_bpf_prog_load(prog, attr, token, uattr.is_kernel); if (err) goto free_prog_sec; /* run eBPF verifier */ err = bpf_check(&prog, attr, uattr, uattr_size); if (err < 0) goto free_used_maps; prog = bpf_prog_select_runtime(prog, &err); if (err < 0) goto free_used_maps; … kernel/bpf/syscall.c:
  2. verifierを呼ばなければいいのでは? diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index a1f18681721c..199529d79ecf 100644 --- a/kernel/bpf/syscall.c

    +++ b/kernel/bpf/syscall.c @@ -2766,9 +2766,9 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) goto free_prog_sec; /* run eBPF verifier */ - err = bpf_check(&prog, attr, uattr, uattr_size); - if (err < 0) - goto free_used_maps; + //err = bpf_check(&prog, attr, uattr, uattr_size); + //if (err < 0) + // goto free_used_maps; prog = bpf_prog_select_runtime(prog, &err); if (err < 0)
  3. …というわけにはいかなかった • カーネル起動時に以下ログを出した後にハングする • 調査結果 ◦ HID(human interface device)ドライバはeBPFプログラムを持っている ▪

    📝 ユーザプログラムだけでなくカーネルサブシステムも eBPFプログラムを動かせる ◦ HIDドライバの初期化処理で、この eBPFプログラムをロード ◦ ロード処理が期待通り動作せずにハング … calling hid_bpf_init …
  4. なにがまずかったのか • verifierはチェック結果をstruct bpf_verifier_envという構造体に保存 ◦ この構造体を後でeBPF VMが使う ◦ 例えばJITコンパイルのヒントに使う •

    bpf_verify()を呼ばないようにするとbpf_verifier_envが適切に更新されない • 後続処理が期待通り動作しなくなり、ハング
  5. ソリューション • 全てのチェック関数のエラーを握り潰す • 全てのエラーreturnをreturn 0にするという単純置換はダメ ◦ エラー時にearly returnしているところは処理を継続させなければいけない ◦

    チェックに直接関係無いエラーはそのまま残しておかなければいけない ▪ 例: データ構造内の要素を得る関数が返す -ENOENT(要素が存在しなかった ) • 規模 ◦ verifierの処理本体(kernel/verifier.go)は約25000行 ◦ エラーを返しているのは 600箇所程度 ▪ verify失敗によりエラーを返している箇所は全て改造
  6. なんとなくできた • 以下のような差分が235箇所 diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index e500ae801ffe..511a4ef42138 100644

    --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c … @@ -5057,14 +5057,12 @@ static int check_mem_region_access(struct bpf_verifier_env *env, u32 regno, reg->smin_value + off < 0)) { verbose(env, "R%d min value is negative, either use unsigned index or do a if (index >=0) check.\n", regno); - return -EACCES; } …
  7. 実行するコードが長い場合は? #include <linux/bpf.h> #include <bpf/bpf_helpers.h> … SEC("tracepoint/syscalls/sys_enter_execve") int trace_exec(struct sys_enter_execve_args

    *ctx) { volatile int dummy = 0; for (int i = 0; i < 1000000; i++) dummy++; bpf_printk("execve: %d\n", dummy); return 0; }
  8. 動いた! … $ sudo bpftool prog load test.o /sys/fs/bpf/waruibpf autoattach

    … sudo bpftool prog load test.o /sys/fs/bpf/waruibpf autoattach sudo cat /sys/kernel/debug/tracing/trace_pipe kubelet-2102 [001] ...21 147.172604: bpf_trace_printk: execve: 1000000 …
  9. パケット長のチェックを省くと? #include "vmlinux.h" #include <bpf/bpf_helpers.h> #include <bpf/bpf_endian.h> char LICENSE[] SEC("license")

    = "GPL"; SEC("xdp") int xdp_print_eth_proto(struct xdp_md *ctx) { void *data = (void *)(long)ctx->data; void *data_end = (void *)(long)ctx->data_end; //if (data + sizeof(struct ethhdr) > data_end) // return XDP_PASS; struct ethhdr *eth = data; __u16 proto = bpf_ntohs(eth->h_proto); bpf_printk("XDP eth proto = 0x%x\n", proto); return XDP_PASS; }
  10. これも動いた! … $ sudo ip link set dev eth0 xdp

    obj test.o sec xdp … <idle>-0 [002] ..s2. 290.277071: bpf_trace_printk: XDP eth proto = 0x800 …
  11. 実はこんなことも… SEC("tracepoint/syscalls/sys_enter_execve") int trace_exec(struct sys_enter_execve_args *ctx) { // Please set

    the output of `sudo grep “ D jiffies_64$” /proc/kallsyms` unsigned long jiffies_64 = (unsigned long)0xffffffffabc079c0; bpf_printk("execve: %lu\n", jiffies_64); return 0; } $ sudo grep " D jiffies_64$" /proc/kallsyms ffffffffabc079c0 D jiffies_64 このシステムでは1msに一回増える
  12. できちゃいます … sudo bpftool prog load test.o /sys/fs/bpf/waruibpf autoattach sudo

    cat /sys/kernel/debug/tracing/trace_pipe … cpuUsage.sh-3371 [002] ...21 703.282295: bpf_trace_printk: execve: 4295370609 sed-3372 [000] ...21 703.283616: bpf_trace_printk: execve: 4295370610 cat-3373 [000] ...21 703.284819: bpf_trace_printk: execve: 4295370611 sleep-3374 [000] ...21 703.285651: bpf_trace_printk: execve: 4295370612 ls-3380 [002] ...21 704.180586: bpf_trace_printk: execve: 4295371507 sed-3382 [000] ...21 704.286887: bpf_trace_printk: execve: 4295371614 cat-3383 [000] ...21 704.288296: bpf_trace_printk: execve: 4295371615 … *:: eBPFはbpf_jiffies64ヘルパー関数を使って jiffies_64にアクセスできます