Thread (13 messages) 13 messages, 3 authors, 2026-02-25

Re: [PATCH bpf-next 2/8] bpf: Disallow !kprobe_write_ctx progs tail-calling kprobe_write_ctx progs

From: Leon Hwang <hidden>
Date: 2026-02-25 15:16:27
Also in: bpf, linux-kselftest, lkml


On 2026/2/25 00:57, Alexei Starovoitov wrote:
On Tue, Feb 24, 2026 at 7:41 AM Leon Hwang [off-list ref] wrote:
quoted
uprobe programs that can modify pt_regs require different runtime
assumptions than pt_regs-read-only uprobe programs. Mixing both in
one prog_array can make owner expectations diverge from callee behavior.

Reject the combination of !kprobe_write_ctx progs with kprobe_write_ctx
progs in __bpf_prog_map_compatible() to address the issue.

Fixes: 7384893d970e ("bpf: Allow uprobe program to change context registers")
Signed-off-by: Leon Hwang <redacted>
---
 include/linux/bpf.h | 7 ++++---
 kernel/bpf/core.c   | 3 +++
 2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index b78b53198a2e..2a2f6448a5fb 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -285,9 +285,10 @@ struct bpf_list_node_kern {
  */
 struct bpf_map_owner {
        enum bpf_prog_type type;
-       bool jited;
-       bool xdp_has_frags;
-       bool sleepable;
+       u32 jited:1,
+           xdp_has_frags:1,
+           sleepable:1,
+           kprobe_write_ctx:1;
Don't you see how much churn you're adding this way?
Every patch has to touch two lines instead of one.
Use
u32 jited:1;
u32 xdp_has_frags:1;
Ack.
also the bot is correct on patch 2 and 3.
Agreed.

Here's a concrete example that breaks the constraint:

struct {
	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
	__uint(max_entries, 1);
	__uint(key_size, sizeof(__u32));
	__uint(value_size, sizeof(__u32));
} jmp_table SEC(".maps");

SEC("?kprobe")
int prog_a(struct pt_regs *regs)
{
	regs->ax = 0;
	bpf_tail_call_static(regs, &jmp_table, 0);
	return 0;
}

SEC("?kprobe")
int prog_b(struct pt_regs *regs)
{
	bpf_tail_call_static(regs, &jmp_table, 0);
	return 0;
}

The jmp_table is shared between prog_a and prog_b.

The constraint can be broken by:

* Load prog_a first.
  At this point, owner->kprobe_write_ctx=true.
* Load prog_b next.
  At this point, prog_b passes the prog map compatibility validation.
* Add prog_a to jmp_table.
* Attach prog_b to a kernel function.

When the kernel function runs, the regs will be updated via prog_a.
Don't be fancy. Require strict conformance both ways in *all* patches.
It was to avoid awkward UX.

For example, the tail call from prog_a (kprobe_write_ctx=true) to prog_b
(kprobe_write_ctx=false) should be allowed. If prog_b is required to be
kprobe_write_ctx=true, prog_b will break user's expectation that prog_b
should not always update ctx.

The same awkward UX applies to call_get_func_ip, call_session_cookie,
and call_session_is_return.

I'll find a way to avoid such awkward UX by keeping the one-directional
validation, and to avoid the aforementioned constraint-broken issue at
the same time.
And your codex selftests are garbage. I don't have other words
to describe it. They are not testing the actual bug that
your patches are fixing. Think of what you're doing.
Asking LLM to write a test for your other patch is not what you
should be asking it to do. The selftest should be such that
it proves the unsafety/crash before the fix.
OK. I'll reimplement the selftests by myself in the next revision.

Thanks,
Leon
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help