diff options
Diffstat (limited to 'arch/arm/net/bpf_jit_32.c')
-rw-r--r-- | arch/arm/net/bpf_jit_32.c | 42 |
1 files changed, 39 insertions, 3 deletions
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c index b5f470ddab6d..4550d247e308 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c | |||
@@ -54,6 +54,7 @@ | |||
54 | #define SEEN_DATA (1 << (BPF_MEMWORDS + 3)) | 54 | #define SEEN_DATA (1 << (BPF_MEMWORDS + 3)) |
55 | 55 | ||
56 | #define FLAG_NEED_X_RESET (1 << 0) | 56 | #define FLAG_NEED_X_RESET (1 << 0) |
57 | #define FLAG_IMM_OVERFLOW (1 << 1) | ||
57 | 58 | ||
58 | struct jit_ctx { | 59 | struct jit_ctx { |
59 | const struct bpf_prog *skf; | 60 | const struct bpf_prog *skf; |
@@ -293,6 +294,15 @@ static u16 imm_offset(u32 k, struct jit_ctx *ctx) | |||
293 | /* PC in ARM mode == address of the instruction + 8 */ | 294 | /* PC in ARM mode == address of the instruction + 8 */ |
294 | imm = offset - (8 + ctx->idx * 4); | 295 | imm = offset - (8 + ctx->idx * 4); |
295 | 296 | ||
297 | if (imm & ~0xfff) { | ||
298 | /* | ||
299 | * literal pool is too far, signal it into flags. we | ||
300 | * can only detect it on the second pass unfortunately. | ||
301 | */ | ||
302 | ctx->flags |= FLAG_IMM_OVERFLOW; | ||
303 | return 0; | ||
304 | } | ||
305 | |||
296 | return imm; | 306 | return imm; |
297 | } | 307 | } |
298 | 308 | ||
@@ -449,10 +459,21 @@ static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx) | |||
449 | return; | 459 | return; |
450 | } | 460 | } |
451 | #endif | 461 | #endif |
452 | if (rm != ARM_R0) | 462 | |
453 | emit(ARM_MOV_R(ARM_R0, rm), ctx); | 463 | /* |
464 | * For BPF_ALU | BPF_DIV | BPF_K instructions, rm is ARM_R4 | ||
465 | * (r_A) and rn is ARM_R0 (r_scratch) so load rn first into | ||
466 | * ARM_R1 to avoid accidentally overwriting ARM_R0 with rm | ||
467 | * before using it as a source for ARM_R1. | ||
468 | * | ||
469 | * For BPF_ALU | BPF_DIV | BPF_X rm is ARM_R4 (r_A) and rn is | ||
470 | * ARM_R5 (r_X) so there is no particular register overlap | ||
471 | * issues. | ||
472 | */ | ||
454 | if (rn != ARM_R1) | 473 | if (rn != ARM_R1) |
455 | emit(ARM_MOV_R(ARM_R1, rn), ctx); | 474 | emit(ARM_MOV_R(ARM_R1, rn), ctx); |
475 | if (rm != ARM_R0) | ||
476 | emit(ARM_MOV_R(ARM_R0, rm), ctx); | ||
456 | 477 | ||
457 | ctx->seen |= SEEN_CALL; | 478 | ctx->seen |= SEEN_CALL; |
458 | emit_mov_i(ARM_R3, (u32)jit_udiv, ctx); | 479 | emit_mov_i(ARM_R3, (u32)jit_udiv, ctx); |
@@ -865,6 +886,14 @@ b_epilogue: | |||
865 | default: | 886 | default: |
866 | return -1; | 887 | return -1; |
867 | } | 888 | } |
889 | |||
890 | if (ctx->flags & FLAG_IMM_OVERFLOW) | ||
891 | /* | ||
892 | * this instruction generated an overflow when | ||
893 | * trying to access the literal pool, so | ||
894 | * delegate this filter to the kernel interpreter. | ||
895 | */ | ||
896 | return -1; | ||
868 | } | 897 | } |
869 | 898 | ||
870 | /* compute offsets only during the first pass */ | 899 | /* compute offsets only during the first pass */ |
@@ -927,7 +956,14 @@ void bpf_jit_compile(struct bpf_prog *fp) | |||
927 | ctx.idx = 0; | 956 | ctx.idx = 0; |
928 | 957 | ||
929 | build_prologue(&ctx); | 958 | build_prologue(&ctx); |
930 | build_body(&ctx); | 959 | if (build_body(&ctx) < 0) { |
960 | #if __LINUX_ARM_ARCH__ < 7 | ||
961 | if (ctx.imm_count) | ||
962 | kfree(ctx.imms); | ||
963 | #endif | ||
964 | bpf_jit_binary_free(header); | ||
965 | goto out; | ||
966 | } | ||
931 | build_epilogue(&ctx); | 967 | build_epilogue(&ctx); |
932 | 968 | ||
933 | flush_icache_range((u32)ctx.target, (u32)(ctx.target + ctx.idx)); | 969 | flush_icache_range((u32)ctx.target, (u32)(ctx.target + ctx.idx)); |