diff options
Diffstat (limited to 'arch/x86/net/bpf_jit_comp.c')
| -rw-r--r-- | arch/x86/net/bpf_jit_comp.c | 171 | 
1 files changed, 87 insertions, 84 deletions
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index b808be77635e..1056bbf55b17 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1003,6 +1003,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image  		u8 b2 = 0, b3 = 0;  		u8 *start_of_ldx;  		s64 jmp_offset; +		s16 insn_off;  		u8 jmp_cond;  		u8 *func;  		int nops; @@ -1369,57 +1370,52 @@ st:			if (is_imm8(insn->off))  		case BPF_LDX | BPF_PROBE_MEM | BPF_W:  		case BPF_LDX | BPF_MEM | BPF_DW:  		case BPF_LDX | BPF_PROBE_MEM | BPF_DW: +			insn_off = insn->off; +  			if (BPF_MODE(insn->code) == BPF_PROBE_MEM) { -				/* Though the verifier prevents negative insn->off in BPF_PROBE_MEM -				 * add abs(insn->off) to the limit to make sure that negative -				 * offset won't be an issue. -				 * insn->off is s16, so it won't affect valid pointers. +				/* Conservatively check that src_reg + insn->off is a kernel address: +				 *   src_reg + insn->off >= TASK_SIZE_MAX + PAGE_SIZE +				 * src_reg is used as scratch for src_reg += insn->off and restored +				 * after emit_ldx if necessary  				 */ -				u64 limit = TASK_SIZE_MAX + PAGE_SIZE + abs(insn->off); -				u8 *end_of_jmp1, *end_of_jmp2; -				/* Conservatively check that src_reg + insn->off is a kernel address: -				 * 1. src_reg + insn->off >= limit -				 * 2. src_reg + insn->off doesn't become small positive. -				 * Cannot do src_reg + insn->off >= limit in one branch, -				 * since it needs two spare registers, but JIT has only one. +				u64 limit = TASK_SIZE_MAX + PAGE_SIZE; +				u8 *end_of_jmp; + +				/* At end of these emitted checks, insn->off will have been added +				 * to src_reg, so no need to do relative load with insn->off offset  				 */ +				insn_off = 0;  				/* movabsq r11, limit */  				EMIT2(add_1mod(0x48, AUX_REG), add_1reg(0xB8, AUX_REG));  				EMIT((u32)limit, 4);  				EMIT(limit >> 32, 4); + +				if (insn->off) { +					/* add src_reg, insn->off */ +					maybe_emit_1mod(&prog, src_reg, true); +					EMIT2_off32(0x81, add_1reg(0xC0, src_reg), insn->off); +				} +  				/* cmp src_reg, r11 */  				maybe_emit_mod(&prog, src_reg, AUX_REG, true);  				EMIT2(0x39, add_2reg(0xC0, src_reg, AUX_REG)); -				/* if unsigned '<' goto end_of_jmp2 */ -				EMIT2(X86_JB, 0); -				end_of_jmp1 = prog; - -				/* mov r11, src_reg */ -				emit_mov_reg(&prog, true, AUX_REG, src_reg); -				/* add r11, insn->off */ -				maybe_emit_1mod(&prog, AUX_REG, true); -				EMIT2_off32(0x81, add_1reg(0xC0, AUX_REG), insn->off); -				/* jmp if not carry to start_of_ldx -				 * Otherwise ERR_PTR(-EINVAL) + 128 will be the user addr -				 * that has to be rejected. -				 */ -				EMIT2(0x73 /* JNC */, 0); -				end_of_jmp2 = prog; + +				/* if unsigned '>=', goto load */ +				EMIT2(X86_JAE, 0); +				end_of_jmp = prog;  				/* xor dst_reg, dst_reg */  				emit_mov_imm32(&prog, false, dst_reg, 0);  				/* jmp byte_after_ldx */  				EMIT2(0xEB, 0); -				/* populate jmp_offset for JB above to jump to xor dst_reg */ -				end_of_jmp1[-1] = end_of_jmp2 - end_of_jmp1; -				/* populate jmp_offset for JNC above to jump to start_of_ldx */ +				/* populate jmp_offset for JAE above to jump to start_of_ldx */  				start_of_ldx = prog; -				end_of_jmp2[-1] = start_of_ldx - end_of_jmp2; +				end_of_jmp[-1] = start_of_ldx - end_of_jmp;  			} -			emit_ldx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off); +			emit_ldx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn_off);  			if (BPF_MODE(insn->code) == BPF_PROBE_MEM) {  				struct exception_table_entry *ex;  				u8 *_insn = image + proglen + (start_of_ldx - temp); @@ -1428,6 +1424,18 @@ st:			if (is_imm8(insn->off))  				/* populate jmp_offset for JMP above */  				start_of_ldx[-1] = prog - start_of_ldx; +				if (insn->off && src_reg != dst_reg) { +					/* sub src_reg, insn->off +					 * Restore src_reg after "add src_reg, insn->off" in prev +					 * if statement. But if src_reg == dst_reg, emit_ldx +					 * above already clobbered src_reg, so no need to restore. +					 * If add src_reg, insn->off was unnecessary, no need to +					 * restore either. +					 */ +					maybe_emit_1mod(&prog, src_reg, true); +					EMIT2_off32(0x81, add_1reg(0xE8, src_reg), insn->off); +				} +  				if (!bpf_prog->aux->extable)  					break; @@ -1849,62 +1857,59 @@ emit_jmp:  	return proglen;  } -static void save_regs(const struct btf_func_model *m, u8 **prog, int nr_args, +static void save_regs(const struct btf_func_model *m, u8 **prog, int nr_regs,  		      int stack_size)  { -	int i, j, arg_size, nr_regs; +	int i, j, arg_size; +	bool next_same_struct = false; +  	/* Store function arguments to stack.  	 * For a function that accepts two pointers the sequence will be:  	 * mov QWORD PTR [rbp-0x10],rdi  	 * mov QWORD PTR [rbp-0x8],rsi  	 */ -	for (i = 0, j = 0; i < min(nr_args, 6); i++) { -		if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) { -			nr_regs = (m->arg_size[i] + 7) / 8; +	for (i = 0, j = 0; i < min(nr_regs, 6); i++) { +		/* The arg_size is at most 16 bytes, enforced by the verifier. */ +		arg_size = m->arg_size[j]; +		if (arg_size > 8) {  			arg_size = 8; -		} else { -			nr_regs = 1; -			arg_size = m->arg_size[i]; +			next_same_struct = !next_same_struct;  		} -		while (nr_regs) { -			emit_stx(prog, bytes_to_bpf_size(arg_size), -				 BPF_REG_FP, -				 j == 5 ? X86_REG_R9 : BPF_REG_1 + j, -				 -(stack_size - j * 8)); -			nr_regs--; -			j++; -		} +		emit_stx(prog, bytes_to_bpf_size(arg_size), +			 BPF_REG_FP, +			 i == 5 ? X86_REG_R9 : BPF_REG_1 + i, +			 -(stack_size - i * 8)); + +		j = next_same_struct ? j : j + 1;  	}  } -static void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_args, +static void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_regs,  			 int stack_size)  { -	int i, j, arg_size, nr_regs; +	int i, j, arg_size; +	bool next_same_struct = false;  	/* Restore function arguments from stack.  	 * For a function that accepts two pointers the sequence will be:  	 * EMIT4(0x48, 0x8B, 0x7D, 0xF0); mov rdi,QWORD PTR [rbp-0x10]  	 * EMIT4(0x48, 0x8B, 0x75, 0xF8); mov rsi,QWORD PTR [rbp-0x8]  	 */ -	for (i = 0, j = 0; i < min(nr_args, 6); i++) { -		if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) { -			nr_regs = (m->arg_size[i] + 7) / 8; +	for (i = 0, j = 0; i < min(nr_regs, 6); i++) { +		/* The arg_size is at most 16 bytes, enforced by the verifier. */ +		arg_size = m->arg_size[j]; +		if (arg_size > 8) {  			arg_size = 8; -		} else { -			nr_regs = 1; -			arg_size = m->arg_size[i]; +			next_same_struct = !next_same_struct;  		} -		while (nr_regs) { -			emit_ldx(prog, bytes_to_bpf_size(arg_size), -				 j == 5 ? X86_REG_R9 : BPF_REG_1 + j, -				 BPF_REG_FP, -				 -(stack_size - j * 8)); -			nr_regs--; -			j++; -		} +		emit_ldx(prog, bytes_to_bpf_size(arg_size), +			 i == 5 ? X86_REG_R9 : BPF_REG_1 + i, +			 BPF_REG_FP, +			 -(stack_size - i * 8)); + +		j = next_same_struct ? j : j + 1;  	}  } @@ -2130,8 +2135,8 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i  				struct bpf_tramp_links *tlinks,  				void *func_addr)  { -	int ret, i, nr_args = m->nr_args, extra_nregs = 0; -	int regs_off, ip_off, args_off, stack_size = nr_args * 8, run_ctx_off; +	int i, ret, nr_regs = m->nr_args, stack_size = 0; +	int regs_off, nregs_off, ip_off, run_ctx_off;  	struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY];  	struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT];  	struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; @@ -2140,17 +2145,14 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i  	u8 *prog;  	bool save_ret; -	/* x86-64 supports up to 6 arguments. 7+ can be added in the future */ -	if (nr_args > 6) -		return -ENOTSUPP; - -	for (i = 0; i < MAX_BPF_FUNC_ARGS; i++) { +	/* extra registers for struct arguments */ +	for (i = 0; i < m->nr_args; i++)  		if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) -			extra_nregs += (m->arg_size[i] + 7) / 8 - 1; -	} -	if (nr_args + extra_nregs > 6) +			nr_regs += (m->arg_size[i] + 7) / 8 - 1; + +	/* x86-64 supports up to 6 arguments. 7+ can be added in the future */ +	if (nr_regs > 6)  		return -ENOTSUPP; -	stack_size += extra_nregs * 8;  	/* Generated trampoline stack layout:  	 * @@ -2164,7 +2166,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i  	 *                 [ ...             ]  	 * RBP - regs_off  [ reg_arg1        ]  program's ctx pointer  	 * -	 * RBP - args_off  [ arg regs count  ]  always +	 * RBP - nregs_off [ regs count	     ]  always  	 *  	 * RBP - ip_off    [ traced function ]  BPF_TRAMP_F_IP_ARG flag  	 * @@ -2176,11 +2178,12 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i  	if (save_ret)  		stack_size += 8; +	stack_size += nr_regs * 8;  	regs_off = stack_size; -	/* args count  */ +	/* regs count  */  	stack_size += 8; -	args_off = stack_size; +	nregs_off = stack_size;  	if (flags & BPF_TRAMP_F_IP_ARG)  		stack_size += 8; /* room for IP address argument */ @@ -2213,11 +2216,11 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i  	EMIT1(0x53);		 /* push rbx */  	/* Store number of argument registers of the traced function: -	 *   mov rax, nr_args + extra_nregs -	 *   mov QWORD PTR [rbp - args_off], rax +	 *   mov rax, nr_regs +	 *   mov QWORD PTR [rbp - nregs_off], rax  	 */ -	emit_mov_imm64(&prog, BPF_REG_0, 0, (u32) nr_args + extra_nregs); -	emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -args_off); +	emit_mov_imm64(&prog, BPF_REG_0, 0, (u32) nr_regs); +	emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -nregs_off);  	if (flags & BPF_TRAMP_F_IP_ARG) {  		/* Store IP address of the traced function: @@ -2228,7 +2231,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i  		emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -ip_off);  	} -	save_regs(m, &prog, nr_args, regs_off); +	save_regs(m, &prog, nr_regs, regs_off);  	if (flags & BPF_TRAMP_F_CALL_ORIG) {  		/* arg1: mov rdi, im */ @@ -2258,7 +2261,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i  	}  	if (flags & BPF_TRAMP_F_CALL_ORIG) { -		restore_regs(m, &prog, nr_args, regs_off); +		restore_regs(m, &prog, nr_regs, regs_off);  		if (flags & BPF_TRAMP_F_ORIG_STACK) {  			emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, 8); @@ -2299,7 +2302,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i  		}  	if (flags & BPF_TRAMP_F_RESTORE_REGS) -		restore_regs(m, &prog, nr_args, regs_off); +		restore_regs(m, &prog, nr_regs, regs_off);  	/* This needs to be done regardless. If there were fmod_ret programs,  	 * the return value is only updated on the stack and still needs to be  |