All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/2] bpf: Fix abs(INT_MIN) undefined behavior in interpreter sdiv/smod
@ 2026-03-09 21:59 Jenny Guanni Qu
  2026-03-09 21:59 ` [PATCH v4 1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN Jenny Guanni Qu
  2026-03-09 21:59 ` [PATCH v4 2/2] selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend Jenny Guanni Qu
  0 siblings, 2 replies; 7+ messages in thread
From: Jenny Guanni Qu @ 2026-03-09 21:59 UTC (permalink / raw)
  To: bpf
  Cc: daniel, ast, andrii, mykyta.yatsenko5, yonghong.song, lkp,
	Jenny Guanni Qu

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 __safe_abs32() which handles S32_MIN correctly
and replaces all 8 abs((s32)...) call sites.

Patch 2 adds selftests covering sdiv32 and smod32 with INT_MIN
dividend to prevent regression.

Changes since v3:
  - Fixed stray blank line deletion in the file header
  - Improved __safe_abs32() comment per Yonghong Song's suggestion
  - Added JIT vs interpreter context to selftest commit message
    per Yonghong Song's feedback

Changes since v2:
  - Simplified __safe_abs32() to use -(u32)x instead of special-casing
    S32_MIN, per Mykyta Yatsenko's suggestion

Changes since v1:
  - Moved __safe_abs32() helper above the kerneldoc comment block
    for ___bpf_prog_run() to fix build warnings reported by
    kernel test robot

Jenny Guanni Qu (2):
  bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN
  selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend

 kernel/bpf/core.c                             | 22 ++++---
 .../selftests/bpf/progs/verifier_sdiv.c       | 58 +++++++++++++++++++
 2 files changed, 72 insertions(+), 8 deletions(-)

-- 
2.34.1

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v4 1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN
  2026-03-09 21:59 [PATCH v4 0/2] bpf: Fix abs(INT_MIN) undefined behavior in interpreter sdiv/smod Jenny Guanni Qu
@ 2026-03-09 21:59 ` Jenny Guanni Qu
  2026-03-10  0:28   ` Mykyta Yatsenko
  2026-03-10 19:03   ` Alexei Starovoitov
  2026-03-09 21:59 ` [PATCH v4 2/2] selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend Jenny Guanni Qu
  1 sibling, 2 replies; 7+ messages in thread
From: Jenny Guanni Qu @ 2026-03-09 21:59 UTC (permalink / raw)
  To: bpf
  Cc: daniel, ast, andrii, mykyta.yatsenko5, yonghong.song, lkp,
	Jenny Guanni Qu

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 __safe_abs32() 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.

