From: KP Singh <kpsingh@xxxxxxxxxx> The BPF LSM programs are implemented as fexit trampolines to avoid the overhead of retpolines. These programs cannot be attached to security_* wrappers as there are quite a few security_* functions that do more than just calling the LSM callbacks. This was discussed on the lists in: https://lore.kernel.org/bpf/20200123152440.28956-1-kpsingh@xxxxxxxxxxxx/T/#m068becce588a0cdf01913f368a97aea4c62d8266 Adding a NOP callback after all the static LSM callbacks are called has the following benefits: - The BPF programs run at the right stage of the security_* wrappers. - They run after all the static LSM hooks allowed the operation, therefore cannot allow an action that was already denied. There are some hooks which do not call call_int_hooks or call_void_hooks. It's not possible to call the bpf_lsm_* functions without checking if there is BPF LSM program attached to these hooks. This is added further in a subsequent patch. For now, these hooks are marked as NO_BPF (i.e. attachment of BPF programs is not possible). Signed-off-by: KP Singh <kpsingh@xxxxxxxxxx> --- include/linux/bpf_lsm.h | 34 ++++++++++++++++++++++++++++++++++ kernel/bpf/bpf_lsm.c | 16 ++++++++++++++++ security/security.c | 3 +++ 3 files changed, 53 insertions(+) create mode 100644 include/linux/bpf_lsm.h diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h new file mode 100644 index 000000000000..f867f72f6aa9 --- /dev/null +++ b/include/linux/bpf_lsm.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright 2019 Google LLC. + */ + +#ifndef _LINUX_BPF_LSM_H +#define _LINUX_BPF_LSM_H + +#include <linux/bpf.h> + +#ifdef CONFIG_BPF_LSM + +#define LSM_HOOK(RET, NAME, ...) RET bpf_lsm_##NAME(__VA_ARGS__); +#include <linux/lsm_hook_names.h> +#undef LSM_HOOK + +#define RUN_BPF_LSM_VOID_PROGS(FUNC, ...) bpf_lsm_##FUNC(__VA_ARGS__) +#define RUN_BPF_LSM_INT_PROGS(RC, FUNC, ...) ({ \ + do { \ + if (RC == 0) \ + RC = bpf_lsm_##FUNC(__VA_ARGS__); \ + } while (0); \ + RC; \ +}) + +#else /* !CONFIG_BPF_LSM */ + +#define RUN_BPF_LSM_INT_PROGS(RC, FUNC, ...) (RC) +#define RUN_BPF_LSM_VOID_PROGS(FUNC, ...) + +#endif /* CONFIG_BPF_LSM */ + +#endif /* _LINUX_BPF_LSM_H */ diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index affb6941622e..abc847c9b9a1 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -7,6 +7,22 @@ #include <linux/filter.h> #include <linux/bpf.h> #include <linux/btf.h> +#include <linux/bpf_lsm.h> + +/* For every LSM hook that allows attachment of BPF programs, declare a NOP + * function where a BPF program can be attached as an fexit trampoline. + */ +#define LSM_HOOK(RET, NAME, ...) LSM_HOOK_##RET(NAME, __VA_ARGS__) +#define LSM_HOOK_int(NAME, ...) noinline int bpf_lsm_##NAME(__VA_ARGS__) \ +{ \ + return 0; \ +} + +#define LSM_HOOK_void(NAME, ...) \ + noinline void bpf_lsm_##NAME(__VA_ARGS__) {} + +#include <linux/lsm_hook_names.h> +#undef LSM_HOOK const struct bpf_prog_ops lsm_prog_ops = { }; diff --git a/security/security.c b/security/security.c index 565bc9b67276..aa111392a700 100644 --- a/security/security.c +++ b/security/security.c @@ -28,6 +28,7 @@ #include <linux/string.h> #include <linux/msg.h> #include <net/flow.h> +#include <linux/bpf_lsm.h> #define MAX_LSM_EVM_XATTR 2 @@ -684,6 +685,7 @@ static void __init lsm_early_task(struct task_struct *task) \ hlist_for_each_entry(P, &security_hook_heads.FUNC, list) \ P->hook.FUNC(__VA_ARGS__); \ + RUN_BPF_LSM_VOID_PROGS(FUNC, __VA_ARGS__); \ } while (0) #define call_int_hook(FUNC, IRC, ...) ({ \ @@ -696,6 +698,7 @@ static void __init lsm_early_task(struct task_struct *task) if (RC != 0) \ break; \ } \ + RC = RUN_BPF_LSM_INT_PROGS(RC, FUNC, __VA_ARGS__); \ } while (0); \ RC; \ }) -- 2.20.1