2019-05-30 19:09:56

by Luke Nelson

[permalink] [raw]
Subject: [PATCH 1/2] bpf, riscv: fix bugs in JIT for 32-bit ALU operations

In BPF, 32-bit ALU operations should zero-extend their results into
the 64-bit registers. The current BPF JIT on RISC-V emits incorrect
instructions that perform either sign extension only (e.g., addw/subw)
or no extension on 32-bit add, sub, and, or, xor, lsh, rsh, arsh,
and neg. This behavior diverges from the interpreter and JITs for
other architectures.

This patch fixes the bugs by performing zero extension on the destination
register of 32-bit ALU operations.

Fixes: 2353ecc6f91f ("bpf, riscv: add BPF JIT for RV64G")
Cc: Xi Wang <[email protected]>
Signed-off-by: Luke Nelson <[email protected]>
---
arch/riscv/net/bpf_jit_comp.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

diff --git a/arch/riscv/net/bpf_jit_comp.c b/arch/riscv/net/bpf_jit_comp.c
index 80b12aa5e10d..426d5c33ea90 100644
--- a/arch/riscv/net/bpf_jit_comp.c
+++ b/arch/riscv/net/bpf_jit_comp.c
@@ -751,22 +751,32 @@ static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
case BPF_ALU | BPF_ADD | BPF_X:
case BPF_ALU64 | BPF_ADD | BPF_X:
emit(is64 ? rv_add(rd, rd, rs) : rv_addw(rd, rd, rs), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;
case BPF_ALU | BPF_SUB | BPF_X:
case BPF_ALU64 | BPF_SUB | BPF_X:
emit(is64 ? rv_sub(rd, rd, rs) : rv_subw(rd, rd, rs), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;
case BPF_ALU | BPF_AND | BPF_X:
case BPF_ALU64 | BPF_AND | BPF_X:
emit(rv_and(rd, rd, rs), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;
case BPF_ALU | BPF_OR | BPF_X:
case BPF_ALU64 | BPF_OR | BPF_X:
emit(rv_or(rd, rd, rs), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;
case BPF_ALU | BPF_XOR | BPF_X:
case BPF_ALU64 | BPF_XOR | BPF_X:
emit(rv_xor(rd, rd, rs), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;
case BPF_ALU | BPF_MUL | BPF_X:
case BPF_ALU64 | BPF_MUL | BPF_X:
@@ -789,14 +799,20 @@ static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
case BPF_ALU | BPF_LSH | BPF_X:
case BPF_ALU64 | BPF_LSH | BPF_X:
emit(is64 ? rv_sll(rd, rd, rs) : rv_sllw(rd, rd, rs), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;
case BPF_ALU | BPF_RSH | BPF_X:
case BPF_ALU64 | BPF_RSH | BPF_X:
emit(is64 ? rv_srl(rd, rd, rs) : rv_srlw(rd, rd, rs), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;
case BPF_ALU | BPF_ARSH | BPF_X:
case BPF_ALU64 | BPF_ARSH | BPF_X:
emit(is64 ? rv_sra(rd, rd, rs) : rv_sraw(rd, rd, rs), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;

/* dst = -dst */
@@ -804,6 +820,8 @@ static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
case BPF_ALU64 | BPF_NEG:
emit(is64 ? rv_sub(rd, RV_REG_ZERO, rd) :
rv_subw(rd, RV_REG_ZERO, rd), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;

/* dst = BSWAP##imm(dst) */
@@ -958,14 +976,20 @@ static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
case BPF_ALU | BPF_LSH | BPF_K:
case BPF_ALU64 | BPF_LSH | BPF_K:
emit(is64 ? rv_slli(rd, rd, imm) : rv_slliw(rd, rd, imm), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;
case BPF_ALU | BPF_RSH | BPF_K:
case BPF_ALU64 | BPF_RSH | BPF_K:
emit(is64 ? rv_srli(rd, rd, imm) : rv_srliw(rd, rd, imm), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;
case BPF_ALU | BPF_ARSH | BPF_K:
case BPF_ALU64 | BPF_ARSH | BPF_K:
emit(is64 ? rv_srai(rd, rd, imm) : rv_sraiw(rd, rd, imm), ctx);
+ if (!is64)
+ emit_zext_32(rd, ctx);
break;

/* JUMP off */
--
2.19.1


2019-05-30 19:10:56

by Luke Nelson

[permalink] [raw]
Subject: [PATCH 2/2] bpf: test_bpf: add tests for upper bits of 32-bit operations

This commit introduces tests that validate the upper 32 bits
of the result of 32-bit BPF ALU operations.

The existing tests for 32-bit operations do not check the upper 32
bits of results because the exit instruction truncates the result.
These tests perform a 32-bit ALU operation followed by a right shift.
These tests can catch subtle bugs in the extension behavior of JITed
instructions, including several bugs in the RISC-V BPF JIT, fixed in
another patch.

The added tests pass the JIT and interpreter on x86, as well as the
JIT and interpreter of RISC-V once the zero extension bugs were fixed.

Cc: Xi Wang <[email protected]>
Signed-off-by: Luke Nelson <[email protected]>
---
lib/test_bpf.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 164 insertions(+)

diff --git a/lib/test_bpf.c b/lib/test_bpf.c
index 0845f635f404..4580dc0220f1 100644
--- a/lib/test_bpf.c
+++ b/lib/test_bpf.c
@@ -2461,6 +2461,20 @@ static struct bpf_test tests[] = {
{ },
{ { 0, 1 } },
},
+ {
+ "ALU_ADD_X: (1 + 4294967294) >> 32 + 4294967294 = 4294967294",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 1U),
+ BPF_ALU32_IMM(BPF_MOV, R1, 4294967294U),
+ BPF_ALU32_REG(BPF_ADD, R0, R1),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU32_REG(BPF_ADD, R0, R1),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 4294967294U } },
+ },
{
"ALU64_ADD_X: 1 + 2 = 3",
.u.insns_int = {
@@ -2812,6 +2826,20 @@ static struct bpf_test tests[] = {
{ },
{ { 0, 1 } },
},
+ {
+ "ALU_SUB_X: (4294967295 - 1) >> 32 + 1 = 1",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 4294967295U),
+ BPF_ALU32_IMM(BPF_MOV, R1, 1U),
+ BPF_ALU32_REG(BPF_SUB, R0, R1),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU32_REG(BPF_ADD, R0, R1),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
{
"ALU64_SUB_X: 3 - 1 = 2",
.u.insns_int = {
@@ -3391,6 +3419,20 @@ static struct bpf_test tests[] = {
{ },
{ { 0, 0xffffffff } },
},
+ {
+ "ALU_AND_X: (-1 & -1) >> 32 + 1 = 1",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, -1UL),
+ BPF_LD_IMM64(R1, -1UL),
+ BPF_ALU32_REG(BPF_AND, R0, R1),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU32_IMM(BPF_ADD, R0, 1U),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
{
"ALU64_AND_X: 3 & 2 = 2",
.u.insns_int = {
@@ -3533,6 +3575,20 @@ static struct bpf_test tests[] = {
{ },
{ { 0, 0xffffffff } },
},
+ {
+ "ALU_OR_X: (0 & -1) >> 32 + 1 = 1",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0),
+ BPF_LD_IMM64(R1, -1UL),
+ BPF_ALU32_REG(BPF_OR, R0, R1),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU32_IMM(BPF_ADD, R0, 1U),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
{
"ALU64_OR_X: 1 | 2 = 3",
.u.insns_int = {
@@ -3675,6 +3731,20 @@ static struct bpf_test tests[] = {
{ },
{ { 0, 0xfffffffe } },
},
+ {
+ "ALU_XOR_X: (0 ^ -1) >> 32 + 1 = 1",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0),
+ BPF_LD_IMM64(R1, -1UL),
+ BPF_ALU32_REG(BPF_XOR, R0, R1),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU32_IMM(BPF_ADD, R0, 1U),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
{
"ALU64_XOR_X: 5 ^ 6 = 3",
.u.insns_int = {
@@ -3817,6 +3887,20 @@ static struct bpf_test tests[] = {
{ },
{ { 0, 0x80000000 } },
},
+ {
+ "ALU_LSH_X: (1 << 31) >> 32 + 1 = 1",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 1),
+ BPF_ALU32_IMM(BPF_MOV, R1, 31),
+ BPF_ALU32_REG(BPF_LSH, R0, R1),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU32_IMM(BPF_ADD, R0, 1),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
{
"ALU64_LSH_X: 1 << 1 = 2",
.u.insns_int = {
@@ -3842,6 +3926,19 @@ static struct bpf_test tests[] = {
{ { 0, 0x80000000 } },
},
/* BPF_ALU | BPF_LSH | BPF_K */
+ {
+ "ALU_LSH_K: (1 << 31) >> 32 + 1 = 1",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 1),
+ BPF_ALU32_IMM(BPF_LSH, R0, 31),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU32_IMM(BPF_ADD, R0, 1),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
{
"ALU_LSH_K: 1 << 1 = 2",
.u.insns_int = {
@@ -3911,6 +4008,20 @@ static struct bpf_test tests[] = {
{ },
{ { 0, 1 } },
},
+ {
+ "ALU_RSH_X: (0x80000000 >> 0) >> 32 + 1 = 1",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0x80000000),
+ BPF_ALU32_IMM(BPF_MOV, R1, 0),
+ BPF_ALU32_REG(BPF_RSH, R0, R1),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU32_IMM(BPF_ADD, R0, 1),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
{
"ALU64_RSH_X: 2 >> 1 = 1",
.u.insns_int = {
@@ -3936,6 +4047,19 @@ static struct bpf_test tests[] = {
{ { 0, 1 } },
},
/* BPF_ALU | BPF_RSH | BPF_K */
+ {
+ "ALU_RSH_K: (0x80000000 >> 0) >> 32 + 1 = 1",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0x80000000),
+ BPF_ALU32_IMM(BPF_RSH, R0, 0),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU32_IMM(BPF_ADD, R0, 1),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
{
"ALU_RSH_K: 2 >> 1 = 1",
.u.insns_int = {
@@ -3993,7 +4117,34 @@ static struct bpf_test tests[] = {
{ },
{ { 0, 0xffff00ff } },
},
+ {
+ "ALU_ARSH_X: (0x80000000 >> 0) >> 32 + 1 = 1",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0x80000000),
+ BPF_ALU32_IMM(BPF_MOV, R1, 0),
+ BPF_ALU32_REG(BPF_ARSH, R0, R1),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU32_IMM(BPF_ADD, R0, 1),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
/* BPF_ALU | BPF_ARSH | BPF_K */
+ {
+ "ALU_ARSH_K: (0x80000000 >> 0) >> 32 + 1 = 1",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0x80000000),
+ BPF_ALU32_IMM(BPF_ARSH, R0, 0),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU32_IMM(BPF_ADD, R0, 1),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
{
"ALU_ARSH_K: 0xff00ff0000000000 >> 40 = 0xffffffffffff00ff",
.u.insns_int = {
@@ -4028,6 +4179,19 @@ static struct bpf_test tests[] = {
{ },
{ { 0, 3 } },
},
+ {
+ "ALU_NEG: -(1) >> 32 + 1 = 1",
+ .u.insns_int = {
+ BPF_ALU32_IMM(BPF_MOV, R0, 1),
+ BPF_ALU32_IMM(BPF_NEG, R0, 0),
+ BPF_ALU64_IMM(BPF_RSH, R0, 32),
+ BPF_ALU64_IMM(BPF_ADD, R0, 1),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ },
{
"ALU64_NEG: -(3) = -3",
.u.insns_int = {
--
2.19.1

2019-05-30 20:33:03

by Jiong Wang

[permalink] [raw]
Subject: Re: [PATCH 2/2] bpf: test_bpf: add tests for upper bits of 32-bit operations


Luke Nelson writes:

> This commit introduces tests that validate the upper 32 bits
> of the result of 32-bit BPF ALU operations.
>
> The existing tests for 32-bit operations do not check the upper 32
> bits of results because the exit instruction truncates the result.
> These tests perform a 32-bit ALU operation followed by a right shift.
> These tests can catch subtle bugs in the extension behavior of JITed
> instructions, including several bugs in the RISC-V BPF JIT, fixed in
> another patch.

Hi Luke,

Have you seen the following?

https://www.spinics.net/lists/netdev/msg573355.html

it has been merged to bpf tree and should have full test coverage of all
bpf insns that could write to sub-register and are exposed to JIT
back-end.

And AFAIK, we add new unit tests to test_verifier which is a userspace
test infrastructure which offers more test functionality plus tests will
go through verifier.

Regards,
Jiong

> The added tests pass the JIT and interpreter on x86, as well as the
> JIT and interpreter of RISC-V once the zero extension bugs were fixed.
>
> Cc: Xi Wang <[email protected]>
> Signed-off-by: Luke Nelson <[email protected]>
> ---
> lib/test_bpf.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 164 insertions(+)
>
> diff --git a/lib/test_bpf.c b/lib/test_bpf.c
> index 0845f635f404..4580dc0220f1 100644
> --- a/lib/test_bpf.c
> +++ b/lib/test_bpf.c
> @@ -2461,6 +2461,20 @@ static struct bpf_test tests[] = {
> { },
> { { 0, 1 } },
> },
> + {
> + "ALU_ADD_X: (1 + 4294967294) >> 32 + 4294967294 = 4294967294",
> + .u.insns_int = {
> + BPF_LD_IMM64(R0, 1U),
> + BPF_ALU32_IMM(BPF_MOV, R1, 4294967294U),
> + BPF_ALU32_REG(BPF_ADD, R0, R1),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU32_REG(BPF_ADD, R0, R1),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 4294967294U } },
> + },
> {
> "ALU64_ADD_X: 1 + 2 = 3",
> .u.insns_int = {
> @@ -2812,6 +2826,20 @@ static struct bpf_test tests[] = {
> { },
> { { 0, 1 } },
> },
> + {
> + "ALU_SUB_X: (4294967295 - 1) >> 32 + 1 = 1",
> + .u.insns_int = {
> + BPF_LD_IMM64(R0, 4294967295U),
> + BPF_ALU32_IMM(BPF_MOV, R1, 1U),
> + BPF_ALU32_REG(BPF_SUB, R0, R1),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU32_REG(BPF_ADD, R0, R1),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 1 } },
> + },
> {
> "ALU64_SUB_X: 3 - 1 = 2",
> .u.insns_int = {
> @@ -3391,6 +3419,20 @@ static struct bpf_test tests[] = {
> { },
> { { 0, 0xffffffff } },
> },
> + {
> + "ALU_AND_X: (-1 & -1) >> 32 + 1 = 1",
> + .u.insns_int = {
> + BPF_LD_IMM64(R0, -1UL),
> + BPF_LD_IMM64(R1, -1UL),
> + BPF_ALU32_REG(BPF_AND, R0, R1),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU32_IMM(BPF_ADD, R0, 1U),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 1 } },
> + },
> {
> "ALU64_AND_X: 3 & 2 = 2",
> .u.insns_int = {
> @@ -3533,6 +3575,20 @@ static struct bpf_test tests[] = {
> { },
> { { 0, 0xffffffff } },
> },
> + {
> + "ALU_OR_X: (0 & -1) >> 32 + 1 = 1",
> + .u.insns_int = {
> + BPF_LD_IMM64(R0, 0),
> + BPF_LD_IMM64(R1, -1UL),
> + BPF_ALU32_REG(BPF_OR, R0, R1),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU32_IMM(BPF_ADD, R0, 1U),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 1 } },
> + },
> {
> "ALU64_OR_X: 1 | 2 = 3",
> .u.insns_int = {
> @@ -3675,6 +3731,20 @@ static struct bpf_test tests[] = {
> { },
> { { 0, 0xfffffffe } },
> },
> + {
> + "ALU_XOR_X: (0 ^ -1) >> 32 + 1 = 1",
> + .u.insns_int = {
> + BPF_LD_IMM64(R0, 0),
> + BPF_LD_IMM64(R1, -1UL),
> + BPF_ALU32_REG(BPF_XOR, R0, R1),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU32_IMM(BPF_ADD, R0, 1U),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 1 } },
> + },
> {
> "ALU64_XOR_X: 5 ^ 6 = 3",
> .u.insns_int = {
> @@ -3817,6 +3887,20 @@ static struct bpf_test tests[] = {
> { },
> { { 0, 0x80000000 } },
> },
> + {
> + "ALU_LSH_X: (1 << 31) >> 32 + 1 = 1",
> + .u.insns_int = {
> + BPF_LD_IMM64(R0, 1),
> + BPF_ALU32_IMM(BPF_MOV, R1, 31),
> + BPF_ALU32_REG(BPF_LSH, R0, R1),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU32_IMM(BPF_ADD, R0, 1),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 1 } },
> + },
> {
> "ALU64_LSH_X: 1 << 1 = 2",
> .u.insns_int = {
> @@ -3842,6 +3926,19 @@ static struct bpf_test tests[] = {
> { { 0, 0x80000000 } },
> },
> /* BPF_ALU | BPF_LSH | BPF_K */
> + {
> + "ALU_LSH_K: (1 << 31) >> 32 + 1 = 1",
> + .u.insns_int = {
> + BPF_LD_IMM64(R0, 1),
> + BPF_ALU32_IMM(BPF_LSH, R0, 31),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU32_IMM(BPF_ADD, R0, 1),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 1 } },
> + },
> {
> "ALU_LSH_K: 1 << 1 = 2",
> .u.insns_int = {
> @@ -3911,6 +4008,20 @@ static struct bpf_test tests[] = {
> { },
> { { 0, 1 } },
> },
> + {
> + "ALU_RSH_X: (0x80000000 >> 0) >> 32 + 1 = 1",
> + .u.insns_int = {
> + BPF_LD_IMM64(R0, 0x80000000),
> + BPF_ALU32_IMM(BPF_MOV, R1, 0),
> + BPF_ALU32_REG(BPF_RSH, R0, R1),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU32_IMM(BPF_ADD, R0, 1),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 1 } },
> + },
> {
> "ALU64_RSH_X: 2 >> 1 = 1",
> .u.insns_int = {
> @@ -3936,6 +4047,19 @@ static struct bpf_test tests[] = {
> { { 0, 1 } },
> },
> /* BPF_ALU | BPF_RSH | BPF_K */
> + {
> + "ALU_RSH_K: (0x80000000 >> 0) >> 32 + 1 = 1",
> + .u.insns_int = {
> + BPF_LD_IMM64(R0, 0x80000000),
> + BPF_ALU32_IMM(BPF_RSH, R0, 0),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU32_IMM(BPF_ADD, R0, 1),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 1 } },
> + },
> {
> "ALU_RSH_K: 2 >> 1 = 1",
> .u.insns_int = {
> @@ -3993,7 +4117,34 @@ static struct bpf_test tests[] = {
> { },
> { { 0, 0xffff00ff } },
> },
> + {
> + "ALU_ARSH_X: (0x80000000 >> 0) >> 32 + 1 = 1",
> + .u.insns_int = {
> + BPF_LD_IMM64(R0, 0x80000000),
> + BPF_ALU32_IMM(BPF_MOV, R1, 0),
> + BPF_ALU32_REG(BPF_ARSH, R0, R1),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU32_IMM(BPF_ADD, R0, 1),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 1 } },
> + },
> /* BPF_ALU | BPF_ARSH | BPF_K */
> + {
> + "ALU_ARSH_K: (0x80000000 >> 0) >> 32 + 1 = 1",
> + .u.insns_int = {
> + BPF_LD_IMM64(R0, 0x80000000),
> + BPF_ALU32_IMM(BPF_ARSH, R0, 0),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU32_IMM(BPF_ADD, R0, 1),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 1 } },
> + },
> {
> "ALU_ARSH_K: 0xff00ff0000000000 >> 40 = 0xffffffffffff00ff",
> .u.insns_int = {
> @@ -4028,6 +4179,19 @@ static struct bpf_test tests[] = {
> { },
> { { 0, 3 } },
> },
> + {
> + "ALU_NEG: -(1) >> 32 + 1 = 1",
> + .u.insns_int = {
> + BPF_ALU32_IMM(BPF_MOV, R0, 1),
> + BPF_ALU32_IMM(BPF_NEG, R0, 0),
> + BPF_ALU64_IMM(BPF_RSH, R0, 32),
> + BPF_ALU64_IMM(BPF_ADD, R0, 1),
> + BPF_EXIT_INSN(),
> + },
> + INTERNAL,
> + { },
> + { { 0, 1 } },
> + },
> {
> "ALU64_NEG: -(3) = -3",
> .u.insns_int = {

2019-05-30 20:54:43

by Song Liu

[permalink] [raw]
Subject: Re: [PATCH 1/2] bpf, riscv: fix bugs in JIT for 32-bit ALU operations

On Thu, May 30, 2019 at 12:09 PM Luke Nelson <[email protected]> wrote:
>
> In BPF, 32-bit ALU operations should zero-extend their results into
> the 64-bit registers. The current BPF JIT on RISC-V emits incorrect
> instructions that perform either sign extension only (e.g., addw/subw)
> or no extension on 32-bit add, sub, and, or, xor, lsh, rsh, arsh,
> and neg. This behavior diverges from the interpreter and JITs for
> other architectures.
>
> This patch fixes the bugs by performing zero extension on the destination
> register of 32-bit ALU operations.
>
> Fixes: 2353ecc6f91f ("bpf, riscv: add BPF JIT for RV64G")
> Cc: Xi Wang <[email protected]>
> Signed-off-by: Luke Nelson <[email protected]>

This is a little messy. How about we introduce some helper function
like:

/* please find a better name... */
emit_32_or_64(bool is64, const u32 insn_32, const u32 inst_64, struct
rv_jit_context *ctx)
{
if (is64)
emit(insn_64, ctx);
else {
emit(insn_32, ctx);
rd = xxxx;
emit_zext_32(rd, ctx);
}
}

Thanks,
Song

> ---
> arch/riscv/net/bpf_jit_comp.c | 24 ++++++++++++++++++++++++
> 1 file changed, 24 insertions(+)
>
> diff --git a/arch/riscv/net/bpf_jit_comp.c b/arch/riscv/net/bpf_jit_comp.c
> index 80b12aa5e10d..426d5c33ea90 100644
> --- a/arch/riscv/net/bpf_jit_comp.c
> +++ b/arch/riscv/net/bpf_jit_comp.c
> @@ -751,22 +751,32 @@ static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
> case BPF_ALU | BPF_ADD | BPF_X:
> case BPF_ALU64 | BPF_ADD | BPF_X:
> emit(is64 ? rv_add(rd, rd, rs) : rv_addw(rd, rd, rs), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
> case BPF_ALU | BPF_SUB | BPF_X:
> case BPF_ALU64 | BPF_SUB | BPF_X:
> emit(is64 ? rv_sub(rd, rd, rs) : rv_subw(rd, rd, rs), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
> case BPF_ALU | BPF_AND | BPF_X:
> case BPF_ALU64 | BPF_AND | BPF_X:
> emit(rv_and(rd, rd, rs), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
> case BPF_ALU | BPF_OR | BPF_X:
> case BPF_ALU64 | BPF_OR | BPF_X:
> emit(rv_or(rd, rd, rs), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
> case BPF_ALU | BPF_XOR | BPF_X:
> case BPF_ALU64 | BPF_XOR | BPF_X:
> emit(rv_xor(rd, rd, rs), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
> case BPF_ALU | BPF_MUL | BPF_X:
> case BPF_ALU64 | BPF_MUL | BPF_X:
> @@ -789,14 +799,20 @@ static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
> case BPF_ALU | BPF_LSH | BPF_X:
> case BPF_ALU64 | BPF_LSH | BPF_X:
> emit(is64 ? rv_sll(rd, rd, rs) : rv_sllw(rd, rd, rs), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
> case BPF_ALU | BPF_RSH | BPF_X:
> case BPF_ALU64 | BPF_RSH | BPF_X:
> emit(is64 ? rv_srl(rd, rd, rs) : rv_srlw(rd, rd, rs), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
> case BPF_ALU | BPF_ARSH | BPF_X:
> case BPF_ALU64 | BPF_ARSH | BPF_X:
> emit(is64 ? rv_sra(rd, rd, rs) : rv_sraw(rd, rd, rs), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
>
> /* dst = -dst */
> @@ -804,6 +820,8 @@ static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
> case BPF_ALU64 | BPF_NEG:
> emit(is64 ? rv_sub(rd, RV_REG_ZERO, rd) :
> rv_subw(rd, RV_REG_ZERO, rd), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
>
> /* dst = BSWAP##imm(dst) */
> @@ -958,14 +976,20 @@ static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
> case BPF_ALU | BPF_LSH | BPF_K:
> case BPF_ALU64 | BPF_LSH | BPF_K:
> emit(is64 ? rv_slli(rd, rd, imm) : rv_slliw(rd, rd, imm), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
> case BPF_ALU | BPF_RSH | BPF_K:
> case BPF_ALU64 | BPF_RSH | BPF_K:
> emit(is64 ? rv_srli(rd, rd, imm) : rv_srliw(rd, rd, imm), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
> case BPF_ALU | BPF_ARSH | BPF_K:
> case BPF_ALU64 | BPF_ARSH | BPF_K:
> emit(is64 ? rv_srai(rd, rd, imm) : rv_sraiw(rd, rd, imm), ctx);
> + if (!is64)
> + emit_zext_32(rd, ctx);
> break;
>
> /* JUMP off */
> --
> 2.19.1
>

2019-05-30 22:36:44

by Luke Nelson

[permalink] [raw]
Subject: Re: [PATCH 1/2] bpf, riscv: fix bugs in JIT for 32-bit ALU operations

On Thu, May 30, 2019 at 1:53 PM Song Liu <[email protected]> wrote:
>
> This is a little messy. How about we introduce some helper function
> like:
>
> /* please find a better name... */
> emit_32_or_64(bool is64, const u32 insn_32, const u32 inst_64, struct
> rv_jit_context *ctx)
> {
> if (is64)
> emit(insn_64, ctx);
> else {
> emit(insn_32, ctx);
> rd = xxxx;
> emit_zext_32(rd, ctx);
> }
> }

This same check is used throughout the file, maybe clean it up in a
separate patch?

2019-05-30 23:10:24

by Song Liu

[permalink] [raw]
Subject: Re: [PATCH 1/2] bpf, riscv: fix bugs in JIT for 32-bit ALU operations

On Thu, May 30, 2019 at 3:34 PM Luke Nelson <[email protected]> wrote:
>
> On Thu, May 30, 2019 at 1:53 PM Song Liu <[email protected]> wrote:
> >
> > This is a little messy. How about we introduce some helper function
> > like:
> >
> > /* please find a better name... */
> > emit_32_or_64(bool is64, const u32 insn_32, const u32 inst_64, struct
> > rv_jit_context *ctx)
> > {
> > if (is64)
> > emit(insn_64, ctx);
> > else {
> > emit(insn_32, ctx);
> > rd = xxxx;
> > emit_zext_32(rd, ctx);
> > }
> > }
>
> This same check is used throughout the file, maybe clean it up in a
> separate patch?

Yes, let's do follow up patch.

Thanks,
Song

2019-05-31 07:25:28

by Jiong Wang

[permalink] [raw]
Subject: Re: [PATCH 1/2] bpf, riscv: fix bugs in JIT for 32-bit ALU operations


Song Liu writes:

> On Thu, May 30, 2019 at 3:34 PM Luke Nelson <[email protected]> wrote:
>>
>> On Thu, May 30, 2019 at 1:53 PM Song Liu <[email protected]> wrote:
>> >
>> > This is a little messy. How about we introduce some helper function
>> > like:
>> >
>> > /* please find a better name... */
>> > emit_32_or_64(bool is64, const u32 insn_32, const u32 inst_64, struct
>> > rv_jit_context *ctx)
>> > {
>> > if (is64)
>> > emit(insn_64, ctx);
>> > else {
>> > emit(insn_32, ctx);
>> > rd = xxxx;
>> > emit_zext_32(rd, ctx);
>> > }
>> > }
>>
>> This same check is used throughout the file, maybe clean it up in a
>> separate patch?

We also need to enable the recent 32-bit opt (on bpf-next) on these missing
insns, like what has been done at:

https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=66d0d5a854a6625974e7de4b874e7934988b0ef8

Perhaps the best way is to wait this patch merged back to bpf-next, then we
do two patches, the first one to enable the opt, the second one then do the
re-factor. I guess this could avoid some code conflict.

Regards,
Jiong

>
> Yes, let's do follow up patch.
>
> Thanks,
> Song