Fixes: ec0e2da95f72 ("bpf: Support new signed div/mod instructions.")
Acked-by: Yonghong Song <yonghong.song@linux.dev>
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..a620d4d6f567 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
+/* Safe absolute value for s32 to prevent undefined behavior for abs(S32_MIN) */
+static inline u32 __safe_abs32(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 = __safe_abs32((s32)DST);
+			AX = do_div(AX, __safe_abs32((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 = __safe_abs32((s32)DST);
+			AX = do_div(AX, __safe_abs32((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 = __safe_abs32((s32)DST);
+			do_div(AX, __safe_abs32((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 = __safe_abs32((s32)DST);
+			do_div(AX, __safe_abs32((s32)IMM));
 			if (((s32)DST < 0) == ((s32)IMM < 0))
 				DST = (u32)AX;
 			else
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 2/2] selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend
  2026-03-09 21:59 [PATCH v4 0/2] bpf: Fix abs(INT_MIN) undefined behavior in interpreter sdiv/smod Jenny Guanni Qu
  2026-03-09 21:59 ` [PATCH v4 1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN Jenny Guanni Qu
@ 2026-03-09 21:59 ` Jenny Guanni Qu
  2026-03-10  2:05   ` Jiayuan Chen
  1 sibling, 1 reply; 7+ messages in thread
From: Jenny Guanni Qu @ 2026-03-09 21:59 UTC (permalink / raw)
  To: bpf
  Cc: daniel, ast, andrii, mykyta.yatsenko5, yonghong.song, 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)

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] 7+ messages in thread

* Re: [PATCH v4 1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN
  2026-03-09 21:59 ` [PATCH v4 1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN Jenny Guanni Qu
@ 2026-03-10  0:28   ` Mykyta Yatsenko
  2026-03-10 19:03   ` Alexei Starovoitov
  1 sibling, 0 replies; 7+ messages in thread
From: Mykyta Yatsenko @ 2026-03-10  0:28 UTC (permalink / raw)
  To: Jenny Guanni Qu, bpf; +Cc: daniel, ast, andrii, yonghong.song, lkp

On 3/9/26 9:59 PM, Jenny Guanni Qu wrote:
> 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 __safe_abs32() 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.
> 
> Fixes: ec0e2da95f72 ("bpf: Support new signed div/mod instructions.")
> Acked-by: Yonghong Song <yonghong.song@linux.dev>
> Signed-off-by: Jenny Guanni Qu <qguanni@gmail.com>
> ---
>   kernel/bpf/core.c | 22 ++++++++++++++--------
>   1 file changed, 14 insertions(+), 8 deletions(-)
> 
Acked-by: Mykyta Yatsenko <yatsenko@meta.com>
> diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
> index 3ece2da55625..a620d4d6f567 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
> +/* Safe absolute value for s32 to prevent undefined behavior for abs(S32_MIN) */
> +static inline u32 __safe_abs32(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 = __safe_abs32((s32)DST);
> +			AX = do_div(AX, __safe_abs32((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 = __safe_abs32((s32)DST);
> +			AX = do_div(AX, __safe_abs32((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 = __safe_abs32((s32)DST);
> +			do_div(AX, __safe_abs32((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 = __safe_abs32((s32)DST);
> +			do_div(AX, __safe_abs32((s32)IMM));
>   			if (((s32)DST < 0) == ((s32)IMM < 0))
>   				DST = (u32)AX;
>   			else


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 2/2] selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend
  2026-03-09 21:59 ` [PATCH v4 2/2] selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend Jenny Guanni Qu
@ 2026-03-10  2:05   ` Jiayuan Chen
  2026-03-10  2:20     ` Yonghong Song
  0 siblings, 1 reply; 7+ messages in thread
From: Jiayuan Chen @ 2026-03-10  2:05 UTC (permalink / raw)
  To: Jenny Guanni Qu, bpf
  Cc: daniel, ast, andrii, mykyta.yatsenko5, yonghong.song, lkp


On 3/10/26 5:59 AM, Jenny Guanni Qu wrote:
> 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)
>
> 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
>   


Minor nits: extra blank line before #else, and smod32 tests only cover 
the imm
form — a reg variant (w0 s%%= w1) would make coverage symmetric with sdiv32.

Neither worth a respin.

Reviewed-by: Jiayuan Chen <jiayuan.chen@linux.dev>

>   SEC("socket")

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 2/2] selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend
  2026-03-10  2:05   ` Jiayuan Chen
@ 2026-03-10  2:20     ` Yonghong Song
  0 siblings, 0 replies; 7+ messages in thread
From: Yonghong Song @ 2026-03-10  2:20 UTC (permalink / raw)
  To: Jiayuan Chen, Jenny Guanni Qu, bpf
  Cc: daniel, ast, andrii, mykyta.yatsenko5, lkp



On 3/9/26 7:05 PM, Jiayuan Chen wrote:
>
> On 3/10/26 5:59 AM, Jenny Guanni Qu wrote:
>> 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)
>>
>> 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
>
>
> Minor nits: extra blank line before #else, and smod32 tests only cover 
> the imm
> form — a reg variant (w0 s%%= w1) would make coverage symmetric with 
> sdiv32.

Right, the committer should be able to remove the extra line.

Acked-by: Yonghong Song <yonghong.song@linux.dev>

>
> Neither worth a respin.
>
> Reviewed-by: Jiayuan Chen <jiayuan.chen@linux.dev>
>
>>   SEC("socket")


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN
  2026-03-09 21:59 ` [PATCH v4 1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN Jenny Guanni Qu
  2026-03-10  0:28   ` Mykyta Yatsenko
@ 2026-03-10 19:03   ` Alexei Starovoitov
  1 sibling, 0 replies; 7+ messages in thread
From: Alexei Starovoitov @ 2026-03-10 19:03 UTC (permalink / raw)
  To: Jenny Guanni Qu
  Cc: bpf, Daniel Borkmann, Alexei Starovoitov, Andrii Nakryiko,
	Mykyta Yatsenko, Yonghong Song, kbuild test robot

On Mon, Mar 9, 2026 at 2:59 PM Jenny Guanni Qu <qguanni@gmail.com> wrote:
>
> 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 __safe_abs32() 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.
>
> Fixes: ec0e2da95f72 ("bpf: Support new signed div/mod instructions.")
> Acked-by: Yonghong Song <yonghong.song@linux.dev>
> 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..a620d4d6f567 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
> +/* Safe absolute value for s32 to prevent undefined behavior for abs(S32_MIN) */
> +static inline u32 __safe_abs32(s32 x)

Double underscore is used for helpers when there is a similar
helper without double underscore.
So pls rename this one to abs32() or abs_s32() and drop 'inline'.

s32 is the only case or there are more?

pw-bot: cr

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-03-10 19:03 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-09 21:59 [PATCH v4 0/2] bpf: Fix abs(INT_MIN) undefined behavior in interpreter sdiv/smod Jenny Guanni Qu
2026-03-09 21:59 ` [PATCH v4 1/2] bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN Jenny Guanni Qu
2026-03-10  0:28   ` Mykyta Yatsenko
2026-03-10 19:03   ` Alexei Starovoitov
2026-03-09 21:59 ` [PATCH v4 2/2] selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend Jenny Guanni Qu
2026-03-10  2:05   ` Jiayuan Chen
2026-03-10  2:20     ` Yonghong Song

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.