[bpf-next,v3,05/10] bpf: lsm: BTF API for LSM hooks
diff mbox series

Message ID 20200123152440.28956-6-kpsingh@chromium.org
State New
Headers show
Series
  • MAC and Audit policy using eBPF (KRSI)
Related show

Commit Message

KP Singh Jan. 23, 2020, 3:24 p.m. UTC
From: KP Singh <kpsingh@google.com>

The BTF API provides information required by the BPF verifier to
attach eBPF programs to the LSM hooks by using the BTF information of
two types:

- struct security_hook_heads: This type provides the offset (using the
  lsm_hook_idx passed by the userspace) to which a new dynamically
  allocated security hook must be attached
- union security_list_options: This provides the information about the
  function prototype required by the hook

The type_ids for these types are calculated once during __init as
bpf_type_by_name_kind does an expensive linear search with string
compariason.  Furthermore, the total number of LSM hooks which can be
determined from the btf_type_vlen of security_hook_heads is needed (in a
subsequent patch) to initialize the mutable hooks for the BPF LSM.

When the program is loaded:

- The verifier receives the index of a member in struct
  security_hook_heads to which a program must be attached as
  prog->aux->lsm_hook_idx.
- bpf_lsm_type_by_idx is used to determine the func_proto of
  the LSM hook and updates prog->aux->attach_func_proto
- bpf_lsm_head_by_idx is used to determine the hlist_head to which
  the BPF program must be attached.

Reviewed-by: Brendan Jackman <jackmanb@google.com>
Reviewed-by: Florent Revest <revest@google.com>
Reviewed-by: Thomas Garnier <thgarnie@google.com>
Signed-off-by: KP Singh <kpsingh@google.com>
---
 include/linux/bpf_lsm.h        | 12 ++++++
 security/bpf/Kconfig           |  1 +
 security/bpf/Makefile          |  2 +
 security/bpf/hooks.c           | 75 ++++++++++++++++++++++++++++++++++
 security/bpf/include/bpf_lsm.h | 21 ++++++++++
 security/bpf/lsm.c             | 35 ++++++++++++++++
 6 files changed, 146 insertions(+)
 create mode 100644 security/bpf/include/bpf_lsm.h

Patch
diff mbox series

diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index 57c20b2cd2f4..5e61c0736001 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -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 */
 
diff --git a/security/bpf/Kconfig b/security/bpf/Kconfig
index 595e4ad597ae..9438d899b618 100644
--- a/security/bpf/Kconfig
+++ b/security/bpf/Kconfig
@@ -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.
diff --git a/security/bpf/Makefile b/security/bpf/Makefile
index c526927c337d..748b9b7d4bc7 100644
--- a/security/bpf/Makefile
+++ b/security/bpf/Makefile
@@ -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
diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
index b123d9cb4cd4..e9dc6933b6fa 100644
--- a/security/bpf/hooks.c
+++ b/security/bpf/hooks.c
@@ -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);
+}
diff --git a/security/bpf/include/bpf_lsm.h b/security/bpf/include/bpf_lsm.h
new file mode 100644
index 000000000000..f142596d97bd
--- /dev/null
+++ b/security/bpf/include/bpf_lsm.h
@@ -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 */
diff --git a/security/bpf/lsm.c b/security/bpf/lsm.c
index a25a068e1781..736e0ee3f926 100644
--- a/security/bpf/lsm.c
+++ b/security/bpf/lsm.c
@@ -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");