Skip to content

Commit ec0e2da

Browse files
Yonghong SongAlexei Starovoitov
Yonghong Song
authored and
Alexei Starovoitov
committed
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: Eduard Zingerman <[email protected]> Signed-off-by: Yonghong Song <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 0845c3d commit ec0e2da

File tree

3 files changed

+117
-26
lines changed

3 files changed

+117
-26
lines changed

arch/x86/net/bpf_jit_comp.c

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,15 +1194,26 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
11941194
/* mov rax, dst_reg */
11951195
emit_mov_reg(&prog, is64, BPF_REG_0, dst_reg);
11961196

1197-
/*
1198-
* xor edx, edx
1199-
* equivalent to 'xor rdx, rdx', but one byte less
1200-
*/
1201-
EMIT2(0x31, 0xd2);
1197+
if (insn->off == 0) {
1198+
/*
1199+
* xor edx, edx
1200+
* equivalent to 'xor rdx, rdx', but one byte less
1201+
*/
1202+
EMIT2(0x31, 0xd2);
12021203

1203-
/* div src_reg */
1204-
maybe_emit_1mod(&prog, src_reg, is64);
1205-
EMIT2(0xF7, add_1reg(0xF0, src_reg));
1204+
/* div src_reg */
1205+
maybe_emit_1mod(&prog, src_reg, is64);
1206+
EMIT2(0xF7, add_1reg(0xF0, src_reg));
1207+
} else {
1208+
if (BPF_CLASS(insn->code) == BPF_ALU)
1209+
EMIT1(0x99); /* cdq */
1210+
else
1211+
EMIT2(0x48, 0x99); /* cqo */
1212+
1213+
/* idiv src_reg */
1214+
maybe_emit_1mod(&prog, src_reg, is64);
1215+
EMIT2(0xF7, add_1reg(0xF8, src_reg));
1216+
}
12061217

12071218
if (BPF_OP(insn->code) == BPF_MOD &&
12081219
dst_reg != BPF_REG_3)

kernel/bpf/core.c

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,36 +1792,114 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
17921792
(*(s64 *) &DST) >>= IMM;
17931793
CONT;
17941794
ALU64_MOD_X:
1795-
div64_u64_rem(DST, SRC, &AX);
1796-
DST = AX;
1795+
switch (OFF) {
1796+
case 0:
1797+
div64_u64_rem(DST, SRC, &AX);
1798+
DST = AX;
1799+
break;
1800+
case 1:
1801+
AX = div64_s64(DST, SRC);
1802+
DST = DST - AX * SRC;
1803+
break;
1804+
}
17971805
CONT;
17981806
ALU_MOD_X:
1799-
AX = (u32) DST;
1800-
DST = do_div(AX, (u32) SRC);
1807+
switch (OFF) {
1808+
case 0:
1809+
AX = (u32) DST;
1810+
DST = do_div(AX, (u32) SRC);
1811+
break;
1812+
case 1:
1813+
AX = abs((s32)DST);
1814+
AX = do_div(AX, abs((s32)SRC));
1815+
if ((s32)DST < 0)
1816+
DST = (u32)-AX;
1817+
else
1818+
DST = (u32)AX;
1819+
break;
1820+
}
18011821
CONT;
18021822
ALU64_MOD_K:
1803-
div64_u64_rem(DST, IMM, &AX);
1804-
DST = AX;
1823+
switch (OFF) {
1824+
case 0:
1825+
div64_u64_rem(DST, IMM, &AX);
1826+
DST = AX;
1827+
break;
1828+
case 1:
1829+
AX = div64_s64(DST, IMM);
1830+
DST = DST - AX * IMM;
1831+
break;
1832+
}
18051833
CONT;
18061834
ALU_MOD_K:
1807-
AX = (u32) DST;
1808-
DST = do_div(AX, (u32) IMM);
1835+
switch (OFF) {
1836+
case 0:
1837+
AX = (u32) DST;
1838+
DST = do_div(AX, (u32) IMM);
1839+
break;
1840+
case 1:
1841+
AX = abs((s32)DST);
1842+
AX = do_div(AX, abs((s32)IMM));
1843+
if ((s32)DST < 0)
1844+
DST = (u32)-AX;
1845+
else
1846+
DST = (u32)AX;
1847+
break;
1848+
}
18091849
CONT;
18101850
ALU64_DIV_X:
1811-
DST = div64_u64(DST, SRC);
1851+
switch (OFF) {
1852+
case 0:
1853+
DST = div64_u64(DST, SRC);
1854+
break;
1855+
case 1:
1856+
DST = div64_s64(DST, SRC);
1857+
break;
1858+
}
18121859
CONT;
18131860
ALU_DIV_X:
1814-
AX = (u32) DST;
1815-
do_div(AX, (u32) SRC);
1816-
DST = (u32) AX;
1861+
switch (OFF) {
1862+
case 0:
1863+
AX = (u32) DST;
1864+
do_div(AX, (u32) SRC);
1865+
DST = (u32) AX;
1866+
break;
1867+
case 1:
1868+
AX = abs((s32)DST);
1869+
do_div(AX, abs((s32)SRC));
1870+
if ((s32)DST < 0 == (s32)SRC < 0)
1871+
DST = (u32)AX;
1872+
else
1873+
DST = (u32)-AX;
1874+
break;
1875+
}
18171876
CONT;
18181877
ALU64_DIV_K:
1819-
DST = div64_u64(DST, IMM);
1878+
switch (OFF) {
1879+
case 0:
1880+
DST = div64_u64(DST, IMM);
1881+
break;
1882+
case 1:
1883+
DST = div64_s64(DST, IMM);
1884+
break;
1885+
}
18201886
CONT;
18211887
ALU_DIV_K:
1822-
AX = (u32) DST;
1823-
do_div(AX, (u32) IMM);
1824-
DST = (u32) AX;
1888+
switch (OFF) {
1889+
case 0:
1890+
AX = (u32) DST;
1891+
do_div(AX, (u32) IMM);
1892+
DST = (u32) AX;
1893+
break;
1894+
case 1:
1895+
AX = abs((s32)DST);
1896+
do_div(AX, abs((s32)IMM));
1897+
if ((s32)DST < 0 == (s32)IMM < 0)
1898+
DST = (u32)AX;
1899+
else
1900+
DST = (u32)-AX;
1901+
break;
1902+
}
18251903
CONT;
18261904
ALU_END_TO_BE:
18271905
switch (IMM) {

kernel/bpf/verifier.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13237,7 +13237,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
1323713237
} else { /* all other ALU ops: and, sub, xor, add, ... */
1323813238

1323913239
if (BPF_SRC(insn->code) == BPF_X) {
13240-
if (insn->imm != 0 || insn->off != 0) {
13240+
if (insn->imm != 0 || insn->off > 1 ||
13241+
(insn->off == 1 && opcode != BPF_MOD && opcode != BPF_DIV)) {
1324113242
verbose(env, "BPF_ALU uses reserved fields\n");
1324213243
return -EINVAL;
1324313244
}
@@ -13246,7 +13247,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
1324613247
if (err)
1324713248
return err;
1324813249
} else {
13249-
if (insn->src_reg != BPF_REG_0 || insn->off != 0) {
13250+
if (insn->src_reg != BPF_REG_0 || insn->off > 1 ||
13251+
(insn->off == 1 && opcode != BPF_MOD && opcode != BPF_DIV)) {
1325013252
verbose(env, "BPF_ALU uses reserved fields\n");
1325113253
return -EINVAL;
1325213254
}

0 commit comments

Comments
 (0)