Commit ec0e2da9 authored by Yonghong Song's avatar Yonghong Song Committed by Alexei Starovoitov
Browse files

bpf: Support new signed div/mod instructions.



Add interpreter/jit support for new signed div/mod insns.
The new signed div/mod instructions are encoded with
unsigned div/mod instructions plus insn->off == 1.
Also add basic verifier support to ensure new insns get
accepted.

Acked-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Signed-off-by: default avatarYonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/r/20230728011219.3714605-1-yonghong.song@linux.dev


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 0845c3db
Loading
Loading
Loading
Loading
+19 −8
Original line number Diff line number Diff line
@@ -1194,6 +1194,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
				/* mov rax, dst_reg */
				emit_mov_reg(&prog, is64, BPF_REG_0, dst_reg);

			if (insn->off == 0) {
				/*
				 * xor edx, edx
				 * equivalent to 'xor rdx, rdx', but one byte less
@@ -1203,6 +1204,16 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
				/* div src_reg */
				maybe_emit_1mod(&prog, src_reg, is64);
				EMIT2(0xF7, add_1reg(0xF0, src_reg));
			} else {
				if (BPF_CLASS(insn->code) == BPF_ALU)
					EMIT1(0x99); /* cdq */
				else
					EMIT2(0x48, 0x99); /* cqo */

				/* idiv src_reg */
				maybe_emit_1mod(&prog, src_reg, is64);
				EMIT2(0xF7, add_1reg(0xF8, src_reg));
			}

			if (BPF_OP(insn->code) == BPF_MOD &&
			    dst_reg != BPF_REG_3)
+94 −16
Original line number Diff line number Diff line
@@ -1792,36 +1792,114 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
		(*(s64 *) &DST) >>= IMM;
		CONT;
	ALU64_MOD_X:
		switch (OFF) {
		case 0:
			div64_u64_rem(DST, SRC, &AX);
			DST = AX;
			break;
		case 1:
			AX = div64_s64(DST, SRC);
			DST = DST - AX * SRC;
			break;
		}
		CONT;
	ALU_MOD_X:
		switch (OFF) {
		case 0:
			AX = (u32) DST;
			DST = do_div(AX, (u32) SRC);
			break;
		case 1:
			AX = abs((s32)DST);
			AX = do_div(AX, abs((s32)SRC));
			if ((s32)DST < 0)
				DST = (u32)-AX;
			else
				DST = (u32)AX;
			break;
		}
		CONT;
	ALU64_MOD_K:
		switch (OFF) {
		case 0:
			div64_u64_rem(DST, IMM, &AX);
			DST = AX;
			break;
		case 1:
			AX = div64_s64(DST, IMM);
			DST = DST - AX * IMM;
			break;
		}
		CONT;
	ALU_MOD_K:
		switch (OFF) {
		case 0:
			AX = (u32) DST;
			DST = do_div(AX, (u32) IMM);
			break;
		case 1:
			AX = abs((s32)DST);
			AX = do_div(AX, abs((s32)IMM));
			if ((s32)DST < 0)
				DST = (u32)-AX;
			else
				DST = (u32)AX;
			break;
		}
		CONT;
	ALU64_DIV_X:
		switch (OFF) {
		case 0:
			DST = div64_u64(DST, SRC);
			break;
		case 1:
			DST = div64_s64(DST, SRC);
			break;
		}
		CONT;
	ALU_DIV_X:
		switch (OFF) {
		case 0:
			AX = (u32) DST;
			do_div(AX, (u32) SRC);
			DST = (u32) AX;
			break;
		case 1:
			AX = abs((s32)DST);
			do_div(AX, abs((s32)SRC));
			if ((s32)DST < 0 == (s32)SRC < 0)
				DST = (u32)AX;
			else
				DST = (u32)-AX;
			break;
		}
		CONT;
	ALU64_DIV_K:
		switch (OFF) {
		case 0:
			DST = div64_u64(DST, IMM);
			break;
		case 1:
			DST = div64_s64(DST, IMM);
			break;
		}
		CONT;
	ALU_DIV_K:
		switch (OFF) {
		case 0:
			AX = (u32) DST;
			do_div(AX, (u32) IMM);
			DST = (u32) AX;
			break;
		case 1:
			AX = abs((s32)DST);
			do_div(AX, abs((s32)IMM));
			if ((s32)DST < 0 == (s32)IMM < 0)
				DST = (u32)AX;
			else
				DST = (u32)-AX;
			break;
		}
		CONT;
	ALU_END_TO_BE:
		switch (IMM) {
+4 −2
Original line number Diff line number Diff line
@@ -13237,7 +13237,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
	} else {	/* all other ALU ops: and, sub, xor, add, ... */
		if (BPF_SRC(insn->code) == BPF_X) {
			if (insn->imm != 0 || insn->off != 0) {
			if (insn->imm != 0 || insn->off > 1 ||
			    (insn->off == 1 && opcode != BPF_MOD && opcode != BPF_DIV)) {
				verbose(env, "BPF_ALU uses reserved fields\n");
				return -EINVAL;
			}
@@ -13246,7 +13247,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
			if (err)
				return err;
		} else {
			if (insn->src_reg != BPF_REG_0 || insn->off != 0) {
			if (insn->src_reg != BPF_REG_0 || insn->off > 1 ||
			    (insn->off == 1 && opcode != BPF_MOD && opcode != BPF_DIV)) {
				verbose(env, "BPF_ALU uses reserved fields\n");
				return -EINVAL;
			}