You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修改说明

  1. 添加*curr_state的边界检查:通过if (*curr_state < 0 || *curr_state >= 4)确保索引不会越界,BPF验证器会识别这个检查,确认后续数组访问的安全性。
  2. 可选的next状态有效性检查:因为状态机中存在-1的无效状态,添加检查避免将无效状态写入state_store。

这样修改后,BPF验证器能确认数组索引的范围,不会再触发"unbounded min value"错误,同时保证状态更新的安全性。

内容的提问来源于stack exchange,提问作者nela

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 10:10:30