Commit d5273fd3 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull bpf fixes from Alexei Starovoitov:

 - Fix how linked registers track zero extension of subregisters (Daniel
   Borkmann)

 - Fix unsound scalar fork for OR instructions (Daniel Wade)

 - Fix exception exit lock check for subprogs (Ihor Solodrai)

 - Fix undefined behavior in interpreter for SDIV/SMOD instructions
   (Jenny Guanni Qu)

 - Release module's BTF when module is unloaded (Kumar Kartikeya
   Dwivedi)

 - Fix constant blinding for PROBE_MEM32 instructions (Sachin Kumar)

 - Reset register ID for END instructions to prevent incorrect value
   tracking (Yazhou Tang)

* tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf:
  selftests/bpf: Add a test cases for sync_linked_regs regarding zext propagation
  bpf: Fix sync_linked_regs regarding BPF_ADD_CONST32 zext propagation
  selftests/bpf: Add tests for maybe_fork_scalars() OR vs AND handling
  bpf: Fix unsound scalar forking in maybe_fork_scalars() for BPF_OR
  selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend
  bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN
  selftests/bpf: Add tests for bpf_throw lock leak from subprogs
  bpf: Fix exception exit lock checking for subprogs
  bpf: Release module BTF IDR before module unload
  selftests/bpf: Fix pkg-config call on static builds
  bpf: Fix constant blinding for PROBE_MEM32 stores
  selftests/bpf: Add test for BPF_END register ID reset
  bpf: Reset register ID for BPF_END value tracking
parents ac57fa9f 4a04d135
Loading
Loading
Loading
Loading
+20 −4
Original line number Diff line number Diff line
@@ -1787,7 +1787,16 @@ static void btf_free_id(struct btf *btf)
	 * of the _bh() version.
	 */
	spin_lock_irqsave(&btf_idr_lock, flags);
	if (btf->id) {
		idr_remove(&btf_idr, btf->id);
		/*
		 * Clear the id here to make this function idempotent, since it will get
		 * called a couple of times for module BTFs: on module unload, and then
		 * the final btf_put(). btf_alloc_id() starts IDs with 1, so we can use
		 * 0 as sentinel value.
		 */
		WRITE_ONCE(btf->id, 0);
	}
	spin_unlock_irqrestore(&btf_idr_lock, flags);
}

@@ -8115,7 +8124,7 @@ static void bpf_btf_show_fdinfo(struct seq_file *m, struct file *filp)
{
	const struct btf *btf = filp->private_data;

	seq_printf(m, "btf_id:\t%u\n", btf->id);
	seq_printf(m, "btf_id:\t%u\n", READ_ONCE(btf->id));
}
#endif

@@ -8197,7 +8206,7 @@ int btf_get_info_by_fd(const struct btf *btf,
	if (copy_from_user(&info, uinfo, info_copy))
		return -EFAULT;

	info.id = btf->id;
	info.id = READ_ONCE(btf->id);
	ubtf = u64_to_user_ptr(info.btf);
	btf_copy = min_t(u32, btf->data_size, info.btf_size);
	if (copy_to_user(ubtf, btf->data, btf_copy))
@@ -8260,7 +8269,7 @@ int btf_get_fd_by_id(u32 id)

u32 btf_obj_id(const struct btf *btf)
{
	return btf->id;
	return READ_ONCE(btf->id);
}

bool btf_is_kernel(const struct btf *btf)
@@ -8382,6 +8391,13 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
			if (btf_mod->module != module)
				continue;

			/*
			 * For modules, we do the freeing of BTF IDR as soon as
			 * module goes away to disable BTF discovery, since the
			 * btf_try_get_module() on such BTFs will fail. This may
			 * be called again on btf_put(), but it's ok to do so.
			 */
			btf_free_id(btf_mod->btf);
			list_del(&btf_mod->list);
			if (btf_mod->sysfs_attr)
				sysfs_remove_bin_file(btf_kobj, btf_mod->sysfs_attr);
+35 −8
Original line number Diff line number Diff line
@@ -1422,6 +1422,27 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from,
		*to++ = BPF_ALU64_IMM(BPF_XOR, BPF_REG_AX, imm_rnd);
		*to++ = BPF_STX_MEM(from->code, from->dst_reg, BPF_REG_AX, from->off);
		break;

	case BPF_ST | BPF_PROBE_MEM32 | BPF_DW:
	case BPF_ST | BPF_PROBE_MEM32 | BPF_W:
	case BPF_ST | BPF_PROBE_MEM32 | BPF_H:
	case BPF_ST | BPF_PROBE_MEM32 | BPF_B:
		*to++ = BPF_ALU64_IMM(BPF_MOV, BPF_REG_AX, imm_rnd ^
				      from->imm);
		*to++ = BPF_ALU64_IMM(BPF_XOR, BPF_REG_AX, imm_rnd);
		/*
		 * Cannot use BPF_STX_MEM() macro here as it
		 * hardcodes BPF_MEM mode, losing PROBE_MEM32
		 * and breaking arena addressing in the JIT.
		 */
		*to++ = (struct bpf_insn) {
			.code  = BPF_STX | BPF_PROBE_MEM32 |
				 BPF_SIZE(from->code),
			.dst_reg = from->dst_reg,
			.src_reg = BPF_REG_AX,
			.off   = from->off,
		};
		break;
	}
