Skip to content

Commit c77b30b

Browse files
Jenny Guanni QuAlexei Starovoitov
authored andcommitted
bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN
The BPF interpreter's signed 32-bit division and modulo handlers use the kernel abs() macro on s32 operands. The abs() macro documentation (include/linux/math.h) explicitly states the result is undefined when the input is the type minimum. When DST contains S32_MIN (0x80000000), abs((s32)DST) triggers undefined behavior and returns S32_MIN unchanged on arm64/x86. This value is then sign-extended to u64 as 0xFFFFFFFF80000000, causing do_div() to compute the wrong result. The verifier's abstract interpretation (scalar32_min_max_sdiv) computes the mathematically correct result for range tracking, creating a verifier/interpreter mismatch that can be exploited for out-of-bounds map value access. Introduce abs_s32() which handles S32_MIN correctly by casting to u32 before negating, avoiding signed overflow entirely. Replace all 8 abs((s32)...) call sites in the interpreter's sdiv32/smod32 handlers. s32 is the only affected case -- the s64 division/modulo handlers do not use abs(). Fixes: ec0e2da ("bpf: Support new signed div/mod instructions.") Acked-by: Yonghong Song <[email protected]> Acked-by: Mykyta Yatsenko <[email protected]> Signed-off-by: Jenny Guanni Qu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent a1e5c46 commit c77b30b

1 file changed

Lines changed: 14 additions & 8 deletions

File tree

kernel/bpf/core.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,12 @@ bool bpf_opcode_in_insntable(u8 code)
17571757
}
17581758

17591759
#ifndef CONFIG_BPF_JIT_ALWAYS_ON
1760+
/* Absolute value of s32 without undefined behavior for S32_MIN */
1761+
static u32 abs_s32(s32 x)
1762+
{
1763+
return x >= 0 ? (u32)x : -(u32)x;
1764+
}
1765+
17601766
/**
17611767
* ___bpf_prog_run - run eBPF program on a given context
17621768
* @regs: is the array of MAX_BPF_EXT_REG eBPF pseudo-registers
@@ -1921,8 +1927,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
19211927
DST = do_div(AX, (u32) SRC);
19221928
break;
19231929
case 1:
1924-
AX = abs((s32)DST);
1925-
AX = do_div(AX, abs((s32)SRC));
1930+
AX = abs_s32((s32)DST);
1931+
AX = do_div(AX, abs_s32((s32)SRC));
19261932
if ((s32)DST < 0)
19271933
DST = (u32)-AX;
19281934
else
@@ -1949,8 +1955,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
19491955
DST = do_div(AX, (u32) IMM);
19501956
break;
19511957
case 1:
1952-
AX = abs((s32)DST);
1953-
AX = do_div(AX, abs((s32)IMM));
1958+
AX = abs_s32((s32)DST);
1959+
AX = do_div(AX, abs_s32((s32)IMM));
19541960
if ((s32)DST < 0)
19551961
DST = (u32)-AX;
19561962
else
@@ -1976,8 +1982,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
19761982
DST = (u32) AX;
19771983
break;
19781984
case 1:
1979-
AX = abs((s32)DST);
1980-
do_div(AX, abs((s32)SRC));
1985+
AX = abs_s32((s32)DST);
1986+
do_div(AX, abs_s32((s32)SRC));
19811987
if (((s32)DST < 0) == ((s32)SRC < 0))
19821988
DST = (u32)AX;
19831989
else
@@ -2003,8 +2009,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
20032009
DST = (u32) AX;
20042010
break;
20052011
case 1:
2006-
AX = abs((s32)DST);
2007-
do_div(AX, abs((s32)IMM));
2012+
AX = abs_s32((s32)DST);
2013+
do_div(AX, abs_s32((s32)IMM));
20082014
if (((s32)DST < 0) == ((s32)IMM < 0))
20092015
DST = (u32)AX;
20102016
else

0 commit comments

Comments
 (0)