]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: Fix setting retval to -EPERM for cgroup hooks not returning errno
authorXu Kuohai <xukuohai@huawei.com>
Wed, 10 Jun 2026 20:17:23 +0000 (20:17 +0000)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 13 Jun 2026 03:33:16 +0000 (20:33 -0700)
When a cgroup BPF program exits with 0, bpf_prog_run_array_cg() sets
the hook return value to -EPERM if it is not a valid errno. This is
correct for errno-based hooks, which return 0 on success and negative
errno on failure, but wrong for boolean and void LSM hooks. Boolean
LSM hooks should only return true or false, and void LSM hooks have
no return value at all.

Fix it by skipping setting -EPERM for hooks not returning errno.

Fixes: 69fd337a975c ("bpf: per-cgroup lsm flavor")
Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
Link: https://lore.kernel.org/r/20260610201724.733943-2-xukuohai@huaweicloud.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/bpf_lsm.h
kernel/bpf/bpf_lsm.c
kernel/bpf/cgroup.c

index 643809cc78c33e198584f6af2b287d24c3e787f0..143775a27a2a0b855e871876f49ad428cc757b32 100644 (file)
@@ -52,6 +52,7 @@ int bpf_set_dentry_xattr_locked(struct dentry *dentry, const char *name__str,
                                const struct bpf_dynptr *value_p, int flags);
 int bpf_remove_dentry_xattr_locked(struct dentry *dentry, const char *name__str);
 bool bpf_lsm_has_d_inode_locked(const struct bpf_prog *prog);
+bool bpf_lsm_hook_returns_errno(u32 btf_id);
 
 #else /* !CONFIG_BPF_LSM */
 
@@ -104,6 +105,11 @@ static inline bool bpf_lsm_has_d_inode_locked(const struct bpf_prog *prog)
 {
        return false;
 }
+
+static inline bool bpf_lsm_hook_returns_errno(u32 btf_id)
+{
+       return true;
+}
 #endif /* CONFIG_BPF_LSM */
 
 #endif /* _LINUX_BPF_LSM_H */
index c5c925f0020216d2b043999bd2f48d26cd014fa5..564071a92d7d2017e92c69d8eff3e8f2c0e15fd8 100644 (file)
@@ -427,6 +427,26 @@ BTF_ID(func, bpf_lsm_audit_rule_known)
 BTF_ID(func, bpf_lsm_inode_xattr_skipcap)
 BTF_SET_END(bool_lsm_hooks)
 
+/* hooks returning void */
+#define LSM_HOOK_void(DEFAULT, NAME, ...) BTF_ID(func, bpf_lsm_##NAME)
+#define LSM_HOOK_int(DEFAULT, NAME, ...)  /* nothing */
+#define LSM_HOOK(RET, DEFAULT, NAME, ...) LSM_HOOK_##RET(DEFAULT, NAME, __VA_ARGS__)
+BTF_SET_START(void_lsm_hooks)
+#include <linux/lsm_hook_defs.h>
+#undef LSM_HOOK
+#undef LSM_HOOK_void
+#undef LSM_HOOK_int
+BTF_SET_END(void_lsm_hooks)
+
+bool bpf_lsm_hook_returns_errno(u32 btf_id)
+{
+       if (btf_id_set_contains(&bool_lsm_hooks, btf_id))
+               return false;
+       if (btf_id_set_contains(&void_lsm_hooks, btf_id))
+               return false;
+       return true;
+}
+
 int bpf_lsm_get_retval_range(const struct bpf_prog *prog,
                             struct bpf_retval_range *retval_range)
 {
index 35d1f1428ef39e0309aa22cf5e1cef1eaee06747..83ce66296ac194142e948e6fb54173156a96684f 100644 (file)
@@ -55,6 +55,28 @@ void __init cgroup_bpf_lifetime_notifier_init(void)
                                                &cgroup_bpf_lifetime_nb));
 }
 
+#ifdef CONFIG_BPF_LSM
+struct cgroup_lsm_atype {
+       u32 attach_btf_id;
+       int refcnt;
+       bool returns_errno;
+};
+
+static struct cgroup_lsm_atype cgroup_lsm_atype[CGROUP_LSM_NUM];
+
+static bool cgroup_bpf_hook_returns_errno(enum cgroup_bpf_attach_type atype)
+{
+       if (atype >= CGROUP_LSM_START && atype <= CGROUP_LSM_END)
+               return READ_ONCE(cgroup_lsm_atype[atype - CGROUP_LSM_START].returns_errno);
+       return true;
+}
+#else
+static bool cgroup_bpf_hook_returns_errno(enum cgroup_bpf_attach_type atype)
+{
+       return true;
+}
+#endif
+
 /* __always_inline is necessary to prevent indirect call through run_prog
  * function pointer.
  */
@@ -83,7 +105,8 @@ bpf_prog_run_array_cg(const struct cgroup_bpf *cgrp,
                        *(ret_flags) |= (func_ret >> 1);
                        func_ret &= 1;
                }
-               if (!func_ret && !IS_ERR_VALUE((long)run_ctx.retval))
+               if (!func_ret && cgroup_bpf_hook_returns_errno(atype) &&
+                   !IS_ERR_VALUE((long)run_ctx.retval))
                        run_ctx.retval = -EPERM;
                item++;
        }
@@ -156,13 +179,6 @@ unsigned int __cgroup_bpf_run_lsm_current(const void *ctx,
 }
 
 #ifdef CONFIG_BPF_LSM
-struct cgroup_lsm_atype {
-       u32 attach_btf_id;
-       int refcnt;
-};
-
-static struct cgroup_lsm_atype cgroup_lsm_atype[CGROUP_LSM_NUM];
-
 static enum cgroup_bpf_attach_type
 bpf_cgroup_atype_find(enum bpf_attach_type attach_type, u32 attach_btf_id)
 {
@@ -191,10 +207,13 @@ void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype)
 
        lockdep_assert_held(&cgroup_mutex);
 
-       WARN_ON_ONCE(cgroup_lsm_atype[i].attach_btf_id &&
-                    cgroup_lsm_atype[i].attach_btf_id != attach_btf_id);
-
-       cgroup_lsm_atype[i].attach_btf_id = attach_btf_id;
+       if (!cgroup_lsm_atype[i].attach_btf_id) {
+               cgroup_lsm_atype[i].attach_btf_id = attach_btf_id;
+               WRITE_ONCE(cgroup_lsm_atype[i].returns_errno,
+                          bpf_lsm_hook_returns_errno(attach_btf_id));
+       } else {
+               WARN_ON_ONCE(cgroup_lsm_atype[i].attach_btf_id != attach_btf_id);
+       }
        cgroup_lsm_atype[i].refcnt++;
 }
 
@@ -203,8 +222,10 @@ void bpf_cgroup_atype_put(int cgroup_atype)
        int i = cgroup_atype - CGROUP_LSM_START;
 
        cgroup_lock();
-       if (--cgroup_lsm_atype[i].refcnt <= 0)
+       if (--cgroup_lsm_atype[i].refcnt <= 0) {
+               WRITE_ONCE(cgroup_lsm_atype[i].returns_errno, true);
                cgroup_lsm_atype[i].attach_btf_id = 0;
+       }
        WARN_ON_ONCE(cgroup_lsm_atype[i].refcnt < 0);
        cgroup_unlock();
 }