Merge branch 'bpf-fix-abs-int_min-undefined-behavior-in-interpreter-sdiv-smod'
Jenny Guanni Qu says:
====================
bpf: Fix abs(INT_MIN) undefined behavior in interpreter sdiv/smod
The BPF interpreter's signed 32-bit division and modulo handlers use
abs() on s32 operands, which is undefined for S32_MIN. This causes
the interpreter to compute wrong results, creating a mismatch with
the verifier's range tracking.
For example, INT_MIN / 2 returns 0x40000000 instead of the correct
0xC0000000. The verifier tracks the correct range, so a crafted BPF
program can exploit the mismatch for out-of-bounds map value access
(confirmed by KASAN).
Patch 1 introduces abs_s32() which handles S32_MIN correctly and
replaces all 8 abs((s32)...) call sites. s32 is the only affected
case -- the s64 handlers do not use abs().
Patch 2 adds selftests covering sdiv32 and smod32 with INT_MIN
dividend to prevent regression.
Changes since v4:
- Renamed __safe_abs32() to abs_s32() and dropped inline keyword
per Alexei Starovoitov's feedback
Changes since v3:
- Fixed stray blank line deletion in the file header
- Improved comment per Yonghong Song's suggestion
- Added JIT vs interpreter context to selftest commit message
Changes since v2:
- Simplified to use -(u32)x per Mykyta Yatsenko's suggestion
Changes since v1:
- Moved helper above kerneldoc comment block to fix build warnings
====================
Link: https://patch.msgid.link/20260311011116.2108005-1-qguanni@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
1abd3feb36
|
|
@ -1757,6 +1757,12 @@ bool bpf_opcode_in_insntable(u8 code)
|
|||
}
|
||||
|
||||
#ifndef CONFIG_BPF_JIT_ALWAYS_ON
|
||||
/* Absolute value of s32 without undefined behavior for S32_MIN */
|
||||
static u32 abs_s32(s32 x)
|
||||
{
|
||||
return x >= 0 ? (u32)x : -(u32)x;
|
||||
}
|
||||
|
||||
/**
|
||||
* ___bpf_prog_run - run eBPF program on a given context
|
||||
* @regs: is the array of MAX_BPF_EXT_REG eBPF pseudo-registers
|
||||
|
|
@ -1921,8 +1927,8 @@ select_insn:
|
|||
DST = do_div(AX, (u32) SRC);
|
||||
break;
|
||||
case 1:
|
||||
AX = abs((s32)DST);
|
||||
AX = do_div(AX, abs((s32)SRC));
|
||||
AX = abs_s32((s32)DST);
|
||||
AX = do_div(AX, abs_s32((s32)SRC));
|
||||
if ((s32)DST < 0)
|
||||
DST = (u32)-AX;
|
||||
else
|
||||
|
|
@ -1949,8 +1955,8 @@ select_insn:
|
|||
DST = do_div(AX, (u32) IMM);
|
||||
break;
|
||||
case 1:
|
||||
AX = abs((s32)DST);
|
||||
AX = do_div(AX, abs((s32)IMM));
|
||||
AX = abs_s32((s32)DST);
|
||||
AX = do_div(AX, abs_s32((s32)IMM));
|
||||
if ((s32)DST < 0)
|
||||
DST = (u32)-AX;
|
||||
else
|
||||
|
|
@ -1976,8 +1982,8 @@ select_insn:
|
|||
DST = (u32) AX;
|
||||
break;
|
||||
case 1:
|
||||
AX = abs((s32)DST);
|
||||
do_div(AX, abs((s32)SRC));
|
||||
AX = abs_s32((s32)DST);
|
||||
do_div(AX, abs_s32((s32)SRC));
|
||||
if (((s32)DST < 0) == ((s32)SRC < 0))
|
||||
DST = (u32)AX;
|
||||
else
|
||||
|
|
@ -2003,8 +2009,8 @@ select_insn:
|
|||
DST = (u32) AX;
|
||||
break;
|
||||
case 1:
|
||||
AX = abs((s32)DST);
|
||||
do_div(AX, abs((s32)IMM));
|
||||
AX = abs_s32((s32)DST);
|
||||
do_div(AX, abs_s32((s32)IMM));
|
||||
if (((s32)DST < 0) == ((s32)IMM < 0))
|
||||
DST = (u32)AX;
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1209,6 +1209,64 @@ __naked void smod32_ri_divisor_neg_1(void)
|
|||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("SDIV32, INT_MIN divided by 2, imm")
|
||||
__success __success_unpriv __retval(-1073741824)
|
||||
__naked void sdiv32_int_min_div_2_imm(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
w0 = %[int_min]; \
|
||||
w0 s/= 2; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm_const(int_min, INT_MIN)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("SDIV32, INT_MIN divided by 2, reg")
|
||||
__success __success_unpriv __retval(-1073741824)
|
||||
__naked void sdiv32_int_min_div_2_reg(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
w0 = %[int_min]; \
|
||||
w1 = 2; \
|
||||
w0 s/= w1; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm_const(int_min, INT_MIN)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("SMOD32, INT_MIN modulo 2, imm")
|
||||
__success __success_unpriv __retval(0)
|
||||
__naked void smod32_int_min_mod_2_imm(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
w0 = %[int_min]; \
|
||||
w0 s%%= 2; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm_const(int_min, INT_MIN)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__description("SMOD32, INT_MIN modulo -2, imm")
|
||||
__success __success_unpriv __retval(0)
|
||||
__naked void smod32_int_min_mod_neg2_imm(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
w0 = %[int_min]; \
|
||||
w0 s%%= -2; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm_const(int_min, INT_MIN)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
SEC("socket")
|
||||
|
|
|
|||
Loading…
Reference in New Issue