Unverified Commit 6cb7ddc0 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!5364 v2 CVE-2023-52452

Merge Pull Request from: @ci-robot 
 
PR sync from: Pu Lehui <pulehui@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/YPURA5IFWBDZYEDFERUJF56SFW4O6BDM/ 
Andrei Matei (2):
  bpf: Fix verification of indirect var-off stack access
  bpf: Fix accesses to uninit stack slots

Eduard Zingerman (1):
  bpf: Allow reads from uninit stack

Kees Cook (1):
  bpf, verifier: Fix memory leak in array reallocation for stack state

Lorenz Bauer (2):
  bpf: verifier: Improve function state reallocation
  bpf: verifier: Use copy_array for jmp_history

Stanislav Fomichev (1):
  bpf: expose bpf_strtol and bpf_strtoul to all program types


-- 
2.34.1
 
https://gitee.com/src-openeuler/kernel/issues/I932VT 
 
Link:https://gitee.com/openeuler/kernel/pulls/5364

 

Reviewed-by: default avatarJialin Zhang <zhangjialin11@huawei.com>
Signed-off-by: default avatarJialin Zhang <zhangjialin11@huawei.com>
parents 3dfb6585 8463d83a
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -1766,10 +1766,6 @@ static const struct bpf_func_proto *
sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{
	switch (func_id) {
	case BPF_FUNC_strtol:
		return &bpf_strtol_proto;
	case BPF_FUNC_strtoul:
		return &bpf_strtoul_proto;
	case BPF_FUNC_sysctl_get_name:
		return &bpf_sysctl_get_name_proto;
	case BPF_FUNC_sysctl_get_current_value:
+6 −1
Original line number Diff line number Diff line
@@ -405,6 +405,8 @@ const struct bpf_func_proto bpf_get_local_storage_proto = {
};
#endif

#endif /* CONFIG_CGROUPS */

#define BPF_STRTOX_BASE_MASK 0x1F

static int __bpf_strtoull(const char *buf, size_t buf_len, u64 flags,
@@ -532,7 +534,6 @@ const struct bpf_func_proto bpf_strtoul_proto = {
	.arg3_type	= ARG_ANYTHING,
	.arg4_type	= ARG_PTR_TO_LONG,
};
#endif

BPF_CALL_4(bpf_get_ns_current_pid_tgid, u64, dev, u64, ino,
	   struct bpf_pidns_info *, nsdata, u32, size)
@@ -701,6 +702,10 @@ bpf_base_func_proto(enum bpf_func_id func_id)
		return &bpf_ringbuf_discard_proto;
	case BPF_FUNC_ringbuf_query:
		return &bpf_ringbuf_query_proto;
	case BPF_FUNC_strtol:
		return &bpf_strtol_proto;
	case BPF_FUNC_strtoul:
		return &bpf_strtoul_proto;
	case BPF_FUNC_sched_tg_tag_of:
		return &bpf_sched_tg_tag_of_proto;
	case BPF_FUNC_sched_task_tag_of:
+152 −150
Original line number Diff line number Diff line
@@ -703,81 +703,123 @@ static void print_verifier_state(struct bpf_verifier_env *env,
	verbose(env, "\n");
}

#define COPY_STATE_FN(NAME, COUNT, FIELD, SIZE)				\
static int copy_##NAME##_state(struct bpf_func_state *dst,		\
			       const struct bpf_func_state *src)	\
{									\
	if (!src->FIELD)						\
		return 0;						\
	if (WARN_ON_ONCE(dst->COUNT < src->COUNT)) {			\
		/* internal bug, make state invalid to reject the program */ \
		memset(dst, 0, sizeof(*dst));				\
		return -EFAULT;						\
	}								\
	memcpy(dst->FIELD, src->FIELD,					\
	       sizeof(*src->FIELD) * (src->COUNT / SIZE));		\
	return 0;							\
}
/* copy_reference_state() */
COPY_STATE_FN(reference, acquired_refs, refs, 1)
/* copy_stack_state() */
COPY_STATE_FN(stack, allocated_stack, stack, BPF_REG_SIZE)
#undef COPY_STATE_FN

#define REALLOC_STATE_FN(NAME, COUNT, FIELD, SIZE)			\
static int realloc_##NAME##_state(struct bpf_func_state *state, int size, \
				  bool copy_old)			\
{									\
	u32 old_size = state->COUNT;					\
	struct bpf_##NAME##_state *new_##FIELD;				\
	int slot = size / SIZE;						\
									\
	if (size <= old_size || !size) {				\
		if (copy_old)						\
			return 0;					\
		state->COUNT = slot * SIZE;				\
		if (!size && old_size) {				\
			kfree(state->FIELD);				\
			state->FIELD = NULL;				\
		}							\
		return 0;						\
	}								\
	new_##FIELD = kmalloc_array(slot, sizeof(struct bpf_##NAME##_state), \
				    GFP_KERNEL);			\
	if (!new_##FIELD)						\
		return -ENOMEM;						\
	if (copy_old) {							\
		if (state->FIELD)					\
			memcpy(new_##FIELD, state->FIELD,		\
			       sizeof(*new_##FIELD) * (old_size / SIZE)); \
		memset(new_##FIELD + old_size / SIZE, 0,		\
		       sizeof(*new_##FIELD) * (size - old_size) / SIZE); \
	}								\
	state->COUNT = slot * SIZE;					\
	kfree(state->FIELD);						\
	state->FIELD = new_##FIELD;					\
	return 0;							\
}
/* realloc_reference_state() */
REALLOC_STATE_FN(reference, acquired_refs, refs, 1)
/* realloc_stack_state() */
REALLOC_STATE_FN(stack, allocated_stack, stack, BPF_REG_SIZE)
#undef REALLOC_STATE_FN

/* do_check() starts with zero-sized stack in struct bpf_verifier_state to
 * make it consume minimal amount of memory. check_stack_write() access from
 * the program calls into realloc_func_state() to grow the stack size.
 * Note there is a non-zero 'parent' pointer inside bpf_verifier_state
 * which realloc_stack_state() copies over. It points to previous
 * bpf_verifier_state which is never reallocated.
 */
static int realloc_func_state(struct bpf_func_state *state, int stack_size,
			      int refs_size, bool copy_old)
{
	int err = realloc_reference_state(state, refs_size, copy_old);
	if (err)
		return err;
	return realloc_stack_state(state, stack_size, copy_old);
/* copy array src of length n * size bytes to dst. dst is reallocated if it's too
 * small to hold src. This is different from krealloc since we don't want to preserve
 * the contents of dst.
 *
 * Leaves dst untouched if src is NULL or length is zero. Returns NULL if memory could
 * not be allocated.
 */
static void *copy_array(void *dst, const void *src, size_t n, size_t size, gfp_t flags)
{
	size_t bytes;

	if (ZERO_OR_NULL_PTR(src))
		goto out;

	if (unlikely(check_mul_overflow(n, size, &bytes)))
		return NULL;

	if (ksize(dst) < bytes) {
		kfree(dst);
		dst = kmalloc_track_caller(bytes, flags);
		if (!dst)
			return NULL;
	}

	memcpy(dst, src, bytes);
out:
	return dst ? dst : ZERO_SIZE_PTR;
}

/* resize an array from old_n items to new_n items. the array is reallocated if it's too
 * small to hold new_n items. new items are zeroed out if the array grows.
 *
 * Contrary to krealloc_array, does not free arr if new_n is zero.
 */
static void *realloc_array(void *arr, size_t old_n, size_t new_n, size_t size)
{
	size_t bytes;
	void *new_arr;

	if (!new_n || old_n == new_n)
		goto out;

	if (unlikely(check_mul_overflow(new_n, size, &bytes))) {
		kfree(arr);
		return NULL;
	}

	new_arr = krealloc(arr, bytes, GFP_KERNEL);
	if (!new_arr) {
		kfree(arr);
		return NULL;
	}
	arr = new_arr;

	if (new_n > old_n)
		memset(arr + old_n * size, 0, (new_n - old_n) * size);

out:
	return arr ? arr : ZERO_SIZE_PTR;
}

static int copy_reference_state(struct bpf_func_state *dst, const struct bpf_func_state *src)
{
	dst->refs = copy_array(dst->refs, src->refs, src->acquired_refs,
			       sizeof(struct bpf_reference_state), GFP_KERNEL);
	if (!dst->refs)
		return -ENOMEM;

	dst->acquired_refs = src->acquired_refs;
	return 0;
}

static int copy_stack_state(struct bpf_func_state *dst, const struct bpf_func_state *src)
{
	size_t n = src->allocated_stack / BPF_REG_SIZE;

	dst->stack = copy_array(dst->stack, src->stack, n, sizeof(struct bpf_stack_state),
				GFP_KERNEL);
	if (!dst->stack)
		return -ENOMEM;

	dst->allocated_stack = src->allocated_stack;
	return 0;
}

static int resize_reference_state(struct bpf_func_state *state, size_t n)
{
	state->refs = realloc_array(state->refs, state->acquired_refs, n,
				    sizeof(struct bpf_reference_state));
	if (!state->refs)
		return -ENOMEM;

	state->acquired_refs = n;
	return 0;
}

/* Possibly update state->allocated_stack to be at least size bytes. Also
 * possibly update the function's high-water mark in its bpf_subprog_info.
 */
static int grow_stack_state(struct bpf_verifier_env *env, struct bpf_func_state *state, int size)
{
	size_t old_n = state->allocated_stack / BPF_REG_SIZE, n = size / BPF_REG_SIZE;

	if (old_n >= n)
		return 0;

	state->stack = realloc_array(state->stack, old_n, n, sizeof(struct bpf_stack_state));
	if (!state->stack)
		return -ENOMEM;

	state->allocated_stack = size;

	/* update known max for given subprogram */
	if (env->subprog_info[state->subprogno].stack_depth < size)
		env->subprog_info[state->subprogno].stack_depth = size;

	return 0;
}

/* Acquire a pointer id from the env and update the state->refs to include
@@ -791,7 +833,7 @@ static int acquire_reference_state(struct bpf_verifier_env *env, int insn_idx)
	int new_ofs = state->acquired_refs;
	int id, err;

	err = realloc_reference_state(state, state->acquired_refs + 1, true);
	err = resize_reference_state(state, state->acquired_refs + 1);
	if (err)
		return err;
	id = ++env->id_gen;
@@ -820,18 +862,6 @@ static int release_reference_state(struct bpf_func_state *state, int ptr_id)
	return -EINVAL;
}

static int transfer_reference_state(struct bpf_func_state *dst,
				    struct bpf_func_state *src)
{
	int err = realloc_reference_state(dst, src->acquired_refs, false);
	if (err)
		return err;
	err = copy_reference_state(dst, src);
	if (err)
		return err;
	return 0;
}

static void free_func_state(struct bpf_func_state *state)
{
	if (!state)
@@ -870,10 +900,6 @@ static int copy_func_state(struct bpf_func_state *dst,
{
	int err;

	err = realloc_func_state(dst, src->allocated_stack, src->acquired_refs,
				 false);
	if (err)
		return err;
	memcpy(dst, src, offsetof(struct bpf_func_state, acquired_refs));
	err = copy_reference_state(dst, src);
	if (err)
@@ -885,16 +911,13 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state,
			       const struct bpf_verifier_state *src)
{
	struct bpf_func_state *dst;
	u32 jmp_sz = sizeof(struct bpf_idx_pair) * src->jmp_history_cnt;
	int i, err;

	if (dst_state->jmp_history_cnt < src->jmp_history_cnt) {
		kfree(dst_state->jmp_history);
		dst_state->jmp_history = kmalloc(jmp_sz, GFP_USER);
	dst_state->jmp_history = copy_array(dst_state->jmp_history, src->jmp_history,
					    src->jmp_history_cnt, sizeof(struct bpf_idx_pair),
					    GFP_USER);
	if (!dst_state->jmp_history)
		return -ENOMEM;
	}
	memcpy(dst_state->jmp_history, src->jmp_history, jmp_sz);
	dst_state->jmp_history_cnt = src->jmp_history_cnt;

	/* if dst has more stack frames then src frame, free them */
@@ -2473,10 +2496,6 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
	struct bpf_reg_state *reg = NULL;
	u32 dst_reg = insn->dst_reg;

	err = realloc_func_state(state, round_up(slot + 1, BPF_REG_SIZE),
				 state->acquired_refs, true);
	if (err)
		return err;
	/* caller checked that off % size == 0 and -MAX_BPF_STACK <= off < 0,
	 * so it's aligned access and [off, off + size) are within stack limits
	 */
@@ -2625,12 +2644,6 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env,
	if (value_reg && register_is_null(value_reg))
		writing_zero = true;

	err = realloc_func_state(state, round_up(-min_off, BPF_REG_SIZE),
				 state->acquired_refs, true);
	if (err)
		return err;


	/* Variable offset writes destroy any spilled pointers in range. */
	for (i = min_off; i < max_off; i++) {
		u8 new_type, *stype;
@@ -2795,6 +2808,8 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
						continue;
					if (type == STACK_MISC)
						continue;
					if (type == STACK_INVALID && env->allow_uninit_stack)
						continue;
					verbose(env, "invalid read from stack off %d+%d size %d\n",
						off, i, size);
					return -EACCES;
@@ -2832,6 +2847,8 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
				continue;
			if (type == STACK_ZERO)
				continue;
			if (type == STACK_INVALID && env->allow_uninit_stack)
				continue;
			verbose(env, "invalid read from stack off %d+%d size %d\n",
				off, i, size);
			return -EACCES;
@@ -3454,20 +3471,6 @@ static int check_ptr_alignment(struct bpf_verifier_env *env,
					   strict);
}

static int update_stack_depth(struct bpf_verifier_env *env,
			      const struct bpf_func_state *func,
			      int off)
{
	u16 stack = env->subprog_info[func->subprogno].stack_depth;

	if (stack >= -off)
		return 0;

	/* update known max for given subprogram */
	env->subprog_info[func->subprogno].stack_depth = -off;
	return 0;
}

/* starting from main bpf function walk all instructions of the function
 * and recursively walk all callees that given function can call.
 * Ignore jump and exit insns.
@@ -3879,13 +3882,14 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,
 * The minimum valid offset is -MAX_BPF_STACK for writes, and
 * -state->allocated_stack for reads.
 */
static int check_stack_slot_within_bounds(int off,
static int check_stack_slot_within_bounds(struct bpf_verifier_env *env,
					  s64 off,
					  struct bpf_func_state *state,
					  enum bpf_access_type t)
{
	int min_valid_off;

	if (t == BPF_WRITE)
	if (t == BPF_WRITE || env->allow_uninit_stack)
		min_valid_off = -MAX_BPF_STACK;
	else
		min_valid_off = -state->allocated_stack;
@@ -3922,10 +3926,7 @@ static int check_stack_access_within_bounds(

	if (tnum_is_const(reg->var_off)) {
		min_off = reg->var_off.value + off;
		if (access_size > 0)
			max_off = min_off + access_size - 1;
		else
			max_off = min_off;
		max_off = min_off + access_size;
	} else {
		if (reg->smax_value >= BPF_MAX_VAR_OFF ||
		    reg->smin_value <= -BPF_MAX_VAR_OFF) {
@@ -3934,15 +3935,12 @@ static int check_stack_access_within_bounds(
			return -EACCES;
		}
		min_off = reg->smin_value + off;
		if (access_size > 0)
			max_off = reg->smax_value + off + access_size - 1;
		else
			max_off = min_off;
		max_off = reg->smax_value + off + access_size;
	}

	err = check_stack_slot_within_bounds(min_off, state, type);
	if (!err)
		err = check_stack_slot_within_bounds(max_off, state, type);
	err = check_stack_slot_within_bounds(env, min_off, state, type);
	if (!err && max_off > 0)
		err = -EINVAL; /* out of stack access into non-negative offsets */

	if (err) {
		if (tnum_is_const(reg->var_off)) {
@@ -3955,10 +3953,12 @@ static int check_stack_access_within_bounds(
			verbose(env, "invalid variable-offset%s stack R%d var_off=%s size=%d\n",
				err_extra, regno, tn_buf, access_size);
		}
	}
		return err;
	}

	return grow_stack_state(env, state, round_up(-min_off, BPF_REG_SIZE));
}

/* check whether memory at (regno + off) is accessible for t = (read | write)
 * if t==write, value_regno is a register which value is stored into memory
 * if t==read, value_regno is a register which will receive the value from memory
@@ -3971,7 +3971,6 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
{
	struct bpf_reg_state *regs = cur_regs(env);
	struct bpf_reg_state *reg = regs + regno;
	struct bpf_func_state *state;
	int size, err = 0;

	size = bpf_size_to_bytes(bpf_size);
@@ -4089,11 +4088,6 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
		if (err)
			return err;

		state = func(env, reg);
		err = update_stack_depth(env, state, off);
		if (err)
			return err;

		if (t == BPF_READ)
			err = check_stack_read(env, regno, off, size,
					       value_regno);
@@ -4228,7 +4222,8 @@ static int check_xadd(struct bpf_verifier_env *env, int insn_idx, struct bpf_ins

/* When register 'regno' is used to read the stack (either directly or through
 * a helper function) make sure that it's within stack boundary and, depending
 * on the access type, that all elements of the stack are initialized.
 * on the access type and privileges, that all elements of the stack are
 * initialized.
 *
 * 'off' includes 'regno->off', but not its dynamic part (if any).
 *
@@ -4311,12 +4306,16 @@ static int check_stack_range_initialized(

		slot = -i - 1;
		spi = slot / BPF_REG_SIZE;
		if (state->allocated_stack <= slot)
			goto err;
		if (state->allocated_stack <= slot) {
			verbose(env, "verifier bug: allocated_stack too small");
			return -EFAULT;
		}

		stype = &state->stack[spi].slot_type[slot % BPF_REG_SIZE];
		if (*stype == STACK_MISC)
			goto mark;
		if (*stype == STACK_ZERO) {
		if ((*stype == STACK_ZERO) ||
		    (*stype == STACK_INVALID && env->allow_uninit_stack)) {
			if (clobber) {
				/* helper can write anything into the stack */
				*stype = STACK_MISC;
@@ -4339,7 +4338,6 @@ static int check_stack_range_initialized(
			goto mark;
		}

err:
		if (tnum_is_const(reg->var_off)) {
			verbose(env, "invalid%s read from stack R%d off %d+%d size %d\n",
				err_extra, regno, min_off, i - min_off, access_size);
@@ -4359,7 +4357,7 @@ static int check_stack_range_initialized(
			      state->stack[spi].spilled_ptr.parent,
			      REG_LIVE_READ64);
	}
	return update_stack_depth(env, state, min_off);
	return 0;
}

static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
@@ -5416,7 +5414,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
			subprog /* subprog number within this prog */);

	/* Transfer references to the callee */
	err = transfer_reference_state(callee, caller);
	err = copy_reference_state(callee, caller);
	if (err)
		return err;

@@ -5469,7 +5467,7 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
	caller->regs[BPF_REG_0] = *r0;

	/* Transfer references to the caller */
	err = transfer_reference_state(caller, callee);
	err = copy_reference_state(caller, callee);
	if (err)
		return err;

@@ -9530,6 +9528,10 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,
		if (old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_INVALID)
			continue;

		if (env->allow_uninit_stack &&
		    old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_MISC)
			continue;

		/* explored stack has more populated slots than current stack
		 * and these slots were used
		 */
+7 −4
Original line number Diff line number Diff line
@@ -17,8 +17,9 @@
	BPF_EXIT_INSN(),
	},
	.fixup_map_hash_8b = { 2 },
	.errstr = "invalid indirect read from stack",
	.result = REJECT,
	.result = ACCEPT,
	.errstr_unpriv = "invalid indirect read from stack",
	.result_unpriv = REJECT,
},
{
	"uninitialized stack2",
@@ -27,8 +28,10 @@
	BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -8),
	BPF_EXIT_INSN(),
	},
	.errstr = "invalid read from stack",
	.result = REJECT,
	.result = ACCEPT,
	.retval = POINTER_VALUE,
	.errstr_unpriv = "invalid read from stack",
	.result_unpriv = REJECT,
},
{
	"invalid fp arithmetic",
+11 −6
Original line number Diff line number Diff line
@@ -1228,7 +1228,9 @@
	.prog_type = BPF_PROG_TYPE_XDP,
	.fixup_map_hash_8b = { 23 },
	.result = REJECT,
	.errstr = "invalid read from stack R7 off=-16 size=8",
	.errstr = "R0 invalid mem access 'inv'",
	.result_unpriv = REJECT,
	.errstr_unpriv = "invalid read from stack R7 off=-16 size=8",
},
{
	"calls: two calls that receive map_value via arg=ptr_stack_of_caller. test1",
@@ -1948,19 +1950,22 @@
	 * that fp-8 stack slot was unused in the fall-through
	 * branch and will accept the program incorrectly
	 */
	BPF_JMP_IMM(BPF_JGT, BPF_REG_1, 2, 2),
	BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32),
	BPF_JMP_IMM(BPF_JGT, BPF_REG_0, 2, 2),
	BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
	BPF_JMP_IMM(BPF_JA, 0, 0, 0),
	BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
	BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
	BPF_LD_MAP_FD(BPF_REG_1, 0),
	BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
	BPF_MOV64_IMM(BPF_REG_0, 0),
	BPF_EXIT_INSN(),
	},
	.fixup_map_hash_48b = { 6 },
	.errstr = "invalid indirect read from stack R2 off -8+0 size 8",
	.result = REJECT,
	.prog_type = BPF_PROG_TYPE_XDP,
	.fixup_map_hash_48b = { 7 },
	.errstr_unpriv = "invalid indirect read from stack R2 off -8+0 size 8",
	.result_unpriv = REJECT,
	/* in privileged mode reads from uninitialized stack locations are permitted */
	.result = ACCEPT,
},
{
	"calls: ctx read at start of subprog",
Loading