BPF验证因寄存器偏移失败:状态机探针加载报错求助
问题描述
尝试用iovisor/BCC编写带状态的BPF探针用于运行时验证,已通过固定映射state_store实现多BPF程序间状态共享。读取并打印state_store中的状态值正常,但通过automaton_s结构体的function矩阵获取next状态后,打印或更新状态都会触发BPF验证错误:math between fp pointer and register with unbounded min value is not allowed。注释相关代码可正常编译,但无法完成状态更新,尝试对*curr_state和next做边界检查未解决问题。
原代码
#include <bcc/proto.h> struct automaton_s { int initial_state; int function[4][4]; }; typedef struct automaton_s automaton_t; BPF_STACK(automaton, automaton_t, 1); BPF_TABLE_PINNED("hash", int, int, state_store, 1, "/sys/fs/bpf/state"); int trace_connect_v4_return(struct pt_regs *ctx) { int key = 0, init_val = 0; int *curr_state; curr_state = state_store.lookup_or_try_init(&key, &init_val); if (!curr_state) { bpf_trace_printk("State store is full"); return -1; } bpf_trace_printk("curr state: %d", *curr_state); automaton_t aut; if (automaton.peek(&aut) < 0) { bpf_trace_printk("automaton init"); aut = (automaton_t) { .initial_state = 0, .function = { { 1, -1, -1, -1 }, { -1, 2, -1, -1 }, { -1, -1, 3, -1 }, { -1, -1, -1, 0 } } }; automaton.push(&aut, BPF_EXIST); } int next = aut.function[*curr_state][0]; bpf_trace_printk("next val: %d", next); state_store.update(&key, &next); return 0; }
错误信息
bpf: Failed to load program: Invalid argument 0: (b7) r7 = 0 1: (63) *(u32 *)(r10 -4) = r7 last_idx 1 first_idx 0 regs=80 stack=0 before 0: (b7) r7 = 0 2: (63) *(u32 *)(r10 -8) = r7 3: (18) r1 = 0xffff98ec8a7dd800 5: (bf) r2 = r10 6: (07) r2 += -4 7: (85) call bpf_map_lookup_elem#1 8: (bf) r6 = r0 9: (55) if r6 != 0x0 goto pc+30 R0_w=inv0 R6_w=inv0 R7_w=invP0 R10=fp0 fp-8=mmmm0000 10: (18) r1 = 0xffff98ec8a7dd800 12: (bf) r6 = r10 13: (07) r6 += -4 14: (bf) r3 = r10 15: (07) r3 += -8 16: (bf) r2 = r6 17: (b7) r4 = 1 18: (85) call bpf_map_update_elem#2 19: (18) r1 = 0xffff98ec8a7dd800 21: (bf) r2 = r6 22: (85) call bpf_map_lookup_elem#1 23: (bf) r6 = r0 24: (55) if r6 != 0x0 goto pc+15 from 24 to 40: R0_w=map_value(id=0,off=0,ks=4,vs=4,imm=0) R6_w=map_value(id=0,off=0,ks=4,vs=4,imm=0) R7=invP0 R10=fp0 fp-8=mmmmmmmm 40: (73) *(u8 *)(r10 -66) = r7 41: (b7) r1 = 25637 42: (6b) *(u16 *)(r10 -68) = r1 43: (b7) r1 = 540697972 44: (63) *(u32 *)(r10 -72) = r1 45: (18) r1 = 0x6174732072727563 47: (7b) *(u64 *)(r10 -80) = r1 48: (61) r3 = *(u32 *)(r6 +0) R0_w=map_value(id=0,off=0,ks=4,vs=4,imm=0) R1_w=inv7022364302173697379 R6_w=map_value(id=0,off=0,ks=4,vs=4,imm=0) R7=invP0 R10=fp0 fp-8=mmmmmmmm fp-72=?0mmmmmm fp-80_w=inv7022364302173697379 49: (bf) r1 = r10 50: (07) r1 += -80 51: (b7) r2 = 15 52: (85) call bpf_trace_printk#6 last_idx 52 first_idx 19 regs=4 stack=0 before 51: (b7) r2 = 15 53: (18) r1 = 0xffff98ed35b5b000 55: (bf) r2 = r10 56: (07) r2 += -80 57: (85) call bpf_map_peek_elem#89 58: (67) r0 <<= 32 59: (c7) r0 s>>= 32 60: (65) if r0 s> 0xffffffff goto pc+41 R0_w=inv(id=0,umin_value=18446744071562067968,var_off=(0xffffffff80000000; 0x7fffffff),u32_min_value=-2147483648) R6=map_value(id=0,off=0,ks=4,vs=4,imm=0) R7=invP0 R10=fp0 fp-8=mmmmmmmm fp-16=????mmmm fp-24=mmmmmmmm fp-32=mmmmmmmm fp-40=mmmmmmmm fp-48=mmmmmmmm fp-56=mmmmmmmm fp-64=mmmmmmmm fp-72=mmmmmmmm fp-80=mmmmmmmm 61: (b7) r1 = 29801 62: (6b) *(u16 *)(r10 -84) = r1 63: (b7) r1 = 1852383342 64: (63) *(u32 *)(r10 -88) = r1 65: (18) r1 = 0x6f74616d6f747561 67: (7b) *(u64 *)(r10 -96) = r1 68: (b7) r7 = 0 69: (73) *(u8 *)(r10 -82) = r7 last_idx 69 first_idx 53 regs=80 stack=0 before 68: (b7) r7 = 0 70: (bf) r1 = r10 71: (07) r1 += -96 72: (b7) r2 = 15 73: (85) call bpf_trace_printk#6 last_idx 73 first_idx 53 regs=4 stack=0 before 72: (b7) r2 = 15 74: (18) r1 = 0xffffffff 76: (63) *(u32 *)(r10 -72) = r1 77: (b7) r2 = 1 78: (63) *(u32 *)(r10 -76) = r2 79: (63) *(u32 *)(r10 -64) = r1 80: (63) *(u32 *)(r10 -68) = r1 81: (b7) r2 = 2 82: (63) *(u32 *)(r10 -56) = r2 83: (63) *(u32 *)(r10 -60) = r1 84: (63) *(u32 *)(r10 -48) = r1 85: (63) *(u32 *)(r10 -52) = r1 86: (63) *(u32 *)(r10 -40) = r1 87: (63) *(u32 *)(r10 -44) = r1 88: (63) *(u32 *)(r10 -32) = r1 89: (b7) r2 = 3 90: (63) *(u32 *)(r10 -36) = r2 91: (63) *(u32 *)(r10 -24) = r1 92: (63) *(u32 *)(r10 -28) = r1 93: (63) *(u32 *)(r10 -16) = r7 94: (63) *(u32 *)(r10 -20) = r1 95: (63) *(u32 *)(r10 -80) = r7 96: (18) r1 = 0xffff98ed35b5b000 98: (bf) r2 = r10 99: (07) r2 += -80 100: (b7) r3 = 2 101: (85) call bpf_map_push_elem#87 102: (61) r1 = *(u32 *)(r6 +0) R0_w=inv(id=0) R6=map_value(id=0,off=0,ks=4,vs=4,imm=0) R7=invP0 R10=fp0 fp-8=mmmmmmmm fp-16=????mmmm fp-24=mmmmmmmm fp-32=mmmmmmmm fp-40=mmmmmmmm fp-48=mmmmmmmm fp-56=mmmmmmmm fp-64=mmmmmmmm fp-72=mmmmmmmm fp-80=mmmmmmmm fp-88=?mmmmmmm fp-96=mmmmmmmm 103: (67) r1 <<= 32 104: (c7) r1 s>>= 32 105: (67) r1 <<= 4 106: (bf) r2 = r10 107: (07) r2 += -80 108: (0f) r2 += r1 last_idx 108 first_idx 74 regs=2 stack=0 before 107: (07) r2 += -80 regs=2 stack=0 before 106: (bf) r2 = r10 regs=2 stack=0 before 105: (67) r1 <<= 4 regs=2 stack=0 before 104: (c7) r1 s>>= 32 regs=2 stack=0 before 103: (67) r1 <<= 32 regs=2 stack=0 before 102: (61) r1 = *(u32 *)(r6 +0) math between fp pointer and register with unbounded min value is not allowed processed 100 insns (limit 1000000) max_states_per_insn 0 total_states 4 peak_states 4 mark_read 2
解决方案
问题根源是BPF验证器无法确定*curr_state的取值范围,认为它可能是负数或超出数组索引的无界值,导致计算数组偏移时触发安全检查。需要添加严格的边界检查,确保*curr_state在0-3之间(因为function是4x4数组),让验证器确认索引安全。
修改后的代码:
#include <bcc/proto.h> struct automaton_s { int initial_state; int function[4][4]; }; typedef struct automaton_s automaton_t; BPF_STACK(automaton, automaton_t, 1); BPF_TABLE_PINNED("hash", int, int, state_store, 1, "/sys/fs/bpf/state"); int trace_connect_v4_return(struct pt_regs *ctx) { int key = 0, init_val = 0; int *curr_state; curr_state = state_store.lookup_or_try_init(&key, &init_val); if (!curr_state) { bpf_trace_printk("State store is full"); return -1; } bpf_trace_printk("curr state: %d", *curr_state); // 新增边界检查:确保curr_state在0-3之间 if (*curr_state < 0 || *curr_state >= 4) { bpf_trace_printk("Invalid current state: %d", *curr_state); return -1; } automaton_t aut; if (automaton.peek(&aut) < 0) { bpf_trace_printk("automaton init"); aut = (automaton_t) { .initial_state = 0, .function = { { 1, -1, -1, -1 }, { -1, 2, -1, -1 }, { -1, -1, 3, -1 }, { -1, -1, -1, 0 } } }; automaton.push(&aut, BPF_EXIST); } int next = aut.function[*curr_state][0]; bpf_trace_printk("next val: %d", next); // 可选:检查next是否为有效状态(非-1)再更新 if (next != -1) { state_store.update(&key, &next); } return 0; }
关键修改说明
- 添加
*curr_state的边界检查:通过if (*curr_state < 0 || *curr_state >= 4)确保索引不会越界,BPF验证器会识别这个检查,确认后续数组访问的安全性。 - 可选的
next状态有效性检查:因为状态机中存在-1的无效状态,添加检查避免将无效状态写入state_store。
这样修改后,BPF验证器能确认数组索引的范围,不会再触发"unbounded min value"错误,同时保证状态更新的安全性。
内容的提问来源于stack exchange,提问作者nela
相关产品推荐
相关产品推荐

