arm64: bpf: fix endianness conversion bugs
authorXi Wang <xi.wang@gmail.com>
Fri, 26 Jun 2015 01:39:15 +0000 (18:39 -0700)
committerCatalin Marinas <catalin.marinas@arm.com>
Fri, 26 Jun 2015 13:15:39 +0000 (14:15 +0100)
Upper bits should be zeroed in endianness conversion:

- even when there's no need to change endianness (i.e., BPF_FROM_BE
  on big endian or BPF_FROM_LE on little endian);

- after rev16.

This patch fixes such bugs by emitting extra instructions to clear
upper bits.

Cc: Zi Shen Lim <zlim.lnx@gmail.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Fixes: e54bcde3d69d ("arm64: eBPF JIT compiler")
Cc: <stable@vger.kernel.org> # 3.18+
Signed-off-by: Xi Wang <xi.wang@gmail.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
arch/arm64/net/bpf_jit.h
arch/arm64/net/bpf_jit_comp.c

index de0a81a..98a26ce 100644 (file)
 /* Rd = Rn >> shift; signed */
 #define A64_ASR(sf, Rd, Rn, shift) A64_SBFM(sf, Rd, Rn, shift, (sf) ? 63 : 31)
 
+/* Zero extend */
+#define A64_UXTH(sf, Rd, Rn) A64_UBFM(sf, Rd, Rn, 0, 15)
+#define A64_UXTW(sf, Rd, Rn) A64_UBFM(sf, Rd, Rn, 0, 31)
+
 /* Move wide (immediate) */
 #define A64_MOVEW(sf, Rd, imm16, shift, type) \
        aarch64_insn_gen_movewide(Rd, imm16, shift, \
index c81ddd4..c047598 100644 (file)
@@ -289,23 +289,41 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
        case BPF_ALU | BPF_END | BPF_FROM_BE:
 #ifdef CONFIG_CPU_BIG_ENDIAN
                if (BPF_SRC(code) == BPF_FROM_BE)
-                       break;
+                       goto emit_bswap_uxt;
 #else /* !CONFIG_CPU_BIG_ENDIAN */
                if (BPF_SRC(code) == BPF_FROM_LE)
-                       break;
+                       goto emit_bswap_uxt;
 #endif
                switch (imm) {
                case 16:
                        emit(A64_REV16(is64, dst, dst), ctx);
+                       /* zero-extend 16 bits into 64 bits */
+                       emit(A64_UXTH(is64, dst, dst), ctx);
                        break;
                case 32:
                        emit(A64_REV32(is64, dst, dst), ctx);
+                       /* upper 32 bits already cleared */
                        break;
                case 64:
                        emit(A64_REV64(dst, dst), ctx);
                        break;
                }
                break;
+emit_bswap_uxt:
+               switch (imm) {
+               case 16:
+                       /* zero-extend 16 bits into 64 bits */
+                       emit(A64_UXTH(is64, dst, dst), ctx);
+                       break;
+               case 32:
+                       /* zero-extend 32 bits into 64 bits */
+                       emit(A64_UXTW(is64, dst, dst), ctx);
+                       break;
+               case 64:
+                       /* nop */
+                       break;
+               }
+               break;
        /* dst = imm */
        case BPF_ALU | BPF_MOV | BPF_K:
        case BPF_ALU64 | BPF_MOV | BPF_K: