From: Nicholas Dudar <main.kalliope@gmail.com>
To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
eddyz87@gmail.com, memxor@gmail.com, tsbogend@alpha.franken.de,
johan.almbladh@anyfinetworks.com, paulburton@kernel.org
Cc: martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev,
jolsa@kernel.org, emil@etsalapatis.com, ihor.solodrai@linux.dev,
philmd@oss.qualcomm.com, bpf@vger.kernel.org,
linux-mips@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH bpf-next v3 3/3] bpf, mips: Add support for BPF_SDIV and BPF_SMOD
Date: Mon, 10 Aug 2026 15:42:15 -0400 [thread overview]
Message-ID: <20260810194215.3754591-4-main.kalliope@gmail.com> (raw)
In-Reply-To: <20260810194215.3754591-1-main.kalliope@gmail.com>
The MIPS JITs handle BPF_DIV and BPF_MOD without inspecting insn->off,
which distinguishes BPF_SDIV and BPF_SMOD. Signed operations therefore
use unsigned instructions, or unsigned helpers for ALU64 on 32-bit
MIPS, and produce unsigned results for negative operands.
Pass insn->off unchanged through immediate validation and register
emission, and interpret it only in div/mod-specific paths. Use signed
DIV/DDIV for pre-R6 and DIV/MOD/DDIV/DMOD for R6. On 32-bit MIPS, use
div64_s64() and a signed remainder helper for ALU64. Keep the signed
quotient signed while computing that remainder, and keep signed
immediates out of the unsigned power-of-two shift/mask rewrite.
check_alu_op() rejects immediate zero divisors. bpf_do_misc_fixups()
rewrites signed immediate -1 and guards register divisors that are zero
or, for signed operations, -1 before JIT compilation. The ALU32 paths
continue to zero-extend their results.
The remaining test_bpf failures are five MOVSX, eight BSWAP, three
MEMSX, and one JMP32_JA. MOVSX and MEMSX will be addressed separately;
Johan Almbladh is handling BSWAP and JMP32_JA.
Found while auditing BPF JIT handling of signed div/mod.
Assisted-by: Codex:gpt-5.6-sol
Link: https://lore.kernel.org/bpf/CAM1=_QS79dDBfaaNQXnWd61AqHd1M4o9aDMMiftnoJveNr=FZg@mail.gmail.com/
Link: https://lore.kernel.org/bpf/CAM1=_QQ7XiJEozaetLt_+kVn91YAXWpJK_pyfT9=paXseW=wwA@mail.gmail.com/
Signed-off-by: Nicholas Dudar <main.kalliope@gmail.com>
---
arch/mips/net/bpf_jit_comp.c | 35 ++++++++++++++++-------
arch/mips/net/bpf_jit_comp.h | 4 +--
arch/mips/net/bpf_jit_comp32.c | 29 ++++++++++++-------
arch/mips/net/bpf_jit_comp64.c | 51 +++++++++++++++++++++-------------
4 files changed, 78 insertions(+), 41 deletions(-)
diff --git a/arch/mips/net/bpf_jit_comp.c b/arch/mips/net/bpf_jit_comp.c
index 320180330fb3..f14de6d73122 100644
--- a/arch/mips/net/bpf_jit_comp.c
+++ b/arch/mips/net/bpf_jit_comp.c
@@ -208,7 +208,7 @@ void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src)
}
/* Validate ALU immediate range */
-bool valid_alu_i(u8 op, s32 imm)
+bool valid_alu_i(u8 op, s32 imm, s16 off)
{
switch (BPF_OP(op)) {
case BPF_NEG:
@@ -237,6 +237,9 @@ bool valid_alu_i(u8 op, s32 imm)
return imm == 0 || (imm > 0 && is_power_of_2(imm));
case BPF_DIV:
case BPF_MOD:
+ /* Do not use unsigned shift/mask rewrites for signed div/mod. */
+ if (off == 1)
+ return false;
/* imm must be an 17-bit power of two */
return (u32)imm <= 0x10000 && is_power_of_2((u32)imm);
}
@@ -339,29 +342,41 @@ void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
}
/* ALU division operation (32-bit) */
-static void emit_div(struct jit_context *ctx, u8 dst, u8 src)
+static void emit_div(struct jit_context *ctx, u8 dst, u8 src, s16 off)
{
if (cpu_has_mips32r6) {
- emit(ctx, divu_r6, dst, dst, src);
+ if (off == 1)
+ emit(ctx, div_r6, dst, dst, src);
+ else
+ emit(ctx, divu_r6, dst, dst, src);
} else {
- emit(ctx, divu, dst, src);
+ if (off == 1)
+ emit(ctx, div, dst, src);
+ else
+ emit(ctx, divu, dst, src);
emit(ctx, mflo, dst);
}
}
/* ALU modulo operation (32-bit) */
-static void emit_mod(struct jit_context *ctx, u8 dst, u8 src)
+static void emit_mod(struct jit_context *ctx, u8 dst, u8 src, s16 off)
{
if (cpu_has_mips32r6) {
- emit(ctx, modu, dst, dst, src);
+ if (off == 1)
+ emit(ctx, mod, dst, dst, src);
+ else
+ emit(ctx, modu, dst, dst, src);
} else {
- emit(ctx, divu, dst, src);
+ if (off == 1)
+ emit(ctx, div, dst, src);
+ else
+ emit(ctx, divu, dst, src);
emit(ctx, mfhi, dst);
}
}
/* ALU register operation (32-bit) */
-void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op)
+void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op, s16 off)
{
switch (BPF_OP(op)) {
/* dst = dst & src */
@@ -407,11 +422,11 @@ void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op)
break;
/* dst = dst / src */
case BPF_DIV:
- emit_div(ctx, dst, src);
+ emit_div(ctx, dst, src, off);
break;
/* dst = dst % src */
case BPF_MOD:
- emit_mod(ctx, dst, src);
+ emit_mod(ctx, dst, src, off);
break;
}
clobber_reg(ctx, dst);
diff --git a/arch/mips/net/bpf_jit_comp.h b/arch/mips/net/bpf_jit_comp.h
index a37fe20818eb..0ddedb733dd1 100644
--- a/arch/mips/net/bpf_jit_comp.h
+++ b/arch/mips/net/bpf_jit_comp.h
@@ -163,7 +163,7 @@ void emit_mov_i(struct jit_context *ctx, u8 dst, s32 imm);
void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src);
/* Validate ALU/ALU64 immediate range */
-bool valid_alu_i(u8 op, s32 imm);
+bool valid_alu_i(u8 op, s32 imm, s16 off);
/* Rewrite ALU/ALU64 immediate operation */
bool rewrite_alu_i(u8 op, s32 imm, u8 *alu, s32 *val);
@@ -172,7 +172,7 @@ bool rewrite_alu_i(u8 op, s32 imm, u8 *alu, s32 *val);
void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op);
/* ALU register operation (32-bit) */
-void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op);
+void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op, s16 off);
/* Atomic read-modify-write (32-bit) */
void emit_atomic_r(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 code);
diff --git a/arch/mips/net/bpf_jit_comp32.c b/arch/mips/net/bpf_jit_comp32.c
index 40a878b672f5..6619182b5ef3 100644
--- a/arch/mips/net/bpf_jit_comp32.c
+++ b/arch/mips/net/bpf_jit_comp32.c
@@ -512,7 +512,7 @@ static void emit_mul_r64(struct jit_context *ctx,
clobber_reg64(ctx, dst);
}
-/* Helper function for 64-bit modulo */
+/* Helper function for unsigned 64-bit modulo */
static u64 jit_mod64(u64 a, u64 b)
{
u64 rem;
@@ -521,13 +521,22 @@ static u64 jit_mod64(u64 a, u64 b)
return rem;
}
+/* Helper function for signed 64-bit modulo */
+static s64 jit_smod64(s64 a, s64 b)
+{
+ s64 quot = div64_s64(a, b);
+
+ return a - quot * b;
+}
+
/* ALU div/mod register (64-bit) */
-static void emit_divmod_r64(struct jit_context *ctx,
- const u8 dst[], const u8 src[], u8 op)
+static void emit_divmod_r64(struct jit_context *ctx, const u8 dst[],
+ const u8 src[], u8 op, s16 off)
{
const u8 *r0 = bpf2mips32[BPF_REG_0]; /* Mapped to v0-v1 */
const u8 *r1 = bpf2mips32[BPF_REG_1]; /* Mapped to a0-a1 */
const u8 *r2 = bpf2mips32[BPF_REG_2]; /* Mapped to a2-a3 */
+ bool is_signed = off == 1;
int exclude, k;
u32 addr = 0;
@@ -546,11 +555,11 @@ static void emit_divmod_r64(struct jit_context *ctx,
switch (BPF_OP(op)) {
/* dst = dst / src */
case BPF_DIV:
- addr = (u32)&div64_u64;
+ addr = is_signed ? (u32)&div64_s64 : (u32)&div64_u64;
break;
/* dst = dst % src */
case BPF_MOD:
- addr = (u32)&jit_mod64;
+ addr = is_signed ? (u32)&jit_smod64 : (u32)&jit_mod64;
break;
}
emit_mov_i(ctx, MIPS_R_T9, addr);
@@ -1516,9 +1525,9 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_MUL | BPF_K:
case BPF_ALU | BPF_DIV | BPF_K:
case BPF_ALU | BPF_MOD | BPF_K:
- if (!valid_alu_i(BPF_OP(code), imm)) {
+ if (!valid_alu_i(BPF_OP(code), imm, off)) {
emit_mov_i(ctx, MIPS_R_T6, imm);
- emit_alu_r(ctx, lo(dst), MIPS_R_T6, BPF_OP(code));
+ emit_alu_r(ctx, lo(dst), MIPS_R_T6, BPF_OP(code), off);
} else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
emit_alu_i(ctx, lo(dst), val, alu);
}
@@ -1546,7 +1555,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_MUL | BPF_X:
case BPF_ALU | BPF_DIV | BPF_X:
case BPF_ALU | BPF_MOD | BPF_X:
- emit_alu_r(ctx, lo(dst), lo(src), BPF_OP(code));
+ emit_alu_r(ctx, lo(dst), lo(src), BPF_OP(code), off);
emit_zext_ver(ctx, dst);
break;
/* dst = imm (64-bit) */
@@ -1599,7 +1608,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
* and then do the operation on this register.
*/
emit_mov_se_i64(ctx, tmp, imm);
- emit_divmod_r64(ctx, dst, tmp, BPF_OP(code));
+ emit_divmod_r64(ctx, dst, tmp, BPF_OP(code), off);
break;
/* dst = dst & src (64-bit) */
/* dst = dst | src (64-bit) */
@@ -1629,7 +1638,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
/* dst = dst % src (64-bit) */
case BPF_ALU64 | BPF_DIV | BPF_X:
case BPF_ALU64 | BPF_MOD | BPF_X:
- emit_divmod_r64(ctx, dst, src, BPF_OP(code));
+ emit_divmod_r64(ctx, dst, src, BPF_OP(code), off);
break;
/* dst = htole(dst) */
/* dst = htobe(dst) */
diff --git a/arch/mips/net/bpf_jit_comp64.c b/arch/mips/net/bpf_jit_comp64.c
index 2520e1db7ab7..db027c2d9221 100644
--- a/arch/mips/net/bpf_jit_comp64.c
+++ b/arch/mips/net/bpf_jit_comp64.c
@@ -198,29 +198,42 @@ static void emit_alu_i64(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
}
/* ALU division operation (64-bit) */
-static void emit_div64(struct jit_context *ctx, u8 dst, u8 src)
+static void emit_div64(struct jit_context *ctx, u8 dst, u8 src, s16 off)
{
if (cpu_has_mips64r6) {
- emit(ctx, ddivu_r6, dst, dst, src);
+ if (off == 1)
+ emit(ctx, ddiv_r6, dst, dst, src);
+ else
+ emit(ctx, ddivu_r6, dst, dst, src);
} else {
- emit(ctx, ddivu, dst, src);
+ if (off == 1)
+ emit(ctx, ddiv, dst, src);
+ else
+ emit(ctx, ddivu, dst, src);
emit(ctx, mflo, dst);
}
}
/* ALU modulo operation (64-bit) */
-static void emit_mod64(struct jit_context *ctx, u8 dst, u8 src)
+static void emit_mod64(struct jit_context *ctx, u8 dst, u8 src, s16 off)
{
if (cpu_has_mips64r6) {
- emit(ctx, dmodu, dst, dst, src);
+ if (off == 1)
+ emit(ctx, dmod, dst, dst, src);
+ else
+ emit(ctx, dmodu, dst, dst, src);
} else {
- emit(ctx, ddivu, dst, src);
+ if (off == 1)
+ emit(ctx, ddiv, dst, src);
+ else
+ emit(ctx, ddivu, dst, src);
emit(ctx, mfhi, dst);
}
}
/* ALU register operation (64-bit) */
-static void emit_alu_r64(struct jit_context *ctx, u8 dst, u8 src, u8 op)
+static void emit_alu_r64(struct jit_context *ctx, u8 dst, u8 src, u8 op,
+ s16 off)
{
switch (BPF_OP(op)) {
/* dst = dst << src */
@@ -257,15 +270,15 @@ static void emit_alu_r64(struct jit_context *ctx, u8 dst, u8 src, u8 op)
break;
/* dst = dst / src */
case BPF_DIV:
- emit_div64(ctx, dst, src);
+ emit_div64(ctx, dst, src, off);
break;
/* dst = dst % src */
case BPF_MOD:
- emit_mod64(ctx, dst, src);
+ emit_mod64(ctx, dst, src, off);
break;
default:
/* Width-generic operations */
- emit_alu_r(ctx, dst, src, op);
+ emit_alu_r(ctx, dst, src, op, off);
}
clobber_reg(ctx, dst);
}
@@ -686,9 +699,9 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_AND | BPF_K:
case BPF_ALU | BPF_XOR | BPF_K:
case BPF_ALU | BPF_LSH | BPF_K:
- if (!valid_alu_i(BPF_OP(code), imm)) {
+ if (!valid_alu_i(BPF_OP(code), imm, off)) {
emit_mov_i(ctx, MIPS_R_T4, imm);
- emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
+ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code), off);
} else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
emit_alu_i(ctx, dst, val, alu);
}
@@ -708,10 +721,10 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_MUL | BPF_K:
case BPF_ALU | BPF_DIV | BPF_K:
case BPF_ALU | BPF_MOD | BPF_K:
- if (!valid_alu_i(BPF_OP(code), imm)) {
+ if (!valid_alu_i(BPF_OP(code), imm, off)) {
emit_sext(ctx, dst, dst);
emit_mov_i(ctx, MIPS_R_T4, imm);
- emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
+ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code), off);
} else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
emit_sext(ctx, dst, dst);
emit_alu_i(ctx, dst, val, alu);
@@ -726,7 +739,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_OR | BPF_X:
case BPF_ALU | BPF_XOR | BPF_X:
case BPF_ALU | BPF_LSH | BPF_X:
- emit_alu_r(ctx, dst, src, BPF_OP(code));
+ emit_alu_r(ctx, dst, src, BPF_OP(code), off);
emit_zext_ver(ctx, dst);
break;
/* dst = dst >> src */
@@ -745,7 +758,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_MOD | BPF_X:
emit_sext(ctx, dst, dst);
emit_sext(ctx, MIPS_R_T4, src);
- emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
+ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code), off);
emit_zext_ver(ctx, dst);
break;
/* dst = imm (64-bit) */
@@ -782,9 +795,9 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU64 | BPF_MUL | BPF_K:
case BPF_ALU64 | BPF_DIV | BPF_K:
case BPF_ALU64 | BPF_MOD | BPF_K:
- if (!valid_alu_i(BPF_OP(code), imm)) {
+ if (!valid_alu_i(BPF_OP(code), imm, off)) {
emit_mov_i(ctx, MIPS_R_T4, imm);
- emit_alu_r64(ctx, dst, MIPS_R_T4, BPF_OP(code));
+ emit_alu_r64(ctx, dst, MIPS_R_T4, BPF_OP(code), off);
} else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
emit_alu_i64(ctx, dst, val, alu);
}
@@ -811,7 +824,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU64 | BPF_MUL | BPF_X:
case BPF_ALU64 | BPF_DIV | BPF_X:
case BPF_ALU64 | BPF_MOD | BPF_X:
- emit_alu_r64(ctx, dst, src, BPF_OP(code));
+ emit_alu_r64(ctx, dst, src, BPF_OP(code), off);
break;
/* dst = htole(dst) */
/* dst = htobe(dst) */
prev parent reply other threads:[~2026-08-10 19:42 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-10 19:42 [PATCH bpf-next v3 0/3] bpf, mips: Add signed div/mod support Nicholas Dudar
2026-08-10 19:42 ` [PATCH bpf-next v3 1/3] bpf, mips: Factor out div/mod emission helpers Nicholas Dudar
2026-08-10 19:42 ` [PATCH bpf-next v3 2/3] MIPS: uasm: Add signed div/mod emitters Nicholas Dudar
2026-08-10 21:05 ` bot+bpf-ci
2026-08-11 0:34 ` Nicholas Dudar
2026-08-10 19:42 ` Nicholas Dudar [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260810194215.3754591-4-main.kalliope@gmail.com \
--to=main.kalliope@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=ihor.solodrai@linux.dev \
--cc=johan.almbladh@anyfinetworks.com \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mips@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=paulburton@kernel.org \
--cc=philmd@oss.qualcomm.com \
--cc=song@kernel.org \
--cc=tsbogend@alpha.franken.de \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox