BPF TC程序验证失败求助:invalid access to memory内存访问错误
问题:BPF TC程序payload过滤时内存访问验证失败
编写BPF TC程序实现基于数据包payload的子串过滤,提取payload前若干字节到栈缓冲区后调用子串搜索函数,程序验证失败,报错:
invalid access to memory, mem_size=1 off=1 size=1 R3 min value is outside of the allowed memory range
相关代码
common.h
#define MAX_RULES 50 #define MAX_RULE_NAME 20 #define MAX_BYTE_PATTERN 11 struct filter_rule { char rule_name[MAX_RULE_NAME]; char byte_pattern[MAX_BYTE_PATTERN]; }; unsigned char mystrlen(const char *s, unsigned char max_len) { unsigned char i = 0; if(s == NULL) return 0; for (i = 0; i < max_len; i++) { if (s[i] == '\0') return i; } return i; } bool find_substring(const char *str, const char *search) { if(str != NULL && search != NULL) { unsigned char l1 = mystrlen(str,50); unsigned char l2 = mystrlen(search, MAX_BYTE_PATTERN); unsigned char i = 0, j = 0; unsigned char flag = 0; if(l1 == 0 || l2 == 0) return false; for (i = 0; i <= l1 - l2; i++) { for (j = i; j < i + l2; j++) { flag = 1; if (str[j] != search[j - i]) { flag = 0; break; } } if (flag == 1) { break; } } if(flag == 1) return true; else return false; } else { return false; } }
bpf.c
unsigned long long load_byte(void *skb, unsigned long long off) asm("llvm.bpf.load.byte"); struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(key_size, sizeof(int)); __uint(value_size, sizeof(struct filter_rule)); __uint(max_entries, MAX_RULES); } filter_rules SEC(".maps"); SEC("tc") int ingress_hndlr(struct __sk_buff *ctx) { //Extract the TCP packet payload into buff unsigned char buff[51] = {0}; for(int i = 0; i<tcp_payload_length && i<50; ++i) { buff[i] = load_byte(ctx, payload_offset+i); //bpf_printk("%x", buff[i]); } unsigned int key = 0; struct filter_rule *rule = bpf_map_lookup_elem(&filter_rules, &key); if(rule) { bool ret = find_substring((const char*)buff, rule->byte_pattern); if(ret) { ++drop_cnt; return TC_ACT_SHOT; } } }
错误日志
Validating find_substring() func#1... ; bool find_substring(const char *str, const char *search) 189: (b7) r0 = 0 ; if(str != NULL && search != NULL) 190: (15) if r1 == 0x0 goto pc+55 R0_w=invP0 R1=mem(id=0,ref_obj_id=0,off=0,imm=0) R2=mem_or_null(id=4,ref_obj_id=0,off=0,imm=0) R10=fp0 191: (15) if r2 == 0x0 goto pc+54 R0_w=invP0 R1=mem(id=0,ref_obj_id=0,off=0,imm=0) R2=mem(id=0,ref_obj_id=0,off=0,imm=0) R10=fp0 192: (b7) r4 = 0 ; if (s[i] == '\0') 193: (bf) r3 = r1 194: (0f) r3 += r4 195: (71) r3 = *(u8 *)(r3 +0) R0_w=invP0 R1=mem(id=0,ref_obj_id=0,off=0,imm=0) R2=mem(id=0,ref_obj_id=0,off=0,imm=0) R3_w=mem(id=0,ref_obj_id=0,off=0,imm=0) R4_w=invP0 R10=fp0 ; if (s[i] == '\0') 196: (15) if r3 == 0x0 goto pc+4 R0=invP0 R1=mem(id=0,ref_obj_id=0,off=0,imm=0) R2=mem(id=0,ref_obj_id=0,off=0,imm=0) R3=invP(id=0,umax_value=255,var_off=(0x0; 0xff)) R4=invP0 R10=fp0 197: (b7) r3 = 50 ; for (i = 0; i < max_len; i++) 198: (07) r4 += 1 ; for (i = 0; i < max_len; i++) 199: (15) if r4 == 0x32 goto pc+2 200: (05) goto pc-8 ; if (s[i] == '\0') 193: (bf) r3 = r1 194: (0f) r3 += r4 195: (71) r3 = *(u8 *)(r3 +0) R0=invP0 R1=mem(id=0,ref_obj_id=0,off=0,imm=0) R2=mem(id=0,ref_obj_id=0,off=0,imm=0) R3_w=mem(id=0,ref_obj_id=0,off=1,imm=0) R4_w=invP1 R10=fp0 invalid access to memory, mem_size=1 off=1 size=1 R3 min value is outside of the allowed memory range processed 15 insns (limit 1000000) max_states_per_insn 0 total_states 1 peak_states 1 mark_read 1 -- END PROG LOAD LOG -- libbpf: prog 'ingress_hndlr': failed to load: -13 libbpf: failed to load object 'minimal_bpf' libbpf: failed to load BPF skeleton 'minimal_bpf': -13 Failed to load and verify BPF skeleton
错误原因分析
BPF验证器的核心作用是确保程序不会非法访问内存。这里的错误是因为验证器无法推断出find_substring函数中str参数(对应栈上的buff数组)的内存边界:
- 虽然
buff是栈上分配的51字节数组,但传递给find_substring时仅以const char*形式传递,验证器无法关联到它的实际大小。 - 在
mystrlen循环中,当i增加到1时,访问str[1],验证器无法确认该地址在合法内存范围内,因此判定为非法访问。
解决方法
1. 给字符串函数补充长度参数,明确内存边界
修改find_substring,让它同时接收目标字符串的最大长度,这样验证器能明确知道内存访问的上限,避免越界判断错误。
2. 优化循环边界检查
确保所有内存访问的索引都被严格限制在缓冲区范围内,让验证器能清晰推断出访问的合法性。
修改后的代码示例
common.h 修改
#define MAX_RULES 50 #define MAX_RULE_NAME 20 #define MAX_BYTE_PATTERN 11 #define MAX_PAYLOAD_BUFF_LEN 50 // 和bpf.c中buff的有效长度对应 struct filter_rule { char rule_name[MAX_RULE_NAME]; char byte_pattern[MAX_BYTE_PATTERN]; }; unsigned char mystrlen(const char *s, unsigned char max_len) { unsigned char i = 0; if(s == NULL) return 0; for (i = 0; i < max_len; i++) { if (s[i] == '\0') return i; } return i; } // 新增目标字符串的最大长度参数str_max_len bool find_substring(const char *str, unsigned char str_max_len, const char *search) { if(str != NULL && search != NULL) { unsigned char l1 = mystrlen(str, str_max_len); unsigned char l2 = mystrlen(search, MAX_BYTE_PATTERN); unsigned char i = 0, j = 0; unsigned char flag = 0; if(l1 == 0 || l2 == 0 || l2 > l1) return false; // 确保外层循环i的上限不会导致内层j越界 for (i = 0; i <= l1 - l2; i++) { // 额外添加检查,确保j不会超过str_max_len if (i + l2 > str_max_len) break; flag = 1; for (j = 0; j < l2; j++) { if (str[i + j] != search[j]) { flag = 0; break; } } if (flag == 1) { break; } } return flag == 1; } else { return false; } }
bpf.c 修改
unsigned long long load_byte(void *skb, unsigned long long off) asm("llvm.bpf.load.byte"); struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(key_size, sizeof(int)); __uint(value_size, sizeof(struct filter_rule)); __uint(max_entries, MAX_RULES); } filter_rules SEC(".maps"); // 补充定义tcp_payload_length和payload_offset(原代码中缺失) unsigned int tcp_payload_length = 0; unsigned int payload_offset = 0; unsigned int drop_cnt = 0; SEC("tc") int ingress_hndlr(struct __sk_buff *ctx) { //Extract the TCP packet payload into buff unsigned char buff[51] = {0}; for(int i = 0; i < tcp_payload_length && i < MAX_PAYLOAD_BUFF_LEN; ++i) { buff[i] = load_byte(ctx, payload_offset + i); //bpf_printk("%x", buff[i]); } unsigned int key = 0; struct filter_rule *rule = bpf_map_lookup_elem(&filter_rules, &key); if(rule) { // 传递buff的最大有效长度MAX_PAYLOAD_BUFF_LEN bool ret = find_substring((const char*)buff, MAX_PAYLOAD_BUFF_LEN, rule->byte_pattern); if(ret) { ++drop_cnt; return TC_ACT_SHOT; } } return TC_ACT_OK; // 补充默认返回值,避免程序异常 }
其他注意事项
- 原代码中
tcp_payload_length、payload_offset、drop_cnt未定义,需补充定义或通过合法方式获取(比如从数据包解析得到)。 - 确保所有路径都有返回值,原
ingress_hndlr函数在不满足条件时没有返回值,会导致验证失败。
内容的提问来源于stack exchange,提问作者unmukt jain
相关产品推荐
相关产品推荐

