* [PATCH bpf v3 0/2] disable private stack for sleepable programs
@ 2026-08-22 22:54 Christian Simon
2026-08-22 22:54 ` [PATCH bpf v3 1/2] bpf: " Christian Simon
2026-08-22 22:54 ` [PATCH bpf v3 2/2] selftests/bpf: verify preemptible uprobes avoid private stack Christian Simon
0 siblings, 2 replies; 18+ messages in thread
From: Christian Simon @ 2026-08-22 22:54 UTC (permalink / raw)
To: bpf
Cc: Christian Simon, ast, andrii, daniel, martin.lau, tj,
yonghong.song, stable, andrii.nakryiko, olsajiri
A JITed BPF program can use one private stack per program and CPU.
Sleepable programs can be preempted, allowing another task to run the
same program on the same CPU. The second invocation then reuses and can
overwrite the first invocation's private stack.
This patch series disables private stacks for sleepable programs and
adds a selftest covering that stack corruption.
---
Changes in v3:
- Target sleepable only.
- Add the powerpc Fixes tag.
- Split fix and selftests
v2: https://lore.kernel.org/bpf/20260818203234.1142913-1-simon@swine.de/
Christian Simon (2):
bpf: disable private stack for sleepable programs
selftests/bpf: verify preemptible uprobes avoid private stack
kernel/bpf/verifier.c | 9 +
.../bpf/prog_tests/uprobe_sleepable_stack.c | 165 ++++++++++++++++++
.../bpf/progs/uprobe_sleepable_stack.c | 89 ++++++++++
3 files changed, 263 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/uprobe_sleepable_stack.c
create mode 100644 tools/testing/selftests/bpf/progs/uprobe_sleepable_stack.c
base-commit: 75b0a6db4300e4c2c9e97a0848deaa7acfb42fb7
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-08-22 22:54 [PATCH bpf v3 0/2] disable private stack for sleepable programs Christian Simon
@ 2026-08-22 22:54 ` Christian Simon
2026-08-22 23:10 ` sashiko-bot
` (2 more replies)
2026-08-22 22:54 ` [PATCH bpf v3 2/2] selftests/bpf: verify preemptible uprobes avoid private stack Christian Simon
1 sibling, 3 replies; 18+ messages in thread
From: Christian Simon @ 2026-08-22 22:54 UTC (permalink / raw)
To: bpf
Cc: Christian Simon, ast, andrii, daniel, martin.lau, tj,
yonghong.song, stable, andrii.nakryiko, olsajiri
A JITed BPF program can use one private stack per program and CPU.
Sleepable programs can be preempted, allowing another task to run the
same program on the same CPU. The second invocation then reuses and can
overwrite the first invocation's private stack.
Disable private stack for sleepable programs so they use the regular kernel
stack, which handles preemption correctly. This change is intentionally
limited to programs marked sleepable; preemptible non-sleepable dispatch
paths require separate protection.
Fixes: 7d1cd70d4b16 ("bpf, x86: Support private stack in jit")
Fixes: 6c17a882d380 ("bpf, arm64: JIT support for private stack")
Fixes: 156d985123b6 ("powerpc64/bpf: Implement JIT support for private stack")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Simon <simon@swine.de>
---
kernel/bpf/verifier.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5e37ca75e5c4..038753ef07a9 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5237,6 +5237,15 @@ static enum priv_stack_mode bpf_enable_priv_stack(struct bpf_prog *prog)
if (!bpf_jit_supports_private_stack())
return NO_PRIV_STACK;
+ /*
+ * Sleepable programs can be preempted, allowing another task to run
+ * the same program on the same CPU. Since private stack is per-CPU
+ * and per-program, the second invocation would corrupt the first's
+ * stack. Disable private stack for sleepable programs.
+ */
+ if (prog->sleepable)
+ return NO_PRIV_STACK;
+
/* bpf_prog_check_recur() checks all prog types that use bpf trampoline
* while kprobe/tp/perf_event/raw_tp don't use trampoline hence checked
* explicitly.
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH bpf v3 2/2] selftests/bpf: verify preemptible uprobes avoid private stack
2026-08-22 22:54 [PATCH bpf v3 0/2] disable private stack for sleepable programs Christian Simon
2026-08-22 22:54 ` [PATCH bpf v3 1/2] bpf: " Christian Simon
@ 2026-08-22 22:54 ` Christian Simon
2026-08-22 23:05 ` sashiko-bot
2026-08-22 23:58 ` bot+bpf-ci
1 sibling, 2 replies; 18+ messages in thread
From: Christian Simon @ 2026-08-22 22:54 UTC (permalink / raw)
To: bpf
Cc: Christian Simon, ast, andrii, daniel, martin.lau, tj,
yonghong.song, stable, andrii.nakryiko, olsajiri
Pin two threads to one CPU and overlap invocations of the same uprobe
program. Verify that both invocations execute and that the first
invocation's stack frame is not corrupted by the second.
Cover sleepable classic and multi-uprobe programs with both 64-byte
stack frames, which select private stack when eligible, and small stack
frames as controls. Skip the test when preemption, JIT, or architecture
private-stack support is unavailable.
Signed-off-by: Christian Simon <simon@swine.de>
---
I have noticed the "volatile" warning, I do think it is fine for this
selftests purposes.
.../bpf/prog_tests/uprobe_sleepable_stack.c | 165 ++++++++++++++++++
.../bpf/progs/uprobe_sleepable_stack.c | 89 ++++++++++
2 files changed, 254 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/uprobe_sleepable_stack.c
create mode 100644 tools/testing/selftests/bpf/progs/uprobe_sleepable_stack.c
diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_sleepable_stack.c b/tools/testing/selftests/bpf/prog_tests/uprobe_sleepable_stack.c
new file mode 100644
index 000000000000..a18970d2c584
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/uprobe_sleepable_stack.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test that preemptible uprobe programs don't use private stack.
+ *
+ * Private stack is per-CPU and per-program, so preemption during a program's
+ * execution would allow another task to corrupt the stack. The verifier must
+ * disable private stack for programs whose invocation can be preempted.
+ *
+ * This test overlaps two invocations of the same uprobe program on the same
+ * CPU and verifies:
+ * 1. Both invocations execute
+ * 2. No stack corruption occurs (each task has its own stack frame)
+ */
+#include <pthread.h>
+#include <sched.h>
+#include <unistd.h>
+
+#include <test_progs.h>
+#include "uprobe_sleepable_stack.skel.h"
+
+static noinline void uprobe_sleepable_stack_trigger(void)
+{
+ asm volatile("");
+}
+
+static void *trigger_uprobe(void *arg)
+{
+ uprobe_sleepable_stack_trigger();
+ return NULL;
+}
+
+static void reset_state(struct uprobe_sleepable_stack *skel)
+{
+ skel->bss->ready = 0;
+ skel->bss->release = 0;
+ skel->bss->executions = 0;
+ skel->bss->corruptions = 0;
+ skel->bss->loop_exhausted = 0;
+}
+
+static void assert_results(struct uprobe_sleepable_stack *skel)
+{
+ ASSERT_EQ(skel->bss->loop_exhausted, 0, "loop_exhausted");
+ ASSERT_EQ(skel->bss->executions, 2, "executions");
+ ASSERT_EQ(skel->bss->corruptions, 0, "corruptions");
+}
+
+static void run_test(struct uprobe_sleepable_stack *skel, bool multi,
+ bool large_stack)
+{
+ LIBBPF_OPTS(bpf_uprobe_opts, opts);
+ LIBBPF_OPTS(bpf_uprobe_multi_opts, multi_opts);
+ const char *binary = "/proc/self/exe";
+ struct bpf_program *prog;
+ struct bpf_link *link;
+ pthread_t thread;
+ long link_err;
+ int err, i;
+
+ reset_state(skel);
+
+ if (multi) {
+ prog = large_stack ? skel->progs.uprobe_multi_sleepable_large_stack :
+ skel->progs.uprobe_multi_sleepable_small_stack;
+ link = bpf_program__attach_uprobe_multi(
+ prog, 0, binary, "uprobe_sleepable_stack_trigger",
+ &multi_opts);
+ } else {
+ opts.func_name = "uprobe_sleepable_stack_trigger";
+ prog = large_stack ? skel->progs.uprobe_sleepable_large_stack :
+ skel->progs.uprobe_sleepable_small_stack;
+ link = bpf_program__attach_uprobe_opts(prog, 0, binary, 0,
+ &opts);
+ }
+
+ link_err = libbpf_get_error(link);
+ if (link_err == -EOPNOTSUPP) {
+ test__skip();
+ return;
+ }
+ if (!ASSERT_OK_PTR(link, "attach_uprobe"))
+ return;
+
+ err = pthread_create(&thread, NULL, trigger_uprobe, NULL);
+ if (!ASSERT_OK(err, "pthread_create"))
+ goto cleanup;
+
+ for (i = 0; i < 10000; i++) {
+ if (__atomic_load_n(&skel->bss->ready, __ATOMIC_ACQUIRE))
+ break;
+ usleep(1000);
+ }
+
+ if (ASSERT_LT(i, 10000, "first_uprobe_ready"))
+ uprobe_sleepable_stack_trigger();
+ __atomic_store_n(&skel->bss->release, 1, __ATOMIC_RELEASE);
+
+ err = pthread_join(thread, NULL);
+ if (!ASSERT_OK(err, "pthread_join"))
+ goto cleanup;
+
+ assert_results(skel);
+
+cleanup:
+ bpf_link__destroy(link);
+}
+
+void test_uprobe_sleepable_stack(void)
+{
+ struct uprobe_sleepable_stack *skel = NULL;
+ cpu_set_t old_mask, mask;
+ bool affinity_set = false;
+ int cpu;
+
+#if !defined(__x86_64__) && !defined(__aarch64__) && !defined(__powerpc64__)
+ test__skip();
+ return;
+#endif
+ if (!env.jit_enabled) {
+ test__skip();
+ return;
+ }
+
+ if (!ASSERT_OK(sched_getaffinity(0, sizeof(old_mask), &old_mask),
+ "get_affinity"))
+ return;
+
+ CPU_ZERO(&mask);
+ for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
+ if (CPU_ISSET(cpu, &old_mask)) {
+ CPU_SET(cpu, &mask);
+ break;
+ }
+ }
+ if (!ASSERT_LT(cpu, CPU_SETSIZE, "available_cpu"))
+ return;
+ /* Both triggers must run on the same CPU to test stack sharing */
+ if (!ASSERT_OK(sched_setaffinity(0, sizeof(mask), &mask),
+ "set_affinity"))
+ return;
+ affinity_set = true;
+
+ skel = uprobe_sleepable_stack__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open"))
+ goto cleanup;
+ if (!skel->kconfig->CONFIG_PREEMPTION) {
+ test__skip();
+ goto cleanup;
+ }
+
+ if (test__start_subtest("sleepable_classic_large"))
+ run_test(skel, false, true);
+ if (test__start_subtest("sleepable_classic_small"))
+ run_test(skel, false, false);
+ if (test__start_subtest("sleepable_multi_large"))
+ run_test(skel, true, true);
+ if (test__start_subtest("sleepable_multi_small"))
+ run_test(skel, true, false);
+
+cleanup:
+ uprobe_sleepable_stack__destroy(skel);
+ if (affinity_set)
+ ASSERT_OK(sched_setaffinity(0, sizeof(old_mask), &old_mask),
+ "restore_affinity");
+}
diff --git a/tools/testing/selftests/bpf/progs/uprobe_sleepable_stack.c b/tools/testing/selftests/bpf/progs/uprobe_sleepable_stack.c
new file mode 100644
index 000000000000..07e0ff548720
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/uprobe_sleepable_stack.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+
+extern bool CONFIG_PREEMPTION __kconfig __weak;
+
+volatile int ready;
+volatile int release;
+volatile int executions;
+volatile int corruptions;
+int loop_exhausted;
+
+static long wait_for_release(__u32 index, void *ctx)
+{
+ return release ? 1 : 0;
+}
+
+/*
+ * Test that preemptible uprobes don't use private stack. A 64-byte frame would
+ * normally trigger private stack selection, but programs whose invocation can
+ * be preempted must not use it. Both invocations must execute without
+ * corruption.
+ */
+static __always_inline int run_stack_test(volatile __u64 *stack)
+{
+ __u64 id = bpf_get_current_pid_tgid();
+ int seq;
+
+ if (!CONFIG_PREEMPTION)
+ return 0;
+
+ stack[0] = id;
+ seq = executions;
+ executions = seq + 1;
+ /*
+ * If private stack were used, an unguarded second invocation would
+ * overwrite the first invocation's frame. With regular stack, each task
+ * has its own stack frame.
+ */
+ if (seq) {
+ stack[0] = ~id;
+ return 0;
+ }
+
+ ready = 1;
+ bpf_loop(1 << 23, wait_for_release, NULL, 0);
+ if (!release)
+ loop_exhausted = 1;
+ if (stack[0] != id)
+ corruptions++;
+
+ return 0;
+}
+
+SEC("uprobe.s//proc/self/exe:uprobe_sleepable_stack_trigger")
+int uprobe_sleepable_large_stack(struct pt_regs *ctx)
+{
+ /* A 64-byte frame would select private stack for non-sleepable progs */
+ volatile __u64 stack[8] = {};
+
+ return run_stack_test(stack);
+}
+
+SEC("uprobe.s//proc/self/exe:uprobe_sleepable_stack_trigger")
+int uprobe_sleepable_small_stack(struct pt_regs *ctx)
+{
+ /* Small stack as control - never triggers private stack */
+ volatile __u64 stack[1] = {};
+
+ return run_stack_test(stack);
+}
+
+SEC("uprobe.multi.s//proc/self/exe:uprobe_sleepable_stack_trigger")
+int uprobe_multi_sleepable_large_stack(struct pt_regs *ctx)
+{
+ volatile __u64 stack[8] = {};
+
+ return run_stack_test(stack);
+}
+
+SEC("uprobe.multi.s//proc/self/exe:uprobe_sleepable_stack_trigger")
+int uprobe_multi_sleepable_small_stack(struct pt_regs *ctx)
+{
+ volatile __u64 stack[1] = {};
+
+ return run_stack_test(stack);
+}
+
+char LICENSE[] SEC("license") = "GPL";
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 2/2] selftests/bpf: verify preemptible uprobes avoid private stack
2026-08-22 22:54 ` [PATCH bpf v3 2/2] selftests/bpf: verify preemptible uprobes avoid private stack Christian Simon
@ 2026-08-22 23:05 ` sashiko-bot
2026-08-22 23:58 ` bot+bpf-ci
1 sibling, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-08-22 23:05 UTC (permalink / raw)
To: Christian Simon; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Spurious test failure (flakiness) due to insufficiently small timeout limit in bpf_loop.
--
--- Patch [2]: [PATCH bpf v3 2/2] selftests/bpf: verify preemptible uprobes avoid private stack ---
commit c17909e48f5484c8e9bf3341a1fc87b98ad29988
Author: Christian Simon <simon@swine.de>
selftests/bpf: verify preemptible uprobes avoid private stack
Pin two threads to one CPU and overlap invocations of the same uprobe
program. Verify that both invocations execute and that the first
invocation's stack frame is not corrupted by the second.
> diff --git a/tools/testing/selftests/bpf/progs/uprobe_sleepable_stack.c b/tools/testing/selftests/bpf/progs/uprobe_sleepable_stack.c
> index 0000000000000..07e0ff5487208
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/uprobe_sleepable_stack.c
[ ... ]
> +static __always_inline int run_stack_test(volatile __u64 *stack)
> +{
[ ... ]
> + ready = 1;
> + bpf_loop(1 << 23, wait_for_release, NULL, 0);
> + if (!release)
> + loop_exhausted = 1;
[Severity: Medium]
Could this iteration limit cause spurious test failures under load?
In a heavily loaded CI environment, the main thread's usleep(1000) can be
delayed significantly (for example, if timer granularity is 10ms or due to
hypervisor latency). On a fast CPU, 8.38M iterations of this trivial callback
might complete in just a few milliseconds, exhausting the loop before the
main thread wakes up and sets release.
If this happens, loop_exhausted is set to 1 and the selftest fails.
Is it possible to safely extend this wait time by wrapping the bpf_loop call
in an outer bounded loop?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260822225444.2774461-1-simon@swine.de?part=2
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-08-22 22:54 ` [PATCH bpf v3 1/2] bpf: " Christian Simon
@ 2026-08-22 23:10 ` sashiko-bot
2026-08-22 23:46 ` bot+bpf-ci
2026-08-26 1:20 ` Alexei Starovoitov
2 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-08-22 23:10 UTC (permalink / raw)
To: Christian Simon; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Non-sleepable BPF uprobe programs remain vulnerable to per-CPU private stack corruption.
--
--- Patch [1]: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs ---
commit 5b27c96c855b5a6df31f1d2543a92540c69a5d98
Author: Christian Simon <simon@swine.de>
bpf: disable private stack for sleepable programs
This commit disables the use of private stacks for sleepable BPF programs
since they can be preempted, which would allow a second invocation of the
program on the same CPU to corrupt the first invocation's stack. The fix
forces sleepable programs to use the regular kernel stack which handles
preemption correctly.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 5e37ca75e5c4a..038753ef07a99 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -5237,6 +5237,15 @@ static enum priv_stack_mode bpf_enable_priv_stack(struct bpf_prog *prog)
> if (!bpf_jit_supports_private_stack())
> return NO_PRIV_STACK;
>
> + /*
> + * Sleepable programs can be preempted, allowing another task to run
> + * the same program on the same CPU. Since private stack is per-CPU
> + * and per-program, the second invocation would corrupt the first's
> + * stack. Disable private stack for sleepable programs.
> + */
> + if (prog->sleepable)
> + return NO_PRIV_STACK;
> +
> /* bpf_prog_check_recur() checks all prog types that use bpf trampoline
> * while kprobe/tp/perf_event/raw_tp don't use trampoline hence checked
> * explicitly.
[Severity: High]
This is a pre-existing issue and was not introduced by this patch, but does
this leave non-sleepable uprobes vulnerable to the same stack corruption?
The patch correctly fixes sleepable programs, but the commit message mentions
that preemptible non-sleepable dispatch paths require separate protection.
However, non-sleepable uprobes are granted private stacks because they fall
under BPF_PROG_TYPE_KPROBE in the switch statement immediately below this code.
If we look at bpf_prog_run_array_uprobe() in include/linux/bpf.h,
non-sleepable uprobes are executed with only rcu_read_lock():
if (!prog->sleepable)
rcu_read_lock();
run_ctx.bpf_cookie = item->bpf_cookie;
ret &= run_prog(prog, ctx);
item++;
if (!prog->sleepable)
rcu_read_unlock();
Under CONFIG_PREEMPT_RCU and CONFIG_PREEMPT, rcu_read_lock() does not disable
preemption. Could a task executing this non-sleepable uprobe be preempted,
allowing another task on the same CPU to trigger the same uprobe and overwrite
its per-CPU private stack?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260822225444.2774461-1-simon@swine.de?part=1
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-08-22 22:54 ` [PATCH bpf v3 1/2] bpf: " Christian Simon
2026-08-22 23:10 ` sashiko-bot
@ 2026-08-22 23:46 ` bot+bpf-ci
2026-08-26 1:20 ` Alexei Starovoitov
2 siblings, 0 replies; 18+ messages in thread
From: bot+bpf-ci @ 2026-08-22 23:46 UTC (permalink / raw)
To: simon, bpf
Cc: simon, ast, andrii, daniel, martin.lau, tj, yonghong.song, stable,
andrii.nakryiko, olsajiri, ast, andrii, daniel, martin.lau,
eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 4332 bytes --]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 5e37ca75e5c4..038753ef07a9 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -5237,6 +5237,15 @@ static enum priv_stack_mode bpf_enable_priv_stack(struct bpf_prog *prog)
> if (!bpf_jit_supports_private_stack())
> return NO_PRIV_STACK;
>
> + /*
> + * Sleepable programs can be preempted, allowing another task to run
> + * the same program on the same CPU. Since private stack is per-CPU
> + * and per-program, the second invocation would corrupt the first's
> + * stack. Disable private stack for sleepable programs.
> + */
Is this comment accurate for all the program types below?
For BPF_PROG_TYPE_TRACING / LSM / STRUCT_OPS, private stack is only
granted when bpf_prog_check_recur(prog) holds, and that same predicate
makes bpf_trampoline_enter() install __bpf_prog_enter_sleepable_recur(),
which bumps the per-CPU prog->active counter under migrate_disable() and
skips the nested invocation.
Similarly, the sleepable raw-tracepoint runner __bpf_trace_run() calls
bpf_prog_get_recursion_context().
The statement "the second invocation would corrupt the first's stack"
would be true for sleepable uprobe programs run via uprobe_prog_run(),
which takes no recursion context. Could the comment explain why the check
is placed before the switch rather than in the case arm that actually
needs it?
> + if (prog->sleepable)
> + return NO_PRIV_STACK;
> +
Does placing this check before the prog-type switch have unintended
consequences?
The check strips the private stack from BPF_PROG_TYPE_TRACING / LSM /
STRUCT_OPS even though those paths are already protected. For these types,
bpf_trampoline_enter() consults bpf_prog_check_recur(prog):
kernel/bpf/trampoline.c:bpf_trampoline_enter() {
if (bpf_prog_check_recur(prog))
return sleepable ? __bpf_prog_enter_sleepable_recur :
__bpf_prog_enter_recur;
and __bpf_prog_enter_sleepable_recur() does migrate_disable() and then
bpf_prog_get_recursion_context(prog), which uses a per-CPU prog->active
counter. A second task entering the same program on the same CPU sees
active != 1 and is skipped, so it can never reach the private stack.
The real hole appears to be below the new check: BPF_PROG_TYPE_KPROBE /
TRACEPOINT / PERF_EVENT / RAW_TRACEPOINT return PRIV_STACK_ADAPTIVE
unconditionally. For sleepable uprobes (the only sleepable case allowed by
can_be_sleepable()), the uprobe runner takes no recursion context:
kernel/trace/bpf_trace.c:uprobe_prog_run() {
if (sleepable)
rcu_read_lock_trace();
...
migrate_disable();
err = bpf_prog_run(link->link.prog, regs);
so a sleepable uprobe can genuinely be re-entered on the same CPU.
Moving the check into the case arm (BPF_PROG_TYPE_KPROBE through
BPF_PROG_TYPE_RAW_TRACEPOINT) would close the uprobe hole without taking
the private stack away from the trampoline types that already handle
re-entry.
There's also a user-visible side effect: priv_stack_mode decides how
check_max_stack_depth_subprog() budgets stack. PRIV_STACK_ADAPTIVE checks
each subprog on its own against MAX_BPF_STACK; NO_PRIV_STACK accumulates
depth += subprog_depth across bpf2bpf frames and checks the combined total
against MAX_BPF_STACK (512).
Any existing sleepable program whose bpf2bpf subtree exceeds 512 bytes
total - SEC("fentry.s/..."), SEC("fexit.s/..."), SEC("fmod_ret.s/..."),
SEC("uprobe.s/...") - now fails to load with -EACCES and "combined stack
size of %d calls is %d. Too large", where it verified fine before. The
selftest at tools/testing/selftests/bpf/progs/verifier_private_stack.c:86
("Private stack, subtree > MAX_BPF_STACK", 512-byte main + 32-byte
subprog, __success) has exactly this shape; it only survives because it
uses the non-sleepable SEC("kprobe"). With Cc: stable@vger.kernel.org,
this would break working programs on a stable update.
> /* bpf_prog_check_recur() checks all prog types that use bpf trampoline
> * while kprobe/tp/perf_event/raw_tp don't use trampoline hence checked
> * explicitly.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/32604492082
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 2/2] selftests/bpf: verify preemptible uprobes avoid private stack
2026-08-22 22:54 ` [PATCH bpf v3 2/2] selftests/bpf: verify preemptible uprobes avoid private stack Christian Simon
2026-08-22 23:05 ` sashiko-bot
@ 2026-08-22 23:58 ` bot+bpf-ci
1 sibling, 0 replies; 18+ messages in thread
From: bot+bpf-ci @ 2026-08-22 23:58 UTC (permalink / raw)
To: simon, bpf
Cc: simon, ast, andrii, daniel, martin.lau, tj, yonghong.song, stable,
andrii.nakryiko, olsajiri, ast, andrii, daniel, martin.lau,
eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3109 bytes --]
> diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_sleepable_stack.c b/tools/testing/selftests/bpf/prog_tests/uprobe_sleepable_stack.c
> new file mode 100644
> index 000000000000..a18970d2c584
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_sleepable_stack.c
A subsystem pattern flags this as potentially concerning: the BPF
selftests guide asks whether a new test file for a narrow variant of
behavior should instead be a new case in an existing more general test.
The behavior being tested - that the verifier disables private stack for
sleepable uprobes - is a verifier-level decision made in
bpf_enable_priv_stack() when prog->sleepable is set. The test suite
already has tools/testing/selftests/bpf/progs/verifier_private_stack.c
in the same directory, which covers private-stack selection
deterministically by asserting on JIT output patterns with __jited()
annotations.
This patch adds a 254-line, two-file runtime test that depends on thread
timing and CPU affinity. Adding a SEC("uprobe.s") case to the verifier
test with a 64-byte frame would pin the fix by asserting the absence of
the private-frame-pointer setup instruction, without needing threads or
timing dependencies.
The runtime test does exercise something the verifier test cannot -
actual concurrent same-CPU execution - so this is raised as a question:
would extending verifier_private_stack.c have been a simpler way to pin
the verifier change, or is the runtime concurrency test worth the added
complexity?
[ ... ]
> @@ -0,0 +1,165 @@
[ ... ]
> +void test_uprobe_sleepable_stack(void)
> +{
> + struct uprobe_sleepable_stack *skel = NULL;
> + cpu_set_t old_mask, mask;
> + bool affinity_set = false;
> + int cpu;
> +
> +#if !defined(__x86_64__) && !defined(__aarch64__) && !defined(__powerpc64__)
> + test__skip();
> + return;
> +#endif
> + if (!env.jit_enabled) {
> + test__skip();
> + return;
> + }
[ ... ]
> + if (!skel->kconfig->CONFIG_PREEMPTION) {
> + test__skip();
> + goto cleanup;
> + }
[ ... ]
> + link_err = libbpf_get_error(link);
> + if (link_err == -EOPNOTSUPP) {
> + test__skip();
> + return;
> + }
This isn't a bug, but could the skip paths include diagnostics? All four
skip sites (unsupported architecture, JIT disabled, CONFIG_PREEMPTION
unset, and attach returning -EOPNOTSUPP) call bare test__skip() with no
message, so a skipped result gives the operator no way to tell which
prerequisite was missing.
The test_progs framework's test__skip() takes no arguments, so this
matches prevailing practice across the test suite. Is it worth noting
because the missing prerequisite that matters most - runtime preemption
actually being enabled - would be most helpful to communicate.
> diff --git a/tools/testing/selftests/bpf/progs/uprobe_sleepable_stack.c b/tools/testing/selftests/bpf/progs/uprobe_sleepable_stack.c
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/32604492082
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-08-22 22:54 ` [PATCH bpf v3 1/2] bpf: " Christian Simon
2026-08-22 23:10 ` sashiko-bot
2026-08-22 23:46 ` bot+bpf-ci
@ 2026-08-26 1:20 ` Alexei Starovoitov
2026-08-26 13:11 ` Jiri Olsa
2026-08-27 14:56 ` Andrii Nakryiko
2 siblings, 2 replies; 18+ messages in thread
From: Alexei Starovoitov @ 2026-08-26 1:20 UTC (permalink / raw)
To: Christian Simon, bpf
Cc: ast, andrii, daniel, martin.lau, tj, yonghong.song, stable,
andrii.nakryiko, olsajiri
On Sat Aug 22, 2026 at 3:54 PM PDT, Christian Simon wrote:
> A JITed BPF program can use one private stack per program and CPU.
> Sleepable programs can be preempted, allowing another task to run the
> same program on the same CPU. The second invocation then reuses and can
> overwrite the first invocation's private stack.
I'm confused by this. sleepable progs go through __bpf_prog_enter_sleepable_recur()
which has per-prog recurison counter. So preemption of the prog
doesn't break private stack.
If the same prog attemps to execute on the same cpu it will be skipped.
syscall prog types go via bpf_prog_run_array_sleepable()
that have per prog recursions counter.
Looks like we're not doing it for bpf_prog_run_array_uprobe().
I'm not sure what the right trade off here.
I feel universally checking for recursion is better
then selectively disabling private stack for uprobe.
> Disable private stack for sleepable programs so they use the regular kernel
> stack, which handles preemption correctly. This change is intentionally
> limited to programs marked sleepable; preemptible non-sleepable dispatch
> paths require separate protection.
>
> Fixes: 7d1cd70d4b16 ("bpf, x86: Support private stack in jit")
> Fixes: 6c17a882d380 ("bpf, arm64: JIT support for private stack")
> Fixes: 156d985123b6 ("powerpc64/bpf: Implement JIT support for private stack")
> Cc: stable@vger.kernel.org
> Signed-off-by: Christian Simon <simon@swine.de>
> ---
> kernel/bpf/verifier.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 5e37ca75e5c4..038753ef07a9 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -5237,6 +5237,15 @@ static enum priv_stack_mode bpf_enable_priv_stack(struct bpf_prog *prog)
> if (!bpf_jit_supports_private_stack())
> return NO_PRIV_STACK;
>
> + /*
> + * Sleepable programs can be preempted, allowing another task to run
> + * the same program on the same CPU. Since private stack is per-CPU
> + * and per-program, the second invocation would corrupt the first's
> + * stack. Disable private stack for sleepable programs.
> + */
> + if (prog->sleepable)
> + return NO_PRIV_STACK;
This is not true in genreal. sleepable progs can use priv stack.
pw-bot: cr
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-08-26 1:20 ` Alexei Starovoitov
@ 2026-08-26 13:11 ` Jiri Olsa
2026-08-27 14:56 ` Andrii Nakryiko
1 sibling, 0 replies; 18+ messages in thread
From: Jiri Olsa @ 2026-08-26 13:11 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Christian Simon, bpf, ast, andrii, daniel, martin.lau, tj,
yonghong.song, stable, andrii.nakryiko, olsajiri
On Tue, Aug 25, 2026 at 06:20:16PM -0700, Alexei Starovoitov wrote:
> On Sat Aug 22, 2026 at 3:54 PM PDT, Christian Simon wrote:
> > A JITed BPF program can use one private stack per program and CPU.
> > Sleepable programs can be preempted, allowing another task to run the
> > same program on the same CPU. The second invocation then reuses and can
> > overwrite the first invocation's private stack.
>
> I'm confused by this. sleepable progs go through __bpf_prog_enter_sleepable_recur()
> which has per-prog recurison counter. So preemption of the prog
> doesn't break private stack.
> If the same prog attemps to execute on the same cpu it will be skipped.
>
> syscall prog types go via bpf_prog_run_array_sleepable()
> that have per prog recursions counter.
>
> Looks like we're not doing it for bpf_prog_run_array_uprobe().
> I'm not sure what the right trade off here.
> I feel universally checking for recursion is better
> then selectively disabling private stack for uprobe.
also during the load we can't tell if kprobe program will be attached
as kprobe or uprobe [1] so having a way to disable private stack for
a program would solve this as well
jirka
[1] https://lore.kernel.org/bpf/ao2f-rBgT0SqX6Pw@krava/
>
> > Disable private stack for sleepable programs so they use the regular kernel
> > stack, which handles preemption correctly. This change is intentionally
> > limited to programs marked sleepable; preemptible non-sleepable dispatch
> > paths require separate protection.
> >
> > Fixes: 7d1cd70d4b16 ("bpf, x86: Support private stack in jit")
> > Fixes: 6c17a882d380 ("bpf, arm64: JIT support for private stack")
> > Fixes: 156d985123b6 ("powerpc64/bpf: Implement JIT support for private stack")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Christian Simon <simon@swine.de>
> > ---
> > kernel/bpf/verifier.c | 9 +++++++++
> > 1 file changed, 9 insertions(+)
> >
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 5e37ca75e5c4..038753ef07a9 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -5237,6 +5237,15 @@ static enum priv_stack_mode bpf_enable_priv_stack(struct bpf_prog *prog)
> > if (!bpf_jit_supports_private_stack())
> > return NO_PRIV_STACK;
> >
> > + /*
> > + * Sleepable programs can be preempted, allowing another task to run
> > + * the same program on the same CPU. Since private stack is per-CPU
> > + * and per-program, the second invocation would corrupt the first's
> > + * stack. Disable private stack for sleepable programs.
> > + */
> > + if (prog->sleepable)
> > + return NO_PRIV_STACK;
>
> This is not true in genreal. sleepable progs can use priv stack.
>
> pw-bot: cr
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-08-26 1:20 ` Alexei Starovoitov
2026-08-26 13:11 ` Jiri Olsa
@ 2026-08-27 14:56 ` Andrii Nakryiko
2026-08-27 16:35 ` Alexei Starovoitov
1 sibling, 1 reply; 18+ messages in thread
From: Andrii Nakryiko @ 2026-08-27 14:56 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Christian Simon, bpf, ast, andrii, daniel, martin.lau, tj,
yonghong.song, stable, olsajiri
On Tue, Aug 25, 2026 at 6:20 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> On Sat Aug 22, 2026 at 3:54 PM PDT, Christian Simon wrote:
> > A JITed BPF program can use one private stack per program and CPU.
> > Sleepable programs can be preempted, allowing another task to run the
> > same program on the same CPU. The second invocation then reuses and can
> > overwrite the first invocation's private stack.
>
> I'm confused by this. sleepable progs go through __bpf_prog_enter_sleepable_recur()
> which has per-prog recurison counter. So preemption of the prog
> doesn't break private stack.
> If the same prog attemps to execute on the same cpu it will be skipped.
>
> syscall prog types go via bpf_prog_run_array_sleepable()
> that have per prog recursions counter.
>
> Looks like we're not doing it for bpf_prog_run_array_uprobe().
> I'm not sure what the right trade off here.
> I feel universally checking for recursion is better
> then selectively disabling private stack for uprobe.
I'd really like to avoid adding this "recursion protection" to uprobe.
With uprobes, there is no recursion, it's called from well defined
context in the kernel and you can't have recursive uprobe BPF
programs.
All you can have is a very valid and possible sleepable uprobe
interleaving, which the user cannot prevent or work around, they have
no control over this and it's just a fact of life.
E.g., a simple scenario, we attach one bpf program (let's call it U)
to some USDT. BPF program U is sleepable and actually can sleep due to
page faults (e.g., unwinding Python stack trace requires sleepable
mode for reliably getting filename strings from Python runtime, which
are not always paged in).
In such a case, you can have thread A and thread B both hitting the
same USDT (e.g., somewhere in memory allocator or whatnot). Let's say
thread A hits it first on CPU X, BPF program U starts executing and
unwinding Python stack, does bpf_copy_from_user() for string contents
and causes page fault, is taken off CPU X. Meanwhile thread B hits
USDT on the same CPU X, kernel runs program U, and it is supposed to
work completely independently and concurrently (no shared state or
whatever) from U's execution in thread A.
Yet, if we add this per-CPU "recursion check", we'll just skip U's
execution for thread B. This is data loss, and it's very bad in
practice because it frequently just invalidates the entire data
collection trustworthiness.
So I think we should disable private stack for uprobes (sleepable or
not) instead. I'm not sure private stack buys us anything for uprobe
cases.
As Jiri mentioned, at verification/jitting time we can't tell kprobe
from uprobe, though, so that's a bit of a problem, but it's a separate
discussion we should have. Perhaps having an extra expected attach
type, or type, or some program flag to designate uprobe program as
such would be appropriate, not sure. Let's discuss that.
But please, let's not make uprobes unreliable for no good reason.
>
> > Disable private stack for sleepable programs so they use the regular kernel
> > stack, which handles preemption correctly. This change is intentionally
> > limited to programs marked sleepable; preemptible non-sleepable dispatch
> > paths require separate protection.
> >
> > Fixes: 7d1cd70d4b16 ("bpf, x86: Support private stack in jit")
> > Fixes: 6c17a882d380 ("bpf, arm64: JIT support for private stack")
> > Fixes: 156d985123b6 ("powerpc64/bpf: Implement JIT support for private stack")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Christian Simon <simon@swine.de>
> > ---
> > kernel/bpf/verifier.c | 9 +++++++++
> > 1 file changed, 9 insertions(+)
> >
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 5e37ca75e5c4..038753ef07a9 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -5237,6 +5237,15 @@ static enum priv_stack_mode bpf_enable_priv_stack(struct bpf_prog *prog)
> > if (!bpf_jit_supports_private_stack())
> > return NO_PRIV_STACK;
> >
> > + /*
> > + * Sleepable programs can be preempted, allowing another task to run
> > + * the same program on the same CPU. Since private stack is per-CPU
> > + * and per-program, the second invocation would corrupt the first's
> > + * stack. Disable private stack for sleepable programs.
> > + */
> > + if (prog->sleepable)
> > + return NO_PRIV_STACK;
>
> This is not true in genreal. sleepable progs can use priv stack.
>
> pw-bot: cr
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-08-27 14:56 ` Andrii Nakryiko
@ 2026-08-27 16:35 ` Alexei Starovoitov
2026-08-27 16:40 ` Andrii Nakryiko
0 siblings, 1 reply; 18+ messages in thread
From: Alexei Starovoitov @ 2026-08-27 16:35 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: Christian Simon, bpf, Alexei Starovoitov, Andrii Nakryiko,
Daniel Borkmann, Martin KaFai Lau, Tejun Heo, Yonghong Song,
stable, Jiri Olsa
On Thu, Aug 27, 2026 at 7:56 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Tue, Aug 25, 2026 at 6:20 PM Alexei Starovoitov
> <alexei.starovoitov@gmail.com> wrote:
> >
> > On Sat Aug 22, 2026 at 3:54 PM PDT, Christian Simon wrote:
> > > A JITed BPF program can use one private stack per program and CPU.
> > > Sleepable programs can be preempted, allowing another task to run the
> > > same program on the same CPU. The second invocation then reuses and can
> > > overwrite the first invocation's private stack.
> >
> > I'm confused by this. sleepable progs go through __bpf_prog_enter_sleepable_recur()
> > which has per-prog recurison counter. So preemption of the prog
> > doesn't break private stack.
> > If the same prog attemps to execute on the same cpu it will be skipped.
> >
> > syscall prog types go via bpf_prog_run_array_sleepable()
> > that have per prog recursions counter.
> >
> > Looks like we're not doing it for bpf_prog_run_array_uprobe().
> > I'm not sure what the right trade off here.
> > I feel universally checking for recursion is better
> > then selectively disabling private stack for uprobe.
>
> I'd really like to avoid adding this "recursion protection" to uprobe.
> With uprobes, there is no recursion, it's called from well defined
> context in the kernel and you can't have recursive uprobe BPF
> programs.
>
> All you can have is a very valid and possible sleepable uprobe
> interleaving, which the user cannot prevent or work around, they have
> no control over this and it's just a fact of life.
>
> E.g., a simple scenario, we attach one bpf program (let's call it U)
> to some USDT. BPF program U is sleepable and actually can sleep due to
> page faults (e.g., unwinding Python stack trace requires sleepable
> mode for reliably getting filename strings from Python runtime, which
> are not always paged in).
>
> In such a case, you can have thread A and thread B both hitting the
> same USDT (e.g., somewhere in memory allocator or whatnot). Let's say
> thread A hits it first on CPU X, BPF program U starts executing and
> unwinding Python stack, does bpf_copy_from_user() for string contents
> and causes page fault, is taken off CPU X. Meanwhile thread B hits
> USDT on the same CPU X, kernel runs program U, and it is supposed to
> work completely independently and concurrently (no shared state or
> whatever) from U's execution in thread A.
>
> Yet, if we add this per-CPU "recursion check", we'll just skip U's
> execution for thread B. This is data loss, and it's very bad in
> practice because it frequently just invalidates the entire data
> collection trustworthiness.
ok. fair
> So I think we should disable private stack for uprobes (sleepable or
> not) instead. I'm not sure private stack buys us anything for uprobe
> cases.
why disable priv stack for non-sleepable uprobes?
While non-sleepable bpf prog is executing the same or different
uprobe cannot execute on the same cpu.
So bpf prog can be preempted by kernel execution,
but a user task cannot start preempt bpf prog,
so 2nd uprobe cannot start running,
no?
> As Jiri mentioned, at verification/jitting time we can't tell kprobe
> from uprobe, though, so that's a bit of a problem, but it's a separate
> discussion we should have. Perhaps having an extra expected attach
> type, or type, or some program flag to designate uprobe program as
> such would be appropriate, not sure. Let's discuss that.
we don't have sleepable kprobes.
So disable sleepable && [ku]probe type is enough. no?
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-08-27 16:35 ` Alexei Starovoitov
@ 2026-08-27 16:40 ` Andrii Nakryiko
2026-08-27 16:55 ` Alexei Starovoitov
0 siblings, 1 reply; 18+ messages in thread
From: Andrii Nakryiko @ 2026-08-27 16:40 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Christian Simon, bpf, Alexei Starovoitov, Andrii Nakryiko,
Daniel Borkmann, Martin KaFai Lau, Tejun Heo, Yonghong Song,
stable, Jiri Olsa
On Thu, Aug 27, 2026 at 9:35 AM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> On Thu, Aug 27, 2026 at 7:56 AM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
> >
> > On Tue, Aug 25, 2026 at 6:20 PM Alexei Starovoitov
> > <alexei.starovoitov@gmail.com> wrote:
> > >
> > > On Sat Aug 22, 2026 at 3:54 PM PDT, Christian Simon wrote:
> > > > A JITed BPF program can use one private stack per program and CPU.
> > > > Sleepable programs can be preempted, allowing another task to run the
> > > > same program on the same CPU. The second invocation then reuses and can
> > > > overwrite the first invocation's private stack.
> > >
> > > I'm confused by this. sleepable progs go through __bpf_prog_enter_sleepable_recur()
> > > which has per-prog recurison counter. So preemption of the prog
> > > doesn't break private stack.
> > > If the same prog attemps to execute on the same cpu it will be skipped.
> > >
> > > syscall prog types go via bpf_prog_run_array_sleepable()
> > > that have per prog recursions counter.
> > >
> > > Looks like we're not doing it for bpf_prog_run_array_uprobe().
> > > I'm not sure what the right trade off here.
> > > I feel universally checking for recursion is better
> > > then selectively disabling private stack for uprobe.
> >
> > I'd really like to avoid adding this "recursion protection" to uprobe.
> > With uprobes, there is no recursion, it's called from well defined
> > context in the kernel and you can't have recursive uprobe BPF
> > programs.
> >
> > All you can have is a very valid and possible sleepable uprobe
> > interleaving, which the user cannot prevent or work around, they have
> > no control over this and it's just a fact of life.
> >
> > E.g., a simple scenario, we attach one bpf program (let's call it U)
> > to some USDT. BPF program U is sleepable and actually can sleep due to
> > page faults (e.g., unwinding Python stack trace requires sleepable
> > mode for reliably getting filename strings from Python runtime, which
> > are not always paged in).
> >
> > In such a case, you can have thread A and thread B both hitting the
> > same USDT (e.g., somewhere in memory allocator or whatnot). Let's say
> > thread A hits it first on CPU X, BPF program U starts executing and
> > unwinding Python stack, does bpf_copy_from_user() for string contents
> > and causes page fault, is taken off CPU X. Meanwhile thread B hits
> > USDT on the same CPU X, kernel runs program U, and it is supposed to
> > work completely independently and concurrently (no shared state or
> > whatever) from U's execution in thread A.
> >
> > Yet, if we add this per-CPU "recursion check", we'll just skip U's
> > execution for thread B. This is data loss, and it's very bad in
> > practice because it frequently just invalidates the entire data
> > collection trustworthiness.
>
> ok. fair
>
great, thanks!
> > So I think we should disable private stack for uprobes (sleepable or
> > not) instead. I'm not sure private stack buys us anything for uprobe
> > cases.
>
> why disable priv stack for non-sleepable uprobes?
> While non-sleepable bpf prog is executing the same or different
> uprobe cannot execute on the same cpu.
> So bpf prog can be preempted by kernel execution,
> but a user task cannot start preempt bpf prog,
> so 2nd uprobe cannot start running,
> no?
I think that changes on preemptible kernels, this was called out in
discussions on previous versions of this patch. So only for that
reason.
For non-preemptible kernels yes, it's only sleepable that is a problem.
>
> > As Jiri mentioned, at verification/jitting time we can't tell kprobe
> > from uprobe, though, so that's a bit of a problem, but it's a separate
> > discussion we should have. Perhaps having an extra expected attach
> > type, or type, or some program flag to designate uprobe program as
> > such would be appropriate, not sure. Let's discuss that.
>
> we don't have sleepable kprobes.
> So disable sleepable && [ku]probe type is enough. no?
Yes, unless we want to also safeguard preemptible kernels. But we can
do that as an extra #ifdef CONFIG_PREEMPT_RT or something?
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-08-27 16:40 ` Andrii Nakryiko
@ 2026-08-27 16:55 ` Alexei Starovoitov
2026-08-27 22:32 ` Jiri Olsa
0 siblings, 1 reply; 18+ messages in thread
From: Alexei Starovoitov @ 2026-08-27 16:55 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: Christian Simon, bpf, Alexei Starovoitov, Andrii Nakryiko,
Daniel Borkmann, Martin KaFai Lau, Tejun Heo, Yonghong Song,
stable, Jiri Olsa
On Thu, Aug 27, 2026 at 9:40 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Thu, Aug 27, 2026 at 9:35 AM Alexei Starovoitov
> <alexei.starovoitov@gmail.com> wrote:
> >
> > On Thu, Aug 27, 2026 at 7:56 AM Andrii Nakryiko
> > <andrii.nakryiko@gmail.com> wrote:
> > >
> > > On Tue, Aug 25, 2026 at 6:20 PM Alexei Starovoitov
> > > <alexei.starovoitov@gmail.com> wrote:
> > > >
> > > > On Sat Aug 22, 2026 at 3:54 PM PDT, Christian Simon wrote:
> > > > > A JITed BPF program can use one private stack per program and CPU.
> > > > > Sleepable programs can be preempted, allowing another task to run the
> > > > > same program on the same CPU. The second invocation then reuses and can
> > > > > overwrite the first invocation's private stack.
> > > >
> > > > I'm confused by this. sleepable progs go through __bpf_prog_enter_sleepable_recur()
> > > > which has per-prog recurison counter. So preemption of the prog
> > > > doesn't break private stack.
> > > > If the same prog attemps to execute on the same cpu it will be skipped.
> > > >
> > > > syscall prog types go via bpf_prog_run_array_sleepable()
> > > > that have per prog recursions counter.
> > > >
> > > > Looks like we're not doing it for bpf_prog_run_array_uprobe().
> > > > I'm not sure what the right trade off here.
> > > > I feel universally checking for recursion is better
> > > > then selectively disabling private stack for uprobe.
> > >
> > > I'd really like to avoid adding this "recursion protection" to uprobe.
> > > With uprobes, there is no recursion, it's called from well defined
> > > context in the kernel and you can't have recursive uprobe BPF
> > > programs.
> > >
> > > All you can have is a very valid and possible sleepable uprobe
> > > interleaving, which the user cannot prevent or work around, they have
> > > no control over this and it's just a fact of life.
> > >
> > > E.g., a simple scenario, we attach one bpf program (let's call it U)
> > > to some USDT. BPF program U is sleepable and actually can sleep due to
> > > page faults (e.g., unwinding Python stack trace requires sleepable
> > > mode for reliably getting filename strings from Python runtime, which
> > > are not always paged in).
> > >
> > > In such a case, you can have thread A and thread B both hitting the
> > > same USDT (e.g., somewhere in memory allocator or whatnot). Let's say
> > > thread A hits it first on CPU X, BPF program U starts executing and
> > > unwinding Python stack, does bpf_copy_from_user() for string contents
> > > and causes page fault, is taken off CPU X. Meanwhile thread B hits
> > > USDT on the same CPU X, kernel runs program U, and it is supposed to
> > > work completely independently and concurrently (no shared state or
> > > whatever) from U's execution in thread A.
> > >
> > > Yet, if we add this per-CPU "recursion check", we'll just skip U's
> > > execution for thread B. This is data loss, and it's very bad in
> > > practice because it frequently just invalidates the entire data
> > > collection trustworthiness.
> >
> > ok. fair
> >
>
> great, thanks!
>
> > > So I think we should disable private stack for uprobes (sleepable or
> > > not) instead. I'm not sure private stack buys us anything for uprobe
> > > cases.
> >
> > why disable priv stack for non-sleepable uprobes?
> > While non-sleepable bpf prog is executing the same or different
> > uprobe cannot execute on the same cpu.
> > So bpf prog can be preempted by kernel execution,
> > but a user task cannot start preempt bpf prog,
> > so 2nd uprobe cannot start running,
> > no?
>
> I think that changes on preemptible kernels, this was called out in
> discussions on previous versions of this patch. So only for that
> reason.
my understanding is that preemptable kernel doesn't mean that
bpf prog can be preempted by user space.
only by kernel.
I looked up earlier thread, but don't understand what Jiri meant.
Jiri,
please clarify what problem do you see with non-sleepable uprobes?
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-08-27 16:55 ` Alexei Starovoitov
@ 2026-08-27 22:32 ` Jiri Olsa
2026-10-30 1:22 ` HyeongJun An
0 siblings, 1 reply; 18+ messages in thread
From: Jiri Olsa @ 2026-08-27 22:32 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Andrii Nakryiko, Christian Simon, bpf, Alexei Starovoitov,
Andrii Nakryiko, Daniel Borkmann, Martin KaFai Lau, Tejun Heo,
Yonghong Song, stable, Jiri Olsa
On Thu, Aug 27, 2026 at 09:55:08AM -0700, Alexei Starovoitov wrote:
> On Thu, Aug 27, 2026 at 9:40 AM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
> >
> > On Thu, Aug 27, 2026 at 9:35 AM Alexei Starovoitov
> > <alexei.starovoitov@gmail.com> wrote:
> > >
> > > On Thu, Aug 27, 2026 at 7:56 AM Andrii Nakryiko
> > > <andrii.nakryiko@gmail.com> wrote:
> > > >
> > > > On Tue, Aug 25, 2026 at 6:20 PM Alexei Starovoitov
> > > > <alexei.starovoitov@gmail.com> wrote:
> > > > >
> > > > > On Sat Aug 22, 2026 at 3:54 PM PDT, Christian Simon wrote:
> > > > > > A JITed BPF program can use one private stack per program and CPU.
> > > > > > Sleepable programs can be preempted, allowing another task to run the
> > > > > > same program on the same CPU. The second invocation then reuses and can
> > > > > > overwrite the first invocation's private stack.
> > > > >
> > > > > I'm confused by this. sleepable progs go through __bpf_prog_enter_sleepable_recur()
> > > > > which has per-prog recurison counter. So preemption of the prog
> > > > > doesn't break private stack.
> > > > > If the same prog attemps to execute on the same cpu it will be skipped.
> > > > >
> > > > > syscall prog types go via bpf_prog_run_array_sleepable()
> > > > > that have per prog recursions counter.
> > > > >
> > > > > Looks like we're not doing it for bpf_prog_run_array_uprobe().
> > > > > I'm not sure what the right trade off here.
> > > > > I feel universally checking for recursion is better
> > > > > then selectively disabling private stack for uprobe.
> > > >
> > > > I'd really like to avoid adding this "recursion protection" to uprobe.
> > > > With uprobes, there is no recursion, it's called from well defined
> > > > context in the kernel and you can't have recursive uprobe BPF
> > > > programs.
> > > >
> > > > All you can have is a very valid and possible sleepable uprobe
> > > > interleaving, which the user cannot prevent or work around, they have
> > > > no control over this and it's just a fact of life.
> > > >
> > > > E.g., a simple scenario, we attach one bpf program (let's call it U)
> > > > to some USDT. BPF program U is sleepable and actually can sleep due to
> > > > page faults (e.g., unwinding Python stack trace requires sleepable
> > > > mode for reliably getting filename strings from Python runtime, which
> > > > are not always paged in).
> > > >
> > > > In such a case, you can have thread A and thread B both hitting the
> > > > same USDT (e.g., somewhere in memory allocator or whatnot). Let's say
> > > > thread A hits it first on CPU X, BPF program U starts executing and
> > > > unwinding Python stack, does bpf_copy_from_user() for string contents
> > > > and causes page fault, is taken off CPU X. Meanwhile thread B hits
> > > > USDT on the same CPU X, kernel runs program U, and it is supposed to
> > > > work completely independently and concurrently (no shared state or
> > > > whatever) from U's execution in thread A.
> > > >
> > > > Yet, if we add this per-CPU "recursion check", we'll just skip U's
> > > > execution for thread B. This is data loss, and it's very bad in
> > > > practice because it frequently just invalidates the entire data
> > > > collection trustworthiness.
> > >
> > > ok. fair
> > >
> >
> > great, thanks!
> >
> > > > So I think we should disable private stack for uprobes (sleepable or
> > > > not) instead. I'm not sure private stack buys us anything for uprobe
> > > > cases.
> > >
> > > why disable priv stack for non-sleepable uprobes?
> > > While non-sleepable bpf prog is executing the same or different
> > > uprobe cannot execute on the same cpu.
> > > So bpf prog can be preempted by kernel execution,
> > > but a user task cannot start preempt bpf prog,
> > > so 2nd uprobe cannot start running,
> > > no?
> >
> > I think that changes on preemptible kernels, this was called out in
> > discussions on previous versions of this patch. So only for that
> > reason.
>
> my understanding is that preemptable kernel doesn't mean that
> bpf prog can be preempted by user space.
> only by kernel.
>
> I looked up earlier thread, but don't understand what Jiri meant.
>
> Jiri,
> please clarify what problem do you see with non-sleepable uprobes?
hum.. non-sleepable uprobe prog is run by bpf_prog_run_array_uprobe
and it disables only task migration, preemption is not disabled and
holds rcu_read_lock (which seems ok for preemption)
so I'm not sure why it wouldn't be preemptible by another task
jirka
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-08-27 22:32 ` Jiri Olsa
@ 2026-10-30 1:22 ` HyeongJun An
2026-10-30 23:01 ` Jiri Olsa
0 siblings, 1 reply; 18+ messages in thread
From: HyeongJun An @ 2026-10-30 1:22 UTC (permalink / raw)
To: olsajiri
Cc: bpf, simon, ast, andrii, daniel, martin.lau, tj, yonghong.song,
stable, andrii.nakryiko, alexei.starovoitov, HyeongJun An
On Fri, Aug 28, 2026 at 12:32:27AM +0200, Jiri Olsa wrote:
> > my understanding is that preemptable kernel doesn't mean that
> > bpf prog can be preempted by user space.
> > only by kernel.
[...]
> hum.. non-sleepable uprobe prog is run by bpf_prog_run_array_uprobe
> and it disables only task migration, preemption is not disabled and
> holds rcu_read_lock (which seems ok for preemption)
> so I'm not sure why it wouldn't be preemptible by another task
It is preemptible, and the private stack gets corrupted when it happens.
We hit this in a uprobe/uretprobe based tracer and reduced it to a small
reproducer.
The second writer is not user space. The scheduler preempts the task
while it is inside the program. migrate_disable() keeps that task on its
CPU, but the CPU can still run another task. That task hits the same
uprobe, runs the same program and writes its frame into the same per-CPU
private stack. When the first task resumes, its frame holds the second
task's values. Neither uprobe run path disables preemption:
bpf_prog_run_array_uprobe() migrate_disable(), plus rcu_read_lock()
for non-sleepable programs
uprobe_prog_run() (multi) migrate_disable(), plus rcu_read_lock()
or rcu_read_lock_trace()
With PREEMPT_RCU, rcu_read_lock() does not disable preemption.
bpf_enable_priv_stack() gives a private stack to every
BPF_PROG_TYPE_KPROBE program with at least BPF_PRIV_STACK_MIN_SIZE of
stack and no tail call. So classic uprobes and uprobe.multi are
affected as well, not only sleepable programs.
The test: four processes pinned to one CPU call getppid() in a loop for
10 s. The program fills 16 stack slots (a 216-byte frame) with
bpf_get_current_pid_tgid(), spins with 200 bpf_ktime_get_ns() calls,
and counts runs where a slot no longer matches a fresh
bpf_get_current_pid_tgid(). The JIT output confirms that this program
uses the private stack. The controls do not: one adds a tail call into
an empty PROG_ARRAY, the other uses a single slot.
Clobbered entry runs / total runs, one 10 s run per cell (7.0: one of
two runs, the other was within a few percent), x86_64, 4-vCPU KVM
guests:
kernel preempt uprobe uprobe.multi uprobe.s
6.12.112 voluntary 0/288000 0/296000 0/288000 *
6.18.56 none 0/272000 0/288000 0/288000
6.18.56 voluntary 0/272000 0/288000 0/288000
6.18.56 full 497/272000 481/280000 501/280000
6.18.56 lazy 0/280000 0/280000 0/288000
7.0 (Ubuntu) full 1221/856004 1202/864004 1158/856004
7.0 (Ubuntu) lazy 0/840004 0/848004 1/856004
7.2.10 full 649/296000 654/288000 642/288000
7.2.10 lazy 642/288000 622/280000 655/288000
bpf-next full 700/288000 595/280000 619/280000
bpf-next lazy 634/288000 670/288000 647/288000
* 6.12 has no private stack. Its JIT output uses the task stack.
The return probes gave numbers of the same size. Both controls stayed at
0 in every row. On 7.0 under lazy, each variant gave 0 to 2 clobbered
runs per 10 s run across repeats. bpf-next was 7.3.0-rc5-g763187dbbf83.
On 6.18, lazy stays at zero while 7.2 and bpf-next corrupt under lazy as
often as under full. We did not look into why.
In the tracer this showed up as copies from another task's buffer
pointer and length (wrong bytes or -EFAULT), and as return probes that
found no entry state because the tid in the frame was not theirs.
This was on the Ubuntu 7.0 kernel with its default lazy preemption, at
one to four bad calls per million under steady load, and more when the
host was busy. It went to zero once the programs stopped using the
private stack.
On the fix: I agree with Andrii that skipping the second run is wrong
for uprobes. It is a separate, legitimate event, and a tracer that
counts calls would silently lose it. Disabling the private stack for
uprobes, sleepable or not, as he suggested, avoids that. At load time a
classic uprobe program cannot be told apart from a kprobe, so this needs
either the load-time marker that was discussed or dropping the private
stack for BPF_PROG_TYPE_KPROBE. For uprobe.multi, expected_attach_type
already says BPF_TRACE_UPROBE_MULTI.
Until then, a single tail call into an empty PROG_ARRAY keeps a program
off the private stack.
I can turn the reproducer into a selftest, or run any version of the fix
on these kernels.
Thanks,
HyeongJun
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-10-30 1:22 ` HyeongJun An
@ 2026-10-30 23:01 ` Jiri Olsa
2026-10-31 1:19 ` HyeongJun An
0 siblings, 1 reply; 18+ messages in thread
From: Jiri Olsa @ 2026-10-30 23:01 UTC (permalink / raw)
To: HyeongJun An
Cc: olsajiri, bpf, simon, ast, andrii, daniel, martin.lau, tj,
yonghong.song, stable, andrii.nakryiko, alexei.starovoitov
On Fri, Oct 30, 2026 at 10:22:58AM +0900, HyeongJun An wrote:
> On Fri, Aug 28, 2026 at 12:32:27AM +0200, Jiri Olsa wrote:
> > > my understanding is that preemptable kernel doesn't mean that
> > > bpf prog can be preempted by user space.
> > > only by kernel.
> [...]
> > hum.. non-sleepable uprobe prog is run by bpf_prog_run_array_uprobe
> > and it disables only task migration, preemption is not disabled and
> > holds rcu_read_lock (which seems ok for preemption)
> > so I'm not sure why it wouldn't be preemptible by another task
>
> It is preemptible, and the private stack gets corrupted when it happens.
> We hit this in a uprobe/uretprobe based tracer and reduced it to a small
> reproducer.
>
> The second writer is not user space. The scheduler preempts the task
> while it is inside the program. migrate_disable() keeps that task on its
> CPU, but the CPU can still run another task. That task hits the same
> uprobe, runs the same program and writes its frame into the same per-CPU
> private stack. When the first task resumes, its frame holds the second
> task's values. Neither uprobe run path disables preemption:
>
> bpf_prog_run_array_uprobe() migrate_disable(), plus rcu_read_lock()
> for non-sleepable programs
> uprobe_prog_run() (multi) migrate_disable(), plus rcu_read_lock()
> or rcu_read_lock_trace()
>
> With PREEMPT_RCU, rcu_read_lock() does not disable preemption.
>
> bpf_enable_priv_stack() gives a private stack to every
> BPF_PROG_TYPE_KPROBE program with at least BPF_PRIV_STACK_MIN_SIZE of
> stack and no tail call. So classic uprobes and uprobe.multi are
> affected as well, not only sleepable programs.
>
> The test: four processes pinned to one CPU call getppid() in a loop for
> 10 s. The program fills 16 stack slots (a 216-byte frame) with
> bpf_get_current_pid_tgid(), spins with 200 bpf_ktime_get_ns() calls,
> and counts runs where a slot no longer matches a fresh
> bpf_get_current_pid_tgid(). The JIT output confirms that this program
> uses the private stack. The controls do not: one adds a tail call into
> an empty PROG_ARRAY, the other uses a single slot.
>
> Clobbered entry runs / total runs, one 10 s run per cell (7.0: one of
> two runs, the other was within a few percent), x86_64, 4-vCPU KVM
> guests:
>
> kernel preempt uprobe uprobe.multi uprobe.s
> 6.12.112 voluntary 0/288000 0/296000 0/288000 *
> 6.18.56 none 0/272000 0/288000 0/288000
> 6.18.56 voluntary 0/272000 0/288000 0/288000
> 6.18.56 full 497/272000 481/280000 501/280000
> 6.18.56 lazy 0/280000 0/280000 0/288000
> 7.0 (Ubuntu) full 1221/856004 1202/864004 1158/856004
> 7.0 (Ubuntu) lazy 0/840004 0/848004 1/856004
> 7.2.10 full 649/296000 654/288000 642/288000
> 7.2.10 lazy 642/288000 622/280000 655/288000
> bpf-next full 700/288000 595/280000 619/280000
> bpf-next lazy 634/288000 670/288000 647/288000
>
> * 6.12 has no private stack. Its JIT output uses the task stack.
>
> The return probes gave numbers of the same size. Both controls stayed at
> 0 in every row. On 7.0 under lazy, each variant gave 0 to 2 clobbered
> runs per 10 s run across repeats. bpf-next was 7.3.0-rc5-g763187dbbf83.
> On 6.18, lazy stays at zero while 7.2 and bpf-next corrupt under lazy as
> often as under full. We did not look into why.
>
> In the tracer this showed up as copies from another task's buffer
> pointer and length (wrong bytes or -EFAULT), and as return probes that
> found no entry state because the tid in the frame was not theirs.
> This was on the Ubuntu 7.0 kernel with its default lazy preemption, at
> one to four bad calls per million under steady load, and more when the
> host was busy. It went to zero once the programs stopped using the
> private stack.
>
> On the fix: I agree with Andrii that skipping the second run is wrong
> for uprobes. It is a separate, legitimate event, and a tracer that
> counts calls would silently lose it. Disabling the private stack for
> uprobes, sleepable or not, as he suggested, avoids that. At load time a
> classic uprobe program cannot be told apart from a kprobe, so this needs
> either the load-time marker that was discussed or dropping the private
> stack for BPF_PROG_TYPE_KPROBE. For uprobe.multi, expected_attach_type
> already says BPF_TRACE_UPROBE_MULTI.
>
> Until then, a single tail call into an empty PROG_ARRAY keeps a program
> off the private stack.
>
> I can turn the reproducer into a selftest, or run any version of the fix
> on these kernels.
sorry.. seems this fell through cracks
could we add a BPF_TRACE_UPROBE expected attach type for classic uprobes?
but I think we can't reject attaching an unmarked BPF_PROG_TYPE_KPROBE
program to a uprobe if it was JITed to use a private stack, that would
break existing users
thoughts?
thanks,
jirka
---
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 8d7228834305..c0bb356b1f46 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1200,6 +1200,7 @@ enum bpf_attach_type {
BPF_TRACE_FENTRY_MULTI,
BPF_TRACE_FEXIT_MULTI,
BPF_TRACE_FSESSION_MULTI,
+ BPF_TRACE_UPROBE,
__MAX_BPF_ATTACH_TYPE
};
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 02ed5368f056..8dfa98b22be9 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2800,6 +2800,12 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
return -EINVAL;
switch (prog_type) {
+ case BPF_PROG_TYPE_KPROBE:
+ if (expected_attach_type == BPF_TRACE_UPROBE ||
+ expected_attach_type == BPF_TRACE_UPROBE_MULTI ||
+ expected_attach_type == BPF_TRACE_UPROBE_SESSION)
+ return 0;
+ return expected_attach_type ? -EINVAL : 0;
case BPF_PROG_TYPE_CGROUP_SOCK:
switch (expected_attach_type) {
case BPF_CGROUP_INET_SOCK_CREATE:
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 26a5ee0708a3..d9f390b0d289 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5699,6 +5699,10 @@ static enum priv_stack_mode bpf_enable_priv_stack(struct bpf_prog *prog)
{
if (!bpf_jit_supports_private_stack())
return NO_PRIV_STACK;
+ if (prog->expected_attach_type == BPF_TRACE_UPROBE ||
+ prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI ||
+ prog->expected_attach_type == BPF_TRACE_UPROBE_SESSION)
+ return NO_PRIV_STACK;
/* bpf_prog_check_recur() checks all prog types that use bpf trampoline
* while kprobe/tp/perf_event/raw_tp don't use trampoline hence checked
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 457d75ea7142..5fb8b40f52e0 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -11835,6 +11835,19 @@ static int __perf_event_set_bpf_prog(struct perf_event *event,
(is_syscall_tp && prog->type != BPF_PROG_TYPE_TRACEPOINT))
return -EINVAL;
+ /* XXX leftover.. Multi and session uprobes must use their BPF link attachment path. */
+ if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI ||
+ prog->expected_attach_type == BPF_TRACE_UPROBE_SESSION)
+ return -EINVAL;
+
+ if (prog->expected_attach_type == BPF_TRACE_UPROBE && !is_uprobe)
+ return -EINVAL;
+
+ /* XXX can we do this? */
+ /* A preempted uprobe can be re-entered and overwrite its private stack. */
+ if (is_uprobe && !prog->expected_attach_type && prog->aux->jits_use_priv_stack)
+ return -EINVAL;
+
if (prog->type == BPF_PROG_TYPE_KPROBE && prog->sleepable && !is_uprobe)
/* only uprobe programs are allowed to be sleepable */
return -EINVAL;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 8d7228834305..c0bb356b1f46 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1200,6 +1200,7 @@ enum bpf_attach_type {
BPF_TRACE_FENTRY_MULTI,
BPF_TRACE_FEXIT_MULTI,
BPF_TRACE_FSESSION_MULTI,
+ BPF_TRACE_UPROBE,
__MAX_BPF_ATTACH_TYPE
};
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 02e75b183ed0..3b7aa4903758 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -10842,11 +10842,11 @@ static const struct bpf_sec_def section_defs[] = {
SEC_DEF("sk_reuseport/migrate", SK_REUSEPORT, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, SEC_ATTACHABLE),
SEC_DEF("sk_reuseport", SK_REUSEPORT, BPF_SK_REUSEPORT_SELECT, SEC_ATTACHABLE),
SEC_DEF("kprobe+", KPROBE, 0, SEC_NONE, attach_kprobe),
- SEC_DEF("uprobe+", KPROBE, 0, SEC_NONE, attach_uprobe),
- SEC_DEF("uprobe.s+", KPROBE, 0, SEC_SLEEPABLE, attach_uprobe),
+ SEC_DEF("uprobe+", KPROBE, BPF_TRACE_UPROBE, SEC_NONE, attach_uprobe),
+ SEC_DEF("uprobe.s+", KPROBE, BPF_TRACE_UPROBE, SEC_SLEEPABLE, attach_uprobe),
SEC_DEF("kretprobe+", KPROBE, 0, SEC_NONE, attach_kprobe),
- SEC_DEF("uretprobe+", KPROBE, 0, SEC_NONE, attach_uprobe),
- SEC_DEF("uretprobe.s+", KPROBE, 0, SEC_SLEEPABLE, attach_uprobe),
+ SEC_DEF("uretprobe+", KPROBE, BPF_TRACE_UPROBE, SEC_NONE, attach_uprobe),
+ SEC_DEF("uretprobe.s+", KPROBE, BPF_TRACE_UPROBE, SEC_SLEEPABLE, attach_uprobe),
SEC_DEF("kprobe.multi+", KPROBE, BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
SEC_DEF("kretprobe.multi+", KPROBE, BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
SEC_DEF("kprobe.session+", KPROBE, BPF_TRACE_KPROBE_SESSION, SEC_NONE, attach_kprobe_session),
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-10-30 23:01 ` Jiri Olsa
@ 2026-10-31 1:19 ` HyeongJun An
2026-10-31 5:04 ` Alexei Starovoitov
0 siblings, 1 reply; 18+ messages in thread
From: HyeongJun An @ 2026-10-31 1:19 UTC (permalink / raw)
To: olsajiri
Cc: bpf, simon, ast, andrii, daniel, martin.lau, tj, yonghong.song,
stable, andrii.nakryiko, alexei.starovoitov, HyeongJun An
On Sat, Oct 31, 2026 at 12:01:02AM +0100, Jiri Olsa wrote:
> could we add a BPF_TRACE_UPROBE expected attach type for classic uprobes?
>
> but I think we can't reject attaching an unmarked BPF_PROG_TYPE_KPROBE
> program to a uprobe if it was JITed to use a private stack, that would
> break existing users
>
> thoughts?
The marker plus the libbpf change looks right to me. I ran your draft
against the reproducer from my last mail to see what it does for the
programs that stay unmarked: every program built with an older libbpf,
and every loader outside libbpf (aya, cilium/ebpf, bpftrace) until it
learns the new attach type. Our tracer is one of those.
Kernels are bpf-next 7101b10cf281 with your draft (jiri), your draft
without the attach-time rejection (jiri-noreject), and jiri-noreject
plus the run-path change below and Christian's v3 1/2 (jiri-a).
"unmarked" is bpftool with the current libbpf, "marked" with your
libbpf change. Same reproducer as before, one 10 s run per cell:
kernel loader uprobe uprobe.s uprobe.multi
base unmarked 672/288000 655/296000 661/304000
base marked 620/304000 669/304000 592/296000
jiri unmarked attach EINVAL attach EINVAL 0/296000
jiri marked 0/296000 0/288000 0/304000
jiri-noreject unmarked 573/272000 640/300000 0/296000
jiri-noreject marked 0/288000 0/304000 0/304000
jiri-a unmarked 0/304000 0/296000 0/296000
jiri-a marked 0/296000 0/296000 0/296000
(full preemption shown. Lazy gave the same picture on every kernel.
The tail-call and small-stack controls were 0 everywhere, and the
return probes matched the entry probes. uprobe.multi is always loaded
with BPF_TRACE_UPROBE_MULTI, so it is marked with both loaders.)
So with the rejection, unmarked uprobes fail to attach with a bare
-EINVAL. Without it, they keep corrupting.
For the unmarked case I'd suggest disabling preemption around the run,
only for non-sleepable programs that were JITed with a private stack.
In the v2 review of the sleepable uprobe series Alexei wrote:
disabled preemption was an artifact of the past.
It became unnecessary when migrate_disable was introduced.
That held when no program kept per-CPU state across a preemption point.
The private stack brought per-CPU state back, so migrate_disable() is
not enough again, but only for programs that use it. Everything else
keeps running preemptible as today, and sleepable programs get no
private stack (Christian's v3 1/2). With this, jiri-a is 0 for both
loaders.
The comment above bpf_prog_run_array_uprobe() already says, for a
non-sleepable program, "we take the rcu read section and disable
preemption for that program alone", but the code has never disabled
preemption there. The run-path part of jiri-a, which also applies to
bpf-next as is:
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 580f89c450b5..c05292ec4d6a 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2825,13 +2825,24 @@ bpf_prog_run_array_uprobe(const struct bpf_prog_array *array,
old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx);
item = &array->items[0];
while ((prog = READ_ONCE(item->prog))) {
+ /*
+ * The private stack is per CPU. With only migration disabled,
+ * another task can run the same program on this CPU and
+ * overwrite the frame of a preempted run.
+ */
+ bool no_preempt = !prog->sleepable && prog->aux->jits_use_priv_stack;
+
if (!prog->sleepable)
rcu_read_lock();
+ if (no_preempt)
+ preempt_disable();
run_ctx.bpf_cookie = item->bpf_cookie;
ret &= run_prog(prog, ctx);
item++;
+ if (no_preempt)
+ preempt_enable();
if (!prog->sleepable)
rcu_read_unlock();
}
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index dfac5e1b8f40..a5721759750a 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -3166,11 +3166,16 @@ static int uprobe_prog_run(struct bpf_uprobe *uprobe,
rcu_read_lock();
migrate_disable();
+ /* Same reason as in bpf_prog_run_array_uprobe() */
+ if (!sleepable && prog->aux->jits_use_priv_stack)
+ preempt_disable();
old_run_ctx = bpf_set_run_ctx(&run_ctx.session_ctx.run_ctx);
err = bpf_prog_run(link->link.prog, regs);
bpf_reset_run_ctx(old_run_ctx);
+ if (!sleepable && prog->aux->jits_use_priv_stack)
+ preempt_enable();
migrate_enable();
if (sleepable)
Two other notes from building your draft:
- The new case BPF_PROG_TYPE_KPROBE in bpf_prog_load_check_attach()
returns -EINVAL for kprobe.multi and kprobe.session, since only the
three uprobe types and 0 pass. jiri-a lists those two as well.
- Not covered by these runs: the same program attached to a kprobe and
a uprobe, where a kprobe run from an interrupt could enter it during
a uprobe run. The reproducer only attaches by section name, so I have
no data on that case.
I can send the run-path change as a proper patch on top of your series,
and turn the reproducer into a selftest for classic, multi and sleepable
uprobes with the two controls.
The run-path change and the test kernels were prepared with Claude Code
(Claude Opus 5.5).
Thanks,
HyeongJun
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH bpf v3 1/2] bpf: disable private stack for sleepable programs
2026-10-31 1:19 ` HyeongJun An
@ 2026-10-31 5:04 ` Alexei Starovoitov
0 siblings, 0 replies; 18+ messages in thread
From: Alexei Starovoitov @ 2026-10-31 5:04 UTC (permalink / raw)
To: HyeongJun An, olsajiri
Cc: bpf, simon, ast, andrii, daniel, martin.lau, tj, yonghong.song,
stable, andrii.nakryiko
On Sat Oct 31, 2026 at 1:19 AM UTC, HyeongJun An wrote:
> if (!prog->sleepable)
> rcu_read_lock();
> + if (no_preempt)
> + preempt_disable();
nack.
The models are sending the same broken thing.
Pls teach them to read lore for nacks.
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-10-31 5:04 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-22 22:54 [PATCH bpf v3 0/2] disable private stack for sleepable programs Christian Simon
2026-08-22 22:54 ` [PATCH bpf v3 1/2] bpf: " Christian Simon
2026-08-22 23:10 ` sashiko-bot
2026-08-22 23:46 ` bot+bpf-ci
2026-08-26 1:20 ` Alexei Starovoitov
2026-08-26 13:11 ` Jiri Olsa
2026-08-27 14:56 ` Andrii Nakryiko
2026-08-27 16:35 ` Alexei Starovoitov
2026-08-27 16:40 ` Andrii Nakryiko
2026-08-27 16:55 ` Alexei Starovoitov
2026-08-27 22:32 ` Jiri Olsa
2026-10-30 1:22 ` HyeongJun An
2026-10-30 23:01 ` Jiri Olsa
2026-10-31 1:19 ` HyeongJun An
2026-10-31 5:04 ` Alexei Starovoitov
2026-08-22 22:54 ` [PATCH bpf v3 2/2] selftests/bpf: verify preemptible uprobes avoid private stack Christian Simon
2026-08-22 23:05 ` sashiko-bot
2026-08-22 23:58 ` bot+bpf-ci
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox