Commit ccd2d799 authored by Anton Protopopov's avatar Anton Protopopov Committed by Alexei Starovoitov
Browse files

bpf: Fix a potential use-after-free of BTF object



Refcounting in the check_pseudo_btf_id() function is incorrect:
the __check_pseudo_btf_id() function might get called with a zero
refcounted btf. Fix this, and patch related code accordingly.

v3: rephrase a comment (AI)
v2: fix a refcount leak introduced in v1 (AI)

Reported-by: default avatar <syzbot+5a0f1995634f7c1dadbf@syzkaller.appspotmail.com>
Closes: https://syzkaller.appspot.com/bug?extid=5a0f1995634f7c1dadbf


Fixes: 76145f72 ("bpf: Refactor check_pseudo_btf_id")
Signed-off-by: default avatarAnton Protopopov <a.s.protopopov@gmail.com>
Link: https://lore.kernel.org/r/20260209132904.63908-1-a.s.protopopov@gmail.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 04999b99
Loading
Loading
Loading
Loading
+26 −26
Original line number Diff line number Diff line
@@ -21333,29 +21333,29 @@ static int find_btf_percpu_datasec(struct btf *btf)
}
/*
 * Add btf to the used_btfs array and return the index. (If the btf was
 * already added, then just return the index.) Upon successful insertion
 * increase btf refcnt, and, if present, also refcount the corresponding
 * kernel module.
 * Add btf to the env->used_btfs array. If needed, refcount the
 * corresponding kernel module. To simplify caller's logic
 * in case of error or if btf was added before the function
 * decreases the btf refcount.
 */
static int __add_used_btf(struct bpf_verifier_env *env, struct btf *btf)
{
	struct btf_mod_pair *btf_mod;
	int ret = 0;
	int i;
	/* check whether we recorded this BTF (and maybe module) already */
	for (i = 0; i < env->used_btf_cnt; i++)
		if (env->used_btfs[i].btf == btf)
			return i;
			goto ret_put;
	if (env->used_btf_cnt >= MAX_USED_BTFS) {
		verbose(env, "The total number of btfs per program has reached the limit of %u\n",
			MAX_USED_BTFS);
		return -E2BIG;
		ret = -E2BIG;
		goto ret_put;
	}
	btf_get(btf);
	btf_mod = &env->used_btfs[env->used_btf_cnt];
	btf_mod->btf = btf;
	btf_mod->module = NULL;
@@ -21364,12 +21364,18 @@ static int __add_used_btf(struct bpf_verifier_env *env, struct btf *btf)
	if (btf_is_module(btf)) {
		btf_mod->module = btf_try_get_module(btf);
		if (!btf_mod->module) {
			btf_put(btf);
			return -ENXIO;
			ret = -ENXIO;
			goto ret_put;
		}
	}
	return env->used_btf_cnt++;
	env->used_btf_cnt++;
	return 0;
ret_put:
	/* Either error or this BTF was already added */
	btf_put(btf);
	return ret;
}
/* replace pseudo btf_id with kernel symbol address */
@@ -21466,9 +21472,7 @@ static int check_pseudo_btf_id(struct bpf_verifier_env *env,
	btf_fd = insn[1].imm;
	if (btf_fd) {
		CLASS(fd, f)(btf_fd);
		btf = __btf_get_by_fd(f);
		btf = btf_get_by_fd(btf_fd);
		if (IS_ERR(btf)) {
			verbose(env, "invalid module BTF object FD specified.\n");
			return -EINVAL;
@@ -21478,17 +21482,17 @@ static int check_pseudo_btf_id(struct bpf_verifier_env *env,
			verbose(env, "kernel is missing BTF, make sure CONFIG_DEBUG_INFO_BTF=y is specified in Kconfig.\n");
			return -EINVAL;
		}
		btf_get(btf_vmlinux);
		btf = btf_vmlinux;
	}
	err = __check_pseudo_btf_id(env, insn, aux, btf);
	if (err)
	if (err) {
		btf_put(btf);
		return err;
	}
	err = __add_used_btf(env, btf);
	if (err < 0)
		return err;
	return 0;
	return __add_used_btf(env, btf);
}
static bool is_tracing_prog_type(enum bpf_prog_type type)
@@ -25368,13 +25372,9 @@ static int add_fd_from_fd_array(struct bpf_verifier_env *env, int fd)
		return 0;
	}
	btf = __btf_get_by_fd(f);
	if (!IS_ERR(btf)) {
		err = __add_used_btf(env, btf);
		if (err < 0)
			return err;
		return 0;
	}
	btf = btf_get_by_fd(fd);
	if (!IS_ERR(btf))
		return __add_used_btf(env, btf);
	verbose(env, "fd %d is not pointing to valid bpf_map or btf\n", fd);
	return PTR_ERR(map);