In include/linux/bpf-cgroup-defs.h, CGROUP_LSM_NUM defines the maximum number of BPF_PROG_TYPE_LSM programs that can be simultaneously attached using the `BPF_LSM_CGROUP` attachment type. We set the value to the newly introduced `CONFIG_CGROUP_LSM_NUM` Kconfig option, allowing users and distributions to tune this limit at build time rather than relying on a hardcoded value. The option ranges from 0 to 300 and defaults to 10, preserving the existing behaviour. There are currently 273 LSM hooks but this number is subject to change. I coudn't find a MACRO counting the sum of LSM interfaces and therefore arbitrarily set the threshold to 300. I am open to suggestions on how to set this limit dynamically or not. --- Signed-off-by: Paul Houssel --- include/linux/bpf-cgroup-defs.h | 2 +- kernel/bpf/Kconfig | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/linux/bpf-cgroup-defs.h b/include/linux/bpf-cgroup-defs.h index c9e6b26abab6..9ab5ca3dbaba 100644 --- a/include/linux/bpf-cgroup-defs.h +++ b/include/linux/bpf-cgroup-defs.h @@ -12,7 +12,7 @@ struct bpf_prog_array; #ifdef CONFIG_BPF_LSM /* Maximum number of concurrently attachable per-cgroup LSM hooks. */ -#define CGROUP_LSM_NUM 10 +#define CGROUP_LSM_NUM CONFIG_CGROUP_LSM_NUM #else #define CGROUP_LSM_NUM 0 #endif diff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig index eb3de35734f0..7f51598aa8fe 100644 --- a/kernel/bpf/Kconfig +++ b/kernel/bpf/Kconfig @@ -101,4 +101,17 @@ config BPF_LSM If you are unsure how to answer this question, answer N. +config CGROUP_LSM_NUM + int "Maximum number of per-cgroup LSM hooks" + depends on BPF_LSM + depends on CGROUP_BPF + range 0 300 + default 10 + help + Maximum number of concurrently attachable per-cgroup LSM hooks. + Increasing this value increases the size of the cgroup_lsm_atype + structure. + + If you are unsure, leave the default value. + endmenu # "BPF subsystem" -- 2.53.0 Add a selftest that verifies the kernel correctly enforces CONFIG_CGROUP_LSM_NUM as the maximum number of concurrently attachable per-cgroup LSM hook slots. The BPF program side (progs/cgroup_lsm_num.c) defines 12 lsm_cgroup programs, each attached to a distinct LSM hook. The test side (prog_tests/cgroup_lsm_num.c) attempts to attach all 12 programs one by one to a cgroup, and verifies that exactly 10 succeed and 2 are rejected, matching the value of CONFIG_CGROUP_LSM_NUM set to 10 in the selftest Kconfig fragment. Signed-off-by: Paul Houssel --- tools/testing/selftests/bpf/config | 1 + .../selftests/bpf/prog_tests/cgroup_lsm_num.c | 60 ++++++++++++ .../selftests/bpf/progs/cgroup_lsm_num.c | 92 +++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_lsm_num.c create mode 100644 tools/testing/selftests/bpf/progs/cgroup_lsm_num.c diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index 24855381290d..e4c5dd86c640 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -11,6 +11,7 @@ CONFIG_BPF_STREAM_PARSER=y CONFIG_BPF_SYSCALL=y # CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set CONFIG_CGROUP_BPF=y +CONFIG_CGROUP_LSM_NUM=10 CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_USER_API=y diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_lsm_num.c b/tools/testing/selftests/bpf/prog_tests/cgroup_lsm_num.c new file mode 100644 index 000000000000..1c5825c6c3d0 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_lsm_num.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Orange */ + +/* + * Test that the kernel enforces CONFIG_CGROUP_LSM_NUM as the maximum + * number of concurrently used per-cgroup LSM hook slots. + * + * - load a BPF object with 12 programs each on a distinct lsm_cgroup hook + * - attach them one by one via bpf_program__attach_cgroup() + * - at some point the slots are exhausted and attachment fails + * - verify that 10 succeed attachment and 2 fail + */ + +#include +#include + +#include "cgroup_lsm_num.skel.h" +#include "cgroup_helpers.h" + +void test_cgroup_lsm_num(void) +{ + struct cgroup_lsm_num *skel = NULL; + struct bpf_program *prog; + int cgroup_fd = -1; + int attached = 0; + int failed = 0; + + cgroup_fd = test__join_cgroup("/cgroup_lsm_num"); + if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup")) + return; + + skel = cgroup_lsm_num__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + goto out; + + bpf_object__for_each_program(prog, skel->obj) { + struct bpf_link *link; + + link = bpf_program__attach_cgroup(prog, cgroup_fd); + if (!link) { + if (errno == EOPNOTSUPP) { + test__skip(); + goto out; + } + failed++; + } else { + attached++; + } + } + + // CONFIG_CGROUP_LSM_NUM set to 10 + // -> 10 programs shall be attached + ASSERT_EQ(attached, 10, "at least one attached"); + // -> 2 programs shall be rejected + ASSERT_EQ(failed, 2, "limit was enforced"); + +out: + close(cgroup_fd); + cgroup_lsm_num__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/cgroup_lsm_num.c b/tools/testing/selftests/bpf/progs/cgroup_lsm_num.c new file mode 100644 index 000000000000..0cce61cd7b26 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/cgroup_lsm_num.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Orange */ + +/* + * 12 LSM programs with lsm_cgroup attachment type, each on a distinct LSM + * hook. Used by prog_tests/cgroup_lsm_num.c to verify that the kernel + * enforces the CONFIG_CGROUP_LSM_NUM limit on unique per-cgroup LSM hook + * slots. With CONFIG_CGROUP_LSM_NUM set to 10, 10 shall be attached and 2 + * rejected. + */ + +#include "vmlinux.h" +#include +#include + +char _license[] SEC("license") = "GPL"; + +SEC("lsm_cgroup/socket_create") +int BPF_PROG(hook0, int family, int type, int protocol, int kern) +{ + return 1; +} + +SEC("lsm_cgroup/socket_post_create") +int BPF_PROG(hook1, struct socket *sock, int family, int type, + int protocol, int kern) +{ + return 1; +} + +SEC("lsm_cgroup/socket_socketpair") +int BPF_PROG(hook2, struct socket *socka, struct socket *sockb) +{ + return 1; +} + +SEC("lsm_cgroup/socket_bind") +int BPF_PROG(hook3, struct socket *sock, struct sockaddr *address, + int addrlen) +{ + return 1; +} + +SEC("lsm_cgroup/socket_connect") +int BPF_PROG(hook4, struct socket *sock, struct sockaddr *address, + int addrlen) +{ + return 1; +} + +SEC("lsm_cgroup/socket_listen") +int BPF_PROG(hook5, struct socket *sock, int backlog) +{ + return 1; +} + +SEC("lsm_cgroup/socket_accept") +int BPF_PROG(hook6, struct socket *sock, struct socket *newsock) +{ + return 1; +} + +SEC("lsm_cgroup/socket_sendmsg") +int BPF_PROG(hook7, struct socket *sock, struct msghdr *msg, int size) +{ + return 1; +} + +SEC("lsm_cgroup/socket_recvmsg") +int BPF_PROG(hook8, struct socket *sock, struct msghdr *msg, int size, + int flags) +{ + return 1; +} + +SEC("lsm_cgroup/socket_getsockname") +int BPF_PROG(hook9, struct socket *sock) +{ + return 1; +} + +SEC("lsm_cgroup/socket_getpeername") +int BPF_PROG(hook10, struct socket *sock) +{ + return 1; +} + +SEC("lsm_cgroup/socket_shutdown") +int BPF_PROG(hook11, struct socket *sock, int how) +{ + return 1; +} -- 2.53.0