out:
	return to - to_buff;
@@ -1736,6 +1757,12 @@ bool bpf_opcode_in_insntable(u8 code)
}

#ifndef CONFIG_BPF_JIT_ALWAYS_ON
/* Absolute value of s32 without undefined behavior for S32_MIN */
static u32 abs_s32(s32 x)
{
	return x >= 0 ? (u32)x : -(u32)x;
}

/**
 *	___bpf_prog_run - run eBPF program on a given context
 *	@regs: is the array of MAX_BPF_EXT_REG eBPF pseudo-registers
@@ -1900,8 +1927,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
			DST = do_div(AX, (u32) SRC);
			break;
		case 1:
			AX = abs((s32)DST);
			AX = do_div(AX, abs((s32)SRC));
			AX = abs_s32((s32)DST);
			AX = do_div(AX, abs_s32((s32)SRC));
			if ((s32)DST < 0)
				DST = (u32)-AX;
			else
@@ -1928,8 +1955,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
			DST = do_div(AX, (u32) IMM);
			break;
		case 1:
			AX = abs((s32)DST);
			AX = do_div(AX, abs((s32)IMM));
			AX = abs_s32((s32)DST);
			AX = do_div(AX, abs_s32((s32)IMM));
			if ((s32)DST < 0)
				DST = (u32)-AX;
			else
@@ -1955,8 +1982,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
			DST = (u32) AX;
			break;
		case 1:
			AX = abs((s32)DST);
			do_div(AX, abs((s32)SRC));
			AX = abs_s32((s32)DST);
			do_div(AX, abs_s32((s32)SRC));
			if (((s32)DST < 0) == ((s32)SRC < 0))
				DST = (u32)AX;
			else
@@ -1982,8 +2009,8 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
			DST = (u32) AX;
			break;
		case 1:
			AX = abs((s32)DST);
			do_div(AX, abs((s32)IMM));
			AX = abs_s32((s32)DST);
			do_div(AX, abs_s32((s32)IMM));
			if (((s32)DST < 0) == ((s32)IMM < 0))
				DST = (u32)AX;
			else
+25 −8
Original line number Diff line number Diff line
@@ -15910,6 +15910,13 @@ static void scalar_byte_swap(struct bpf_reg_state *dst_reg, struct bpf_insn *ins
	/* Apply bswap if alu64 or switch between big-endian and little-endian machines */
	bool need_bswap = alu64 || (to_le == is_big_endian);
	/*
	 * If the register is mutated, manually reset its scalar ID to break
	 * any existing ties and avoid incorrect bounds propagation.
	 */
	if (need_bswap || insn->imm == 16 || insn->imm == 32)
		dst_reg->id = 0;
	if (need_bswap) {
		if (insn->imm == 16)
			dst_reg->var_off = tnum_bswap16(dst_reg->var_off);
@@ -15992,7 +15999,7 @@ static int maybe_fork_scalars(struct bpf_verifier_env *env, struct bpf_insn *ins
	else
		return 0;
	branch = push_stack(env, env->insn_idx + 1, env->insn_idx, false);
	branch = push_stack(env, env->insn_idx, env->insn_idx, false);
	if (IS_ERR(branch))
		return PTR_ERR(branch);
@@ -17408,6 +17415,12 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s
			continue;
		if ((reg->id & ~BPF_ADD_CONST) != (known_reg->id & ~BPF_ADD_CONST))
			continue;
		/*
		 * Skip mixed 32/64-bit links: the delta relationship doesn't
		 * hold across different ALU widths.
		 */
		if (((reg->id ^ known_reg->id) & BPF_ADD_CONST) == BPF_ADD_CONST)
			continue;
		if ((!(reg->id & BPF_ADD_CONST) && !(known_reg->id & BPF_ADD_CONST)) ||
		    reg->off == known_reg->off) {
			s32 saved_subreg_def = reg->subreg_def;
@@ -17435,7 +17448,7 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s
			scalar32_min_max_add(reg, &fake_reg);
			scalar_min_max_add(reg, &fake_reg);
			reg->var_off = tnum_add(reg->var_off, fake_reg.var_off);
			if (known_reg->id & BPF_ADD_CONST32)
			if ((reg->id | known_reg->id) & BPF_ADD_CONST32)
				zext_32_to_64(reg);
			reg_bounds_sync(reg);
		}
@@ -19863,11 +19876,14 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
		 * Also verify that new value satisfies old value range knowledge.
		 */
		/* ADD_CONST mismatch: different linking semantics */
		if ((rold->id & BPF_ADD_CONST) && !(rcur->id & BPF_ADD_CONST))
			return false;
		if (rold->id && !(rold->id & BPF_ADD_CONST) && (rcur->id & BPF_ADD_CONST))
		/*
		 * ADD_CONST flags must match exactly: BPF_ADD_CONST32 and
		 * BPF_ADD_CONST64 have different linking semantics in
		 * sync_linked_regs() (alu32 zero-extends, alu64 does not),
		 * so pruning across different flag types is unsafe.
		 */
		if (rold->id &&
		    (rold->id & BPF_ADD_CONST) != (rcur->id & BPF_ADD_CONST))
			return false;
		/* Both have offset linkage: offsets must match */
@@ -20904,7 +20920,8 @@ static int process_bpf_exit_full(struct bpf_verifier_env *env,
	 * state when it exits.
	 */
	int err = check_resource_leak(env, exception_exit,
				      !env->cur_state->curframe,
				      exception_exit || !env->cur_state->curframe,
				      exception_exit ? "bpf_throw" :
				      "BPF_EXIT instruction in main prog");
	if (err)
		return err;
+1 −1
Original line number Diff line number Diff line
@@ -409,7 +409,7 @@ $(RESOLVE_BTFIDS): $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/resolve_btfids \
		CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)" \
		LIBBPF_INCLUDE=$(HOST_INCLUDE_DIR) \
		EXTRA_LDFLAGS='$(SAN_LDFLAGS) $(EXTRA_LDFLAGS)' \
		HOSTPKG_CONFIG=$(PKG_CONFIG) \
		HOSTPKG_CONFIG='$(PKG_CONFIG)' \
		OUTPUT=$(HOST_BUILD_DIR)/resolve_btfids/ BPFOBJ=$(HOST_BPFOBJ)

# Get Clang's default includes on this system, as opposed to those seen by
+53 −3
Original line number Diff line number Diff line
@@ -8,6 +8,11 @@
#include "bpf_experimental.h"

extern void bpf_rcu_read_lock(void) __ksym;
extern void bpf_rcu_read_unlock(void) __ksym;
extern void bpf_preempt_disable(void) __ksym;
extern void bpf_preempt_enable(void) __ksym;
extern void bpf_local_irq_save(unsigned long *) __ksym;
extern void bpf_local_irq_restore(unsigned long *) __ksym;

#define private(name) SEC(".bss." #name) __hidden __attribute__((aligned(8)))

@@ -131,7 +136,7 @@ int reject_subprog_with_lock(void *ctx)
}

SEC("?tc")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_rcu_read_lock-ed region")
__failure __msg("bpf_throw cannot be used inside bpf_rcu_read_lock-ed region")
int reject_with_rcu_read_lock(void *ctx)
{
	bpf_rcu_read_lock();
@@ -147,11 +152,13 @@ __noinline static int throwing_subprog(struct __sk_buff *ctx)
}

SEC("?tc")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_rcu_read_lock-ed region")
__failure __msg("bpf_throw cannot be used inside bpf_rcu_read_lock-ed region")
int reject_subprog_with_rcu_read_lock(void *ctx)
{
	bpf_rcu_read_lock();
	return throwing_subprog(ctx);
	throwing_subprog(ctx);
	bpf_rcu_read_unlock();
	return 0;
}

static bool rbless(struct bpf_rb_node *n1, const struct bpf_rb_node *n2)
@@ -346,4 +353,47 @@ int reject_exception_throw_cb_diff(struct __sk_buff *ctx)
	return 0;
}

__noinline static int always_throws(void)
{
	bpf_throw(0);
	return 0;
}

__noinline static int rcu_lock_then_throw(void)
{
	bpf_rcu_read_lock();
	bpf_throw(0);
	return 0;
}

SEC("?tc")
__failure __msg("bpf_throw cannot be used inside bpf_rcu_read_lock-ed region")
int reject_subprog_rcu_lock_throw(void *ctx)
{
	rcu_lock_then_throw();
	return 0;
}

SEC("?tc")
__failure __msg("bpf_throw cannot be used inside bpf_preempt_disable-ed region")
int reject_subprog_throw_preempt_lock(void *ctx)
{
	bpf_preempt_disable();
	always_throws();
	bpf_preempt_enable();
	return 0;
}

SEC("?tc")
__failure __msg("bpf_throw cannot be used inside bpf_local_irq_save-ed region")
int reject_subprog_throw_irq_lock(void *ctx)
{
	unsigned long flags;

	bpf_local_irq_save(&flags);
	always_throws();
	bpf_local_irq_restore(&flags);
	return 0;
}

char _license[] SEC("license") = "GPL";
Loading