Re: [PATCH bpf-next] bpf: add BPF_F_SEALED flag to seal BPF links at creation
"Kumar Kartikeya Dwivedi" <[email protected]> Mon, 03 Aug 2026 05:18:11 +0200
| Newsgroups | org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
+Cc Christian since he was involved in the discussion at LSF/MM/BPF. On Mon Aug 3, 2026 at 4:16 AM CEST, David Windsor wrote: > 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 <[email protected]> > --- > 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