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 e1268f905026..e0e23582c8b4 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c @@ -54,6 +54,7 @@  #define SEEN_DATA		(1 << (BPF_MEMWORDS + 3))  #define FLAG_NEED_X_RESET	(1 << 0) +#define FLAG_IMM_OVERFLOW	(1 << 1)  struct jit_ctx {  	const struct bpf_prog *skf; @@ -293,6 +294,15 @@ static u16 imm_offset(u32 k, struct jit_ctx *ctx)  	/* PC in ARM mode == address of the instruction + 8 */  	imm = offset - (8 + ctx->idx * 4); +	if (imm & ~0xfff) { +		/* +		 * literal pool is too far, signal it into flags. we +		 * can only detect it on the second pass unfortunately. +		 */ +		ctx->flags |= FLAG_IMM_OVERFLOW; +		return 0; +	} +  	return imm;  } @@ -449,10 +459,21 @@ static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx)  		return;  	}  #endif -	if (rm != ARM_R0) -		emit(ARM_MOV_R(ARM_R0, rm), ctx); + +	/* +	 * For BPF_ALU | BPF_DIV | BPF_K instructions, rm is ARM_R4 +	 * (r_A) and rn is ARM_R0 (r_scratch) so load rn first into +	 * ARM_R1 to avoid accidentally overwriting ARM_R0 with rm +	 * before using it as a source for ARM_R1. +	 * +	 * For BPF_ALU | BPF_DIV | BPF_X rm is ARM_R4 (r_A) and rn is +	 * ARM_R5 (r_X) so there is no particular register overlap +	 * issues. +	 */  	if (rn != ARM_R1)  		emit(ARM_MOV_R(ARM_R1, rn), ctx); +	if (rm != ARM_R0) +		emit(ARM_MOV_R(ARM_R0, rm), ctx);  	ctx->seen |= SEEN_CALL;  	emit_mov_i(ARM_R3, (u32)jit_udiv, ctx); @@ -855,6 +876,14 @@ b_epilogue:  		default:  			return -1;  		} + +		if (ctx->flags & FLAG_IMM_OVERFLOW) +			/* +			 * this instruction generated an overflow when +			 * trying to access the literal pool, so +			 * delegate this filter to the kernel interpreter. +			 */ +			return -1;  	}  	/* compute offsets only during the first pass */ @@ -917,7 +946,14 @@ void bpf_jit_compile(struct bpf_prog *fp)  	ctx.idx = 0;  	build_prologue(&ctx); -	build_body(&ctx); +	if (build_body(&ctx) < 0) { +#if __LINUX_ARM_ARCH__ < 7 +		if (ctx.imm_count) +			kfree(ctx.imms); +#endif +		bpf_jit_binary_free(header); +		goto out; +	}  	build_epilogue(&ctx);  	flush_icache_range((u32)ctx.target, (u32)(ctx.target + ctx.idx)); |