Add support to look up bpf_link by ID and iterate over all existing bpf_links in the system. GET_FD_BY_ID code handles not-yet-ready bpf_link by checking that its ID hasn't been set to non-zero value yet. Setting bpf_link's ID is done as the very last step in finalizing bpf_link, together with installing FD. This approach allows users of bpf_link in kernel code to not worry about races between user-space and kernel code that hasn't finished attaching and initializing bpf_link. Further, it's critical that BPF_LINK_GET_FD_BY_ID only ever allows to create bpf_link FD that's O_RDONLY. This is to protect processes owning bpf_link and thus allowed to perform modifications on them (like LINK_UPDATE), from other processes that got bpf_link ID from GET_NEXT_ID API. In the latter case, only querying bpf_link information (implemented later in the series) will be allowed. Signed-off-by: Andrii Nakryiko <andriin@xxxxxx> --- include/uapi/linux/bpf.h | 2 ++ kernel/bpf/syscall.c | 56 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index eccfd1dea951..407c086bc9e4 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -113,6 +113,8 @@ enum bpf_cmd { BPF_MAP_DELETE_BATCH, BPF_LINK_CREATE, BPF_LINK_UPDATE, + BPF_LINK_GET_FD_BY_ID, + BPF_LINK_GET_NEXT_ID, }; enum bpf_map_type { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 8b3a7d5814ae..527ec16702be 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3719,6 +3719,55 @@ static int link_update(union bpf_attr *attr) return ret; } +static int bpf_link_inc_not_zero(struct bpf_link *link) +{ + return atomic64_fetch_add_unless(&link->refcnt, 1, 0) ? 0 : -ENOENT; +} + +#define BPF_LINK_GET_FD_BY_ID_LAST_FIELD open_flags + +static int bpf_link_get_fd_by_id(const union bpf_attr *attr) +{ + struct bpf_link *link; + u32 id = attr->link_id; + int f_flags; + int fd, err; + + if (CHECK_ATTR(BPF_LINK_GET_FD_BY_ID) || + /* links are not allowed to be open by ID as writable */ + attr->open_flags & ~BPF_F_RDONLY) + return -EINVAL; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + f_flags = bpf_get_file_flag(attr->open_flags); + if (f_flags < 0) + return f_flags; + + spin_lock_bh(&link_idr_lock); + link = idr_find(&link_idr, id); + /* before link is "settled", ID is 0, pretend it doesn't exist yet */ + if (link) { + if (link->id) + err = bpf_link_inc_not_zero(link); + else + err = -EAGAIN; + } else { + err = -ENOENT; + } + spin_unlock_bh(&link_idr_lock); + + if (err) + return err; + + fd = bpf_link_new_fd(link, f_flags); + if (fd < 0) + bpf_link_put(link); + + return fd; +} + SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) { union bpf_attr attr; @@ -3836,6 +3885,13 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz case BPF_LINK_UPDATE: err = link_update(&attr); break; + case BPF_LINK_GET_FD_BY_ID: + err = bpf_link_get_fd_by_id(&attr); + break; + case BPF_LINK_GET_NEXT_ID: + err = bpf_obj_get_next_id(&attr, uattr, + &link_idr, &link_idr_lock); + break; default: err = -EINVAL; break; -- 2.24.1