From: Ibrahim Zein In regs_refine_cond_op(), the BPF_JNE case narrows register bounds without guarding against wrap-around at type boundaries. When umin_value equals U64_MAX, incrementing it wraps to 0, producing an inconsistent bounds state (umin=0, umax=U64_MAX-1). This can be triggered by constructing a register state where: r1 = unknown_byte | 0xFFFFFFFFFFFFFFFE -> var_off = (value=U64_MAX-1, mask=1) -> constrain with JGE U64_MAX -> umin=umax=U64_MAX, tnum non-const -> compare with BPF_JNE const=U64_MAX -> wrap occurs The comment above these checks incorrectly claims this boundary condition is impossible. However, is_reg_const() uses tnum_is_const() which requires mask == 0, so a register can have umin == umax while tnum is non-const (mask != 0). Remove the misleading comment and add explicit boundary guards to prevent wrap-around in all {u,s}{min,max}{32,64} variants. Fixes: d028f87517d6 ("bpf: make the verifier tracks the "not equal" for regs") Reported-by: Ibrahim Zein Signed-off-by: Ibrahim Zein --- --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -17046,31 +17046,30 @@ */ val = reg_const_value(reg2, is_jmp32); if (is_jmp32) { - /* u32_min_value is not equal to 0xffffffff at this point, - * because otherwise u32_max_value is 0xffffffff as well, - * in such a case both reg1 and reg2 would be constants, - * jump would be predicted and reg_set_min_max() won't - * be called. - * - * Same reasoning works for all {u,s}{min,max}{32,64} cases - * below. - */ - if (reg1->u32_min_value == (u32)val) + if (reg1->u32_min_value == (u32)val && + reg1->u32_min_value != U32_MAX) reg1->u32_min_value++; - if (reg1->u32_max_value == (u32)val) + if (reg1->u32_max_value == (u32)val && + reg1->u32_max_value != 0) reg1->u32_max_value--; - if (reg1->s32_min_value == (s32)val) + if (reg1->s32_min_value == (s32)val && + reg1->s32_min_value != S32_MAX) reg1->s32_min_value++; - if (reg1->s32_max_value == (s32)val) + if (reg1->s32_max_value == (s32)val && + reg1->s32_max_value != S32_MIN) reg1->s32_max_value--; } else { - if (reg1->umin_value == (u64)val) + if (reg1->umin_value == (u64)val && + reg1->umin_value != U64_MAX) reg1->umin_value++; - if (reg1->umax_value == (u64)val) + if (reg1->umax_value == (u64)val && + reg1->umax_value != 0) reg1->umax_value--; - if (reg1->smin_value == (s64)val) + if (reg1->smin_value == (s64)val && + reg1->smin_value != S64_MAX) reg1->smin_value++; - if (reg1->smax_value == (s64)val) + if (reg1->smax_value == (s64)val && + reg1->smax_value != S64_MIN) reg1->smax_value--; } break;