Commit b2e37a71 authored by Ilya Leoshkevich's avatar Ilya Leoshkevich Committed by Alexei Starovoitov
Browse files

bpf: Fix subreg optimization for BPF_FETCH



All 32-bit variants of BPF_FETCH (add, and, or, xor, xchg, cmpxchg)
define a 32-bit subreg and thus have zext_dst set. Their encoding,
however, uses dst_reg field as a base register, which causes
opt_subreg_zext_lo32_rnd_hi32() to zero-extend said base register
instead of the one the insn really defines (r0 or src_reg).

Fix by properly choosing a register being defined, similar to how
check_atomic() already does that.

Signed-off-by: default avatarIlya Leoshkevich <iii@linux.ibm.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20210210204502.83429-1-iii@linux.ibm.com
parent 6a5df969
Loading
Loading
Loading
Loading
+21 −2
Original line number Diff line number Diff line
@@ -10957,6 +10957,7 @@ static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
	for (i = 0; i < len; i++) {
		int adj_idx = i + delta;
		struct bpf_insn insn;
		u8 load_reg;

		insn = insns[adj_idx];
		if (!aux[adj_idx].zext_dst) {
@@ -10999,9 +11000,27 @@ static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
		if (!bpf_jit_needs_zext())
			continue;

		/* zext_dst means that we want to zero-extend whatever register
		 * the insn defines, which is dst_reg most of the time, with
		 * the notable exception of BPF_STX + BPF_ATOMIC + BPF_FETCH.
		 */
		if (BPF_CLASS(insn.code) == BPF_STX &&
		    BPF_MODE(insn.code) == BPF_ATOMIC) {
			/* BPF_STX + BPF_ATOMIC insns without BPF_FETCH do not
			 * define any registers, therefore zext_dst cannot be
			 * set.
			 */
			if (WARN_ON(!(insn.imm & BPF_FETCH)))
				return -EINVAL;
			load_reg = insn.imm == BPF_CMPXCHG ? BPF_REG_0
							   : insn.src_reg;
		} else {
			load_reg = insn.dst_reg;
		}

		zext_patch[0] = insn;
		zext_patch[1].dst_reg = insn.dst_reg;
		zext_patch[1].src_reg = insn.dst_reg;
		zext_patch[1].dst_reg = load_reg;
		zext_patch[1].src_reg = load_reg;
		patch = zext_patch;
		patch_len = 2;
apply_patch_buffer: