CVE-2020-27194 or another 0-day?
在看触发的程序时,有一个点令我很困惑,触发的漏洞寄存器边界条件要求是 [1, 0x600000001]。
漏洞相关的代码是
if (dst_reg->smin_value < 0 || smin_val < 0) {
// ...
} else {
/* ORing two positives gives a positive, so safe to
* cast result into s64.
*/
dst_reg->smin_value = dst_reg->umin_value;
dst_reg->smax_value = dst_reg->umax_value;
}
按照作者的说法,会进入 else 分支触发漏洞代码,给 smin_value 和 smax_value 都赋值上 1,从而让 verifier 误认为其是一个常量。
然而华点是什么?拥有 [1, 0x600000001] 边界范围的寄存器,smin_value 应该会小于 0。例如 0xffffffff 在 32bit 情况下是一个负数,根本就不会进入 else 分支!
调试发现此时 verifier 认为 smin_value 为 1,溯源一下发现漏洞点出现在 __reg_combine_64_into_32() 函数之中
if (__reg64_bound_s32(reg->smin_value) {
reg->s32_min_value = (s32)reg->smin_value;
reg->s32_max_value = (s32)reg->smax_value;
}
代码认为如果 smin_value 在 32b 能表示的范围,那么 s32_min_value 就应该等于 smin_value。然而这是不正确的,就如同上面那个 [1, 0x600000001] 边界的寄存器,其 s32_min_value 被错误赋值为了 1。
漏洞!0day!CVE!
然而我是属于 2022 年的人,这个漏洞已经在 patch 中被修复了 ; )
@@ -1314,10 +1312,10 @@ static void __reg_combine_64_into_32(struct bpf_reg_state *reg)
{
__mark_reg32_unbounded(reg);
- if (__reg64_bound_s32(reg->smin_value))
+ if (__reg64_bound_s32(reg->smin_value) && __reg64_bound_s32(reg->smax_value)) {
reg->s32_min_value = (s32)reg->smin_value;
- if (__reg64_bound_s32(reg->smax_value))
reg->s32_max_value = (s32)reg->smax_value;
+ }
if (__reg64_bound_u32(reg->umin_value))
reg->u32_min_value = (u32)reg->umin_value;
if (__reg64_bound_u32(reg->umax_value))
确实,当 smin_value 和 smax_value 都在 32bit 可表示范围内时逻辑才成立。
有符号边界这样计算有问题,无符号边界就可以了?查看无符号边界的计算方式
if (__reg64_bound_u32(reg->umin_value)) {
reg->u32_min_value = (u32)reg->umin_value;
reg->u32_max_value = (u32)reg->umax_value;
}
考虑刚才的例子 [1, 0x600000001],u32_min_value 被计算为了 1,然而正确值应该为 0,u32_max_value 也被计算为了 1,然而正确值应该为 0xffffffff!
漏洞!0day!CVE!
然而这个漏洞也在 patch 中被修复了,而且分配了 CVE-2021-31440,还参加了 pwn2own ;(