eBPF加载报错:8193跳转序列过于复杂原因咨询
我正尝试在eBPF程序中解析数据包载荷,该载荷是一组属性的集合,每个属性包含2字节头部及实际属性值:头部首字节为属性ID,次字节为属性长度。我的eBPF程序试图找到数据包中特定属性的偏移量,主循环逻辑为:检查当前偏移处的属性ID,若匹配目标ID则跳出循环;否则解析属性长度,将偏移量增加该长度后重复操作。相关C代码如下:
while (field_offset < MAX_PAYLOAD_OFFSET) { if (data + field_offset + 2 >= data_end) { goto EXIT; } uint16_t* x = (uint16_t*) (data + field_offset); char y = (*x & ATT_ID_MASK) >> ATT_ID_SHIFT; if (y == args->field_id) { flag = 1; break; } char len = *x & ATT_LEN_MASK; if (len > MAX_VALUE_SIZE) { goto EXIT; } field_offset += len; }
生成的对应eBPF字节码如下:
while (field_offset < MAX_PAYLOAD_OFFSET) 115: bf 39 00 00 00 00 00 00 r9 = r3 116: 57 09 00 00 ff ff 00 00 r9 &= 65535 117: 25 09 22 00 31 00 00 00 if r9 > 49 goto +34 <LBB1_14> ; if (data + field_offset + 2 >= data_end) 118: bf 48 00 00 00 00 00 00 r8 = r4 119: 0f 98 00 00 00 00 00 00 r8 += r9 120: bf 89 00 00 00 00 00 00 r9 = r8 121: 07 09 00 00 02 00 00 00 r9 += 2 122: 3d 59 17 00 00 00 00 00 if r9 >= r5 goto +23 <LBB1_20> ; if (y == args->field_id) 123: 69 09 00 00 00 00 00 00 r9 = *(u16 *)(r0 + 0) ; char y = (*x & ATT_ID_MASK) >> ATT_ID_SHIFT; 124: 69 82 00 00 00 00 00 00 r2 = *(u16 *)(r8 + 0) 125: bf 28 00 00 00 00 00 00 r8 = r2 126: 77 08 00 00 0c 00 00 00 r8 >>= 12 ; if (y == args->field_id) 127: 5d 98 0b 00 00 00 00 00 if r8 != r9 goto +11 <LBB1_21> ; for(;idx<args->action_count && idx < MAX_ACTION_LIST; idx++){ 128: 79 a2 f0 ff 00 00 00 00 r2 = *(u64 *)(r10 - 16) 129: 0f 12 00 00 00 00 00 00 r2 += r1 130: 69 24 06 00 00 00 00 00 r4 = *(u16 *)(r2 + 6) 131: 15 04 14 00 00 00 00 00 if r4 == 0 goto +20 <LBB1_14> 132: b7 05 00 00 00 00 00 00 r5 = 0 133: 07 03 00 00 02 00 00 00 r3 += 2 134: 0f 71 00 00 00 00 00 00 r1 += r7 135: bf 70 00 00 00 00 00 00 r0 = r7 136: 07 00 00 00 10 00 00 00 r0 += 16 137: 07 01 00 00 18 01 00 00 r1 += 280 138: 05 00 2a 00 00 00 00 00 goto +42 <LBB1_9> 0000000000000458 LBB1_21: ; if (len > MAX_VALUE_SIZE) 139: 67 02 00 00 38 00 00 00 r2 <<= 56 140: bf 28 00 00 00 00 00 00 r8 = r2 141: c7 08 00 00 38 00 00 00 r8 s>>= 56 142: 0f 83 00 00 00 00 00 00 r3 += r8 143: c7 02 00 00 20 00 00 00 r2 s>>= 32 144: 65 02 01 00 00 00 00 04 if r2 s> 67108864 goto +1 <LBB1_20> 145: 05 00 e1 ff 00 00 00 00 goto -31 <LBB1_4>
程序加载时出现如下错误:
The sequence of 8193 jumps is too complex. processed 67395 insns (limit 1000000) max_states_per_insn 17 total_states 733 peak_states 723 mark_read 6
恳请帮忙解释为何eBPF验证器认为该循环包含8193次跳转。
eBPF验证器判定存在8193次跳转,核心是它对循环的最大迭代次数做了保守的上限估算,具体逻辑如下:
循环增量的不可预测性
你的循环中,field_offset的增量len是从数据包载荷中读取的运行时数据,验证器无法提前确定这个值的大小,只能做最保守的假设:每次迭代field_offset仅增加1(最小的合法增量)。默认安全上限的触发
eBPF验证器有一个默认的循环迭代次数安全上限(通常为8192次),当它估算出循环的最大可能迭代次数超过这个值时,就会触发复杂度报错。你的循环终止条件是field_offset < MAX_PAYLOAD_OFFSET,验证器假设field_offset从0开始每次加1,直到达到阈值,这样总迭代次数会达到8193次,对应8193次跳转,超过了安全上限。代码设计的加剧因素
你的代码没有对len的最小值做检查(比如len == 0的情况),这会让验证器认为存在无限循环的风险,因此只能采用最严格的增量假设,进一步放大了估算的迭代次数。
简单来说,验证器无法证明你的循环会快速终止,只能按最坏情况估算迭代次数,当这个次数超过8192的默认上限时,就会抛出“8193次跳转过于复杂”的错误。
内容的提问来源于Stack Exchange,提问作者Ashwin Kumar

