@@ -19,6 +19,8 @@ extern struct security_hook_heads bpf_lsm_hook_heads;
int bpf_lsm_srcu_read_lock(void);
void bpf_lsm_srcu_read_unlock(int idx);
+const struct btf_type *bpf_lsm_type_by_idx(struct btf *btf, u32 offset);
+const struct btf_member *bpf_lsm_head_by_idx(struct btf *btf, u32 idx);
#define CALL_BPF_LSM_VOID_HOOKS(FUNC, ...) \
do { \
@@ -66,6 +68,16 @@ static inline int bpf_lsm_srcu_read_lock(void)
return 0;
}
static inline void bpf_lsm_srcu_read_unlock(int idx) {}
+static inline const struct btf_type *bpf_lsm_type_by_idx(
+ struct btf *btf, u32 idx)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+static inline const struct btf_member *bpf_lsm_head_by_idx(
+ struct btf *btf, u32 idx)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
#endif /* CONFIG_SECURITY_BPF */
@@ -7,6 +7,7 @@ config SECURITY_BPF
depends on SECURITY
depends on BPF_SYSCALL
depends on SRCU
+ depends on DEBUG_INFO_BTF
help
This enables instrumentation of the security hooks with
eBPF programs.
@@ -3,3 +3,5 @@
# Copyright 2019 Google LLC.
obj-$(CONFIG_SECURITY_BPF) := lsm.o ops.o hooks.o
+
+ccflags-y := -I$(srctree)/security/bpf -I$(srctree)/security/bpf/include
@@ -5,8 +5,12 @@
*/
#include <linux/bpf_lsm.h>
+#include <linux/bpf.h>
+#include <linux/btf.h>
#include <linux/srcu.h>
+#include "bpf_lsm.h"
+
DEFINE_STATIC_SRCU(security_hook_srcu);
int bpf_lsm_srcu_read_lock(void)
@@ -18,3 +22,74 @@ void bpf_lsm_srcu_read_unlock(int idx)
{
return srcu_read_unlock(&security_hook_srcu, idx);
}
+
+static inline int validate_hlist_head(struct btf *btf,
+ const struct btf_member *member)
+{
+ const struct btf_type *t;
+
+ t = btf_type_by_id(btf, member->type);
+ if (unlikely(!t))
+ return -EINVAL;
+
+ if (BTF_INFO_KIND(t->info) != BTF_KIND_STRUCT)
+ return -EINVAL;
+
+ if (t->size != sizeof(struct hlist_head))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* Find the BTF representation of the security_hook_heads member for a member
+ * with a given index in struct security_hook_heads.
+ */
+const struct btf_member *bpf_lsm_head_by_idx(struct btf *btf, u32 idx)
+{
+ const struct btf_member *member;
+ int ret;
+
+ if (idx >= btf_type_vlen(bpf_lsm_info.btf_hook_heads))
+ return ERR_PTR(-EINVAL);
+
+ member = btf_type_member(bpf_lsm_info.btf_hook_heads) + idx;
+ ret = validate_hlist_head(btf, member);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return member;
+}
+
+/* Given an index of a member in security_hook_heads return the
+ * corresponding type for the LSM hook. The members of the union
+ * security_list_options have the same name as the security_hook_heads which
+ * is ensured by the LSM_HOOK_INIT macro defined in include/linux/lsm_hooks.h
+ */
+const struct btf_type *bpf_lsm_type_by_idx(struct btf *btf, u32 idx)
+{
+ const struct btf_member *member, *hook_head = NULL;
+ const struct btf_type *t;
+ u32 i;
+
+ hook_head = bpf_lsm_head_by_idx(btf, idx);
+ if (IS_ERR(hook_head))
+ return ERR_PTR(PTR_ERR(hook_head));
+
+ for_each_member(i, bpf_lsm_info.btf_hook_types, member) {
+ if (hook_head->name_off == member->name_off) {
+ t = btf_type_by_id(btf, member->type);
+ if (unlikely(!t))
+ return ERR_PTR(-EINVAL);
+
+ if (!btf_type_is_ptr(t))
+ return ERR_PTR(-EINVAL);
+
+ t = btf_type_by_id(btf, t->type);
+ if (unlikely(!t))
+ return ERR_PTR(-EINVAL);
+ return t;
+ }
+ }
+
+ return ERR_PTR(-ESRCH);
+}
new file mode 100644
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _BPF_LSM_H
+#define _BPF_LSM_H
+
+#include <linux/filter.h>
+#include <linux/bpf.h>
+#include <linux/btf.h>
+
+struct bpf_lsm_info {
+ /* BTF type for security_hook_heads populated at init.
+ */
+ const struct btf_type *btf_hook_heads;
+ /* BTF type for security_list_options populated at init.
+ */
+ const struct btf_type *btf_hook_types;
+};
+
+extern struct bpf_lsm_info bpf_lsm_info;
+
+#endif /* _BPF_LSM_H */
@@ -7,6 +7,8 @@
#include <linux/bpf_lsm.h>
#include <linux/lsm_hooks.h>
+#include "bpf_lsm.h"
+
/* This is only for internal hooks, always statically shipped as part of the
* BPF LSM. Statically defined hooks are appended to the security_hook_heads
* which is common for LSMs and R/O after init.
@@ -19,6 +21,39 @@ static struct security_hook_list bpf_lsm_hooks[] __lsm_ro_after_init = {};
*/
struct security_hook_heads bpf_lsm_hook_heads;
+struct bpf_lsm_info bpf_lsm_info;
+
+static __init int bpf_lsm_info_init(void)
+{
+ const struct btf_type *t;
+
+ if (!btf_vmlinux)
+ /* No need to grab any locks because we are still in init */
+ btf_vmlinux = btf_parse_vmlinux();
+
+ if (IS_ERR(btf_vmlinux)) {
+ pr_err("btf_vmlinux is malformed\n");
+ return PTR_ERR(btf_vmlinux);
+ }
+
+ t = btf_type_by_name_kind(btf_vmlinux, "security_hook_heads",
+ BTF_KIND_STRUCT);
+ if (WARN_ON(IS_ERR(t)))
+ return PTR_ERR(t);
+
+ bpf_lsm_info.btf_hook_heads = t;
+
+ t = btf_type_by_name_kind(btf_vmlinux, "security_list_options",
+ BTF_KIND_UNION);
+ if (WARN_ON(IS_ERR(t)))
+ return PTR_ERR(t);
+
+ bpf_lsm_info.btf_hook_types = t;
+ return 0;
+}
+
+late_initcall(bpf_lsm_info_init);
+
static int __init bpf_lsm_init(void)
{
security_add_hooks(bpf_lsm_hooks, ARRAY_SIZE(bpf_lsm_hooks), "bpf");