Commit 3f5a238f authored by Hengqi Chen's avatar Hengqi Chen Committed by Huacai Chen
Browse files

LoongArch: BPF: Sign extend kfunc call arguments



The kfunc calls are native calls so they should follow LoongArch calling
conventions. Sign extend its arguments properly to avoid kernel panic.
This is done by adding a new emit_abi_ext() helper. The emit_abi_ext()
helper performs extension in place meaning a value already store in the
target register (Note: this is different from the existing sign_extend()
helper and thus we can't reuse it).

Cc: stable@vger.kernel.org
Fixes: 5dc61552 ("LoongArch: Add BPF JIT support")
Signed-off-by: default avatarHengqi Chen <hengqi.chen@gmail.com>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent 45cb47c6
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -950,6 +950,22 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext
			emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, tcc_ptr_off);
		}

		if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
			const struct btf_func_model *m;
			int i;

			m = bpf_jit_find_kfunc_model(ctx->prog, insn);
			if (!m)
				return -EINVAL;

			for (i = 0; i < m->nr_args; i++) {
				u8 reg = regmap[BPF_REG_1 + i];
				bool sign = m->arg_flags[i] & BTF_FMODEL_SIGNED_ARG;

				emit_abi_ext(ctx, reg, m->arg_size[i], sign);
			}
		}

		move_addr(ctx, t1, func_addr);
		emit_insn(ctx, jirl, LOONGARCH_GPR_RA, t1, 0);

+26 −0
Original line number Diff line number Diff line
@@ -88,6 +88,32 @@ static inline void emit_sext_32(struct jit_ctx *ctx, enum loongarch_gpr reg, boo
	emit_insn(ctx, addiw, reg, reg, 0);
}

/* Emit proper extension according to ABI requirements.
 * Note that it requires a value of size `size` already resides in register `reg`.
 */
static inline void emit_abi_ext(struct jit_ctx *ctx, int reg, u8 size, bool sign)
{
	/* ABI requires unsigned char/short to be zero-extended */
	if (!sign && (size == 1 || size == 2))
		return;

	switch (size) {
	case 1:
		emit_insn(ctx, extwb, reg, reg);
		break;
	case 2:
		emit_insn(ctx, extwh, reg, reg);
		break;
	case 4:
		emit_insn(ctx, addiw, reg, reg, 0);
		break;
	case 8:
		break;
	default:
		pr_warn("bpf_jit: invalid size %d for extension\n", size);
	}
}

static inline void move_addr(struct jit_ctx *ctx, enum loongarch_gpr rd, u64 addr)
{
	u64 imm_11_0, imm_31_12, imm_51_32, imm_63_52;