From patchwork Fri Jun 2 14:59:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265454 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D44FDC7EE29 for ; Fri, 2 Jun 2023 15:00:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236336AbjFBPA3 convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:00:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53270 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236326AbjFBPAX (ORCPT ); Fri, 2 Jun 2023 11:00:23 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 938631B7 for ; Fri, 2 Jun 2023 08:00:21 -0700 (PDT) Received: from pps.filterd (m0148461.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 3528jv98022497 for ; Fri, 2 Jun 2023 08:00:21 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qyd6ptc8f-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:20 -0700 Received: from twshared40933.03.prn6.facebook.com (2620:10d:c0a8:1c::11) by mail.thefacebook.com (2620:10d:c0a8:82::d) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:16 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id E4B7C31E0479A; Fri, 2 Jun 2023 08:00:13 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 01/18] bpf: introduce BPF token object Date: Fri, 2 Jun 2023 07:59:54 -0700 Message-ID: <20230602150011.1657856-2-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: BlCFV2Z-2WxrmyW61ejZU8uYZX1u946_ X-Proofpoint-ORIG-GUID: BlCFV2Z-2WxrmyW61ejZU8uYZX1u946_ X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Add new kind of BPF kernel object, BPF token. BPF token is meant to to allow delegating privileged BPF functionality, like loading a BPF program or creating a BPF map, from privileged process to a *trusted* unprivileged process, all while have a good amount of control over which privileged operation could be done using provided BPF token. This patch adds new BPF_TOKEN_CREATE command to bpf() syscall, which allows to create a new BPF token object along with a set of allowed commands. Currently only BPF_TOKEN_CREATE command itself can be delegated, but other patches gradually add ability to delegate BPF_MAP_CREATE, BPF_BTF_LOAD, and BPF_PROG_LOAD commands. The above means that BPF token creation can be allowed by another existing BPF token, if original privileged creator allowed that. New derived BPF token cannot be more powerful than the original BPF token. BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS flag is added to allow application to do express "all supported BPF commands should be allowed" without worrying about which subset of desired commands is actually supported by potentially outdated kernel. Allowing this semantics doesn't seem to introduce any backwards compatibility issues and doesn't introduce any risk of abusing or misusing bit set field, but makes backwards compatibility story for various applications and tools much more straightforward, making it unnecessary to probe support for each individual possible bit. This is especially useful in follow up patches where we add BPF map types and prog types bit sets. Lastly, BPF token can be pinned in and retrieved from BPF FS, just like progs, maps, BTFs, and links. This allows applications (like container managers) to share BPF token with other applications through file system just like any other BPF object, and further control access to it using file system permissions, if desired. Signed-off-by: Andrii Nakryiko --- include/linux/bpf.h | 34 +++++++++ include/uapi/linux/bpf.h | 42 ++++++++++++ kernel/bpf/Makefile | 2 +- kernel/bpf/inode.c | 26 +++++++ kernel/bpf/syscall.c | 74 ++++++++++++++++++++ kernel/bpf/token.c | 122 +++++++++++++++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 40 +++++++++++ 7 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 kernel/bpf/token.c diff --git a/include/linux/bpf.h b/include/linux/bpf.h index f58895830ada..fe6d51c3a5b1 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -51,6 +51,7 @@ struct module; struct bpf_func_state; struct ftrace_ops; struct cgroup; +struct bpf_token; extern struct idr btf_idr; extern spinlock_t btf_idr_lock; @@ -1533,6 +1534,12 @@ struct bpf_link_primer { u32 id; }; +struct bpf_token { + struct work_struct work; + atomic64_t refcnt; + u64 allowed_cmds; +}; + struct bpf_struct_ops_value; struct btf_member; @@ -2077,6 +2084,15 @@ struct file *bpf_link_new_file(struct bpf_link *link, int *reserved_fd); struct bpf_link *bpf_link_get_from_fd(u32 ufd); struct bpf_link *bpf_link_get_curr_or_next(u32 *id); +void bpf_token_inc(struct bpf_token *token); +void bpf_token_put(struct bpf_token *token); +struct bpf_token *bpf_token_alloc(void); +int bpf_token_new_fd(struct bpf_token *token); +struct bpf_token *bpf_token_get_from_fd(u32 ufd); + +bool bpf_token_capable(const struct bpf_token *token, int cap); +bool bpf_token_allow_cmd(const struct bpf_token *token, enum bpf_cmd cmd); + int bpf_obj_pin_user(u32 ufd, int path_fd, const char __user *pathname); int bpf_obj_get_user(int path_fd, const char __user *pathname, int flags); @@ -2436,6 +2452,24 @@ static inline int bpf_obj_get_user(const char __user *pathname, int flags) return -EOPNOTSUPP; } +static inline void bpf_token_inc(struct bpf_token *token) +{ +} + +static inline void bpf_token_put(struct bpf_token *token) +{ +} + +static inline struct bpf_token *bpf_token_new_fd(struct bpf_token *token) +{ + return -EOPNOTSUPP; +} + +static inline struct bpf_token *bpf_token_get_from_fd(u32 ufd) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static inline void __dev_flush(void) { } diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 9273c654743c..01ab79f2ad9f 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -846,6 +846,16 @@ union bpf_iter_link_info { * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * + * BPF_TOKEN_CREATE + * Description + * Create BPF token with embedded information about what + * BPF-related functionality is allowed. This BPF token can be + * passed as an extra parameter to various bpf() syscall command. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * * NOTES * eBPF objects (maps and programs) can be shared between processes. * @@ -900,6 +910,7 @@ enum bpf_cmd { BPF_ITER_CREATE, BPF_LINK_DETACH, BPF_PROG_BIND_MAP, + BPF_TOKEN_CREATE, }; enum bpf_map_type { @@ -1169,6 +1180,24 @@ enum bpf_link_type { */ #define BPF_F_KPROBE_MULTI_RETURN (1U << 0) +/* BPF_TOKEN_CREATE command flags + */ +enum { + /* Ignore unrecognized bits in token_create.allowed_cmds bit set. If + * this flag is set, kernel won't return -EINVAL for a bit + * corresponding to a non-existing command or the one that doesn't + * support BPF token passing. This flags allows application to request + * BPF token creation for a desired set of commands without worrying + * about older kernels not supporting some of the commands. + * Presumably, deployed applications will do separate feature + * detection and will avoid calling not-yet-supported bpf() commands, + * so this BPF token will work equally well both on older and newer + * kernels, even if some of the requested commands won't be BPF + * token-enabled. + */ + BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS = 1U << 0, +}; + /* When BPF ldimm64's insn[0].src_reg != 0 then this can have * the following extensions: * @@ -1621,6 +1650,19 @@ union bpf_attr { __u32 flags; /* extra flags */ } prog_bind_map; + struct { /* struct used by BPF_TOKEN_CREATE command */ + __u32 flags; + __u32 token_fd; + /* a bit set of allowed bpf() syscall commands, + * e.g., (1ULL << BPF_TOKEN_CREATE) | (1ULL << BPF_PROG_LOAD) + * will allow creating derived BPF tokens and loading new BPF + * programs; + * see also BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS for its effect on + * validity checking of this set + */ + __u64 allowed_cmds; + } token_create; + } __attribute__((aligned(8))); /* The description below is an attempt at providing documentation to eBPF diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 1d3892168d32..bbc17ea3878f 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -6,7 +6,7 @@ cflags-nogcse-$(CONFIG_X86)$(CONFIG_CC_IS_GCC) := -fno-gcse endif CFLAGS_core.o += $(call cc-disable-warning, override-init) $(cflags-nogcse-yy) -obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o log.o +obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o log.o token.o obj-$(CONFIG_BPF_SYSCALL) += bpf_iter.o map_iter.o task_iter.o prog_iter.o link_iter.o obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o bloom_filter.o obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index 4174f76133df..55d9a945ad18 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -27,6 +27,7 @@ enum bpf_type { BPF_TYPE_PROG, BPF_TYPE_MAP, BPF_TYPE_LINK, + BPF_TYPE_TOKEN, }; static void *bpf_any_get(void *raw, enum bpf_type type) @@ -41,6 +42,9 @@ static void *bpf_any_get(void *raw, enum bpf_type type) case BPF_TYPE_LINK: bpf_link_inc(raw); break; + case BPF_TYPE_TOKEN: + bpf_token_inc(raw); + break; default: WARN_ON_ONCE(1); break; @@ -61,6 +65,9 @@ static void bpf_any_put(void *raw, enum bpf_type type) case BPF_TYPE_LINK: bpf_link_put(raw); break; + case BPF_TYPE_TOKEN: + bpf_token_put(raw); + break; default: WARN_ON_ONCE(1); break; @@ -89,6 +96,12 @@ static void *bpf_fd_probe_obj(u32 ufd, enum bpf_type *type) return raw; } + raw = bpf_token_get_from_fd(ufd); + if (!IS_ERR(raw)) { + *type = BPF_TYPE_TOKEN; + return raw; + } + return ERR_PTR(-EINVAL); } @@ -97,6 +110,7 @@ static const struct inode_operations bpf_dir_iops; static const struct inode_operations bpf_prog_iops = { }; static const struct inode_operations bpf_map_iops = { }; static const struct inode_operations bpf_link_iops = { }; +static const struct inode_operations bpf_token_iops = { }; static struct inode *bpf_get_inode(struct super_block *sb, const struct inode *dir, @@ -136,6 +150,8 @@ static int bpf_inode_type(const struct inode *inode, enum bpf_type *type) *type = BPF_TYPE_MAP; else if (inode->i_op == &bpf_link_iops) *type = BPF_TYPE_LINK; + else if (inode->i_op == &bpf_token_iops) + *type = BPF_TYPE_TOKEN; else return -EACCES; @@ -369,6 +385,11 @@ static int bpf_mklink(struct dentry *dentry, umode_t mode, void *arg) &bpf_iter_fops : &bpffs_obj_fops); } +static int bpf_mktoken(struct dentry *dentry, umode_t mode, void *arg) +{ + return bpf_mkobj_ops(dentry, mode, arg, &bpf_token_iops, &bpffs_obj_fops); +} + static struct dentry * bpf_lookup(struct inode *dir, struct dentry *dentry, unsigned flags) { @@ -469,6 +490,9 @@ static int bpf_obj_do_pin(int path_fd, const char __user *pathname, void *raw, case BPF_TYPE_LINK: ret = vfs_mkobj(dentry, mode, bpf_mklink, raw); break; + case BPF_TYPE_TOKEN: + ret = vfs_mkobj(dentry, mode, bpf_mktoken, raw); + break; default: ret = -EPERM; } @@ -547,6 +571,8 @@ int bpf_obj_get_user(int path_fd, const char __user *pathname, int flags) ret = bpf_map_new_fd(raw, f_flags); else if (type == BPF_TYPE_LINK) ret = (f_flags != O_RDWR) ? -EINVAL : bpf_link_new_fd(raw); + else if (type == BPF_TYPE_TOKEN) + ret = (f_flags != O_RDWR) ? -EINVAL : bpf_token_new_fd(raw); else return -ENOENT; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 92a57efc77de..edafb0f3053f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -5024,6 +5024,77 @@ static int bpf_prog_bind_map(union bpf_attr *attr) return ret; } +#define BPF_TOKEN_FLAGS_MASK (BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS) +#define BPF_TOKEN_CMDS_MASK ((1ULL << BPF_TOKEN_CREATE)) + +#define BPF_TOKEN_CREATE_LAST_FIELD token_create.allowed_cmds + +static int token_create(union bpf_attr *attr) +{ + struct bpf_token *new_token, *token = NULL; + u64 allowed_cmds; + int fd, err; + + if (CHECK_ATTR(BPF_TOKEN_CREATE)) + return -EINVAL; + + if (attr->token_create.flags & ~BPF_TOKEN_FLAGS_MASK) + return -EINVAL; + + if (attr->token_create.token_fd) { + token = bpf_token_get_from_fd(attr->token_create.token_fd); + if (IS_ERR(token)) + return PTR_ERR(token); + /* if provided BPF token doesn't allow creating new tokens, + * then use system-wide capability checks only + */ + if (!bpf_token_allow_cmd(token, BPF_TOKEN_CREATE)) { + bpf_token_put(token); + token = NULL; + } + } + + allowed_cmds = attr->token_create.allowed_cmds; + if (!(attr->token_create.flags & BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS) && + allowed_cmds & ~BPF_TOKEN_CMDS_MASK) { + err = -ENOTSUPP; + goto err_out; + } + + if (!bpf_token_capable(token, CAP_SYS_ADMIN)) { + err = -EPERM; + goto err_out; + } + + /* requested cmds should be a subset of associated token's set */ + if (token && (token->allowed_cmds & allowed_cmds) != allowed_cmds) { + err = -EPERM; + goto err_out; + } + + new_token = bpf_token_alloc(); + if (!new_token) { + err = -ENOMEM; + goto err_out; + } + + new_token->allowed_cmds = allowed_cmds & BPF_TOKEN_CMDS_MASK; + + fd = bpf_token_new_fd(new_token); + if (fd < 0) { + bpf_token_put(new_token); + err = fd; + goto err_out; + } + + bpf_token_put(token); + return fd; + +err_out: + bpf_token_put(token); + return err; +} + static int __sys_bpf(int cmd, bpfptr_t uattr, unsigned int size) { union bpf_attr attr; @@ -5172,6 +5243,9 @@ static int __sys_bpf(int cmd, bpfptr_t uattr, unsigned int size) case BPF_PROG_BIND_MAP: err = bpf_prog_bind_map(&attr); break; + case BPF_TOKEN_CREATE: + err = token_create(&attr); + break; default: err = -EINVAL; break; diff --git a/kernel/bpf/token.c b/kernel/bpf/token.c new file mode 100644 index 000000000000..7e989b25fa06 --- /dev/null +++ b/kernel/bpf/token.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +DEFINE_IDR(token_idr); +DEFINE_SPINLOCK(token_idr_lock); + +void bpf_token_inc(struct bpf_token *token) +{ + atomic64_inc(&token->refcnt); +} + +static void bpf_token_put_deferred(struct work_struct *work) +{ + struct bpf_token *token = container_of(work, struct bpf_token, work); + + kvfree(token); +} + +void bpf_token_put(struct bpf_token *token) +{ + if (!token) + return; + + if (!atomic64_dec_and_test(&token->refcnt)) + return; + + INIT_WORK(&token->work, bpf_token_put_deferred); + schedule_work(&token->work); +} + +static int bpf_token_release(struct inode *inode, struct file *filp) +{ + struct bpf_token *token = filp->private_data; + + bpf_token_put(token); + return 0; +} + +static ssize_t bpf_dummy_read(struct file *filp, char __user *buf, size_t siz, + loff_t *ppos) +{ + /* We need this handler such that alloc_file() enables + * f_mode with FMODE_CAN_READ. + */ + return -EINVAL; +} + +static ssize_t bpf_dummy_write(struct file *filp, const char __user *buf, + size_t siz, loff_t *ppos) +{ + /* We need this handler such that alloc_file() enables + * f_mode with FMODE_CAN_WRITE. + */ + return -EINVAL; +} + +static const struct file_operations bpf_token_fops = { + .release = bpf_token_release, + .read = bpf_dummy_read, + .write = bpf_dummy_write, +}; + +struct bpf_token *bpf_token_alloc(void) +{ + struct bpf_token *token; + + token = kvzalloc(sizeof(*token), GFP_USER); + if (token == NULL) + return NULL; + + atomic64_set(&token->refcnt, 1); + + return token; +} + +#define BPF_TOKEN_INODE_NAME "bpf-token" + +/* Alloc anon_inode and FD for prepared token. + * Returns fd >= 0 on success; negative error, otherwise. + */ +int bpf_token_new_fd(struct bpf_token *token) +{ + return anon_inode_getfd(BPF_TOKEN_INODE_NAME, &bpf_token_fops, token, O_CLOEXEC); +} + +struct bpf_token *bpf_token_get_from_fd(u32 ufd) +{ + struct fd f = fdget(ufd); + struct bpf_token *token; + + if (!f.file) + return ERR_PTR(-EBADF); + if (f.file->f_op != &bpf_token_fops) { + fdput(f); + return ERR_PTR(-EINVAL); + } + + token = f.file->private_data; + bpf_token_inc(token); + fdput(f); + + return token; +} + +bool bpf_token_allow_cmd(const struct bpf_token *token, enum bpf_cmd cmd) +{ + if (!token) + return false; + + return token->allowed_cmds & (1ULL << cmd); +} + +bool bpf_token_capable(const struct bpf_token *token, int cap) +{ + return token || capable(cap) || (cap != CAP_SYS_ADMIN && capable(CAP_SYS_ADMIN)); +} diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 9273c654743c..d1d7ca71756f 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -846,6 +846,16 @@ union bpf_iter_link_info { * Returns zero on success. On error, -1 is returned and *errno* * is set appropriately. * + * BPF_TOKEN_CREATE + * Description + * Create BPF token with embedded information about what + * BPF-related functionality is allowed. This BPF token can be + * passed as an extra parameter to various bpf() syscall command. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * * NOTES * eBPF objects (maps and programs) can be shared between processes. * @@ -900,6 +910,7 @@ enum bpf_cmd { BPF_ITER_CREATE, BPF_LINK_DETACH, BPF_PROG_BIND_MAP, + BPF_TOKEN_CREATE, }; enum bpf_map_type { @@ -1169,6 +1180,24 @@ enum bpf_link_type { */ #define BPF_F_KPROBE_MULTI_RETURN (1U << 0) +/* BPF_TOKEN_CREATE command flags + */ +enum { + /* Ignore unrecognized bits in token_create.allowed_cmds bit set. If + * this flag is set, kernel won't return -EINVAL for a bit + * corresponding to a non-existing command or the one that doesn't + * support BPF token passing. This flags allows application to request + * BPF token creation for a desired set of commands without worrying + * about older kernels not supporting some of the commands. + * Presumably, deployed applications will do separate feature + * detection and will avoid calling not-yet-supported bpf() commands, + * so this BPF token will work equally well both on older and newer + * kernels, even if some of the requested commands won't be BPF + * token-enabled. + */ + BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS = 1U << 0, +}; + /* When BPF ldimm64's insn[0].src_reg != 0 then this can have * the following extensions: * @@ -1621,6 +1650,17 @@ union bpf_attr { __u32 flags; /* extra flags */ } prog_bind_map; + struct { /* struct used by BPF_TOKEN_CREATE command */ + __u32 flags; + __u32 token_fd; + /* a bit set of allowed bpf() syscall commands, + * e.g., (1ULL << BPF_TOKEN_CREATE) | (1ULL << BPF_PROG_LOAD) + * will allow creating derived BPF tokens and loading new BPF + * programs + */ + __u64 allowed_cmds; + } token_create; + } __attribute__((aligned(8))); /* The description below is an attempt at providing documentation to eBPF From patchwork Fri Jun 2 14:59:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265458 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C55AAC7EE29 for ; Fri, 2 Jun 2023 15:01:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235874AbjFBPBE convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53802 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236346AbjFBPAx (ORCPT ); Fri, 2 Jun 2023 11:00:53 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 944DAE78 for ; Fri, 2 Jun 2023 08:00:42 -0700 (PDT) Received: from pps.filterd (m0109333.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 352AeA9a001123 for ; Fri, 2 Jun 2023 08:00:42 -0700 Received: from mail.thefacebook.com ([163.114.132.120]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qxxpa8r38-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:42 -0700 Received: from twshared52232.38.frc1.facebook.com (2620:10d:c085:208::f) by mail.thefacebook.com (2620:10d:c085:11d::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:23 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 2D82431E047DD; Fri, 2 Jun 2023 08:00:15 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 02/18] libbpf: add bpf_token_create() API Date: Fri, 2 Jun 2023 07:59:55 -0700 Message-ID: <20230602150011.1657856-3-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: u6Mmrp2BNIXwPNxpWkJ5PQvBRpesx1Yx X-Proofpoint-ORIG-GUID: u6Mmrp2BNIXwPNxpWkJ5PQvBRpesx1Yx X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Add low-level wrapper API for BPF_TOKEN_CREATE command in bpf() syscall. Signed-off-by: Andrii Nakryiko --- tools/lib/bpf/bpf.c | 18 ++++++++++++++++++ tools/lib/bpf/bpf.h | 11 +++++++++++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 30 insertions(+) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index ed86b37d8024..38be66719485 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -1201,3 +1201,21 @@ int bpf_prog_bind_map(int prog_fd, int map_fd, ret = sys_bpf(BPF_PROG_BIND_MAP, &attr, attr_sz); return libbpf_err_errno(ret); } + +int bpf_token_create(struct bpf_token_create_opts *opts) +{ + const size_t attr_sz = offsetofend(union bpf_attr, token_create); + union bpf_attr attr; + int ret; + + if (!OPTS_VALID(opts, bpf_token_create_opts)) + return libbpf_err(-EINVAL); + + memset(&attr, 0, attr_sz); + attr.token_create.flags = OPTS_GET(opts, flags, 0); + attr.token_create.token_fd = OPTS_GET(opts, token_fd, 0); + attr.token_create.allowed_cmds = OPTS_GET(opts, allowed_cmds, 0); + + ret = sys_bpf_fd(BPF_TOKEN_CREATE, &attr, attr_sz); + return libbpf_err_errno(ret); +} diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 9aa0ee473754..f2b8041ca27a 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -551,6 +551,17 @@ struct bpf_test_run_opts { LIBBPF_API int bpf_prog_test_run_opts(int prog_fd, struct bpf_test_run_opts *opts); +struct bpf_token_create_opts { + size_t sz; /* size of this struct for forward/backward compatibility */ + __u32 flags; + __u32 token_fd; + __u64 allowed_cmds; + size_t :0; +}; +#define bpf_token_create_opts__last_field allowed_cmds + +LIBBPF_API int bpf_token_create(struct bpf_token_create_opts *opts); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 7521a2fb7626..62cbe4775081 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -395,4 +395,5 @@ LIBBPF_1.2.0 { LIBBPF_1.3.0 { global: bpf_obj_pin_opts; + bpf_token_create; } LIBBPF_1.2.0; From patchwork Fri Jun 2 14:59:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265471 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 523DBC7EE29 for ; Fri, 2 Jun 2023 15:02:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234989AbjFBPCD convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:02:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53398 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236413AbjFBPBZ (ORCPT ); Fri, 2 Jun 2023 11:01:25 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DD25110D0 for ; Fri, 2 Jun 2023 08:01:18 -0700 (PDT) Received: from pps.filterd (m0044012.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 352BuSHF020100 for ; Fri, 2 Jun 2023 08:01:18 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qxcvfnpj8-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:01:01 -0700 Received: from ash-exhub204.TheFacebook.com (2620:10d:c0a8:83::4) by ash-exhub201.TheFacebook.com (2620:10d:c0a8:83::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:24 -0700 Received: from twshared40933.03.prn6.facebook.com (2620:10d:c0a8:1c::1b) by mail.thefacebook.com (2620:10d:c0a8:83::4) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:23 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 3A31B31E04817; Fri, 2 Jun 2023 08:00:18 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 03/18] selftests/bpf: add BPF_TOKEN_CREATE test Date: Fri, 2 Jun 2023 07:59:56 -0700 Message-ID: <20230602150011.1657856-4-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-ORIG-GUID: vO8Ey1xfI7qu2HOW2xEuEVlXmd9PzOGX X-Proofpoint-GUID: vO8Ey1xfI7qu2HOW2xEuEVlXmd9PzOGX X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Add a subtest validating BPF_TOKEN_CREATE command, pinning/getting BPF token in/from BPF FS, and creating derived BPF tokens using token_fd parameter. Signed-off-by: Andrii Nakryiko --- .../testing/selftests/bpf/prog_tests/token.c | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/token.c diff --git a/tools/testing/selftests/bpf/prog_tests/token.c b/tools/testing/selftests/bpf/prog_tests/token.c new file mode 100644 index 000000000000..fe78b558d697 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/token.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ +#include "linux/bpf.h" +#include +#include +#include "cap_helpers.h" + +static int drop_priv_caps(__u64 *old_caps) +{ + return cap_disable_effective((1ULL << CAP_BPF) | + (1ULL << CAP_PERFMON) | + (1ULL << CAP_NET_ADMIN) | + (1ULL << CAP_SYS_ADMIN), old_caps); +} + +static int restore_priv_caps(__u64 old_caps) +{ + return cap_enable_effective(old_caps, NULL); +} + +#define TOKEN_PATH "/sys/fs/bpf/test_token" + +static void subtest_token_create(void) +{ + LIBBPF_OPTS(bpf_token_create_opts, opts); + int token_fd = 0, limited_token_fd = 0, tmp_fd = 0, err; + __u64 old_caps = 0; + + /* create BPF token which allows creating derived BPF tokens */ + opts.allowed_cmds = 1ULL << BPF_TOKEN_CREATE; + token_fd = bpf_token_create(&opts); + if (!ASSERT_GT(token_fd, 0, "token_create")) + return; + + /* check that IGNORE_UNKNOWN_CMDS flag is respected */ + opts.flags = BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS; + opts.allowed_cmds = ~0ULL; /* any current and future cmd is allowed */ + tmp_fd = bpf_token_create(&opts); + if (!ASSERT_GT(token_fd, 0, "token_create_future_proof")) + return; + close(tmp_fd); + tmp_fd = 0; + + /* validate pinning and getting works as expected */ + err = bpf_obj_pin(token_fd, TOKEN_PATH); + if (!ASSERT_OK(err, "token_pin")) + goto cleanup; + + tmp_fd = bpf_obj_get(TOKEN_PATH); + ASSERT_GT(tmp_fd, 0, "token_get"); + close(tmp_fd); + tmp_fd = 0; + unlink(TOKEN_PATH); + + /* drop privileges to test token_fd passing */ + if (!ASSERT_OK(drop_priv_caps(&old_caps), "drop_caps")) + goto cleanup; + + /* unprivileged BPF_TOKEN_CREATE should fail */ + tmp_fd = bpf_token_create(NULL); + if (!ASSERT_LT(tmp_fd, 0, "token_create_unpriv_fail")) + goto cleanup; + + /* unprivileged BPF_TOKEN_CREATE with associated BPF token succeeds */ + opts.flags = 0; + opts.allowed_cmds = 0; /* ask for BPF token which doesn't allow new tokens */ + opts.token_fd = token_fd; + limited_token_fd = bpf_token_create(&opts); + if (!ASSERT_GT(limited_token_fd, 0, "token_create_limited")) + goto cleanup; + + /* creating yet another token using "limited" BPF token should fail */ + opts.flags = 0; + opts.allowed_cmds = 0; + opts.token_fd = limited_token_fd; + tmp_fd = bpf_token_create(&opts); + if (!ASSERT_LT(tmp_fd, 0, "token_create_from_lim_fail")) + goto cleanup; + +cleanup: + if (tmp_fd) + close(tmp_fd); + if (token_fd) + close(token_fd); + if (limited_token_fd) + close(limited_token_fd); + if (old_caps) + ASSERT_OK(restore_priv_caps(old_caps), "restore_caps"); +} + +void test_token(void) +{ + if (test__start_subtest("token_create")) + subtest_token_create(); +} From patchwork Fri Jun 2 14:59:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265455 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 82F6FC7EE29 for ; Fri, 2 Jun 2023 15:00:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236376AbjFBPAx convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:00:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53366 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236347AbjFBPAe (ORCPT ); Fri, 2 Jun 2023 11:00:34 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 19313E43 for ; Fri, 2 Jun 2023 08:00:31 -0700 (PDT) Received: from pps.filterd (m0148461.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 3528jSis021504 for ; Fri, 2 Jun 2023 08:00:30 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qyd6ptc95-5 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:30 -0700 Received: from twshared25760.37.frc1.facebook.com (2620:10d:c0a8:1b::2d) by mail.thefacebook.com (2620:10d:c0a8:83::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:27 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 488ED31E0484F; Fri, 2 Jun 2023 08:00:20 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 04/18] bpf: move unprivileged checks into map_create() and bpf_prog_load() Date: Fri, 2 Jun 2023 07:59:57 -0700 Message-ID: <20230602150011.1657856-5-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: 2VOqSPE9_9TPXn6rAWbxpXVMGF77vY7v X-Proofpoint-ORIG-GUID: 2VOqSPE9_9TPXn6rAWbxpXVMGF77vY7v X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Make each bpf() syscall command a bit more self-contained, making it easier to further enhance it. We move sysctl_unprivileged_bpf_disabled handling down to map_create() and bpf_prog_load(), two special commands in this regard. Also swap the order of checks, calling bpf_capable() only if sysctl_unprivileged_bpf_disabled is true, avoiding unnecessary audit messages. Signed-off-by: Andrii Nakryiko --- kernel/bpf/syscall.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index edafb0f3053f..4c9e79ec40e2 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1157,6 +1157,15 @@ static int map_create(union bpf_attr *attr) !node_online(numa_node))) return -EINVAL; + /* Intent here is for unprivileged_bpf_disabled to block BPF map + * creation for unprivileged users; other actions depend + * on fd availability and access to bpffs, so are dependent on + * object creation success. Even with unprivileged BPF disabled, + * capability checks are still carried out. + */ + if (sysctl_unprivileged_bpf_disabled && !bpf_capable()) + return -EPERM; + /* find map type and init map: hashtable vs rbtree vs bloom vs ... */ map = find_and_alloc_map(attr); if (IS_ERR(map)) @@ -2532,6 +2541,16 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) /* eBPF programs must be GPL compatible to use GPL-ed functions */ is_gpl = license_is_gpl_compatible(license); + /* Intent here is for unprivileged_bpf_disabled to block BPF program + * creation for unprivileged users; other actions depend + * on fd availability and access to bpffs, so are dependent on + * object creation success. Even with unprivileged BPF disabled, + * capability checks are still carried out for these + * and other operations. + */ + if (sysctl_unprivileged_bpf_disabled && !bpf_capable()) + return -EPERM; + if (attr->insn_cnt == 0 || attr->insn_cnt > (bpf_capable() ? BPF_COMPLEXITY_LIMIT_INSNS : BPF_MAXINSNS)) return -E2BIG; @@ -5098,23 +5117,8 @@ static int token_create(union bpf_attr *attr) static int __sys_bpf(int cmd, bpfptr_t uattr, unsigned int size) { union bpf_attr attr; - bool capable; int err; - capable = bpf_capable() || !sysctl_unprivileged_bpf_disabled; - - /* Intent here is for unprivileged_bpf_disabled to block key object - * creation commands for unprivileged users; other actions depend - * of fd availability and access to bpffs, so are dependent on - * object creation success. Capabilities are later verified for - * operations such as load and map create, so even with unprivileged - * BPF disabled, capability checks are still carried out for these - * and other operations. - */ - if (!capable && - (cmd == BPF_MAP_CREATE || cmd == BPF_PROG_LOAD)) - return -EPERM; - err = bpf_check_uarg_tail_zero(uattr, sizeof(attr), size); if (err) return err; From patchwork Fri Jun 2 14:59:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265469 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DBB38C7EE29 for ; Fri, 2 Jun 2023 15:01:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236378AbjFBPBx convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54184 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236397AbjFBPBU (ORCPT ); Fri, 2 Jun 2023 11:01:20 -0400 Received: from mx0b-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 91D6BE71 for ; Fri, 2 Jun 2023 08:01:10 -0700 (PDT) Received: from pps.filterd (m0109332.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 3527XcKt003394 for ; Fri, 2 Jun 2023 08:01:09 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qyc4yap3t-4 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:01:09 -0700 Received: from twshared4466.29.prn2.facebook.com (2620:10d:c0a8:1c::1b) by mail.thefacebook.com (2620:10d:c0a8:82::f) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:26 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 56F3A31E04874; Fri, 2 Jun 2023 08:00:22 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 05/18] bpf: inline map creation logic in map_create() function Date: Fri, 2 Jun 2023 07:59:58 -0700 Message-ID: <20230602150011.1657856-6-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-ORIG-GUID: IUvPE4g3lPFFId2OXmIDYTb9apf_rWO4 X-Proofpoint-GUID: IUvPE4g3lPFFId2OXmIDYTb9apf_rWO4 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Currently find_and_alloc_map() performs two separate functions: some argument sanity checking and partial map creation workflow hanling. Neither of those functions are self-sufficient and are augmented by further checks and initialization logic in the caller (map_create() function). So unify all the sanity checks, permission checks, and creation and initialization logic in one linear piece of code in map_create() instead. This also make it easier to further enhance permission checks and keep them located in one place. Signed-off-by: Andrii Nakryiko --- kernel/bpf/syscall.c | 57 +++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 4c9e79ec40e2..cd68c57c0689 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -109,37 +109,6 @@ const struct bpf_map_ops bpf_map_offload_ops = { .map_mem_usage = bpf_map_offload_map_mem_usage, }; -static struct bpf_map *find_and_alloc_map(union bpf_attr *attr) -{ - const struct bpf_map_ops *ops; - u32 type = attr->map_type; - struct bpf_map *map; - int err; - - if (type >= ARRAY_SIZE(bpf_map_types)) - return ERR_PTR(-EINVAL); - type = array_index_nospec(type, ARRAY_SIZE(bpf_map_types)); - ops = bpf_map_types[type]; - if (!ops) - return ERR_PTR(-EINVAL); - - if (ops->map_alloc_check) { - err = ops->map_alloc_check(attr); - if (err) - return ERR_PTR(err); - } - if (attr->map_ifindex) - ops = &bpf_map_offload_ops; - if (!ops->map_mem_usage) - return ERR_PTR(-EINVAL); - map = ops->map_alloc(attr); - if (IS_ERR(map)) - return map; - map->ops = ops; - map->map_type = type; - return map; -} - static void bpf_map_write_active_inc(struct bpf_map *map) { atomic64_inc(&map->writecnt); @@ -1127,7 +1096,9 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, /* called via syscall */ static int map_create(union bpf_attr *attr) { + const struct bpf_map_ops *ops; int numa_node = bpf_map_attr_numa_node(attr); + u32 map_type = attr->map_type; struct bpf_map *map; int f_flags; int err; @@ -1157,6 +1128,25 @@ static int map_create(union bpf_attr *attr) !node_online(numa_node))) return -EINVAL; + /* find map type and init map: hashtable vs rbtree vs bloom vs ... */ + map_type = attr->map_type; + if (map_type >= ARRAY_SIZE(bpf_map_types)) + return -EINVAL; + map_type = array_index_nospec(map_type, ARRAY_SIZE(bpf_map_types)); + ops = bpf_map_types[map_type]; + if (!ops) + return -EINVAL; + + if (ops->map_alloc_check) { + err = ops->map_alloc_check(attr); + if (err) + return err; + } + if (attr->map_ifindex) + ops = &bpf_map_offload_ops; + if (!ops->map_mem_usage) + return -EINVAL; + /* Intent here is for unprivileged_bpf_disabled to block BPF map * creation for unprivileged users; other actions depend * on fd availability and access to bpffs, so are dependent on @@ -1166,10 +1156,11 @@ static int map_create(union bpf_attr *attr) if (sysctl_unprivileged_bpf_disabled && !bpf_capable()) return -EPERM; - /* find map type and init map: hashtable vs rbtree vs bloom vs ... */ - map = find_and_alloc_map(attr); + map = ops->map_alloc(attr); if (IS_ERR(map)) return PTR_ERR(map); + map->ops = ops; + map->map_type = map_type; err = bpf_obj_name_cpy(map->name, attr->map_name, sizeof(attr->map_name)); From patchwork Fri Jun 2 14:59:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265467 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8FDA1C7EE2E for ; Fri, 2 Jun 2023 15:01:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236332AbjFBPBj convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:39 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54002 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236372AbjFBPBI (ORCPT ); Fri, 2 Jun 2023 11:01:08 -0400 Received: from mx0a-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A740AE5D for ; Fri, 2 Jun 2023 08:01:05 -0700 (PDT) Received: from pps.filterd (m0001303.ppops.net [127.0.0.1]) by m0001303.ppops.net (8.17.1.19/8.17.1.19) with ESMTP id 3527V8DI028611 for ; Fri, 2 Jun 2023 08:01:04 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by m0001303.ppops.net (PPS) with ESMTPS id 3qyc3papkt-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:01:04 -0700 Received: from twshared68078.02.prn6.facebook.com (2620:10d:c0a8:1c::1b) by mail.thefacebook.com (2620:10d:c0a8:82::d) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:30 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 6148131E0489D; Fri, 2 Jun 2023 08:00:24 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 06/18] bpf: centralize permissions checks for all BPF map types Date: Fri, 2 Jun 2023 07:59:59 -0700 Message-ID: <20230602150011.1657856-7-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: kzDOswOTDphxAezpiFRTxVey36vglGKC X-Proofpoint-ORIG-GUID: kzDOswOTDphxAezpiFRTxVey36vglGKC X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: This allows to do more centralized decisions later on, and generally makes it very explicit which maps are privileged and which are not (e.g., LRU_HASH and LRU_PERCPU_HASH, which are privileged HASH variants, as opposed to unprivileged HASH and HASH_PERCPU; now this is explicit and easy to verify). Signed-off-by: Andrii Nakryiko --- kernel/bpf/bloom_filter.c | 3 -- kernel/bpf/bpf_local_storage.c | 3 -- kernel/bpf/bpf_struct_ops.c | 3 -- kernel/bpf/cpumap.c | 4 -- kernel/bpf/devmap.c | 3 -- kernel/bpf/hashtab.c | 6 --- kernel/bpf/lpm_trie.c | 3 -- kernel/bpf/queue_stack_maps.c | 4 -- kernel/bpf/reuseport_array.c | 3 -- kernel/bpf/stackmap.c | 3 -- kernel/bpf/syscall.c | 47 +++++++++++++++++++ net/core/sock_map.c | 4 -- net/xdp/xskmap.c | 4 -- .../bpf/prog_tests/unpriv_bpf_disabled.c | 6 ++- 14 files changed, 52 insertions(+), 44 deletions(-) diff --git a/kernel/bpf/bloom_filter.c b/kernel/bpf/bloom_filter.c index 540331b610a9..addf3dd57b59 100644 --- a/kernel/bpf/bloom_filter.c +++ b/kernel/bpf/bloom_filter.c @@ -86,9 +86,6 @@ static struct bpf_map *bloom_map_alloc(union bpf_attr *attr) int numa_node = bpf_map_attr_numa_node(attr); struct bpf_bloom_filter *bloom; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - if (attr->key_size != 0 || attr->value_size == 0 || attr->max_entries == 0 || attr->map_flags & ~BLOOM_CREATE_FLAG_MASK || diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c index 47d9948d768f..b5149cfce7d4 100644 --- a/kernel/bpf/bpf_local_storage.c +++ b/kernel/bpf/bpf_local_storage.c @@ -723,9 +723,6 @@ int bpf_local_storage_map_alloc_check(union bpf_attr *attr) !attr->btf_key_type_id || !attr->btf_value_type_id) return -EINVAL; - if (!bpf_capable()) - return -EPERM; - if (attr->value_size > BPF_LOCAL_STORAGE_MAX_VALUE_SIZE) return -E2BIG; diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c index d3f0a4825fa6..116a0ce378ec 100644 --- a/kernel/bpf/bpf_struct_ops.c +++ b/kernel/bpf/bpf_struct_ops.c @@ -655,9 +655,6 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) const struct btf_type *t, *vt; struct bpf_map *map; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - st_ops = bpf_struct_ops_find_value(attr->btf_vmlinux_value_type_id); if (!st_ops) return ERR_PTR(-ENOTSUPP); diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index 8ec18faa74ac..8a33e8747a0e 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -89,9 +88,6 @@ static struct bpf_map *cpu_map_alloc(union bpf_attr *attr) u32 value_size = attr->value_size; struct bpf_cpu_map *cmap; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 4 || (value_size != offsetofend(struct bpf_cpumap_val, qsize) && diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index 802692fa3905..49cc0b5671c6 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -160,9 +160,6 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr) struct bpf_dtab *dtab; int err; - if (!capable(CAP_NET_ADMIN)) - return ERR_PTR(-EPERM); - dtab = bpf_map_area_alloc(sizeof(*dtab), NUMA_NO_NODE); if (!dtab) return ERR_PTR(-ENOMEM); diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 9901efee4339..56d3da7d0bc6 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -422,12 +422,6 @@ static int htab_map_alloc_check(union bpf_attr *attr) BUILD_BUG_ON(offsetof(struct htab_elem, fnode.next) != offsetof(struct htab_elem, hash_node.pprev)); - if (lru && !bpf_capable()) - /* LRU implementation is much complicated than other - * maps. Hence, limit to CAP_BPF. - */ - return -EPERM; - if (zero_seed && !capable(CAP_SYS_ADMIN)) /* Guard against local DoS, and discourage production use. */ return -EPERM; diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c index e0d3ddf2037a..17c7e7782a1f 100644 --- a/kernel/bpf/lpm_trie.c +++ b/kernel/bpf/lpm_trie.c @@ -544,9 +544,6 @@ static struct bpf_map *trie_alloc(union bpf_attr *attr) { struct lpm_trie *trie; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - /* check sanity of attributes */ if (attr->max_entries == 0 || !(attr->map_flags & BPF_F_NO_PREALLOC) || diff --git a/kernel/bpf/queue_stack_maps.c b/kernel/bpf/queue_stack_maps.c index 601609164ef3..8d2ddcb7566b 100644 --- a/kernel/bpf/queue_stack_maps.c +++ b/kernel/bpf/queue_stack_maps.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include "percpu_freelist.h" @@ -46,9 +45,6 @@ static bool queue_stack_map_is_full(struct bpf_queue_stack *qs) /* Called from syscall */ static int queue_stack_map_alloc_check(union bpf_attr *attr) { - if (!bpf_capable()) - return -EPERM; - /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 0 || attr->value_size == 0 || diff --git a/kernel/bpf/reuseport_array.c b/kernel/bpf/reuseport_array.c index cbf2d8d784b8..4b4f9670f1a9 100644 --- a/kernel/bpf/reuseport_array.c +++ b/kernel/bpf/reuseport_array.c @@ -151,9 +151,6 @@ static struct bpf_map *reuseport_array_alloc(union bpf_attr *attr) int numa_node = bpf_map_attr_numa_node(attr); struct reuseport_array *array; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - /* allocate all map elements and zero-initialize them */ array = bpf_map_area_alloc(struct_size(array, ptrs, attr->max_entries), numa_node); if (!array) diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index b25fce425b2c..458bb80b14d5 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -74,9 +74,6 @@ static struct bpf_map *stack_map_alloc(union bpf_attr *attr) u64 cost, n_buckets; int err; - if (!bpf_capable()) - return ERR_PTR(-EPERM); - if (attr->map_flags & ~STACK_CREATE_FLAG_MASK) return ERR_PTR(-EINVAL); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index cd68c57c0689..6e7ccbd54524 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1156,6 +1156,53 @@ static int map_create(union bpf_attr *attr) if (sysctl_unprivileged_bpf_disabled && !bpf_capable()) return -EPERM; + /* check privileged map type permissions */ + switch (map_type) { + case BPF_MAP_TYPE_ARRAY: + case BPF_MAP_TYPE_PERCPU_ARRAY: + case BPF_MAP_TYPE_PROG_ARRAY: + case BPF_MAP_TYPE_PERF_EVENT_ARRAY: + case BPF_MAP_TYPE_CGROUP_ARRAY: + case BPF_MAP_TYPE_ARRAY_OF_MAPS: + case BPF_MAP_TYPE_HASH: + case BPF_MAP_TYPE_PERCPU_HASH: + case BPF_MAP_TYPE_HASH_OF_MAPS: + case BPF_MAP_TYPE_RINGBUF: + case BPF_MAP_TYPE_USER_RINGBUF: + case BPF_MAP_TYPE_CGROUP_STORAGE: + case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE: + /* unprivileged */ + break; + case BPF_MAP_TYPE_SK_STORAGE: + case BPF_MAP_TYPE_INODE_STORAGE: + case BPF_MAP_TYPE_TASK_STORAGE: + case BPF_MAP_TYPE_CGRP_STORAGE: + case BPF_MAP_TYPE_BLOOM_FILTER: + case BPF_MAP_TYPE_LPM_TRIE: + case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY: + case BPF_MAP_TYPE_STACK_TRACE: + case BPF_MAP_TYPE_QUEUE: + case BPF_MAP_TYPE_STACK: + case BPF_MAP_TYPE_LRU_HASH: + case BPF_MAP_TYPE_LRU_PERCPU_HASH: + case BPF_MAP_TYPE_STRUCT_OPS: + case BPF_MAP_TYPE_CPUMAP: + if (!bpf_capable()) + return -EPERM; + break; + case BPF_MAP_TYPE_SOCKMAP: + case BPF_MAP_TYPE_SOCKHASH: + case BPF_MAP_TYPE_DEVMAP: + case BPF_MAP_TYPE_DEVMAP_HASH: + case BPF_MAP_TYPE_XSKMAP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + break; + default: + WARN(1, "unsupported map type %d", map_type); + return -EPERM; + } + map = ops->map_alloc(attr); if (IS_ERR(map)) return PTR_ERR(map); diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 00afb66cd095..19538d628714 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -32,8 +32,6 @@ static struct bpf_map *sock_map_alloc(union bpf_attr *attr) { struct bpf_stab *stab; - if (!capable(CAP_NET_ADMIN)) - return ERR_PTR(-EPERM); if (attr->max_entries == 0 || attr->key_size != 4 || (attr->value_size != sizeof(u32) && @@ -1085,8 +1083,6 @@ static struct bpf_map *sock_hash_alloc(union bpf_attr *attr) struct bpf_shtab *htab; int i, err; - if (!capable(CAP_NET_ADMIN)) - return ERR_PTR(-EPERM); if (attr->max_entries == 0 || attr->key_size == 0 || (attr->value_size != sizeof(u32) && diff --git a/net/xdp/xskmap.c b/net/xdp/xskmap.c index 2c1427074a3b..e1c526f97ce3 100644 --- a/net/xdp/xskmap.c +++ b/net/xdp/xskmap.c @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -68,9 +67,6 @@ static struct bpf_map *xsk_map_alloc(union bpf_attr *attr) int numa_node; u64 size; - if (!capable(CAP_NET_ADMIN)) - return ERR_PTR(-EPERM); - if (attr->max_entries == 0 || attr->key_size != 4 || attr->value_size != 4 || attr->map_flags & ~(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)) diff --git a/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c b/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c index 8383a99f610f..0adf8d9475cb 100644 --- a/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c +++ b/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c @@ -171,7 +171,11 @@ static void test_unpriv_bpf_disabled_negative(struct test_unpriv_bpf_disabled *s prog_insns, prog_insn_cnt, &load_opts), -EPERM, "prog_load_fails"); - for (i = BPF_MAP_TYPE_HASH; i <= BPF_MAP_TYPE_BLOOM_FILTER; i++) + /* some map types require particular correct parameters which could be + * sanity-checked before enforcing -EPERM, so only validate that + * the simple ARRAY and HASH maps are failing with -EPERM + */ + for (i = BPF_MAP_TYPE_HASH; i <= BPF_MAP_TYPE_ARRAY; i++) ASSERT_EQ(bpf_map_create(i, NULL, sizeof(int), sizeof(int), 1, NULL), -EPERM, "map_create_fails"); From patchwork Fri Jun 2 15:00:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265468 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 30CDDC7EE29 for ; Fri, 2 Jun 2023 15:01:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236333AbjFBPBk convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54030 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236377AbjFBPBJ (ORCPT ); Fri, 2 Jun 2023 11:01:09 -0400 Received: from mx0a-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 50E6DE44 for ; Fri, 2 Jun 2023 08:01:07 -0700 (PDT) Received: from pps.filterd (m0001303.ppops.net [127.0.0.1]) by m0001303.ppops.net (8.17.1.19/8.17.1.19) with ESMTP id 3527V8DK028611 for ; Fri, 2 Jun 2023 08:01:06 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by m0001303.ppops.net (PPS) with ESMTPS id 3qyc3papkt-3 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:01:05 -0700 Received: from ash-exhub204.TheFacebook.com (2620:10d:c0a8:83::4) by ash-exhub104.TheFacebook.com (2620:10d:c0a8:82::d) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:39 -0700 Received: from twshared40933.03.prn6.facebook.com (2620:10d:c0a8:1c::1b) by mail.thefacebook.com (2620:10d:c0a8:83::4) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:38 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 6DA0231E048C5; Fri, 2 Jun 2023 08:00:26 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 07/18] bpf: add BPF token support to BPF_MAP_CREATE command Date: Fri, 2 Jun 2023 08:00:00 -0700 Message-ID: <20230602150011.1657856-8-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: 0PwlhmSfg0_A0jETJnWx6bNTNMQdzP_J X-Proofpoint-ORIG-GUID: 0PwlhmSfg0_A0jETJnWx6bNTNMQdzP_J X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Allow providing token_fd for BPF_MAP_CREATE command to allow controlled BPF map creation from unprivileged process through delegated BPF token. Further, add a filter of allowed BPF map types to BPF token, specified at BPF token creation time. This, in combination with allowed_cmds allows to create a narrowly-focused BPF token (controlled by privileged agent) with a restrictive set of BPF maps that application can attempt to create. BPF_F_TOKEN_IGNORE_UNKNOWN_MAP_TYPES flag allows application to express "any supported BPF map type" without having to do an elaborate feature detection of each supported BPF map. This is a very untrivial process, especially as some BPF maps have special requirements just to be able to instantiate a minimal instance (e.g., custom BTF). Allowing application to just specify ~0 as a bit set makes writing application much simple without degrading any of kernel's safety of backwards compatibility concerns. Signed-off-by: Andrii Nakryiko --- include/linux/bpf.h | 3 + include/uapi/linux/bpf.h | 12 +++ kernel/bpf/syscall.c | 82 +++++++++++++++---- kernel/bpf/token.c | 8 ++ tools/include/uapi/linux/bpf.h | 16 +++- .../selftests/bpf/prog_tests/libbpf_str.c | 3 + 6 files changed, 108 insertions(+), 16 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index fe6d51c3a5b1..657bec546351 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -251,6 +251,7 @@ struct bpf_map { u32 btf_value_type_id; u32 btf_vmlinux_value_type_id; struct btf *btf; + struct bpf_token *token; #ifdef CONFIG_MEMCG_KMEM struct obj_cgroup *objcg; #endif @@ -1538,6 +1539,7 @@ struct bpf_token { struct work_struct work; atomic64_t refcnt; u64 allowed_cmds; + u64 allowed_map_types; }; struct bpf_struct_ops_value; @@ -2092,6 +2094,7 @@ struct bpf_token *bpf_token_get_from_fd(u32 ufd); bool bpf_token_capable(const struct bpf_token *token, int cap); bool bpf_token_allow_cmd(const struct bpf_token *token, enum bpf_cmd cmd); +bool bpf_token_allow_map_type(const struct bpf_token *token, enum bpf_map_type type); int bpf_obj_pin_user(u32 ufd, int path_fd, const char __user *pathname); int bpf_obj_get_user(int path_fd, const char __user *pathname, int flags); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 01ab79f2ad9f..7cfaa2da84ee 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -954,6 +954,7 @@ enum bpf_map_type { BPF_MAP_TYPE_BLOOM_FILTER, BPF_MAP_TYPE_USER_RINGBUF, BPF_MAP_TYPE_CGRP_STORAGE, + __MAX_BPF_MAP_TYPE }; /* Note that tracing related programs such as @@ -1196,6 +1197,10 @@ enum { * token-enabled. */ BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS = 1U << 0, + /* Similar to BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS flag, but for + * token_create.allowed_map_types bit set. + */ + BPF_F_TOKEN_IGNORE_UNKNOWN_MAP_TYPES = 1U << 1, }; /* When BPF ldimm64's insn[0].src_reg != 0 then this can have @@ -1377,6 +1382,7 @@ union bpf_attr { * to using 5 hash functions). */ __u64 map_extra; + __u32 map_token_fd; }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ @@ -1661,6 +1667,12 @@ union bpf_attr { * validity checking of this set */ __u64 allowed_cmds; + /* similarly to allowed_cmds, a bit set of BPF map types that + * are allowed to be created by requested BPF token; + * see also BPF_F_TOKEN_IGNORE_UNKNOWN_MAP_TYPES for its + * effect on validity checking of this set + */ + __u64 allowed_map_types; } token_create; } __attribute__((aligned(8))); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 6e7ccbd54524..eb77ba71fbcf 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -691,6 +691,7 @@ static void bpf_map_free_deferred(struct work_struct *work) { struct bpf_map *map = container_of(work, struct bpf_map, work); struct btf_record *rec = map->record; + struct bpf_token *token = map->token; security_bpf_map_free(map); bpf_map_release_memcg(map); @@ -706,6 +707,7 @@ static void bpf_map_free_deferred(struct work_struct *work) * template bpf_map struct used during verification. */ btf_record_free(rec); + bpf_token_put(token); } static void bpf_map_put_uref(struct bpf_map *map) @@ -1010,7 +1012,7 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, if (!IS_ERR_OR_NULL(map->record)) { int i; - if (!bpf_capable()) { + if (!bpf_token_capable(map->token, CAP_BPF)) { ret = -EPERM; goto free_map_tab; } @@ -1092,11 +1094,12 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf, return ret; } -#define BPF_MAP_CREATE_LAST_FIELD map_extra +#define BPF_MAP_CREATE_LAST_FIELD map_token_fd /* called via syscall */ static int map_create(union bpf_attr *attr) { const struct bpf_map_ops *ops; + struct bpf_token *token = NULL; int numa_node = bpf_map_attr_numa_node(attr); u32 map_type = attr->map_type; struct bpf_map *map; @@ -1147,14 +1150,32 @@ static int map_create(union bpf_attr *attr) if (!ops->map_mem_usage) return -EINVAL; + if (attr->map_token_fd) { + token = bpf_token_get_from_fd(attr->map_token_fd); + if (IS_ERR(token)) + return PTR_ERR(token); + + /* if current token doesn't grant map creation permissions, + * then we can't use this token, so ignore it and rely on + * system-wide capabilities checks + */ + if (!bpf_token_allow_cmd(token, BPF_MAP_CREATE) || + !bpf_token_allow_map_type(token, attr->map_type)) { + bpf_token_put(token); + token = NULL; + } + } + + err = -EPERM; + /* Intent here is for unprivileged_bpf_disabled to block BPF map * creation for unprivileged users; other actions depend * on fd availability and access to bpffs, so are dependent on * object creation success. Even with unprivileged BPF disabled, * capability checks are still carried out. */ - if (sysctl_unprivileged_bpf_disabled && !bpf_capable()) - return -EPERM; + if (sysctl_unprivileged_bpf_disabled && !bpf_token_capable(token, CAP_BPF)) + goto put_token; /* check privileged map type permissions */ switch (map_type) { @@ -1187,28 +1208,36 @@ static int map_create(union bpf_attr *attr) case BPF_MAP_TYPE_LRU_PERCPU_HASH: case BPF_MAP_TYPE_STRUCT_OPS: case BPF_MAP_TYPE_CPUMAP: - if (!bpf_capable()) - return -EPERM; + if (!bpf_token_capable(token, CAP_BPF)) + goto put_token; break; case BPF_MAP_TYPE_SOCKMAP: case BPF_MAP_TYPE_SOCKHASH: case BPF_MAP_TYPE_DEVMAP: case BPF_MAP_TYPE_DEVMAP_HASH: case BPF_MAP_TYPE_XSKMAP: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; + if (!bpf_token_capable(token, CAP_NET_ADMIN)) + goto put_token; break; default: WARN(1, "unsupported map type %d", map_type); - return -EPERM; + goto put_token; } map = ops->map_alloc(attr); - if (IS_ERR(map)) - return PTR_ERR(map); + if (IS_ERR(map)) { + err = PTR_ERR(map); + goto put_token; + } map->ops = ops; map->map_type = map_type; + if (token) { + /* move token reference into map->token, reuse our refcnt */ + map->token = token; + token = NULL; + } + err = bpf_obj_name_cpy(map->name, attr->map_name, sizeof(attr->map_name)); if (err < 0) @@ -1281,8 +1310,11 @@ static int map_create(union bpf_attr *attr) free_map_sec: security_bpf_map_free(map); free_map: + bpf_token_put(map->token); btf_put(map->btf); map->ops->map_free(map); +put_token: + bpf_token_put(token); return err; } @@ -5081,15 +5113,23 @@ static int bpf_prog_bind_map(union bpf_attr *attr) return ret; } -#define BPF_TOKEN_FLAGS_MASK (BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS) -#define BPF_TOKEN_CMDS_MASK ((1ULL << BPF_TOKEN_CREATE)) +#define BPF_TOKEN_FLAGS_MASK ( \ + BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS \ + | BPF_F_TOKEN_IGNORE_UNKNOWN_MAP_TYPES \ +) +#define BPF_TOKEN_CMDS_MASK ( \ + (1ULL << BPF_TOKEN_CREATE) \ + | (1ULL << BPF_MAP_CREATE) \ +) +#define BPF_TOKEN_MAP_TYPES_MASK \ + ((BIT_ULL(__MAX_BPF_MAP_TYPE) - 1) & ~BIT_ULL(BPF_MAP_TYPE_UNSPEC)) -#define BPF_TOKEN_CREATE_LAST_FIELD token_create.allowed_cmds +#define BPF_TOKEN_CREATE_LAST_FIELD token_create.allowed_map_types static int token_create(union bpf_attr *attr) { struct bpf_token *new_token, *token = NULL; - u64 allowed_cmds; + u64 allowed_cmds, allowed_map_types; int fd, err; if (CHECK_ATTR(BPF_TOKEN_CREATE)) @@ -5117,6 +5157,12 @@ static int token_create(union bpf_attr *attr) err = -ENOTSUPP; goto err_out; } + allowed_map_types = attr->token_create.allowed_map_types; + if (!(attr->token_create.flags & BPF_F_TOKEN_IGNORE_UNKNOWN_MAP_TYPES) && + allowed_map_types & ~BPF_TOKEN_MAP_TYPES_MASK) { + err = -ENOTSUPP; + goto err_out; + } if (!bpf_token_capable(token, CAP_SYS_ADMIN)) { err = -EPERM; @@ -5128,6 +5174,11 @@ static int token_create(union bpf_attr *attr) err = -EPERM; goto err_out; } + /* requested map types should be a subset of associated token's set */ + if (token && (token->allowed_map_types & allowed_map_types) != allowed_map_types) { + err = -EPERM; + goto err_out; + } new_token = bpf_token_alloc(); if (!new_token) { @@ -5136,6 +5187,7 @@ static int token_create(union bpf_attr *attr) } new_token->allowed_cmds = allowed_cmds & BPF_TOKEN_CMDS_MASK; + new_token->allowed_map_types = allowed_map_types & BPF_TOKEN_MAP_TYPES_MASK; fd = bpf_token_new_fd(new_token); if (fd < 0) { diff --git a/kernel/bpf/token.c b/kernel/bpf/token.c index 7e989b25fa06..ef053c48d7db 100644 --- a/kernel/bpf/token.c +++ b/kernel/bpf/token.c @@ -116,6 +116,14 @@ bool bpf_token_allow_cmd(const struct bpf_token *token, enum bpf_cmd cmd) return token->allowed_cmds & (1ULL << cmd); } +bool bpf_token_allow_map_type(const struct bpf_token *token, enum bpf_map_type type) +{ + if (!token || type >= __MAX_BPF_MAP_TYPE) + return false; + + return token->allowed_map_types & (1ULL << type); +} + bool bpf_token_capable(const struct bpf_token *token, int cap) { return token || capable(cap) || (cap != CAP_SYS_ADMIN && capable(CAP_SYS_ADMIN)); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index d1d7ca71756f..7cfaa2da84ee 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -954,6 +954,7 @@ enum bpf_map_type { BPF_MAP_TYPE_BLOOM_FILTER, BPF_MAP_TYPE_USER_RINGBUF, BPF_MAP_TYPE_CGRP_STORAGE, + __MAX_BPF_MAP_TYPE }; /* Note that tracing related programs such as @@ -1196,6 +1197,10 @@ enum { * token-enabled. */ BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS = 1U << 0, + /* Similar to BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS flag, but for + * token_create.allowed_map_types bit set. + */ + BPF_F_TOKEN_IGNORE_UNKNOWN_MAP_TYPES = 1U << 1, }; /* When BPF ldimm64's insn[0].src_reg != 0 then this can have @@ -1377,6 +1382,7 @@ union bpf_attr { * to using 5 hash functions). */ __u64 map_extra; + __u32 map_token_fd; }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ @@ -1656,9 +1662,17 @@ union bpf_attr { /* a bit set of allowed bpf() syscall commands, * e.g., (1ULL << BPF_TOKEN_CREATE) | (1ULL << BPF_PROG_LOAD) * will allow creating derived BPF tokens and loading new BPF - * programs + * programs; + * see also BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS for its effect on + * validity checking of this set */ __u64 allowed_cmds; + /* similarly to allowed_cmds, a bit set of BPF map types that + * are allowed to be created by requested BPF token; + * see also BPF_F_TOKEN_IGNORE_UNKNOWN_MAP_TYPES for its + * effect on validity checking of this set + */ + __u64 allowed_map_types; } token_create; } __attribute__((aligned(8))); diff --git a/tools/testing/selftests/bpf/prog_tests/libbpf_str.c b/tools/testing/selftests/bpf/prog_tests/libbpf_str.c index efb8bd43653c..e677c0435cec 100644 --- a/tools/testing/selftests/bpf/prog_tests/libbpf_str.c +++ b/tools/testing/selftests/bpf/prog_tests/libbpf_str.c @@ -132,6 +132,9 @@ static void test_libbpf_bpf_map_type_str(void) const char *map_type_str; char buf[256]; + if (map_type == __MAX_BPF_MAP_TYPE) + continue; + map_type_name = btf__str_by_offset(btf, e->name_off); map_type_str = libbpf_bpf_map_type_str(map_type); ASSERT_OK_PTR(map_type_str, map_type_name); From patchwork Fri Jun 2 15:00:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265456 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3E812C77B7A for ; Fri, 2 Jun 2023 15:00:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236343AbjFBPAz convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:00:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53604 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236364AbjFBPAm (ORCPT ); Fri, 2 Jun 2023 11:00:42 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8EB7AE55 for ; Fri, 2 Jun 2023 08:00:38 -0700 (PDT) Received: from pps.filterd (m0148461.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 3528jUAL021569 for ; Fri, 2 Jun 2023 08:00:38 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qyd6ptc27-11 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:38 -0700 Received: from ash-exhub204.TheFacebook.com (2620:10d:c0a8:83::4) by ash-exhub103.TheFacebook.com (2620:10d:c0a8:82::c) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:31 -0700 Received: from twshared68078.02.prn6.facebook.com (2620:10d:c0a8:1b::2d) by mail.thefacebook.com (2620:10d:c0a8:83::4) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:31 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 8220431E048FF; Fri, 2 Jun 2023 08:00:28 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 08/18] libbpf: add BPF token support to bpf_map_create() API Date: Fri, 2 Jun 2023 08:00:01 -0700 Message-ID: <20230602150011.1657856-9-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: JiVhkUkZMruPWLhdAGCX1LWClHy0t5E8 X-Proofpoint-ORIG-GUID: JiVhkUkZMruPWLhdAGCX1LWClHy0t5E8 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Add ability to provide token_fd for BPF_MAP_CREATE command through bpf_map_create() API. Also wire through token_create.allowed_map_types param for BPF_TOKEN_CREATE command. Signed-off-by: Andrii Nakryiko --- tools/lib/bpf/bpf.c | 5 ++++- tools/lib/bpf/bpf.h | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 38be66719485..0318538d43eb 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -169,7 +169,7 @@ int bpf_map_create(enum bpf_map_type map_type, __u32 max_entries, const struct bpf_map_create_opts *opts) { - const size_t attr_sz = offsetofend(union bpf_attr, map_extra); + const size_t attr_sz = offsetofend(union bpf_attr, map_token_fd); union bpf_attr attr; int fd; @@ -198,6 +198,8 @@ int bpf_map_create(enum bpf_map_type map_type, attr.numa_node = OPTS_GET(opts, numa_node, 0); attr.map_ifindex = OPTS_GET(opts, map_ifindex, 0); + attr.map_token_fd = OPTS_GET(opts, token_fd, 0); + fd = sys_bpf_fd(BPF_MAP_CREATE, &attr, attr_sz); return libbpf_err_errno(fd); } @@ -1215,6 +1217,7 @@ int bpf_token_create(struct bpf_token_create_opts *opts) attr.token_create.flags = OPTS_GET(opts, flags, 0); attr.token_create.token_fd = OPTS_GET(opts, token_fd, 0); attr.token_create.allowed_cmds = OPTS_GET(opts, allowed_cmds, 0); + attr.token_create.allowed_map_types = OPTS_GET(opts, allowed_map_types, 0); ret = sys_bpf_fd(BPF_TOKEN_CREATE, &attr, attr_sz); return libbpf_err_errno(ret); diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index f2b8041ca27a..19a43201d1af 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -51,8 +51,10 @@ struct bpf_map_create_opts { __u32 numa_node; __u32 map_ifindex; + + __u32 token_fd; }; -#define bpf_map_create_opts__last_field map_ifindex +#define bpf_map_create_opts__last_field token_fd LIBBPF_API int bpf_map_create(enum bpf_map_type map_type, const char *map_name, @@ -556,9 +558,10 @@ struct bpf_token_create_opts { __u32 flags; __u32 token_fd; __u64 allowed_cmds; + __u64 allowed_map_types; size_t :0; }; -#define bpf_token_create_opts__last_field allowed_cmds +#define bpf_token_create_opts__last_field allowed_map_types LIBBPF_API int bpf_token_create(struct bpf_token_create_opts *opts); From patchwork Fri Jun 2 15:00:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265466 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BD48EC77B7A for ; Fri, 2 Jun 2023 15:01:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236331AbjFBPBi convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54004 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236373AbjFBPBI (ORCPT ); Fri, 2 Jun 2023 11:01:08 -0400 Received: from mx0a-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D6768E4D for ; Fri, 2 Jun 2023 08:01:07 -0700 (PDT) Received: from pps.filterd (m0001303.ppops.net [127.0.0.1]) by m0001303.ppops.net (8.17.1.19/8.17.1.19) with ESMTP id 3527V8DL028611 for ; Fri, 2 Jun 2023 08:01:07 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by m0001303.ppops.net (PPS) with ESMTPS id 3qyc3papkt-4 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:01:06 -0700 Received: from twshared25760.37.frc1.facebook.com (2620:10d:c0a8:1c::11) by mail.thefacebook.com (2620:10d:c0a8:82::d) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:39 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 969CD31E04936; Fri, 2 Jun 2023 08:00:30 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 09/18] selftests/bpf: add BPF token-enabled test for BPF_MAP_CREATE command Date: Fri, 2 Jun 2023 08:00:02 -0700 Message-ID: <20230602150011.1657856-10-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: gA7s-O4J64iwI_62hJ9py3NHh4dQV9EN X-Proofpoint-ORIG-GUID: gA7s-O4J64iwI_62hJ9py3NHh4dQV9EN X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Add test for creating BPF token with support for BPF_MAP_CREATE delegation. And validate that its allowed_map_types filter works as expected and allows to create privileged BPF maps through delegated token, as long as they are allowed by privileged creator of a token. Signed-off-by: Andrii Nakryiko --- .../testing/selftests/bpf/prog_tests/token.c | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/token.c b/tools/testing/selftests/bpf/prog_tests/token.c index fe78b558d697..0b6e699d2439 100644 --- a/tools/testing/selftests/bpf/prog_tests/token.c +++ b/tools/testing/selftests/bpf/prog_tests/token.c @@ -88,8 +88,60 @@ static void subtest_token_create(void) ASSERT_OK(restore_priv_caps(old_caps), "restore_caps"); } +static void subtest_map_token(void) +{ + LIBBPF_OPTS(bpf_token_create_opts, token_opts); + LIBBPF_OPTS(bpf_map_create_opts, map_opts); + int token_fd = 0, map_fd = 0; + __u64 old_caps = 0; + + /* check that IGNORE_UNKNOWN_MAP_TYPES flag is respected */ + token_opts.flags = BPF_F_TOKEN_IGNORE_UNKNOWN_MAP_TYPES; + token_opts.allowed_map_types = ~0ULL; /* any current and future map types is allowed */ + token_fd = bpf_token_create(&token_opts); + if (!ASSERT_GT(token_fd, 0, "token_create_future_proof")) + return; + close(token_fd); + + /* create BPF token allowing STACK, but not QUEUE map */ + token_opts.flags = 0; + token_opts.allowed_cmds = 1ULL << BPF_MAP_CREATE; + token_opts.allowed_map_types = 1ULL << BPF_MAP_TYPE_STACK; /* but not QUEUE */ + token_fd = bpf_token_create(&token_opts); + if (!ASSERT_GT(token_fd, 0, "token_create")) + return; + + /* drop privileges to test token_fd passing */ + if (!ASSERT_OK(drop_priv_caps(&old_caps), "drop_caps")) + goto cleanup; + + /* BPF_MAP_TYPE_STACK is privileged, but with given token_fd should succeed */ + map_opts.token_fd = token_fd; + map_fd = bpf_map_create(BPF_MAP_TYPE_STACK, "token_stack", 0, 8, 1, &map_opts); + if (!ASSERT_GT(map_fd, 0, "stack_map_fd")) + goto cleanup; + close(map_fd); + map_fd = 0; + + /* BPF_MAP_TYPE_QUEUE is privileged, and token doesn't allow it, so should fail */ + map_opts.token_fd = token_fd; + map_fd = bpf_map_create(BPF_MAP_TYPE_QUEUE, "token_queue", 0, 8, 1, &map_opts); + if (!ASSERT_EQ(map_fd, -EPERM, "queue_map_fd")) + goto cleanup; + +cleanup: + if (map_fd > 0) + close(map_fd); + if (token_fd) + close(token_fd); + if (old_caps) + ASSERT_OK(restore_priv_caps(old_caps), "restore_caps"); +} + void test_token(void) { if (test__start_subtest("token_create")) subtest_token_create(); + if (test__start_subtest("map_token")) + subtest_map_token(); } From patchwork Fri Jun 2 15:00:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265457 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 41031C77B7A for ; Fri, 2 Jun 2023 15:01:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236344AbjFBPBD convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53366 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235874AbjFBPAw (ORCPT ); Fri, 2 Jun 2023 11:00:52 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E8AD3E79 for ; Fri, 2 Jun 2023 08:00:42 -0700 (PDT) Received: from pps.filterd (m0148461.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 3528jUAQ021569 for ; Fri, 2 Jun 2023 08:00:42 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qyd6ptc27-16 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:42 -0700 Received: from twshared40933.03.prn6.facebook.com (2620:10d:c0a8:1c::11) by mail.thefacebook.com (2620:10d:c0a8:82::c) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:39 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 9F3A931E0499B; Fri, 2 Jun 2023 08:00:32 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 10/18] bpf: add BPF token support to BPF_BTF_LOAD command Date: Fri, 2 Jun 2023 08:00:03 -0700 Message-ID: <20230602150011.1657856-11-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: Pq1LBkp0Zhuzs_A5ThlcJy4xOMyBkoHn X-Proofpoint-ORIG-GUID: Pq1LBkp0Zhuzs_A5ThlcJy4xOMyBkoHn X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Accept BPF token FD in BPF_BTF_LOAD command to allow BTF data loading through delegated BPF token. BTF loading is a pretty straightforward operation, so as long as BPF token is created with allow_cmds granting BPF_BTF_LOAD command, kernel proceeds to parsing BTF data and creating BTF object. Signed-off-by: Andrii Nakryiko --- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 21 +++++++++++++++++-- tools/include/uapi/linux/bpf.h | 1 + .../selftests/bpf/prog_tests/libbpf_probes.c | 2 ++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 7cfaa2da84ee..d30fb567d22a 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -1549,6 +1549,7 @@ union bpf_attr { * truncated), or smaller (if log buffer wasn't filled completely). */ __u32 btf_log_true_size; + __u32 btf_token_fd; }; struct { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index eb77ba71fbcf..05e941e9bbe6 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -4475,15 +4475,31 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr, return err; } -#define BPF_BTF_LOAD_LAST_FIELD btf_log_true_size +#define BPF_BTF_LOAD_LAST_FIELD btf_token_fd static int bpf_btf_load(const union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_size) { + struct bpf_token *token = NULL; + if (CHECK_ATTR(BPF_BTF_LOAD)) return -EINVAL; - if (!bpf_capable()) + if (attr->btf_token_fd) { + token = bpf_token_get_from_fd(attr->btf_token_fd); + if (IS_ERR(token)) + return PTR_ERR(token); + if (!bpf_token_allow_cmd(token, BPF_BTF_LOAD)) { + bpf_token_put(token); + token = NULL; + } + } + + if (!bpf_token_capable(token, CAP_BPF)) { + bpf_token_put(token); return -EPERM; + } + + bpf_token_put(token); return btf_new_fd(attr, uattr, uattr_size); } @@ -5120,6 +5136,7 @@ static int bpf_prog_bind_map(union bpf_attr *attr) #define BPF_TOKEN_CMDS_MASK ( \ (1ULL << BPF_TOKEN_CREATE) \ | (1ULL << BPF_MAP_CREATE) \ + | (1ULL << BPF_BTF_LOAD) \ ) #define BPF_TOKEN_MAP_TYPES_MASK \ ((BIT_ULL(__MAX_BPF_MAP_TYPE) - 1) & ~BIT_ULL(BPF_MAP_TYPE_UNSPEC)) diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 7cfaa2da84ee..d30fb567d22a 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -1549,6 +1549,7 @@ union bpf_attr { * truncated), or smaller (if log buffer wasn't filled completely). */ __u32 btf_log_true_size; + __u32 btf_token_fd; }; struct { diff --git a/tools/testing/selftests/bpf/prog_tests/libbpf_probes.c b/tools/testing/selftests/bpf/prog_tests/libbpf_probes.c index 9f766ddd946a..573249a2814d 100644 --- a/tools/testing/selftests/bpf/prog_tests/libbpf_probes.c +++ b/tools/testing/selftests/bpf/prog_tests/libbpf_probes.c @@ -68,6 +68,8 @@ void test_libbpf_probe_map_types(void) if (map_type == BPF_MAP_TYPE_UNSPEC) continue; + if (strcmp(map_type_name, "__MAX_BPF_MAP_TYPE") == 0) + continue; if (!test__start_subtest(map_type_name)) continue; From patchwork Fri Jun 2 15:00:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265459 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8078EC7EE2D for ; Fri, 2 Jun 2023 15:01:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235837AbjFBPBF convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53818 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236347AbjFBPAx (ORCPT ); Fri, 2 Jun 2023 11:00:53 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1E1B110C0 for ; Fri, 2 Jun 2023 08:00:44 -0700 (PDT) Received: from pps.filterd (m0109333.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 352BOJGY001158 for ; Fri, 2 Jun 2023 08:00:43 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qxxpa8r2x-5 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:43 -0700 Received: from twshared25760.37.frc1.facebook.com (2620:10d:c0a8:1b::30) by mail.thefacebook.com (2620:10d:c0a8:82::e) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:39 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id AF69A31E049C6; Fri, 2 Jun 2023 08:00:35 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 11/18] libbpf: add BPF token support to bpf_btf_load() API Date: Fri, 2 Jun 2023 08:00:04 -0700 Message-ID: <20230602150011.1657856-12-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: UmencEC2rOUT4PUvxvQs8ZB-sPLi0_sR X-Proofpoint-ORIG-GUID: UmencEC2rOUT4PUvxvQs8ZB-sPLi0_sR X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Allow user to specify token_fd for bpf_btf_load() API that wraps kernel's BPF_BTF_LOAD command. This allows loading BTF from unprivileged process as long as it has BPF token allowing BPF_BTF_LOAD command, which can be created and delegated by privileged process. Signed-off-by: Andrii Nakryiko --- tools/lib/bpf/bpf.c | 4 +++- tools/lib/bpf/bpf.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 0318538d43eb..193993dbbdc4 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -1098,7 +1098,7 @@ int bpf_raw_tracepoint_open(const char *name, int prog_fd) int bpf_btf_load(const void *btf_data, size_t btf_size, struct bpf_btf_load_opts *opts) { - const size_t attr_sz = offsetofend(union bpf_attr, btf_log_true_size); + const size_t attr_sz = offsetofend(union bpf_attr, btf_token_fd); union bpf_attr attr; char *log_buf; size_t log_size; @@ -1123,6 +1123,8 @@ int bpf_btf_load(const void *btf_data, size_t btf_size, struct bpf_btf_load_opts attr.btf = ptr_to_u64(btf_data); attr.btf_size = btf_size; + attr.btf_token_fd = OPTS_GET(opts, token_fd, 0); + /* log_level == 0 and log_buf != NULL means "try loading without * log_buf, but retry with log_buf and log_level=1 on error", which is * consistent across low-level and high-level BTF and program loading diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 19a43201d1af..3153a9e697e2 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -132,9 +132,10 @@ struct bpf_btf_load_opts { * If kernel doesn't support this feature, log_size is left unchanged. */ __u32 log_true_size; + __u32 token_fd; size_t :0; }; -#define bpf_btf_load_opts__last_field log_true_size +#define bpf_btf_load_opts__last_field token_fd LIBBPF_API int bpf_btf_load(const void *btf_data, size_t btf_size, struct bpf_btf_load_opts *opts); From patchwork Fri Jun 2 15:00:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265460 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EE8FFC77B7A for ; Fri, 2 Jun 2023 15:01:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236330AbjFBPBG convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53380 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236378AbjFBPAx (ORCPT ); Fri, 2 Jun 2023 11:00:53 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4CC9310C1 for ; Fri, 2 Jun 2023 08:00:44 -0700 (PDT) Received: from pps.filterd (m0148461.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 3528jUo3021577 for ; Fri, 2 Jun 2023 08:00:44 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qyd6ptcbp-5 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:43 -0700 Received: from twshared40933.03.prn6.facebook.com (2620:10d:c0a8:1c::11) by mail.thefacebook.com (2620:10d:c0a8:83::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:39 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id B9B6D31E049FF; Fri, 2 Jun 2023 08:00:37 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 12/18] selftests/bpf: add BPF token-enabled BPF_BTF_LOAD selftest Date: Fri, 2 Jun 2023 08:00:05 -0700 Message-ID: <20230602150011.1657856-13-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: i2oahFoaBDcTvGIasAZc3PWZNw6EXXgM X-Proofpoint-ORIG-GUID: i2oahFoaBDcTvGIasAZc3PWZNw6EXXgM X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Add a simple test validating that BTF loading can be done from unprivileged process through delegated BPF token. Signed-off-by: Andrii Nakryiko --- .../testing/selftests/bpf/prog_tests/token.c | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/token.c b/tools/testing/selftests/bpf/prog_tests/token.c index 0b6e699d2439..b141f722c0c6 100644 --- a/tools/testing/selftests/bpf/prog_tests/token.c +++ b/tools/testing/selftests/bpf/prog_tests/token.c @@ -138,10 +138,65 @@ static void subtest_map_token(void) ASSERT_OK(restore_priv_caps(old_caps), "restore_caps"); } +static void subtest_btf_token(void) +{ + LIBBPF_OPTS(bpf_token_create_opts, token_opts); + LIBBPF_OPTS(bpf_btf_load_opts, btf_opts); + int token_fd = 0, btf_fd = 0; + const void *raw_btf_data; + struct btf *btf = NULL; + __u32 raw_btf_size; + __u64 old_caps = 0; + + /* create BPF token allowing BPF_BTF_LOAD command */ + token_opts.allowed_cmds = 1ULL << BPF_BTF_LOAD; + token_fd = bpf_token_create(&token_opts); + if (!ASSERT_GT(token_fd, 0, "token_create")) + return; + + /* drop privileges to test token_fd passing */ + if (!ASSERT_OK(drop_priv_caps(&old_caps), "drop_caps")) + goto cleanup; + + btf = btf__new_empty(); + if (!ASSERT_OK_PTR(btf, "empty_btf")) + goto cleanup; + + ASSERT_GT(btf__add_int(btf, "int", 4, 0), 0, "int_type"); + + raw_btf_data = btf__raw_data(btf, &raw_btf_size); + if (!ASSERT_OK_PTR(raw_btf_data, "raw_btf_data")) + goto cleanup; + + /* validate we can successfully load new BTF with token */ + btf_opts.token_fd = token_fd; + btf_fd = bpf_btf_load(raw_btf_data, raw_btf_size, &btf_opts); + if (!ASSERT_GT(btf_fd, 0, "btf_fd")) + goto cleanup; + close(btf_fd); + + /* now validate that we *cannot* load BTF without token */ + btf_opts.token_fd = 0; + btf_fd = bpf_btf_load(raw_btf_data, raw_btf_size, &btf_opts); + if (!ASSERT_EQ(btf_fd, -EPERM, "btf_fd_eperm")) + goto cleanup; + +cleanup: + btf__free(btf); + if (btf_fd > 0) + close(btf_fd); + if (token_fd) + close(token_fd); + if (old_caps) + ASSERT_OK(restore_priv_caps(old_caps), "restore_caps"); +} + void test_token(void) { if (test__start_subtest("token_create")) subtest_token_create(); if (test__start_subtest("map_token")) subtest_map_token(); + if (test__start_subtest("btf_token")) + subtest_btf_token(); } From patchwork Fri Jun 2 15:00:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265461 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 62433C77B7A for ; Fri, 2 Jun 2023 15:01:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235773AbjFBPBG convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53828 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236331AbjFBPAy (ORCPT ); Fri, 2 Jun 2023 11:00:54 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 35D9E10C7 for ; Fri, 2 Jun 2023 08:00:47 -0700 (PDT) Received: from pps.filterd (m0109334.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 352AxE9C016163 for ; Fri, 2 Jun 2023 08:00:47 -0700 Received: from mail.thefacebook.com ([163.114.132.120]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qxxb5s3cy-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:46 -0700 Received: from twshared52232.38.frc1.facebook.com (2620:10d:c085:108::8) by mail.thefacebook.com (2620:10d:c085:11d::4) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:46 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id C40DB31E04A64; Fri, 2 Jun 2023 08:00:39 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 13/18] bpf: keep BPF_PROG_LOAD permission checks clear of validations Date: Fri, 2 Jun 2023 08:00:06 -0700 Message-ID: <20230602150011.1657856-14-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: _Ym2KrL-tLV4ji9FJhzyCJwg5a0LZaqp X-Proofpoint-ORIG-GUID: _Ym2KrL-tLV4ji9FJhzyCJwg5a0LZaqp X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Move out flags validation and license checks out of the permission checks. They were intermingled, which makes subsequent changes harder. Clean this up: perform straightforward flag validation upfront, and fetch and check license later, right where we use it. Also consolidate capabilities check in one block, right after basic attribute sanity checks. Signed-off-by: Andrii Nakryiko --- kernel/bpf/syscall.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 05e941e9bbe6..b0a85ac9a42f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2582,7 +2582,6 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) struct btf *attach_btf = NULL; int err; char license[128]; - bool is_gpl; if (CHECK_ATTR(BPF_PROG_LOAD)) return -EINVAL; @@ -2601,16 +2600,6 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) !bpf_capable()) return -EPERM; - /* copy eBPF program license from user space */ - if (strncpy_from_bpfptr(license, - make_bpfptr(attr->license, uattr.is_kernel), - sizeof(license) - 1) < 0) - return -EFAULT; - license[sizeof(license) - 1] = 0; - - /* eBPF programs must be GPL compatible to use GPL-ed functions */ - is_gpl = license_is_gpl_compatible(license); - /* Intent here is for unprivileged_bpf_disabled to block BPF program * creation for unprivileged users; other actions depend * on fd availability and access to bpffs, so are dependent on @@ -2703,12 +2692,20 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) make_bpfptr(attr->insns, uattr.is_kernel), bpf_prog_insn_size(prog)) != 0) goto free_prog_sec; + /* copy eBPF program license from user space */ + if (strncpy_from_bpfptr(license, + make_bpfptr(attr->license, uattr.is_kernel), + sizeof(license) - 1) < 0) + goto free_prog_sec; + license[sizeof(license) - 1] = 0; + + /* eBPF programs must be GPL compatible to use GPL-ed functions */ + prog->gpl_compatible = license_is_gpl_compatible(license) ? 1 : 0; prog->orig_prog = NULL; prog->jited = 0; atomic64_set(&prog->aux->refcnt, 1); - prog->gpl_compatible = is_gpl ? 1 : 0; if (bpf_prog_is_dev_bound(prog->aux)) { err = bpf_prog_dev_bound_init(prog, attr); From patchwork Fri Jun 2 15:00:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265464 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EC56FC7EE2E for ; Fri, 2 Jun 2023 15:01:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235775AbjFBPB1 convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53486 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236335AbjFBPBC (ORCPT ); Fri, 2 Jun 2023 11:01:02 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6F90DE46 for ; Fri, 2 Jun 2023 08:00:55 -0700 (PDT) Received: from pps.filterd (m0109333.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 352Bcp6e001168 for ; Fri, 2 Jun 2023 08:00:55 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qxxpa8r4c-5 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:54 -0700 Received: from twshared25760.37.frc1.facebook.com (2620:10d:c0a8:1b::2d) by mail.thefacebook.com (2620:10d:c0a8:82::c) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:52 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id D064F31E04A8E; Fri, 2 Jun 2023 08:00:41 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 14/18] bpf: add BPF token support to BPF_PROG_LOAD command Date: Fri, 2 Jun 2023 08:00:07 -0700 Message-ID: <20230602150011.1657856-15-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: Rp3M_xPh0CUNzo6dy7DcRCOwe5h0S_T7 X-Proofpoint-ORIG-GUID: Rp3M_xPh0CUNzo6dy7DcRCOwe5h0S_T7 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Add basic support of BPF token to BPF_PROG_LOAD. Extend BPF token to allow specifying BPF_PROG_LOAD as an allowed command, and also allow to specify bit sets of program type and attach type combination that would be allowed to be loaded by requested BPF token. Signed-off-by: Andrii Nakryiko --- include/linux/bpf.h | 6 + include/uapi/linux/bpf.h | 19 +++ kernel/bpf/core.c | 1 + kernel/bpf/syscall.c | 118 ++++++++++++++---- kernel/bpf/token.c | 11 ++ tools/include/uapi/linux/bpf.h | 19 +++ .../selftests/bpf/prog_tests/libbpf_probes.c | 2 + .../selftests/bpf/prog_tests/libbpf_str.c | 3 + 8 files changed, 154 insertions(+), 25 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 657bec546351..320d93c542ed 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1411,6 +1411,7 @@ struct bpf_prog_aux { #ifdef CONFIG_SECURITY void *security; #endif + struct bpf_token *token; struct bpf_prog_offload *offload; struct btf *btf; struct bpf_func_info *func_info; @@ -1540,6 +1541,8 @@ struct bpf_token { atomic64_t refcnt; u64 allowed_cmds; u64 allowed_map_types; + u64 allowed_prog_types; + u64 allowed_attach_types; }; struct bpf_struct_ops_value; @@ -2095,6 +2098,9 @@ struct bpf_token *bpf_token_get_from_fd(u32 ufd); bool bpf_token_capable(const struct bpf_token *token, int cap); bool bpf_token_allow_cmd(const struct bpf_token *token, enum bpf_cmd cmd); bool bpf_token_allow_map_type(const struct bpf_token *token, enum bpf_map_type type); +bool bpf_token_allow_prog_type(const struct bpf_token *token, + enum bpf_prog_type prog_type, + enum bpf_attach_type attach_type); int bpf_obj_pin_user(u32 ufd, int path_fd, const char __user *pathname); int bpf_obj_get_user(int path_fd, const char __user *pathname, int flags); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index d30fb567d22a..c2867e622c30 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -999,6 +999,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */ BPF_PROG_TYPE_NETFILTER, + __MAX_BPF_PROG_TYPE }; enum bpf_attach_type { @@ -1201,6 +1202,14 @@ enum { * token_create.allowed_map_types bit set. */ BPF_F_TOKEN_IGNORE_UNKNOWN_MAP_TYPES = 1U << 1, + /* Similar to BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS flag, but for + * token_create.allowed_prog_types bit set. + */ + BPF_F_TOKEN_IGNORE_UNKNOWN_PROG_TYPES = 1U << 2, + /* Similar to BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS flag, but for + * token_create.allowed_attach_types bit set. + */ + BPF_F_TOKEN_IGNORE_UNKNOWN_ATTACH_TYPES = 1U << 3, }; /* When BPF ldimm64's insn[0].src_reg != 0 then this can have @@ -1452,6 +1461,7 @@ union bpf_attr { * truncated), or smaller (if log buffer wasn't filled completely). */ __u32 log_true_size; + __u32 prog_token_fd; }; struct { /* anonymous struct used by BPF_OBJ_* commands */ @@ -1674,6 +1684,15 @@ union bpf_attr { * effect on validity checking of this set */ __u64 allowed_map_types; + /* similarly to allowed_map_types, bit sets of BPF program + * types and BPF program attach types that are allowed to be + * loaded by requested BPF token; + * see also BPF_F_TOKEN_IGNORE_UNKNOWN_PROG_TYPES and + * BPF_F_TOKEN_IGNORE_UNKNOWN_ATTACH_TYPES for their + * effect on validity checking of these sets + */ + __u64 allowed_prog_types; + __u64 allowed_attach_types; } token_create; } __attribute__((aligned(8))); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 7421487422d4..cd0a93968009 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2597,6 +2597,7 @@ void bpf_prog_free(struct bpf_prog *fp) if (aux->dst_prog) bpf_prog_put(aux->dst_prog); + bpf_token_put(aux->token); INIT_WORK(&aux->work, bpf_prog_free_deferred); schedule_work(&aux->work); } diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b0a85ac9a42f..e02688bebf8e 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2573,13 +2573,15 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type) } /* last field in 'union bpf_attr' used by this command */ -#define BPF_PROG_LOAD_LAST_FIELD log_true_size +#define BPF_PROG_LOAD_LAST_FIELD prog_token_fd static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) { enum bpf_prog_type type = attr->prog_type; struct bpf_prog *prog, *dst_prog = NULL; struct btf *attach_btf = NULL; + struct bpf_token *token = NULL; + bool bpf_cap; int err; char license[128]; @@ -2595,10 +2597,31 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) BPF_F_XDP_DEV_BOUND_ONLY)) return -EINVAL; + bpf_prog_load_fixup_attach_type(attr); + + if (attr->prog_token_fd) { + token = bpf_token_get_from_fd(attr->prog_token_fd); + if (IS_ERR(token)) + return PTR_ERR(token); + /* if current token doesn't grant prog loading permissions, + * then we can't use this token, so ignore it and rely on + * system-wide capabilities checks + */ + if (!bpf_token_allow_cmd(token, BPF_PROG_LOAD) || + !bpf_token_allow_prog_type(token, attr->prog_type, + attr->expected_attach_type)) { + bpf_token_put(token); + token = NULL; + } + } + + bpf_cap = bpf_token_capable(token, CAP_BPF); + err = -EPERM; + if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && (attr->prog_flags & BPF_F_ANY_ALIGNMENT) && - !bpf_capable()) - return -EPERM; + !bpf_cap) + goto put_token; /* Intent here is for unprivileged_bpf_disabled to block BPF program * creation for unprivileged users; other actions depend @@ -2607,21 +2630,23 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) * capability checks are still carried out for these * and other operations. */ - if (sysctl_unprivileged_bpf_disabled && !bpf_capable()) - return -EPERM; + if (sysctl_unprivileged_bpf_disabled && !bpf_cap) + goto put_token; if (attr->insn_cnt == 0 || - attr->insn_cnt > (bpf_capable() ? BPF_COMPLEXITY_LIMIT_INSNS : BPF_MAXINSNS)) - return -E2BIG; + attr->insn_cnt > (bpf_cap ? BPF_COMPLEXITY_LIMIT_INSNS : BPF_MAXINSNS)) { + err = -E2BIG; + goto put_token; + } if (type != BPF_PROG_TYPE_SOCKET_FILTER && type != BPF_PROG_TYPE_CGROUP_SKB && - !bpf_capable()) - return -EPERM; + !bpf_cap) + goto put_token; - if (is_net_admin_prog_type(type) && !capable(CAP_NET_ADMIN) && !capable(CAP_SYS_ADMIN)) - return -EPERM; - if (is_perfmon_prog_type(type) && !perfmon_capable()) - return -EPERM; + if (is_net_admin_prog_type(type) && !bpf_token_capable(token, CAP_NET_ADMIN)) + goto put_token; + if (is_perfmon_prog_type(type) && !bpf_token_capable(token, CAP_PERFMON)) + goto put_token; /* attach_prog_fd/attach_btf_obj_fd can specify fd of either bpf_prog * or btf, we need to check which one it is @@ -2631,27 +2656,33 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) if (IS_ERR(dst_prog)) { dst_prog = NULL; attach_btf = btf_get_by_fd(attr->attach_btf_obj_fd); - if (IS_ERR(attach_btf)) - return -EINVAL; + if (IS_ERR(attach_btf)) { + err = -EINVAL; + goto put_token; + } if (!btf_is_kernel(attach_btf)) { /* attaching through specifying bpf_prog's BTF * objects directly might be supported eventually */ btf_put(attach_btf); - return -ENOTSUPP; + err = -ENOTSUPP; + goto put_token; } } } else if (attr->attach_btf_id) { /* fall back to vmlinux BTF, if BTF type ID is specified */ attach_btf = bpf_get_btf_vmlinux(); - if (IS_ERR(attach_btf)) - return PTR_ERR(attach_btf); - if (!attach_btf) - return -EINVAL; + if (IS_ERR(attach_btf)) { + err = PTR_ERR(attach_btf); + goto put_token; + } + if (!attach_btf) { + err = -EINVAL; + goto put_token; + } btf_get(attach_btf); } - bpf_prog_load_fixup_attach_type(attr); if (bpf_prog_load_check_attach(type, attr->expected_attach_type, attach_btf, attr->attach_btf_id, dst_prog)) { @@ -2659,7 +2690,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) bpf_prog_put(dst_prog); if (attach_btf) btf_put(attach_btf); - return -EINVAL; + err = -EINVAL; + goto put_token; } /* plain bpf_prog allocation */ @@ -2669,7 +2701,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) bpf_prog_put(dst_prog); if (attach_btf) btf_put(attach_btf); - return -ENOMEM; + err = -EINVAL; + goto put_token; } prog->expected_attach_type = attr->expected_attach_type; @@ -2680,6 +2713,10 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) prog->aux->sleepable = attr->prog_flags & BPF_F_SLEEPABLE; prog->aux->xdp_has_frags = attr->prog_flags & BPF_F_XDP_HAS_FRAGS; + /* move token into prog->aux, reuse taken refcnt */ + prog->aux->token = token; + token = NULL; + err = security_bpf_prog_alloc(prog->aux); if (err) goto free_prog; @@ -2781,6 +2818,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) if (prog->aux->attach_btf) btf_put(prog->aux->attach_btf); bpf_prog_free(prog); +put_token: + bpf_token_put(token); return err; } @@ -3537,7 +3576,7 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, case BPF_PROG_TYPE_SK_LOOKUP: return attach_type == prog->expected_attach_type ? 0 : -EINVAL; case BPF_PROG_TYPE_CGROUP_SKB: - if (!capable(CAP_NET_ADMIN)) + if (!bpf_token_capable(prog->aux->token, CAP_NET_ADMIN)) /* cg-skb progs can be loaded by unpriv user. * check permissions at attach time. */ @@ -5129,21 +5168,29 @@ static int bpf_prog_bind_map(union bpf_attr *attr) #define BPF_TOKEN_FLAGS_MASK ( \ BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS \ | BPF_F_TOKEN_IGNORE_UNKNOWN_MAP_TYPES \ + | BPF_F_TOKEN_IGNORE_UNKNOWN_PROG_TYPES \ + | BPF_F_TOKEN_IGNORE_UNKNOWN_ATTACH_TYPES \ ) #define BPF_TOKEN_CMDS_MASK ( \ (1ULL << BPF_TOKEN_CREATE) \ | (1ULL << BPF_MAP_CREATE) \ | (1ULL << BPF_BTF_LOAD) \ + | (1ULL << BPF_PROG_LOAD) \ ) #define BPF_TOKEN_MAP_TYPES_MASK \ ((BIT_ULL(__MAX_BPF_MAP_TYPE) - 1) & ~BIT_ULL(BPF_MAP_TYPE_UNSPEC)) +#define BPF_TOKEN_PROG_TYPES_MASK \ + ((BIT_ULL(__MAX_BPF_PROG_TYPE) - 1) & ~BIT_ULL(BPF_PROG_TYPE_UNSPEC)) +#define BPF_TOKEN_ATTACH_TYPES_MASK \ + (BIT_ULL(__MAX_BPF_ATTACH_TYPE) - 1) -#define BPF_TOKEN_CREATE_LAST_FIELD token_create.allowed_map_types +#define BPF_TOKEN_CREATE_LAST_FIELD token_create.allowed_attach_types static int token_create(union bpf_attr *attr) { struct bpf_token *new_token, *token = NULL; u64 allowed_cmds, allowed_map_types; + u64 allowed_prog_types, allowed_attach_types; int fd, err; if (CHECK_ATTR(BPF_TOKEN_CREATE)) @@ -5177,6 +5224,18 @@ static int token_create(union bpf_attr *attr) err = -ENOTSUPP; goto err_out; } + allowed_prog_types = attr->token_create.allowed_prog_types; + if (!(attr->token_create.flags & BPF_F_TOKEN_IGNORE_UNKNOWN_PROG_TYPES) && + allowed_prog_types & ~BPF_TOKEN_PROG_TYPES_MASK) { + err = -ENOTSUPP; + goto err_out; + } + allowed_attach_types = attr->token_create.allowed_attach_types; + if (!(attr->token_create.flags & BPF_F_TOKEN_IGNORE_UNKNOWN_ATTACH_TYPES) && + allowed_attach_types & ~BPF_TOKEN_ATTACH_TYPES_MASK) { + err = -ENOTSUPP; + goto err_out; + } if (!bpf_token_capable(token, CAP_SYS_ADMIN)) { err = -EPERM; @@ -5193,6 +5252,13 @@ static int token_create(union bpf_attr *attr) err = -EPERM; goto err_out; } + /* requested prog/attach types should be a subset of associated token's set */ + if (token && + (((token->allowed_prog_types & allowed_prog_types) != allowed_prog_types) || + ((token->allowed_attach_types & allowed_attach_types) != allowed_attach_types))) { + err = -EPERM; + goto err_out; + } new_token = bpf_token_alloc(); if (!new_token) { @@ -5202,6 +5268,8 @@ static int token_create(union bpf_attr *attr) new_token->allowed_cmds = allowed_cmds & BPF_TOKEN_CMDS_MASK; new_token->allowed_map_types = allowed_map_types & BPF_TOKEN_MAP_TYPES_MASK; + new_token->allowed_prog_types = allowed_prog_types & BPF_TOKEN_PROG_TYPES_MASK; + new_token->allowed_attach_types = allowed_attach_types & BPF_TOKEN_ATTACH_TYPES_MASK; fd = bpf_token_new_fd(new_token); if (fd < 0) { diff --git a/kernel/bpf/token.c b/kernel/bpf/token.c index ef053c48d7db..e9f651ba07da 100644 --- a/kernel/bpf/token.c +++ b/kernel/bpf/token.c @@ -124,6 +124,17 @@ bool bpf_token_allow_map_type(const struct bpf_token *token, enum bpf_map_type t return token->allowed_map_types & (1ULL << type); } +bool bpf_token_allow_prog_type(const struct bpf_token *token, + enum bpf_prog_type prog_type, + enum bpf_attach_type attach_type) +{ + if (!token || prog_type >= __MAX_BPF_PROG_TYPE || attach_type >= __MAX_BPF_ATTACH_TYPE) + return false; + + return (token->allowed_prog_types & (1ULL << prog_type)) && + (token->allowed_attach_types & (1ULL << attach_type)); +} + bool bpf_token_capable(const struct bpf_token *token, int cap) { return token || capable(cap) || (cap != CAP_SYS_ADMIN && capable(CAP_SYS_ADMIN)); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index d30fb567d22a..c2867e622c30 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -999,6 +999,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */ BPF_PROG_TYPE_NETFILTER, + __MAX_BPF_PROG_TYPE }; enum bpf_attach_type { @@ -1201,6 +1202,14 @@ enum { * token_create.allowed_map_types bit set. */ BPF_F_TOKEN_IGNORE_UNKNOWN_MAP_TYPES = 1U << 1, + /* Similar to BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS flag, but for + * token_create.allowed_prog_types bit set. + */ + BPF_F_TOKEN_IGNORE_UNKNOWN_PROG_TYPES = 1U << 2, + /* Similar to BPF_F_TOKEN_IGNORE_UNKNOWN_CMDS flag, but for + * token_create.allowed_attach_types bit set. + */ + BPF_F_TOKEN_IGNORE_UNKNOWN_ATTACH_TYPES = 1U << 3, }; /* When BPF ldimm64's insn[0].src_reg != 0 then this can have @@ -1452,6 +1461,7 @@ union bpf_attr { * truncated), or smaller (if log buffer wasn't filled completely). */ __u32 log_true_size; + __u32 prog_token_fd; }; struct { /* anonymous struct used by BPF_OBJ_* commands */ @@ -1674,6 +1684,15 @@ union bpf_attr { * effect on validity checking of this set */ __u64 allowed_map_types; + /* similarly to allowed_map_types, bit sets of BPF program + * types and BPF program attach types that are allowed to be + * loaded by requested BPF token; + * see also BPF_F_TOKEN_IGNORE_UNKNOWN_PROG_TYPES and + * BPF_F_TOKEN_IGNORE_UNKNOWN_ATTACH_TYPES for their + * effect on validity checking of these sets + */ + __u64 allowed_prog_types; + __u64 allowed_attach_types; } token_create; } __attribute__((aligned(8))); diff --git a/tools/testing/selftests/bpf/prog_tests/libbpf_probes.c b/tools/testing/selftests/bpf/prog_tests/libbpf_probes.c index 573249a2814d..4ed46ed58a7b 100644 --- a/tools/testing/selftests/bpf/prog_tests/libbpf_probes.c +++ b/tools/testing/selftests/bpf/prog_tests/libbpf_probes.c @@ -30,6 +30,8 @@ void test_libbpf_probe_prog_types(void) if (prog_type == BPF_PROG_TYPE_UNSPEC) continue; + if (strcmp(prog_type_name, "__MAX_BPF_PROG_TYPE") == 0) + continue; if (!test__start_subtest(prog_type_name)) continue; diff --git a/tools/testing/selftests/bpf/prog_tests/libbpf_str.c b/tools/testing/selftests/bpf/prog_tests/libbpf_str.c index e677c0435cec..ea2a8c4063a8 100644 --- a/tools/testing/selftests/bpf/prog_tests/libbpf_str.c +++ b/tools/testing/selftests/bpf/prog_tests/libbpf_str.c @@ -185,6 +185,9 @@ static void test_libbpf_bpf_prog_type_str(void) const char *prog_type_str; char buf[256]; + if (prog_type == __MAX_BPF_PROG_TYPE) + continue; + prog_type_name = btf__str_by_offset(btf, e->name_off); prog_type_str = libbpf_bpf_prog_type_str(prog_type); ASSERT_OK_PTR(prog_type_str, prog_type_name); From patchwork Fri Jun 2 15:00:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265463 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9C922C7EE29 for ; Fri, 2 Jun 2023 15:01:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236241AbjFBPB1 convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53400 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236389AbjFBPBB (ORCPT ); Fri, 2 Jun 2023 11:01:01 -0400 Received: from mx0b-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5B7BCE49 for ; Fri, 2 Jun 2023 08:00:53 -0700 (PDT) Received: from pps.filterd (m0148460.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 352BxSYJ019647 for ; Fri, 2 Jun 2023 08:00:52 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qxt1sas0t-3 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:52 -0700 Received: from ash-exhub204.TheFacebook.com (2620:10d:c0a8:83::4) by ash-exhub202.TheFacebook.com (2620:10d:c0a8:83::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:50 -0700 Received: from twshared4466.29.prn2.facebook.com (2620:10d:c0a8:1c::11) by mail.thefacebook.com (2620:10d:c0a8:83::4) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:50 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id E4E9131E04AC0; Fri, 2 Jun 2023 08:00:43 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 15/18] bpf: take into account BPF token when fetching helper protos Date: Fri, 2 Jun 2023 08:00:08 -0700 Message-ID: <20230602150011.1657856-16-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-ORIG-GUID: d9KEy_jCTSka8Pd52B3kw4M7NYrz_eD1 X-Proofpoint-GUID: d9KEy_jCTSka8Pd52B3kw4M7NYrz_eD1 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Instead of performing unconditional system-wide bpf_capable() and perfmon_capable() calls inside bpf_base_func_proto() function (and other similar ones) to determine eligibility of a given BPF helper for a given program, use previously recorded BPF token during BPF_PROG_LOAD command handling to inform the decision. Signed-off-by: Andrii Nakryiko --- drivers/media/rc/bpf-lirc.c | 2 +- include/linux/bpf.h | 5 +++-- kernel/bpf/cgroup.c | 6 +++--- kernel/bpf/helpers.c | 6 +++--- kernel/bpf/syscall.c | 5 +++-- kernel/trace/bpf_trace.c | 2 +- net/core/filter.c | 32 ++++++++++++++++---------------- net/ipv4/bpf_tcp_ca.c | 2 +- net/netfilter/nf_bpf_link.c | 2 +- 9 files changed, 32 insertions(+), 30 deletions(-) diff --git a/drivers/media/rc/bpf-lirc.c b/drivers/media/rc/bpf-lirc.c index fe17c7f98e81..6d07693c6b9f 100644 --- a/drivers/media/rc/bpf-lirc.c +++ b/drivers/media/rc/bpf-lirc.c @@ -110,7 +110,7 @@ lirc_mode2_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_get_prandom_u32: return &bpf_get_prandom_u32_proto; case BPF_FUNC_trace_printk: - if (perfmon_capable()) + if (bpf_token_capable(prog->aux->token, CAP_PERFMON)) return bpf_get_trace_printk_proto(); fallthrough; default: diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 320d93c542ed..9467d093e88e 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2345,7 +2345,8 @@ int btf_check_type_match(struct bpf_verifier_log *log, const struct bpf_prog *pr struct bpf_prog *bpf_prog_by_id(u32 id); struct bpf_link *bpf_link_by_id(u32 id); -const struct bpf_func_proto *bpf_base_func_proto(enum bpf_func_id func_id); +const struct bpf_func_proto *bpf_base_func_proto(enum bpf_func_id func_id, + const struct bpf_prog *prog); void bpf_task_storage_free(struct task_struct *task); void bpf_cgrp_storage_free(struct cgroup *cgroup); bool bpf_prog_has_kfunc_call(const struct bpf_prog *prog); @@ -2602,7 +2603,7 @@ static inline int btf_struct_access(struct bpf_verifier_log *log, } static inline const struct bpf_func_proto * -bpf_base_func_proto(enum bpf_func_id func_id) +bpf_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { return NULL; } diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 5b2741aa0d9b..39d6cfb6f304 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1615,7 +1615,7 @@ cgroup_dev_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_perf_event_output: return &bpf_event_output_data_proto; default: - return bpf_base_func_proto(func_id); + return bpf_base_func_proto(func_id, prog); } } @@ -2173,7 +2173,7 @@ sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_perf_event_output: return &bpf_event_output_data_proto; default: - return bpf_base_func_proto(func_id); + return bpf_base_func_proto(func_id, prog); } } @@ -2330,7 +2330,7 @@ cg_sockopt_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_perf_event_output: return &bpf_event_output_data_proto; default: - return bpf_base_func_proto(func_id); + return bpf_base_func_proto(func_id, prog); } } diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 4ef4c4f8a355..31cd0b956c7e 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1663,7 +1663,7 @@ const struct bpf_func_proto bpf_probe_read_kernel_str_proto __weak; const struct bpf_func_proto bpf_task_pt_regs_proto __weak; const struct bpf_func_proto * -bpf_base_func_proto(enum bpf_func_id func_id) +bpf_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_map_lookup_elem: @@ -1714,7 +1714,7 @@ bpf_base_func_proto(enum bpf_func_id func_id) break; } - if (!bpf_capable()) + if (!bpf_token_capable(prog->aux->token, CAP_BPF)) return NULL; switch (func_id) { @@ -1772,7 +1772,7 @@ bpf_base_func_proto(enum bpf_func_id func_id) break; } - if (!perfmon_capable()) + if (!bpf_token_capable(prog->aux->token, CAP_PERFMON)) return NULL; switch (func_id) { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index e02688bebf8e..4ec366f20760 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -5528,7 +5528,7 @@ static const struct bpf_func_proto bpf_sys_bpf_proto = { const struct bpf_func_proto * __weak tracing_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { - return bpf_base_func_proto(func_id); + return bpf_base_func_proto(func_id, prog); } BPF_CALL_1(bpf_sys_close, u32, fd) @@ -5578,7 +5578,8 @@ syscall_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_sys_bpf: - return !perfmon_capable() ? NULL : &bpf_sys_bpf_proto; + return !bpf_token_capable(prog->aux->token, CAP_PERFMON) + ? NULL : &bpf_sys_bpf_proto; case BPF_FUNC_btf_find_by_name_kind: return &bpf_btf_find_by_name_kind_proto; case BPF_FUNC_sys_close: diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 2bc41e6ac9fe..f5382d8bb690 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -1511,7 +1511,7 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_trace_vprintk: return bpf_get_trace_vprintk_proto(); default: - return bpf_base_func_proto(func_id); + return bpf_base_func_proto(func_id, prog); } } diff --git a/net/core/filter.c b/net/core/filter.c index 968139f4a1ac..10d655c140c9 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -83,7 +83,7 @@ #include static const struct bpf_func_proto * -bpf_sk_base_func_proto(enum bpf_func_id func_id); +bpf_sk_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog); int copy_bpf_fprog_from_user(struct sock_fprog *dst, sockptr_t src, int len) { @@ -7726,7 +7726,7 @@ sock_filter_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_ktime_get_coarse_ns: return &bpf_ktime_get_coarse_ns_proto; default: - return bpf_base_func_proto(func_id); + return bpf_base_func_proto(func_id, prog); } } @@ -7809,7 +7809,7 @@ sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return NULL; } default: - return bpf_sk_base_func_proto(func_id); + return bpf_sk_base_func_proto(func_id, prog); } } @@ -7828,7 +7828,7 @@ sk_filter_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_perf_event_output: return &bpf_skb_event_output_proto; default: - return bpf_sk_base_func_proto(func_id); + return bpf_sk_base_func_proto(func_id, prog); } } @@ -8015,7 +8015,7 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) #endif #endif default: - return bpf_sk_base_func_proto(func_id); + return bpf_sk_base_func_proto(func_id, prog); } } @@ -8074,7 +8074,7 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) #endif #endif default: - return bpf_sk_base_func_proto(func_id); + return bpf_sk_base_func_proto(func_id, prog); } #if IS_MODULE(CONFIG_NF_CONNTRACK) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES) @@ -8135,7 +8135,7 @@ sock_ops_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_tcp_sock_proto; #endif /* CONFIG_INET */ default: - return bpf_sk_base_func_proto(func_id); + return bpf_sk_base_func_proto(func_id, prog); } } @@ -8177,7 +8177,7 @@ sk_msg_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_get_cgroup_classid_curr_proto; #endif default: - return bpf_sk_base_func_proto(func_id); + return bpf_sk_base_func_proto(func_id, prog); } } @@ -8221,7 +8221,7 @@ sk_skb_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_skc_lookup_tcp_proto; #endif default: - return bpf_sk_base_func_proto(func_id); + return bpf_sk_base_func_proto(func_id, prog); } } @@ -8232,7 +8232,7 @@ flow_dissector_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_skb_load_bytes: return &bpf_flow_dissector_load_bytes_proto; default: - return bpf_sk_base_func_proto(func_id); + return bpf_sk_base_func_proto(func_id, prog); } } @@ -8259,7 +8259,7 @@ lwt_out_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_skb_under_cgroup: return &bpf_skb_under_cgroup_proto; default: - return bpf_sk_base_func_proto(func_id); + return bpf_sk_base_func_proto(func_id, prog); } } @@ -11090,7 +11090,7 @@ sk_reuseport_func_proto(enum bpf_func_id func_id, case BPF_FUNC_ktime_get_coarse_ns: return &bpf_ktime_get_coarse_ns_proto; default: - return bpf_base_func_proto(func_id); + return bpf_base_func_proto(func_id, prog); } } @@ -11272,7 +11272,7 @@ sk_lookup_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_sk_release: return &bpf_sk_release_proto; default: - return bpf_sk_base_func_proto(func_id); + return bpf_sk_base_func_proto(func_id, prog); } } @@ -11606,7 +11606,7 @@ const struct bpf_func_proto bpf_sock_from_file_proto = { }; static const struct bpf_func_proto * -bpf_sk_base_func_proto(enum bpf_func_id func_id) +bpf_sk_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { const struct bpf_func_proto *func; @@ -11635,10 +11635,10 @@ bpf_sk_base_func_proto(enum bpf_func_id func_id) case BPF_FUNC_ktime_get_coarse_ns: return &bpf_ktime_get_coarse_ns_proto; default: - return bpf_base_func_proto(func_id); + return bpf_base_func_proto(func_id, prog); } - if (!perfmon_capable()) + if (!bpf_token_capable(prog->aux->token, CAP_PERFMON)) return NULL; return func; diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c index 4406d796cc2f..0a3a60e7c282 100644 --- a/net/ipv4/bpf_tcp_ca.c +++ b/net/ipv4/bpf_tcp_ca.c @@ -193,7 +193,7 @@ bpf_tcp_ca_get_func_proto(enum bpf_func_id func_id, case BPF_FUNC_ktime_get_coarse_ns: return &bpf_ktime_get_coarse_ns_proto; default: - return bpf_base_func_proto(func_id); + return bpf_base_func_proto(func_id, prog); } } diff --git a/net/netfilter/nf_bpf_link.c b/net/netfilter/nf_bpf_link.c index c36da56d756f..d7786ea9c01a 100644 --- a/net/netfilter/nf_bpf_link.c +++ b/net/netfilter/nf_bpf_link.c @@ -219,7 +219,7 @@ static bool nf_is_valid_access(int off, int size, enum bpf_access_type type, static const struct bpf_func_proto * bpf_nf_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { - return bpf_base_func_proto(func_id); + return bpf_base_func_proto(func_id, prog); } const struct bpf_verifier_ops netfilter_verifier_ops = { From patchwork Fri Jun 2 15:00:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265462 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8524CC77B7A for ; Fri, 2 Jun 2023 15:01:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236358AbjFBPB0 convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53604 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236357AbjFBPA4 (ORCPT ); Fri, 2 Jun 2023 11:00:56 -0400 Received: from mx0a-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A76B010CC for ; Fri, 2 Jun 2023 08:00:51 -0700 (PDT) Received: from pps.filterd (m0001303.ppops.net [127.0.0.1]) by m0001303.ppops.net (8.17.1.19/8.17.1.19) with ESMTP id 3527V6d7028577 for ; Fri, 2 Jun 2023 08:00:50 -0700 Received: from maileast.thefacebook.com ([163.114.130.16]) by m0001303.ppops.net (PPS) with ESMTPS id 3qyc3papha-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:50 -0700 Received: from twshared40933.03.prn6.facebook.com (2620:10d:c0a8:1c::1b) by mail.thefacebook.com (2620:10d:c0a8:83::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:49 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id F145E31E04AFF; Fri, 2 Jun 2023 08:00:45 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 16/18] bpf: consistenly use BPF token throughout BPF verifier logic Date: Fri, 2 Jun 2023 08:00:09 -0700 Message-ID: <20230602150011.1657856-17-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: 77K8l4aX5MErzfuQaxDmzSaGbUBdgx7l X-Proofpoint-ORIG-GUID: 77K8l4aX5MErzfuQaxDmzSaGbUBdgx7l X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Remove remaining direct queries to perfmon_capable() and bpf_capable() in BPF verifier logic and instead use BPF token (if available) to make decisions about privileges. Signed-off-by: Andrii Nakryiko --- include/linux/bpf.h | 18 ++++++++++-------- include/linux/filter.h | 2 +- kernel/bpf/arraymap.c | 2 +- kernel/bpf/core.c | 2 +- kernel/bpf/verifier.c | 13 ++++++------- net/core/filter.c | 4 ++-- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 9467d093e88e..bd0b448e1a22 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2054,24 +2054,26 @@ bpf_map_alloc_percpu(const struct bpf_map *map, size_t size, size_t align, extern int sysctl_unprivileged_bpf_disabled; -static inline bool bpf_allow_ptr_leaks(void) +bool bpf_token_capable(const struct bpf_token *token, int cap); + +static inline bool bpf_allow_ptr_leaks(const struct bpf_token *token) { - return perfmon_capable(); + return bpf_token_capable(token, CAP_PERFMON); } -static inline bool bpf_allow_uninit_stack(void) +static inline bool bpf_allow_uninit_stack(const struct bpf_token *token) { - return perfmon_capable(); + return bpf_token_capable(token, CAP_PERFMON); } -static inline bool bpf_bypass_spec_v1(void) +static inline bool bpf_bypass_spec_v1(const struct bpf_token *token) { - return perfmon_capable(); + return bpf_token_capable(token, CAP_PERFMON); } -static inline bool bpf_bypass_spec_v4(void) +static inline bool bpf_bypass_spec_v4(const struct bpf_token *token) { - return perfmon_capable(); + return bpf_token_capable(token, CAP_PERFMON); } int bpf_map_new_fd(struct bpf_map *map, int flags); diff --git a/include/linux/filter.h b/include/linux/filter.h index bbce89937fde..60c0420143b3 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -1110,7 +1110,7 @@ static inline bool bpf_jit_blinding_enabled(struct bpf_prog *prog) return false; if (!bpf_jit_harden) return false; - if (bpf_jit_harden == 1 && bpf_capable()) + if (bpf_jit_harden == 1 && bpf_token_capable(prog->aux->token, CAP_BPF)) return false; return true; diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 2058e89b5ddd..f0c64df6b6ff 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -82,7 +82,7 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr) bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY; int numa_node = bpf_map_attr_numa_node(attr); u32 elem_size, index_mask, max_entries; - bool bypass_spec_v1 = bpf_bypass_spec_v1(); + bool bypass_spec_v1 = bpf_bypass_spec_v1(NULL); u64 array_size, mask64; struct bpf_array *array; diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index cd0a93968009..c48303e097ec 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -661,7 +661,7 @@ static bool bpf_prog_kallsyms_candidate(const struct bpf_prog *fp) void bpf_prog_kallsyms_add(struct bpf_prog *fp) { if (!bpf_prog_kallsyms_candidate(fp) || - !bpf_capable()) + !bpf_token_capable(fp->aux->token, CAP_BPF)) return; bpf_prog_ksym_set_addr(fp); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 086b2a14905b..9a0a93fa2c15 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -19219,7 +19219,12 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3 env->prog = *prog; env->ops = bpf_verifier_ops[env->prog->type]; env->fd_array = make_bpfptr(attr->fd_array, uattr.is_kernel); - is_priv = bpf_capable(); + + env->allow_ptr_leaks = bpf_allow_ptr_leaks(env->prog->aux->token); + env->allow_uninit_stack = bpf_allow_uninit_stack(env->prog->aux->token); + env->bypass_spec_v1 = bpf_bypass_spec_v1(env->prog->aux->token); + env->bypass_spec_v4 = bpf_bypass_spec_v4(env->prog->aux->token); + env->bpf_capable = is_priv = bpf_token_capable(env->prog->aux->token, CAP_BPF); bpf_get_btf_vmlinux(); @@ -19251,12 +19256,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3 if (attr->prog_flags & BPF_F_ANY_ALIGNMENT) env->strict_alignment = false; - env->allow_ptr_leaks = bpf_allow_ptr_leaks(); - env->allow_uninit_stack = bpf_allow_uninit_stack(); - env->bypass_spec_v1 = bpf_bypass_spec_v1(); - env->bypass_spec_v4 = bpf_bypass_spec_v4(); - env->bpf_capable = bpf_capable(); - if (is_priv) env->test_state_freq = attr->prog_flags & BPF_F_TEST_STATE_FREQ; diff --git a/net/core/filter.c b/net/core/filter.c index 10d655c140c9..1b4b2ae1cedc 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -8434,7 +8434,7 @@ static bool cg_skb_is_valid_access(int off, int size, return false; case bpf_ctx_range(struct __sk_buff, data): case bpf_ctx_range(struct __sk_buff, data_end): - if (!bpf_capable()) + if (!bpf_token_capable(prog->aux->token, CAP_BPF)) return false; break; } @@ -8446,7 +8446,7 @@ static bool cg_skb_is_valid_access(int off, int size, case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]): break; case bpf_ctx_range(struct __sk_buff, tstamp): - if (!bpf_capable()) + if (!bpf_token_capable(prog->aux->token, CAP_BPF)) return false; break; default: From patchwork Fri Jun 2 15:00:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265470 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5C61FC7EE29 for ; Fri, 2 Jun 2023 15:01:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236388AbjFBPB4 convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53604 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236405AbjFBPBZ (ORCPT ); Fri, 2 Jun 2023 11:01:25 -0400 Received: from mx0a-00082601.pphosted.com (mx0a-00082601.pphosted.com [67.231.145.42]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7D50B10C1 for ; Fri, 2 Jun 2023 08:01:16 -0700 (PDT) Received: from pps.filterd (m0044012.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 352CCwRs020050 for ; Fri, 2 Jun 2023 08:01:15 -0700 Received: from mail.thefacebook.com ([163.114.132.120]) by mx0a-00082601.pphosted.com (PPS) with ESMTPS id 3qxcvfnphw-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:01:12 -0700 Received: from twshared52232.38.frc1.facebook.com (2620:10d:c085:208::f) by mail.thefacebook.com (2620:10d:c085:21d::4) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:58 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 17BF531E04B54; Fri, 2 Jun 2023 08:00:48 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 17/18] libbpf: add BPF token support to bpf_prog_load() API Date: Fri, 2 Jun 2023 08:00:10 -0700 Message-ID: <20230602150011.1657856-18-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-ORIG-GUID: UWpiaPOxzTbICuBDQwU_3WLTFUcgZ_kN X-Proofpoint-GUID: UWpiaPOxzTbICuBDQwU_3WLTFUcgZ_kN X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Wire through token_fd into bpf_prog_load(). Also make sure to pass allowed_{prog,attach}_types to kernel in bpf_token_create(). Signed-off-by: Andrii Nakryiko --- tools/lib/bpf/bpf.c | 5 ++++- tools/lib/bpf/bpf.h | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 193993dbbdc4..cd8f0c525de6 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -234,7 +234,7 @@ int bpf_prog_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, size_t insn_cnt, struct bpf_prog_load_opts *opts) { - const size_t attr_sz = offsetofend(union bpf_attr, log_true_size); + const size_t attr_sz = offsetofend(union bpf_attr, prog_token_fd); void *finfo = NULL, *linfo = NULL; const char *func_info, *line_info; __u32 log_size, log_level, attach_prog_fd, attach_btf_obj_fd; @@ -263,6 +263,7 @@ int bpf_prog_load(enum bpf_prog_type prog_type, attr.prog_flags = OPTS_GET(opts, prog_flags, 0); attr.prog_ifindex = OPTS_GET(opts, prog_ifindex, 0); attr.kern_version = OPTS_GET(opts, kern_version, 0); + attr.prog_token_fd = OPTS_GET(opts, token_fd, 0); if (prog_name && kernel_supports(NULL, FEAT_PROG_NAME)) libbpf_strlcpy(attr.prog_name, prog_name, sizeof(attr.prog_name)); @@ -1220,6 +1221,8 @@ int bpf_token_create(struct bpf_token_create_opts *opts) attr.token_create.token_fd = OPTS_GET(opts, token_fd, 0); attr.token_create.allowed_cmds = OPTS_GET(opts, allowed_cmds, 0); attr.token_create.allowed_map_types = OPTS_GET(opts, allowed_map_types, 0); + attr.token_create.allowed_prog_types = OPTS_GET(opts, allowed_prog_types, 0); + attr.token_create.allowed_attach_types = OPTS_GET(opts, allowed_attach_types, 0); ret = sys_bpf_fd(BPF_TOKEN_CREATE, &attr, attr_sz); return libbpf_err_errno(ret); diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 3153a9e697e2..f9afc7846762 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -104,9 +104,10 @@ struct bpf_prog_load_opts { * If kernel doesn't support this feature, log_size is left unchanged. */ __u32 log_true_size; + __u32 token_fd; size_t :0; }; -#define bpf_prog_load_opts__last_field log_true_size +#define bpf_prog_load_opts__last_field token_fd LIBBPF_API int bpf_prog_load(enum bpf_prog_type prog_type, const char *prog_name, const char *license, @@ -560,9 +561,11 @@ struct bpf_token_create_opts { __u32 token_fd; __u64 allowed_cmds; __u64 allowed_map_types; + __u64 allowed_prog_types; + __u64 allowed_attach_types; size_t :0; }; -#define bpf_token_create_opts__last_field allowed_map_types +#define bpf_token_create_opts__last_field allowed_attach_types LIBBPF_API int bpf_token_create(struct bpf_token_create_opts *opts); From patchwork Fri Jun 2 15:00:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrii Nakryiko X-Patchwork-Id: 13265465 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1B588C7EE29 for ; Fri, 2 Jun 2023 15:01:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229447AbjFBPBg convert rfc822-to-8bit (ORCPT ); Fri, 2 Jun 2023 11:01:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53820 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236314AbjFBPBF (ORCPT ); Fri, 2 Jun 2023 11:01:05 -0400 Received: from mx0a-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 593CAE53 for ; Fri, 2 Jun 2023 08:01:00 -0700 (PDT) Received: from pps.filterd (m0001303.ppops.net [127.0.0.1]) by m0001303.ppops.net (8.17.1.19/8.17.1.19) with ESMTP id 3527Y5qj003045 for ; Fri, 2 Jun 2023 08:00:59 -0700 Received: from mail.thefacebook.com ([163.114.132.120]) by m0001303.ppops.net (PPS) with ESMTPS id 3qyc3papjm-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Fri, 02 Jun 2023 08:00:59 -0700 Received: from twshared44841.48.prn1.facebook.com (2620:10d:c085:208::f) by mail.thefacebook.com (2620:10d:c085:11d::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Fri, 2 Jun 2023 08:00:58 -0700 Received: by devbig019.vll3.facebook.com (Postfix, from userid 137359) id 261E131E04B7C; Fri, 2 Jun 2023 08:00:50 -0700 (PDT) From: Andrii Nakryiko To: CC: , , , , , Subject: [PATCH RESEND bpf-next 18/18] selftests/bpf: add BPF token-enabled BPF_PROG_LOAD tests Date: Fri, 2 Jun 2023 08:00:11 -0700 Message-ID: <20230602150011.1657856-19-andrii@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230602150011.1657856-1-andrii@kernel.org> References: <20230602150011.1657856-1-andrii@kernel.org> MIME-Version: 1.0 X-FB-Internal: Safe X-Proofpoint-GUID: kH4hUrNOt53NNaUsCxM7Xt875ejki43i X-Proofpoint-ORIG-GUID: kH4hUrNOt53NNaUsCxM7Xt875ejki43i X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-02_11,2023-06-02_02,2023-05-22_02 Precedence: bulk List-ID: Add a test validating that BPF token can be used to load privileged BPF program using privileged BPF helpers through delegated BPF token created by privileged process. Signed-off-by: Andrii Nakryiko --- .../testing/selftests/bpf/prog_tests/token.c | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/token.c b/tools/testing/selftests/bpf/prog_tests/token.c index b141f722c0c6..d5093ededf06 100644 --- a/tools/testing/selftests/bpf/prog_tests/token.c +++ b/tools/testing/selftests/bpf/prog_tests/token.c @@ -4,6 +4,7 @@ #include #include #include "cap_helpers.h" +#include static int drop_priv_caps(__u64 *old_caps) { @@ -191,6 +192,83 @@ static void subtest_btf_token(void) ASSERT_OK(restore_priv_caps(old_caps), "restore_caps"); } +static void subtest_prog_token(void) +{ + LIBBPF_OPTS(bpf_token_create_opts, token_opts); + LIBBPF_OPTS(bpf_prog_load_opts, prog_opts); + int token_fd = 0, prog_fd = 0; + __u64 old_caps = 0; + struct bpf_insn insns[] = { + /* bpf_jiffies64() requires CAP_BPF */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), + /* bpf_get_current_task() requires CAP_PERFMON */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_current_task), + /* r0 = 0; exit; */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + size_t insn_cnt = ARRAY_SIZE(insns); + + /* check that IGNORE_UNKNOWN_PROG_TYPES flag is respected */ + token_opts.flags = BPF_F_TOKEN_IGNORE_UNKNOWN_PROG_TYPES; + token_opts.allowed_prog_types = ~0ULL; /* any current and future prog type is allowed */ + token_opts.allowed_attach_types = 0; + token_fd = bpf_token_create(&token_opts); + if (!ASSERT_GT(token_fd, 0, "token_create_prog_type_future_proof")) + return; + close(token_fd); + + /* check that IGNORE_UNKNOWN_ATTACH_TYPES flag is respected */ + token_opts.flags = BPF_F_TOKEN_IGNORE_UNKNOWN_ATTACH_TYPES; + token_opts.allowed_prog_types = 0; + token_opts.allowed_attach_types = ~0ULL; /* any current and future attach type is allowed */ + token_fd = bpf_token_create(&token_opts); + if (!ASSERT_GT(token_fd, 0, "token_create_prog_type_future_proof")) + return; + close(token_fd); + + /* create BPF token allowing BPF_PROG_LOAD command */ + token_opts.flags = 0; + token_opts.allowed_cmds = 1ULL << BPF_PROG_LOAD; + token_opts.allowed_prog_types = 1ULL << BPF_PROG_TYPE_XDP; + token_opts.allowed_attach_types = 1ULL << BPF_XDP; + token_fd = bpf_token_create(&token_opts); + if (!ASSERT_GT(token_fd, 0, "token_create")) + return; + + /* drop privileges to test token_fd passing */ + if (!ASSERT_OK(drop_priv_caps(&old_caps), "drop_caps")) + goto cleanup; + + /* validate we can successfully load BPF program with token; this + * being XDP program (CAP_NET_ADMIN) using bpf_jiffies64() (CAP_BPF) + * and bpf_get_current_task() (CAP_PERFMON) helpers validates we have + * BPF token wired properly in a bunch of places in the kernel + */ + prog_opts.token_fd = token_fd; + prog_opts.expected_attach_type = BPF_XDP; + prog_fd = bpf_prog_load(BPF_PROG_TYPE_XDP, "token_prog", "GPL", + insns, insn_cnt, &prog_opts); + if (!ASSERT_GT(prog_fd, 0, "prog_fd")) + goto cleanup; + close(prog_fd); + + /* now validate that we *cannot* load BPF program without token */ + prog_opts.token_fd = 0; + prog_fd = bpf_prog_load(BPF_PROG_TYPE_XDP, "token_prog", "GPL", + insns, insn_cnt, &prog_opts); + if (!ASSERT_EQ(prog_fd, -EPERM, "prog_fd_eperm")) + goto cleanup; + +cleanup: + if (prog_fd > 0) + close(prog_fd); + if (token_fd) + close(token_fd); + if (old_caps) + ASSERT_OK(restore_priv_caps(old_caps), "restore_caps"); +} + void test_token(void) { if (test__start_subtest("token_create")) @@ -199,4 +277,6 @@ void test_token(void) subtest_map_token(); if (test__start_subtest("btf_token")) subtest_btf_token(); + if (test__start_subtest("prog_token")) + subtest_prog_token(); }