arm64: bpf: fix signedness bug in loading 64-bit immediate
Subsystems (4)
PATCH 1/1 arm64: bpf: fix signedness bug in loading 64-bit immediate
Consider "(u64)insn1.imm << 32 | imm" in the arm64 JIT. Since imm is signed 32-bit, it is sign-extended to 64-bit, losing the high 32 bits. The fix is to convert imm to u32 first and zero-extend it to u64.
Also extend test_bpf to catch this JIT bug; the interpreter is correct.
Before:test_bpf: #58 load 64-bit immediate ret -1 != 1 FAIL (1 times)
After:test_bpf: #58 load 64-bit immediate 74 PASS
--- arch/arm64/net/bpf_jit_comp.c | 2 +- lib/test_bpf.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-)
arch/arm64/net/bpf_jit_comp.c +1 -1
@@ -487,7 +487,7 @@ emit_cond_jmp:return -EINVAL ; } - imm64 = (u64 )insn1.imm <<32 | imm; + imm64 = ((u64 )(u32 )insn1.imm) <<32 | (u64 )(u32 )imm;emit_a64_mov_i64 (dst, imm64, ctx);return 1 ;
lib/test_bpf.c +2 -1
@@ -1755,7 +1755,8 @@ static struct bpf_test tests[] = {BPF_EXIT_INSN (),BPF_JMP_IMM (BPF_JEQ ,R3 ,0x1234 ,1 ),BPF_EXIT_INSN (), -BPF_ALU64_IMM (BPF_MOV ,R0 ,1 ), +BPF_LD_IMM64 (R0 ,0x1ffffffffLL ), +BPF_ALU64_IMM (BPF_RSH ,R0 ,32 ),/* R0 = 1 */ BPF_EXIT_INSN (), }, INTERNAL,
Signature · 1 line
1.9.1
Quoted 28 lines · Xi Wang
Consider "(u64)insn1.imm << 32 | imm" in the arm64 JIT. Since imm is signed 32-bit, it is sign-extended to 64-bit, losing the high 32 bits. The fix is to convert imm to u32 first and zero-extend it to u64.
Also extend
test_bpfto catch this JIT bug; the interpreter is correct.Before:
test_bpf: #58 load 64-bit immediate ret -1 != 1 FAIL (1 times)After:
test_bpf: #58 load 64-bit immediate 74 PASSFixes 30d3d94cc3d5 ("arm64: bpf: add 'load 64-bit immediate' instruction")Cc Zi Shen Lim <[email protected]>Cc Alexei Starovoitov <[email protected]>Cc Catalin Marinas <[email protected]>Cc Will Deacon <[email protected]>Signed-off-by Xi Wang <[email protected]>--- arch/arm64/net/bpf_jit_comp.c | 2 +- lib/test_bpf.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-)arch/arm64/net/bpf_jit_comp.c +0
-0
arch/arm64/net/bpf_jit_comp.c emit_cond_jmp: · line 490
Quoted 5 lines
arch/arm64/net/bpf_jit_comp.c +1
-1@@ -487,7 +487,7 @@ emit_cond_jmp:return -EINVAL ; } - imm64 = (u64 )insn1.imm <<32 | imm; + imm64 = ((u64 )(u32 )insn1.imm) <<32 | (u64 )(u32 )imm;
This seems a bit convoluted to me. Don't you just need to add a (u32) cast to imm and that's it? The (u64)(u32) looks redundant.
Quoted 8 lines
arch/arm64/net/bpf_jit_comp.c +0
-0emit_a64_mov_i64 (dst, imm64, ctx);return 1 ;lib/test_bpf.c +0
-0
lib/test_bpf.c static struct bpf_test tests[] = { · line 1760
Quoted 7 lines
lib/test_bpf.c +2
-1@@ -1755,7 +1755,8 @@ static struct bpf_test tests[] = {BPF_EXIT_INSN (),BPF_JMP_IMM (BPF_JEQ ,R3 ,0x1234 ,1 ),BPF_EXIT_INSN (), -BPF_ALU64_IMM (BPF_MOV ,R0 ,1 ), +BPF_LD_IMM64 (R0 ,0x1ffffffffLL ), +BPF_ALU64_IMM (BPF_RSH ,R0 ,32 ),/* R0 = 1 */ BPF_EXIT_INSN(),
This hunk should probably be a separate patch, unless you get Alexei's ack for me to take it via the arm64 tree too.
Will
Quoted 5 lines · Will Deacon
arch/arm64/net/bpf_jit_comp.c emit_cond_jmp: · line 490
Quoted 2 lines
arch/arm64/net/bpf_jit_comp.c +1
-1- imm64 = (u64 )insn1.imm <<32 | imm; + imm64 = ((u64 )(u32 )insn1.imm) <<32 | (u64 )(u32 )imm;This seems a bit convoluted to me. Don't you just need to add a (u32) cast to imm and that's it? The (u64)(u32) looks redundant.
You're right - the second (u64) is redundant; the hope was to make the code easier to understand. It's from the interpreter code in kernel/core/bpf.c, which uses (u64)(u32) as well.
Quoted 7 lines
lib/test_bpf.c static struct bpf_test tests[] = { · line 1760
Quoted 4 lines
lib/test_bpf.c +2
-1-BPF_ALU64_IMM (BPF_MOV ,R0 ,1 ), +BPF_LD_IMM64 (R0 ,0x1ffffffffLL ), +BPF_ALU64_IMM (BPF_RSH ,R0 ,32 ),/* R0 = 1 */ BPF_EXIT_INSN(),This hunk should probably be a separate patch, unless you get Alexei's ack for me to take it via the arm64 tree too.
I would be happy to split this into a separate patch if that works better, or simply drop this part.
- xi
Quoted 16 lines · Xi Wang
Quoted 5 lines · Will Deacon
Review
Quoted 2 lines
(quoted diff) +1
-1- imm64 = (u64)insn1.imm << 32 | imm; + imm64 = ((u64)(u32)insn1.imm) << 32 | (u64)(u32)imm;This seems a bit convoluted to me. Don't you just need to add a (u32) cast to imm and that's it? The (u64)(u32) looks redundant.
You're right - the second (u64) is redundant; the hope was to make the code easier to understand. It's from the interpreter code in
kernel/core/bpf.c, which uses (u64)(u32) as well.Quoted 5 lines
Quoted 4 lines
(quoted diff) +2
-1- BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_LD_IMM64(R0, 0x1ffffffffLL), + BPF_ALU64_IMM(BPF_RSH, R0, 32), /* R0 = 1 */ BPF_EXIT_INSN(),
Quoted 5 lines
Quoted 2 lines
This hunk should probably be a separate patch, unless you get Alexei's ack for me to take it via the arm64 tree too.
I would be happy to split this into a separate patch if that works better, or simply drop this part.
Ok, I plan to apply the patch below for 4.1.
Will
--->8
Quoted 1 line
From
1e4df6b7208140f3c49f316d33a409d3a161f350Mon Sep 17 00:00:00 2001
From: Xi Wang <[email protected]>
Date: Fri, 8 May 2015 06:39:51 +0100
Subject: [PATCH] arm64: bpf: fix signedness bug in loading 64-bit immediate
Consider "(u64)insn1.imm << 32 | imm" in the arm64 JIT. Since imm is signed 32-bit, it is sign-extended to 64-bit, losing the high 32 bits. The fix is to convert imm to u32 first, which will be zero-extended to u64 implicitly.
--- arch/arm64/net/bpf_jit_comp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/net/bpf_jit_comp.c +1 -1
@@ -487,7 +487,7 @@ emit_cond_jmp:return -EINVAL ; } - imm64 = (u64 )insn1.imm <<32 | imm; + imm64 = (u64 )insn1.imm <<32 | (u32 )imm;emit_a64_mov_i64 (dst, imm64, ctx);return 1 ;
Signature · 1 line
2.1.4
Quoted 2 lines · Will Deacon
Ok, I plan to apply the patch below for 4.1.
great catch. Looks good to me.
Xi, could you send a separate patch for test_bpf update to net-next? Thanks!