[PATCH RFC v2 6/8] selinux: Restrict dma-buf direct cgroup charging
From: Albert Esteve
Date: Wed Oct 21 2026 - 11:51:40 EST
The security_dma_buf_charge_cgroup() hook allows security modules
to control which processes may charge dma-buf to an arbitrary
cgroup via cgroupfd passed in charge_fd. Without a policy
implementation, the hook is a no-op and any process that can open a
cgroup directory (effectively any process) can charge memory to it.
Implement selinux_dma_buf_charge_cgroup() using avc_has_perm() with
the dma_buf object class and a charge_cgroup permission. The target
SID is derived from the SELinux label on the cgroup's kernfs
directory, so access control follow the cgroup filesystem labeling.
Policy authors can then grant cross-cgroup charging selectively,
for example:
allow allocator_app_t cgroup_t:dma_buf charge_cgroup;
The hook matches the fail-open semantics of
selinux_kernfs_init_security(). This means unlabeled cgroups are
unrestricted by default.
Signed-off-by: Albert Esteve <aesteve@xxxxxxxxxx>
---
security/selinux/hooks.c | 33 +++++++++++++++++++++++++++++++++
security/selinux/include/classmap.h | 2 +-
2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 8f39d91eabc9..8de5e16951de 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2210,6 +2210,38 @@ static int selinux_dma_buf_charge_task(const struct cred *from, const struct cre
SECCLASS_DMA_BUF, DMA_BUF__CHARGE_TASK, NULL);
}
+static int selinux_dma_buf_charge_cgroup(const struct cred *cred, struct cgroup *cgrp)
+{
+ u32 cgrp_sid, clen;
+ int rc;
+ char *context;
+
+ rc = kernfs_xattr_get(cgrp->kn, XATTR_NAME_SELINUX, NULL, 0);
+ if (rc == -ENODATA)
+ return 0;
+ else if (rc < 0)
+ return rc;
+
+ clen = (u32)rc;
+ context = kmalloc(clen, GFP_KERNEL);
+ if (!context)
+ return -ENOMEM;
+
+ rc = kernfs_xattr_get(cgrp->kn, XATTR_NAME_SELINUX, context, clen);
+ if (rc < 0) {
+ kfree(context);
+ return rc;
+ }
+
+ rc = security_context_to_sid(context, clen, &cgrp_sid, GFP_KERNEL);
+ kfree(context);
+ if (rc)
+ return rc;
+
+ return avc_has_perm(cred_sid(cred), cgrp_sid,
+ SECCLASS_DMA_BUF, DMA_BUF__CHARGE_CGROUP, NULL);
+}
+
static int selinux_quotactl(int cmds, int type, int id, const struct super_block *sb)
{
const struct cred *cred = current_cred();
@@ -7681,6 +7713,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(capset, selinux_capset),
LSM_HOOK_INIT(capable, selinux_capable),
LSM_HOOK_INIT(dma_buf_charge_task, selinux_dma_buf_charge_task),
+ LSM_HOOK_INIT(dma_buf_charge_cgroup, selinux_dma_buf_charge_cgroup),
LSM_HOOK_INIT(quotactl, selinux_quotactl),
LSM_HOOK_INIT(quota_on, selinux_quota_on),
LSM_HOOK_INIT(syslog, selinux_syslog),
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index b4ebedc6e424..90d1e04c3d27 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -181,7 +181,7 @@ const struct security_class_mapping secclass_map[] = {
{ "user_namespace", { "create", NULL } },
{ "memfd_file",
{ COMMON_FILE_PERMS, "execute_no_trans", "entrypoint", NULL } },
- { "dma_buf", { "charge_task", NULL } },
+ { "dma_buf", { "charge_task", "charge_cgroup", NULL } },
/* last one */ { NULL, {} }
};
--
2.55.0