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

Kumar Kartikeya Dwivedi says:

====================
Misc bug fixes - part 1

A set of miscellaneous fixes for bugs reported by Nicholas. These are
easy ones and should not require any major discussion, hence batched
together. See commit logs and selftests for details.
====================

Link: https://patch.msgid.link/20260903144433.1716731-1-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alexei Starovoitov 2026-09-03 09:44:56 -07:00
commit e044668419
7 changed files with 187 additions and 0 deletions

View File

@ -6672,6 +6672,10 @@ struct bpf_raw_tp_null_args {
static const struct bpf_raw_tp_null_args raw_tp_null_args[] = {
/* sched */
{ "sched_pi_setprio", 0x10 },
/*
* do_wait() passes NULL for wait4(-1) and waitid(P_ALL).
*/
{ "sched_process_wait", 0x1 },
/* ... from sched_numa_pair_template event class */
{ "sched_stick_numa", 0x100 },
{ "sched_swap_numa", 0x100 },
@ -6732,6 +6736,9 @@ static const struct bpf_raw_tp_null_args raw_tp_null_args[] = {
{ "rxrpc_resend", 0x10 },
{ "rxrpc_tq", 0x10 },
{ "rxrpc_client", 0x1 },
/* signal */
{ "signal_generate", 0x20 },
{ "signal_deliver", 0x20 },
/* skb */
{"kfree_skb", 0x1000},
/* sunrpc */

View File

@ -6568,6 +6568,7 @@ EXPORT_SYMBOL_NS(kern_sys_bpf, "BPF_INTERNAL");
static const struct bpf_func_proto bpf_sys_bpf_proto = {
.func = bpf_sys_bpf,
.gpl_only = false,
.might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_ANYTHING,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
@ -6593,6 +6594,7 @@ BPF_CALL_1(bpf_sys_close, u32, fd)
static const struct bpf_func_proto bpf_sys_close_proto = {
.func = bpf_sys_close,
.gpl_only = false,
.might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_ANYTHING,
};

View File

@ -11228,6 +11228,17 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
if (env->cur_state->curframe) {
struct bpf_verifier_state *branch;
/*
* A taken tail call is modeled as a return from the current
* frame. A callback frame cannot be left that way because
* prepare_func_exit() would apply its return contract to the
* unknown R0 synthesized below. Stack-depth validation rejects
* this construct anyway.
*/
if (cur_func(env)->in_callback_fn) {
verbose(env, "cannot tail call within callback\n");
return -EINVAL;
}
mark_reg_scratched(env, BPF_REG_0);
branch = push_stack(env, env->insn_idx + 1, env->insn_idx, false);
if (IS_ERR(branch))
@ -13230,6 +13241,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
{
int flags = PROCESS_RES_LOCK;
if (in_rbtree_lock_required_cb(env)) {
verbose(env, "can't res_spin_{lock,unlock} in rbtree cb\n");
return -EACCES;
}
if (reg->type != PTR_TO_MAP_VALUE && reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
verbose(env, "%s doesn't point to map value or allocated object\n",
reg_arg_name(env, argno));

View File

@ -22,3 +22,56 @@ int test_raw_tp_null_sched_pi_setprio_arg_2(void *ctx) {
asm volatile("r1 = *(u64 *)(r1 +8); r1 = *(u64 *)(r1 +0);" ::: __clobber_all);
return 0;
}
/* Plain raw tracepoint arguments remain scalar values. */
SEC("raw_tp/signal_generate")
__success
int test_raw_tp_signal_generate_info_scalar(void *ctx)
{
asm volatile("r1 = *(u64 *)(r1 +8); if r1 != 1 goto +0;" ::: __clobber_all);
return 0;
}
/* tp_btf programs may inspect the sentinel as a scalar value. */
SEC("tp_btf/signal_generate")
__success
int test_tp_btf_signal_generate_info_scalar(void *ctx)
{
asm volatile("r1 = *(u64 *)(r1 +8); if r1 != 1 goto +0;" ::: __clobber_all);
return 0;
}
/* SEND_SIG_PRIV is non-NULL, so a NULL check cannot make info safe. */
SEC("tp_btf/signal_generate")
__failure __msg("R1 invalid mem access 'scalar'")
int test_tp_btf_signal_generate_info_no_deref(void *ctx)
{
asm volatile("r1 = *(u64 *)(r1 +8); if r1 == 0 goto +1; "
"r1 = *(u32 *)(r1 +0);" ::: __clobber_all);
return 0;
}
SEC("tp_btf/signal_deliver")
__failure __msg("R1 invalid mem access 'scalar'")
int test_tp_btf_signal_deliver_info_no_deref(void *ctx)
{
asm volatile("r1 = *(u64 *)(r1 +8); r1 = *(u32 *)(r1 +0);" ::: __clobber_all);
return 0;
}
SEC("tp_btf/sched_process_wait")
__failure __msg("R1 invalid mem access 'trusted_ptr_or_null_'")
int test_raw_tp_null_sched_process_wait_arg_1(void *ctx)
{
asm volatile("r1 = *(u64 *)(r1 +0); r1 = *(u32 *)(r1 +0);" ::: __clobber_all);
return 0;
}
SEC("tp_btf/sched_process_wait")
__success
int test_raw_tp_null_sched_process_wait_arg_1_checked(void *ctx)
{
asm volatile("r1 = *(u64 *)(r1 +0); if r1 == 0 goto +1; "
"r1 = *(u32 *)(r1 +0);" ::: __clobber_all);
return 0;
}

View File

@ -16,6 +16,7 @@ struct node_data {
private(A) struct bpf_spin_lock glock;
private(A) struct bpf_rb_root groot __contains(node_data, node);
private(A) struct bpf_rb_root groot2 __contains(node_data, node);
private(B) struct bpf_res_spin_lock res_glock;
static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b)
{
@ -265,6 +266,12 @@ static bool less__bad_fn_call_first_unlock_after(struct bpf_rb_node *a, const st
return node_a->key < node_b->key;
}
static bool less__bad_res_spin_unlock(struct bpf_rb_node *a, const struct bpf_rb_node *b)
{
bpf_res_spin_unlock(&res_glock);
return false;
}
static __always_inline
long add_with_cb(bool (cb)(struct bpf_rb_node *a, const struct bpf_rb_node *b))
{
@ -301,4 +308,26 @@ long rbtree_api_add_bad_cb_bad_fn_call_first_unlock_after(void *ctx)
return add_with_cb(less__bad_fn_call_first_unlock_after);
}
SEC("?tc")
__failure __msg("can't res_spin_{lock,unlock} in rbtree cb")
long rbtree_api_add_bad_cb_res_spin_unlock(void *ctx)
{
struct node_data *n;
n = bpf_obj_new(typeof(*n));
if (!n)
return 1;
bpf_spin_lock(&glock);
if (bpf_res_spin_lock(&res_glock)) {
bpf_spin_unlock(&glock);
bpf_obj_drop(n);
return 1;
}
bpf_rbtree_add(&groot, &n->node, less__bad_res_spin_unlock);
bpf_res_spin_unlock(&res_glock);
bpf_spin_unlock(&glock);
return 0;
}
char _license[] SEC("license") = "GPL";

View File

@ -44,6 +44,13 @@ int callback_loop(int index, void **cb_ctx)
return ret ? 1 : 0;
}
static __noinline
int callback_tail(int index, void **cb_ctx)
{
bpf_tail_call_static(*cb_ctx, &jmp_table, 0);
return 0;
}
static __noinline
int callback_empty(int index, void *data)
{
@ -78,4 +85,13 @@ int tailcall_callback_2(struct __sk_buff *skb)
return 0;
}
/* callback with a direct tail call is rejected without a verifier bug */
SEC("tc")
__failure __msg("cannot tail call within callback")
int tailcall_callback_3(struct __sk_buff *skb)
{
bpf_loop(1, callback_tail, &skb, 0);
return 0;
}
char __license[] SEC("license") = "GPL";

View File

@ -62,6 +62,70 @@ int timer_sleepable_prog(void *ctx)
return 0;
}
static int timer_sys_bpf_cb(void *map, int *key, struct bpf_timer *timer)
{
__u64 attr = 0;
bpf_sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr));
return 0;
}
SEC("syscall")
__failure __msg("sleepable helper bpf_sys_bpf#{{[0-9]+}} in non-sleepable prog")
int timer_sys_bpf_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_sys_bpf_cb);
return 0;
}
static int timer_sys_close_cb(void *map, int *key, struct bpf_timer *timer)
{
bpf_sys_close(0);
return 0;
}
SEC("syscall")
__failure __msg("sleepable helper bpf_sys_close#{{[0-9]+}} in non-sleepable prog")
int timer_sys_close_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_sys_close_cb);
return 0;
}
SEC("syscall")
__success
int syscall_sys_bpf_prog(void *ctx)
{
__u64 attr = 0;
bpf_sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr));
return 0;
}
SEC("syscall")
__success
int syscall_sys_close_prog(void *ctx)
{
bpf_sys_close(0);
return 0;
}
/* Workqueue tests */
struct wq_elem {