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 --- 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