Merge branch 'misc-bug-fixes-part-2'

Kumar Kartikeya Dwivedi says:

====================
Misc bug fixes - part 2

A set of miscellaneous fixes for bugs reported by Nicholas, plus some
new findings by GPT-5.6-Sol and Sashiko. See commit logs for details.
====================

Link: https://patch.msgid.link/20260903214758.2727663-1-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alexei Starovoitov 2026-09-03 19:22:53 -07:00
commit 0237317ffc
7 changed files with 271 additions and 11 deletions

View File

@ -8749,6 +8749,7 @@ BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int
const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {
.func = bpf_btf_find_by_name_kind,
.gpl_only = false,
.might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg2_type = ARG_MEM_SIZE,

View File

@ -875,6 +875,7 @@ BPF_CALL_4(bpf_get_stack_sleepable, struct pt_regs *, regs, void *, buf, u32, si
const struct bpf_func_proto bpf_get_stack_sleepable_proto = {
.func = bpf_get_stack_sleepable,
.gpl_only = true,
.might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
@ -928,6 +929,7 @@ BPF_CALL_4(bpf_get_task_stack_sleepable, struct task_struct *, task, void *, buf
const struct bpf_func_proto bpf_get_task_stack_sleepable_proto = {
.func = bpf_get_task_stack_sleepable,
.gpl_only = false,
.might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],

View File

@ -5302,6 +5302,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
if (!priv_stack_supported)
subprog[idx].priv_stack_mode = NO_PRIV_STACK;
process_func:
if (subprog[idx].has_ld_abs) {
for (tmp = idx; tmp >= 0; tmp = dinfo[tmp].caller) {
if (subprog[tmp].is_cb) {
verbose(env, "cannot use BPF_LD_[ABS|IND] within callback\n");
return -EINVAL;
}
}
}
/* protect against potential stack overflow that might happen when
* bpf2bpf calls get combined with tailcalls. Limit the caller's stack
* depth for such case down to 256 so that the worst case scenario
@ -10235,9 +10244,10 @@ static void account_current_path(struct bpf_verifier_env *env)
frame ? state->frame[frame - 1] : NULL);
}
/* Are we currently verifying the callback for a rbtree helper that must
* be called with lock held? If so, no need to complain about unreleased
* lock
/*
* Are we currently verifying the callback for an rbtree kfunc that must
* be called with a lock held, or one of that callback's subprogs? If so,
* no need to complain about an unreleased lock.
*/
static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
{
@ -10245,17 +10255,19 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
struct bpf_insn *insn = env->prog->insnsi;
struct bpf_func_state *callee;
int kfunc_btf_id;
u32 frame;
if (!state->curframe)
return false;
for (frame = state->curframe; frame; frame--) {
callee = state->frame[frame];
if (!callee->in_callback_fn)
continue;
callee = state->frame[state->curframe];
kfunc_btf_id = insn[callee->callsite].imm;
if (is_rbtree_lock_required_kfunc(kfunc_btf_id))
return true;
}
if (!callee->in_callback_fn)
return false;
kfunc_btf_id = insn[callee->callsite].imm;
return is_rbtree_lock_required_kfunc(kfunc_btf_id);
return false;
}
static bool retval_range_within(struct bpf_retval_range range, const struct bpf_reg_state *reg)
@ -17179,6 +17191,7 @@ static bool may_access_skb(enum bpf_prog_type type)
*/
static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
{
struct bpf_verifier_state *state = env->cur_state;
struct bpf_reg_state *regs = cur_regs(env);
static const int ctx_reg = BPF_REG_6;
u8 mode = BPF_MODE(insn->code);
@ -17189,6 +17202,13 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
return -EINVAL;
}
for (i = state->curframe; i; i--) {
if (state->frame[i]->in_callback_fn) {
verbose(env, "cannot use BPF_LD_[ABS|IND] within callback\n");
return -EINVAL;
}
}
if (!env->ops->gen_ld_abs) {
verifier_bug(env, "gen_ld_abs is null");
return -EFAULT;

View File

@ -115,6 +115,58 @@ int preempt_sleepable_helper(void *ctx)
return 0;
}
SEC("?uprobe.s")
__failure __msg("sleepable helper bpf_get_stack#")
int preempt_sleepable_get_stack(struct pt_regs *ctx)
{
struct bpf_stack_build_id stack;
bpf_preempt_disable();
bpf_get_stack(ctx, &stack, sizeof(stack),
BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
bpf_preempt_enable();
return 0;
}
SEC("?uprobe.s")
__failure __msg("sleepable helper bpf_get_task_stack#")
int preempt_sleepable_get_task_stack(void *ctx)
{
struct bpf_stack_build_id stack;
struct task_struct *task;
task = bpf_get_current_task_btf();
bpf_preempt_disable();
bpf_get_task_stack(task, &stack, sizeof(stack),
BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
bpf_preempt_enable();
return 0;
}
SEC("?uprobe.s")
__success
int sleepable_get_stack(struct pt_regs *ctx)
{
struct bpf_stack_build_id stack;
bpf_get_stack(ctx, &stack, sizeof(stack),
BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
return 0;
}
SEC("?uprobe.s")
__success
int sleepable_get_task_stack(void *ctx)
{
struct bpf_stack_build_id stack;
struct task_struct *task;
task = bpf_get_current_task_btf();
bpf_get_task_stack(task, &stack, sizeof(stack),
BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
return 0;
}
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
__failure __msg("kernel func bpf_copy_from_user_str is sleepable within non-preemptible region")
int preempt_sleepable_kfunc(void *ctx)

View File

@ -272,6 +272,47 @@ static bool less__bad_res_spin_unlock(struct bpf_rb_node *a, const struct bpf_rb
return false;
}
static __noinline void rbtree_cb_unlock_relock(void)
{
bpf_spin_unlock(&glock);
bpf_spin_lock(&glock);
}
static __noinline void rbtree_cb_nested_unlock(void)
{
rbtree_cb_unlock_relock();
asm volatile ("");
}
static bool less__bad_subprog_unlock(struct bpf_rb_node *a, const struct bpf_rb_node *b)
{
struct node_data *node_a;
struct node_data *node_b;
node_a = container_of(a, struct node_data, node);
node_b = container_of(b, struct node_data, node);
rbtree_cb_nested_unlock();
return node_a->key < node_b->key;
}
static __noinline void rbtree_cb_noop(void)
{
asm volatile ("");
}
static bool less__subprog_allowed(struct bpf_rb_node *a, const struct bpf_rb_node *b)
{
struct node_data *node_a;
struct node_data *node_b;
node_a = container_of(a, struct node_data, node);
node_b = container_of(b, struct node_data, node);
rbtree_cb_noop();
return node_a->key < node_b->key;
}
static __always_inline
long add_with_cb(bool (cb)(struct bpf_rb_node *a, const struct bpf_rb_node *b))
{
@ -330,4 +371,18 @@ long rbtree_api_add_bad_cb_res_spin_unlock(void *ctx)
return 0;
}
SEC("?tc")
__failure __msg("can't spin_{lock,unlock} in rbtree cb")
long rbtree_api_add_bad_cb_subprog_unlock(void *ctx)
{
return add_with_cb(less__bad_subprog_unlock);
}
SEC("?tc")
__success
long rbtree_api_add_cb_subprog_allowed(void *ctx)
{
return add_with_cb(less__subprog_allowed);
}
char _license[] SEC("license") = "GPL";

View File

@ -108,6 +108,30 @@ int timer_sys_close_prog(void *ctx)
return 0;
}
static int timer_btf_find_cb(void *map, int *key, struct bpf_timer *timer)
{
char name[] = "task_struct";
bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0);
return 0;
}
SEC("syscall")
__failure __msg("sleepable helper bpf_btf_find_by_name_kind#{{[0-9]+}} in non-sleepable prog")
int timer_btf_find_prog(void *ctx)
{
struct timer_elem *val;
int key = 0;
val = bpf_map_lookup_elem(&timer_map, &key);
if (!val)
return 0;
bpf_timer_init(&val->t, &timer_map, 0);
bpf_timer_set_callback(&val->t, timer_btf_find_cb);
return 0;
}
SEC("syscall")
__success
int syscall_sys_bpf_prog(void *ctx)
@ -126,6 +150,16 @@ int syscall_sys_close_prog(void *ctx)
return 0;
}
SEC("syscall")
__success
int syscall_btf_find_prog(void *ctx)
{
char name[] = "task_struct";
bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0);
return 0;
}
/* Workqueue tests */
struct wq_elem {

View File

@ -194,6 +194,102 @@ __naked void ld_ind_subprog_both_paths_safe(void)
::: __clobber_all);
}
__naked __noinline __used
static int ld_abs_callback(void)
{
asm volatile (
"r6 = *(u64 *)(r2 + 0);"
".8byte %[ld_abs];"
"r0 = 0;"
"exit;"
:
: __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0))
: __clobber_all);
}
SEC("socket")
__description("ld_abs: reject in callback")
__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
int ld_abs_callback_reject(struct __sk_buff *skb)
{
bpf_loop(1, ld_abs_callback, &skb, 0);
return 0;
}
__naked __noinline __used
static int ld_ind_callback_subprog(void)
{
asm volatile (
"r6 = r1;"
"r7 = 0;"
".8byte %[ld_ind];"
"r0 = 0;"
"exit;"
:
: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
: __clobber_all);
}
__naked __noinline __used
static int ld_ind_callback(void)
{
asm volatile (
"r1 = *(u64 *)(r2 + 0);"
"call ld_ind_callback_subprog;"
"exit;"
::: __clobber_all);
}
SEC("socket")
__description("ld_ind: reject in callback subprog")
__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
int ld_ind_callback_subprog_reject(struct __sk_buff *skb)
{
bpf_loop(1, ld_ind_callback, &skb, 0);
return 0;
}
static __noinline int ld_ind_global_static(struct __sk_buff *skb)
{
asm volatile (
"r6 = %[skb];"
"r7 = 0;"
".8byte %[ld_ind];"
:
: [skb] "r"(skb),
__imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
: __clobber_common, "r6", "r7");
return skb->mark;
}
__noinline int ld_ind_global(struct __sk_buff *skb)
{
return ld_ind_global_static(skb);
}
static int ld_ind_global_callback(__u32 index, struct __sk_buff **ctx)
{
ld_ind_global(*ctx);
return 0;
}
SEC("socket")
__description("ld_ind: reject in callback global subprog")
__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
int ld_ind_global_callback_reject(struct __sk_buff *skb)
{
bpf_loop(1, ld_ind_global_callback, &skb, 0);
return 0;
}
SEC("socket")
__description("ld_ind: allow in non-callback global subprog")
__success
int ld_ind_global_subprog_ok(struct __sk_buff *skb)
{
return ld_ind_global(skb);
}
/*
* ld_{abs,ind} in subprogs require scalar (int) return type in BTF.
* A test with void return must be rejected.