summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@kernel.org>2026-09-03 09:44:56 -0700
committerAlexei Starovoitov <ast@kernel.org>2026-09-03 09:44:59 -0700
commite044668419b9c444282bd09521f7ea07edbc10b1 (patch)
tree8ea6a628210202aa7fb0537e3caa5f2a11fe70ac
parent1329a7aa33848c6f7e01eec9b9985086a44647d2 (diff)
parent26a3a510cd3433e15f37ea1d5a6f2c17a0170316 (diff)
downloadlinux-next-e044668419b9c444282bd09521f7ea07edbc10b1.tar.gz
linux-next-e044668419b9c444282bd09521f7ea07edbc10b1.zip
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>
-rw-r--r--kernel/bpf/btf.c7
-rw-r--r--kernel/bpf/syscall.c2
-rw-r--r--kernel/bpf/verifier.c16
-rw-r--r--tools/testing/selftests/bpf/progs/raw_tp_null_fail.c53
-rw-r--r--tools/testing/selftests/bpf/progs/rbtree_fail.c29
-rw-r--r--tools/testing/selftests/bpf/progs/tailcall_callback.c16
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_async_cb_context.c64
7 files changed, 187 insertions, 0 deletions
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 85ae92c920e4..5d93fd82e764 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -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 */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 6874ba1424af..c7bc9ba9b331 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -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,
};
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 7d8ddb1bee00..32d31fa67036 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -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 @@ check_ok:
{
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));
diff --git a/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c b/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c
index 0d58114a4955..725d73c9ffe1 100644
--- a/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c
+++ b/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c
@@ -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;
+}
diff --git a/tools/testing/selftests/bpf/progs/rbtree_fail.c b/tools/testing/selftests/bpf/progs/rbtree_fail.c
index 555379952dcc..803419a47c62 100644
--- a/tools/testing/selftests/bpf/progs/rbtree_fail.c
+++ b/tools/testing/selftests/bpf/progs/rbtree_fail.c
@@ -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";
diff --git a/tools/testing/selftests/bpf/progs/tailcall_callback.c b/tools/testing/selftests/bpf/progs/tailcall_callback.c
index c41632cf423b..14fa7a87028e 100644
--- a/tools/testing/selftests/bpf/progs/tailcall_callback.c
+++ b/tools/testing/selftests/bpf/progs/tailcall_callback.c
@@ -45,6 +45,13 @@ int callback_loop(int index, void **cb_ctx)
}
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)
{
return 0;
@@ -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";
diff --git a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
index 6bf95550a024..a7c84d3fa4c7 100644
--- a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
+++ b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
@@ -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 {