All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next] bpf: add BPF_F_SEALED flag to seal BPF links at creation
@ 2026-08-03  2:16 David Windsor
  2026-08-03  2:20 ` David Windsor
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: David Windsor @ 2026-08-03  2:16 UTC (permalink / raw)
  To: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	John Fastabend, Emil Tsalapatis, Ihor Solodrai, David Windsor

Introduce a new BPF_F_SEALED flag for BPF_LINK_CREATE that creates the
link permanently sealed. A sealed link can never have its program
replaced via BPF_LINK_UPDATE, can never be detached via BPF_LINK_DETACH,
and holds an extra self-reference that is never released, so the link and
its program attachment persist until the machine reboots, even after user
space closes every fd referring to it. There is no way to unseal a link.

The sealed state is tracked by a new bool field on struct bpf_link.

Signed-off-by: David Windsor <dwindsor@gmail.com>
---
 include/linux/bpf.h            |  2 ++
 include/uapi/linux/bpf.h       |  1 +
 kernel/bpf/syscall.c           | 41 +++++++++++++++++++++++++++++++---
 tools/include/uapi/linux/bpf.h |  1 +
 4 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 7bfc28673124..a9600a1483ec 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1934,6 +1934,8 @@ struct bpf_link {
 	 * link's semantics is determined by target attach hook
 	 */
 	bool sleepable;
+	/* set once by BPF_F_SEALED; blocks update/detach, pins link until reboot */
+	bool sealed;
 };
 
 struct bpf_link_ops {
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index ffd96e8b920b..8cb30d18fec7 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1251,6 +1251,7 @@ enum bpf_perf_event_type {
 #define BPF_F_AFTER		(1U << 4)
 #define BPF_F_ID		(1U << 5)
 #define BPF_F_PREORDER		(1U << 6)
+#define BPF_F_SEALED		(1U << 7)
 #define BPF_F_LINK		BPF_F_LINK /* 1 << 13 */
 
 /* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 94091130bcc5..e8bd57d5c907 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3411,6 +3411,7 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
 		seq_printf(m, "link_type:\t<%u>\n", type);
 	}
 	seq_printf(m, "link_id:\t%u\n", link->id);
+	seq_printf(m, "sealed:\t%d\n", READ_ONCE(link->sealed) ? 1 : 0);
 
 	rcu_read_lock();
 	prog = READ_ONCE(link->prog);
@@ -5776,17 +5777,41 @@ static int bpf_map_do_batch(const union bpf_attr *attr,
 	return err;
 }
 
+/* Seal the just-created link: take a self-reference that is never released. */
+static void link_seal_fd(int fd)
+{
+	struct bpf_link *link;
+
+	link = bpf_link_get_from_fd(fd);
+	if (IS_ERR(link))
+		return;
+
+	if (!READ_ONCE(link->sealed)) {
+		bpf_link_inc(link);
+		WRITE_ONCE(link->sealed, true);
+	}
+
+	bpf_link_put_direct(link);
+}
+
 #define BPF_LINK_CREATE_LAST_FIELD link_create.uprobe_multi.path_fd
 static int link_create(union bpf_attr *attr, bpfptr_t uattr)
 {
 	struct bpf_prog *prog;
+	bool seal;
 	int ret;
 
 	if (CHECK_ATTR(BPF_LINK_CREATE))
 		return -EINVAL;
 
-	if (attr->link_create.attach_type == BPF_STRUCT_OPS)
-		return bpf_struct_ops_link_create(attr);
+	/* Strip BPF_F_SEALED before per-type flag validation. */
+	seal = attr->link_create.flags & BPF_F_SEALED;
+	attr->link_create.flags &= ~BPF_F_SEALED;
+
+	if (attr->link_create.attach_type == BPF_STRUCT_OPS) {
+		ret = bpf_struct_ops_link_create(attr);
+		goto out_seal;
+	}
 
 	prog = bpf_prog_get(attr->link_create.prog_fd);
 	if (IS_ERR(prog))
@@ -5880,6 +5905,9 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
 out:
 	if (ret < 0)
 		bpf_prog_put(prog);
+out_seal:
+	if (ret >= 0 && seal)
+		link_seal_fd(ret);
 	return ret;
 }
 
@@ -5932,6 +5960,11 @@ static int link_update(union bpf_attr *attr)
 	if (IS_ERR(link))
 		return PTR_ERR(link);
 
+	if (READ_ONCE(link->sealed)) {
+		ret = -EPERM;
+		goto out_put_link;
+	}
+
 	if (link->ops->update_map) {
 		ret = link_update_map(link, attr);
 		goto out_put_link;
@@ -5984,7 +6017,9 @@ static int link_detach(union bpf_attr *attr)
 	if (IS_ERR(link))
 		return PTR_ERR(link);
 
-	if (link->ops->detach)
+	if (READ_ONCE(link->sealed))
+		ret = -EPERM;
+	else if (link->ops->detach)
 		ret = link->ops->detach(link);
 	else
 		ret = -EOPNOTSUPP;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index ffd96e8b920b..8cb30d18fec7 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1251,6 +1251,7 @@ enum bpf_perf_event_type {
 #define BPF_F_AFTER		(1U << 4)
 #define BPF_F_ID		(1U << 5)
 #define BPF_F_PREORDER		(1U << 6)
+#define BPF_F_SEALED		(1U << 7)
 #define BPF_F_LINK		BPF_F_LINK /* 1 << 13 */
 
 /* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the

base-commit: 28e911d61d66b92a3bded8b54622ed3cd2795bf6
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-08-11 21:23 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-03  2:16 [PATCH bpf-next] bpf: add BPF_F_SEALED flag to seal BPF links at creation David Windsor
2026-08-03  2:20 ` David Windsor
2026-08-03  3:23   ` Kumar Kartikeya Dwivedi
2026-08-03  2:28 ` sashiko-bot
2026-08-03  2:44 ` Leon Hwang
2026-08-03  3:17   ` Kumar Kartikeya Dwivedi
2026-08-03  3:26   ` David Windsor
2026-08-07 22:30     ` Andrii Nakryiko
2026-08-11 21:23       ` David Windsor
2026-08-03  3:18 ` Kumar Kartikeya Dwivedi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.