* [PATCH v5 1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN
2026-03-11 1:11 [PATCH v5 0/2] bpf: Fix abs(INT_MIN) undefined behavior in interpreter sdiv/smod Jenny Guanni Qu
@ 2026-03-11 1:11 ` Jenny Guanni Qu
2026-03-11 1:11 ` [PATCH v5 2/2] selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend Jenny Guanni Qu
2026-03-21 20:20 ` [PATCH v5 0/2] bpf: Fix abs(INT_MIN) undefined behavior in interpreter sdiv/smod patchwork-bot+netdevbpf
2 siblings, 0 replies; 4+ messages in thread
From: Jenny Guanni Qu @ 2026-03-11 1:11 UTC (permalink / raw)
To: bpf
Cc: daniel, ast, andrii, mykyta.yatsenko5, yonghong.song,
jiayuan.chen, lkp, Jenny Guanni Qu, Mykyta Yatsenko
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: ec0e2da95f72 ("bpf: Support new signed div/mod instructions.")
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Acked-by: Mykyta Yatsenko <yatsenko@meta.com>
Signed-off-by: Jenny Guanni Qu <qguanni@gmail.com>
---
kernel/bpf/core.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 3ece2da55625..3e2843ffb098 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1736,6 +1736,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
@@ -1900,8 +1906,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *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
@@ -1928,8 +1934,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *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
@@ -1955,8 +1961,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *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
@@ -1982,8 +1988,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *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
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH v5 2/2] selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend
2026-03-11 1:11 [PATCH v5 0/2] bpf: Fix abs(INT_MIN) undefined behavior in interpreter sdiv/smod Jenny Guanni Qu
2026-03-11 1:11 ` [PATCH v5 1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN Jenny Guanni Qu
@ 2026-03-11 1:11 ` Jenny Guanni Qu
2026-03-21 20:20 ` [PATCH v5 0/2] bpf: Fix abs(INT_MIN) undefined behavior in interpreter sdiv/smod patchwork-bot+netdevbpf
2 siblings, 0 replies; 4+ messages in thread
From: Jenny Guanni Qu @ 2026-03-11 1:11 UTC (permalink / raw)
To: bpf
Cc: daniel, ast, andrii, mykyta.yatsenko5, yonghong.song,
jiayuan.chen, lkp, Jenny Guanni Qu
Add tests to verify that signed 32-bit division and modulo operations
produce correct results when the dividend is INT_MIN (0x80000000).
The bug fixed in the previous commit only affects the BPF interpreter
path. When JIT is enabled (the default on most architectures), the
native CPU division instruction produces the correct result and these
tests pass regardless. With bpf_jit_enable=0, the interpreter is used
and without the previous fix, INT_MIN / 2 incorrectly returns
0x40000000 instead of 0xC0000000 due to abs(S32_MIN) undefined
behavior, causing these tests to fail.
Test cases:
- SDIV32 INT_MIN / 2 = -1073741824 (imm and reg divisor)
- SMOD32 INT_MIN % 2 = 0 (positive and negative divisor)
Reviewed-by: Jiayuan Chen <jiayuan.chen@linux.dev>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Signed-off-by: Jenny Guanni Qu <qguanni@gmail.com>
---
.../selftests/bpf/progs/verifier_sdiv.c | 58 +++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/verifier_sdiv.c b/tools/testing/selftests/bpf/progs/verifier_sdiv.c
index 148d2299e5b4..fd59d57e8e37 100644
--- a/tools/testing/selftests/bpf/progs/verifier_sdiv.c
+++ b/tools/testing/selftests/bpf/progs/verifier_sdiv.c
@@ -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")
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH v5 0/2] bpf: Fix abs(INT_MIN) undefined behavior in interpreter sdiv/smod
2026-03-11 1:11 [PATCH v5 0/2] bpf: Fix abs(INT_MIN) undefined behavior in interpreter sdiv/smod Jenny Guanni Qu
2026-03-11 1:11 ` [PATCH v5 1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN Jenny Guanni Qu
2026-03-11 1:11 ` [PATCH v5 2/2] selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend Jenny Guanni Qu
@ 2026-03-21 20:20 ` patchwork-bot+netdevbpf
2 siblings, 0 replies; 4+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-03-21 20:20 UTC (permalink / raw)
To: Guanni Qu
Cc: bpf, daniel, ast, andrii, mykyta.yatsenko5, yonghong.song,
jiayuan.chen, lkp
Hello:
This series was applied to bpf/bpf.git (master)
by Alexei Starovoitov <ast@kernel.org>:
On Wed, 11 Mar 2026 01:11:14 +0000 you wrote:
> 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).
>
> [...]
Here is the summary with links:
- [v5,1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN
https://git.kernel.org/bpf/bpf/c/c77b30bd1dcb
- [v5,2/2] selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend
https://git.kernel.org/bpf/bpf/c/4ac95c65efea
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 4+ messages in thread