Commit ec19e02b authored by Russell King's avatar Russell King

ARM: net: bpf: fix LDX instructions

When the source and destination register are identical, our JIT does not
generate correct code, which leads to kernel oopses.

Fix this by (a) generating more efficient code, and (b) making use of
the temporary earlier if we will overwrite the address register.

Fixes: 39c13c20 ("arm: eBPF JIT compiler")
Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
parent 02088d9b
...@@ -913,33 +913,53 @@ static inline void emit_str_r(const u8 dst, const u8 src, bool dstk, ...@@ -913,33 +913,53 @@ static inline void emit_str_r(const u8 dst, const u8 src, bool dstk,
} }
/* dst = *(size*)(src + off) */ /* dst = *(size*)(src + off) */
static inline void emit_ldx_r(const u8 dst, const u8 src, bool dstk, static inline void emit_ldx_r(const u8 dst[], const u8 src, bool dstk,
const s32 off, struct jit_ctx *ctx, const u8 sz){ s32 off, struct jit_ctx *ctx, const u8 sz){
const u8 *tmp = bpf2a32[TMP_REG_1]; const u8 *tmp = bpf2a32[TMP_REG_1];
u8 rd = dstk ? tmp[1] : dst; const u8 *rd = dstk ? tmp : dst;
u8 rm = src; u8 rm = src;
s32 off_max;
if (off) { if (sz == BPF_H)
off_max = 0xff;
else
off_max = 0xfff;
if (off < 0 || off > off_max) {
emit_a32_mov_i(tmp[0], off, false, ctx); emit_a32_mov_i(tmp[0], off, false, ctx);
emit(ARM_ADD_R(tmp[0], tmp[0], src), ctx); emit(ARM_ADD_R(tmp[0], tmp[0], src), ctx);
rm = tmp[0]; rm = tmp[0];
off = 0;
} else if (rd[1] == rm) {
emit(ARM_MOV_R(tmp[0], rm), ctx);
rm = tmp[0];
} }
switch (sz) { switch (sz) {
case BPF_W: case BPF_B:
/* Load a Word */ /* Load a Byte */
emit(ARM_LDR_I(rd, rm, 0), ctx); emit(ARM_LDRB_I(rd[1], rm, off), ctx);
emit_a32_mov_i(dst[0], 0, dstk, ctx);
break; break;
case BPF_H: case BPF_H:
/* Load a HalfWord */ /* Load a HalfWord */
emit(ARM_LDRH_I(rd, rm, 0), ctx); emit(ARM_LDRH_I(rd[1], rm, off), ctx);
emit_a32_mov_i(dst[0], 0, dstk, ctx);
break; break;
case BPF_B: case BPF_W:
/* Load a Byte */ /* Load a Word */
emit(ARM_LDRB_I(rd, rm, 0), ctx); emit(ARM_LDR_I(rd[1], rm, off), ctx);
emit_a32_mov_i(dst[0], 0, dstk, ctx);
break;
case BPF_DW:
/* Load a Double Word */
emit(ARM_LDR_I(rd[1], rm, off), ctx);
emit(ARM_LDR_I(rd[0], rm, off + 4), ctx);
break; break;
} }
if (dstk) if (dstk)
emit(ARM_STR_I(rd, ARM_SP, STACK_VAR(dst)), ctx); emit(ARM_STR_I(rd[1], ARM_SP, STACK_VAR(dst[1])), ctx);
if (dstk && sz == BPF_DW)
emit(ARM_STR_I(rd[0], ARM_SP, STACK_VAR(dst[0])), ctx);
} }
/* Arithmatic Operation */ /* Arithmatic Operation */
...@@ -1440,22 +1460,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx) ...@@ -1440,22 +1460,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
rn = sstk ? tmp2[1] : src_lo; rn = sstk ? tmp2[1] : src_lo;
if (sstk) if (sstk)
emit(ARM_LDR_I(rn, ARM_SP, STACK_VAR(src_lo)), ctx); emit(ARM_LDR_I(rn, ARM_SP, STACK_VAR(src_lo)), ctx);
switch (BPF_SIZE(code)) { emit_ldx_r(dst, rn, dstk, off, ctx, BPF_SIZE(code));
case BPF_W:
/* Load a Word */
case BPF_H:
/* Load a Half-Word */
case BPF_B:
/* Load a Byte */
emit_ldx_r(dst_lo, rn, dstk, off, ctx, BPF_SIZE(code));
emit_a32_mov_i(dst_hi, 0, dstk, ctx);
break;
case BPF_DW:
/* Load a double word */
emit_ldx_r(dst_lo, rn, dstk, off, ctx, BPF_W);
emit_ldx_r(dst_hi, rn, dstk, off+4, ctx, BPF_W);
break;
}
break; break;
/* R0 = ntohx(*(size *)(((struct sk_buff *)R6)->data + imm)) */ /* R0 = ntohx(*(size *)(((struct sk_buff *)R6)->data + imm)) */
case BPF_LD | BPF_ABS | BPF_W: case BPF_LD | BPF_ABS | BPF_W:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment