On 2025/8/15 下午4:29, Haoran Jiang wrote:
In some eBPF programs, the return value is a pointer.
When the kernel call an eBPF program (such as struct_ops),
it expects a 64-bit address to be returned, but instead a 32-bit value.
With this v2 patch and the v1 patch:
$ sudo modprobe test_bpf
modprobe: ERROR: could not insert 'test_bpf': Invalid argument
$ dmesg -t | grep Summary
test_bpf: Summary: 1039 PASSED, 14 FAILED, [1041/1041 JIT'ed]
Without the patches, all of the tests passed.
This is related with the previous fix:
LoongArch: BPF: Sign-extend return values
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=73c359d1d356
diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
index 277d2140676b..20f4fc745bea 100644
--- a/arch/loongarch/include/asm/inst.h
+++ b/arch/loongarch/include/asm/inst.h
@@ -92,6 +92,8 @@ enum reg2i6_op {
};
enum reg2i12_op {
+ slti_op = 0x08,
+ sltui_op = 0x09,
addiw_op = 0x0a,
addid_op = 0x0b,
lu52id_op = 0x0c,
@@ -148,6 +150,8 @@ enum reg3_op {
addd_op = 0x21,
subw_op = 0x22,
subd_op = 0x23,
+ slt_op = 0x24,
+ sltu_op = 0x25,
Only slt is used in bpf_jit.c,
slti, sltui and sltu can be removed
because they are not used in this patch.
diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c
index abfdb6bb5c38..50067be79c4f 100644
--- a/arch/loongarch/net/bpf_jit.c
+++ b/arch/loongarch/net/bpf_jit.c
@@ -229,8 +229,21 @@ static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call)
emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, stack_adjust);
if (!is_tail_call) {
- /* Set return value */
- emit_insn(ctx, addiw, LOONGARCH_GPR_A0, regmap[BPF_REG_0], 0);
+ /*
+ * Set return value
It is better to update the comment:
The return value is either a unsigned 32-bit value or a unsigned 64-bit
address.
+ * Check if the 64th bit in regmap[BPF_REG_0] is 1. If it is,
+ * the value in regmap[BPF_REG_0] is a kernel-space address.
+
+ * long long val = regmap[BPF_REG_0];
+ * int shift = 0 < val ? 32 : 0;
+ * return (val << shift) >> shift;
+ */
+ move_reg(ctx, LOONGARCH_GPR_A0, regmap[BPF_REG_0]);
+ emit_insn(ctx, slt, LOONGARCH_GPR_T0, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_A0);
LOONGARCH_GPR_T0 may be not the expected value 1 if the return value
is a unsigned 32-bit value.
A quick way to verify this issue:
Add "move_imm(ctx, LOONGARCH_GPR_T0, 1, true)" after "slt" to force
LOONGARCH_GPR_T0 as 1 can fix the above failed tests.
The initial conclusion is that if the 64th bit in regmap[BPF_REG_0]
is 1, it can not recognize the value in regmap[BPF_REG_0] must be a
kernel-space address, it may be a unsigned 32-bit return value, for
this case just use addi.w to extend bit 31 into bits 63 through 32
of a5 to a0.
Thanks,
Tiezhu