On Wed, Apr 06, 2022 at 03:13:37AM IST, Stanislav Fomichev wrote: > Allow attaching to lsm hooks in the cgroup context. > > Attaching to per-cgroup LSM works exactly like attaching > to other per-cgroup hooks. New BPF_LSM_CGROUP is added > to trigger new mode; the actual lsm hook we attach to is > signaled via existing attach_btf_id. > > For the hooks that have 'struct socket' as its first argument, > we use the cgroup associated with that socket. For the rest, > we use 'current' cgroup (this is all on default hierarchy == v2 only). > Note that for the hooks that work on 'struct sock' we still > take the cgroup from 'current' because most of the time, > the 'sock' argument is not properly initialized. > > Behind the scenes, we allocate a shim program that is attached > to the trampoline and runs cgroup effective BPF programs array. > This shim has some rudimentary ref counting and can be shared > between several programs attaching to the same per-cgroup lsm hook. > > Note that this patch bloats cgroup size because we add 211 > cgroup_bpf_attach_type(s) for simplicity sake. This will be > addressed in the subsequent patch. > > Signed-off-by: Stanislav Fomichev <sdf@xxxxxxxxxx> > --- > include/linux/bpf-cgroup-defs.h | 6 ++ > include/linux/bpf.h | 13 +++ > include/linux/bpf_lsm.h | 14 ++++ > include/uapi/linux/bpf.h | 1 + > kernel/bpf/bpf_lsm.c | 92 ++++++++++++++++++++ > kernel/bpf/btf.c | 11 +++ > kernel/bpf/cgroup.c | 116 ++++++++++++++++++++++--- > kernel/bpf/syscall.c | 10 +++ > kernel/bpf/trampoline.c | 144 ++++++++++++++++++++++++++++++++ > kernel/bpf/verifier.c | 1 + > tools/include/uapi/linux/bpf.h | 1 + > 11 files changed, 397 insertions(+), 12 deletions(-) > > diff --git a/include/linux/bpf-cgroup-defs.h b/include/linux/bpf-cgroup-defs.h > index 695d1224a71b..6c661b4df9fa 100644 > --- a/include/linux/bpf-cgroup-defs.h > +++ b/include/linux/bpf-cgroup-defs.h > @@ -10,6 +10,8 @@ > > struct bpf_prog_array; > > +#define CGROUP_LSM_NUM 211 /* will be addressed in the next patch */ > + > enum cgroup_bpf_attach_type { > CGROUP_BPF_ATTACH_TYPE_INVALID = -1, > CGROUP_INET_INGRESS = 0, > @@ -35,6 +37,10 @@ enum cgroup_bpf_attach_type { > CGROUP_INET4_GETSOCKNAME, > CGROUP_INET6_GETSOCKNAME, > CGROUP_INET_SOCK_RELEASE, > +#ifdef CONFIG_BPF_LSM > + CGROUP_LSM_START, > + CGROUP_LSM_END = CGROUP_LSM_START + CGROUP_LSM_NUM - 1, > +#endif > MAX_CGROUP_BPF_ATTACH_TYPE > }; > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h > index 487aba40ce52..17bbe2f7b2be 100644 > --- a/include/linux/bpf.h > +++ b/include/linux/bpf.h > @@ -807,6 +807,9 @@ static __always_inline __nocfi unsigned int bpf_dispatcher_nop_func( > #ifdef CONFIG_BPF_JIT > int bpf_trampoline_link_prog(struct bpf_prog *prog, struct bpf_trampoline *tr); > int bpf_trampoline_unlink_prog(struct bpf_prog *prog, struct bpf_trampoline *tr); > +int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog, > + struct bpf_attach_target_info *tgt_info); > +void bpf_trampoline_unlink_cgroup_shim(struct bpf_prog *prog); > struct bpf_trampoline *bpf_trampoline_get(u64 key, > struct bpf_attach_target_info *tgt_info); > void bpf_trampoline_put(struct bpf_trampoline *tr); > @@ -865,6 +868,14 @@ static inline int bpf_trampoline_unlink_prog(struct bpf_prog *prog, > { > return -ENOTSUPP; > } > +static inline int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog, > + struct bpf_attach_target_info *tgt_info) > +{ > + return -EOPNOTSUPP; > +} > +static inline void bpf_trampoline_unlink_cgroup_shim(struct bpf_prog *prog) > +{ > +} > static inline struct bpf_trampoline *bpf_trampoline_get(u64 key, > struct bpf_attach_target_info *tgt_info) > { > @@ -980,6 +991,7 @@ struct bpf_prog_aux { > u64 load_time; /* ns since boottime */ > u32 verified_insns; > struct bpf_map *cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE]; > + int cgroup_atype; /* enum cgroup_bpf_attach_type */ > char name[BPF_OBJ_NAME_LEN]; > #ifdef CONFIG_SECURITY > void *security; > @@ -2383,6 +2395,7 @@ void *bpf_arch_text_copy(void *dst, void *src, size_t len); > > struct btf_id_set; > bool btf_id_set_contains(const struct btf_id_set *set, u32 id); > +int btf_id_set_index(const struct btf_id_set *set, u32 id); > > #define MAX_BPRINTF_VARARGS 12 > > diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h > index 479c101546ad..7f0e59f5f9be 100644 > --- a/include/linux/bpf_lsm.h > +++ b/include/linux/bpf_lsm.h > @@ -42,6 +42,9 @@ extern const struct bpf_func_proto bpf_inode_storage_get_proto; > extern const struct bpf_func_proto bpf_inode_storage_delete_proto; > void bpf_inode_storage_free(struct inode *inode); > > +int bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog, bpf_func_t *bpf_func); > +int bpf_lsm_hook_idx(u32 btf_id); > + > #else /* !CONFIG_BPF_LSM */ > > static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id) > @@ -65,6 +68,17 @@ static inline void bpf_inode_storage_free(struct inode *inode) > { > } > > +static inline int bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog, > + bpf_func_t *bpf_func) > +{ > + return -ENOENT; > +} > + > +static inline int bpf_lsm_hook_idx(u32 btf_id) > +{ > + return -EINVAL; > +} > + > #endif /* CONFIG_BPF_LSM */ > > #endif /* _LINUX_BPF_LSM_H */ > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h > index d14b10b85e51..bbe48a2dd852 100644 > --- a/include/uapi/linux/bpf.h > +++ b/include/uapi/linux/bpf.h > @@ -998,6 +998,7 @@ enum bpf_attach_type { > BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, > BPF_PERF_EVENT, > BPF_TRACE_KPROBE_MULTI, > + BPF_LSM_CGROUP, > __MAX_BPF_ATTACH_TYPE > }; > > diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c > index 064eccba641d..eca258ba71d8 100644 > --- a/kernel/bpf/bpf_lsm.c > +++ b/kernel/bpf/bpf_lsm.c > @@ -35,6 +35,98 @@ BTF_SET_START(bpf_lsm_hooks) > #undef LSM_HOOK > BTF_SET_END(bpf_lsm_hooks) > > +static unsigned int __cgroup_bpf_run_lsm_socket(const void *ctx, > + const struct bpf_insn *insn) > +{ > + const struct bpf_prog *prog; > + struct socket *sock; > + struct cgroup *cgrp; > + struct sock *sk; > + int ret = 0; > + u64 *regs; > + > + regs = (u64 *)ctx; > + sock = (void *)(unsigned long)regs[BPF_REG_0]; > + /*prog = container_of(insn, struct bpf_prog, insnsi);*/ > + prog = (const struct bpf_prog *)((void *)insn - offsetof(struct bpf_prog, insnsi)); > + > + if (unlikely(!sock)) > + return 0; > + > + sk = sock->sk; > + if (unlikely(!sk)) > + return 0; > + > + cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); > + if (likely(cgrp)) > + ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[prog->aux->cgroup_atype], > + ctx, bpf_prog_run, 0); > + return ret; > +} > + > +static unsigned int __cgroup_bpf_run_lsm_current(const void *ctx, > + const struct bpf_insn *insn) > +{ > + const struct bpf_prog *prog; > + struct cgroup *cgrp; > + int ret = 0; > + > + if (unlikely(!current)) > + return 0; > + > + /*prog = container_of(insn, struct bpf_prog, insnsi);*/ > + prog = (const struct bpf_prog *)((void *)insn - offsetof(struct bpf_prog, insnsi)); > + > + rcu_read_lock(); > + cgrp = task_dfl_cgroup(current); > + if (likely(cgrp)) > + ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[prog->aux->cgroup_atype], > + ctx, bpf_prog_run, 0); > + rcu_read_unlock(); > + return ret; > +} > + > +int bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog, > + bpf_func_t *bpf_func) > +{ > + const struct btf_type *first_arg_type; > + const struct btf_type *sock_type; > + const struct btf *btf_vmlinux; > + const struct btf_param *args; > + s32 type_id; > + > + if (!prog->aux->attach_func_proto || > + !btf_type_is_func_proto(prog->aux->attach_func_proto)) > + return -EINVAL; > + > + if (btf_type_vlen(prog->aux->attach_func_proto) < 1) > + return -EINVAL; > + > + args = (const struct btf_param *)(prog->aux->attach_func_proto + 1); > + > + btf_vmlinux = bpf_get_btf_vmlinux(); > + if (!btf_vmlinux) We should use IS_ERR_OR_NULL to check the result, e.g. see: 7ada3787e91c ("bpf: Check for NULL return from bpf_get_btf_vmlinux"). > [...] -- Kartikeya