* [PATCH v5] security: Expand task_setscheduler LSM hook
@ 2026-11-05 17:22 Aaron Tomlin
2026-11-05 17:35 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Aaron Tomlin @ 2026-11-05 17:22 UTC (permalink / raw)
To: tsbogend, paul, jmorris, serge, mingo, peterz, juri.lelli,
vincent.guittot, stephen.smalley.work, casey, longman, tj, hannes,
mkoutny
Cc: chenridong, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
kprateek.nayak, omosnace, kees, atomlin, neelx, sean, chjohnst,
steve, mproche, nick.lange, cgroups, linux-mips, linux-fsdevel,
linux-security-module, selinux, linux-kernel
At present, the task_setscheduler LSM hook only accepts the target
task_struct pointer. It lacks visibility into the requested scheduling
attributes (sched_attr) and the requested CPU affinity mask (cpumask).
Consequently, security modules are blind to both the target scheduling
parameters and target CPUs, preventing granular access control decisions
based on scheduling policy or spatial isolation.
In modern multi-tenant and real-time environments, CPU and scheduling
isolation are critical boundaries. The inability to audit or restrict
specific scheduling policy changes or CPU pinning requests limits the
effectiveness of security policies, particularly when attempting to
shield isolated or cryptographic cores from unprivileged or compromised
tasks.
This patch expands the security_task_setscheduler() hook signature to
include pointers to the requested sched_attr and cpumask alongside the
target task_struct. In __sched_setscheduler(), a local copy of
sched_attr is populated with the resolved scheduling policy so that
parameter-only updates (such as sched_setparam() or sched_setattr() with
SCHED_FLAG_KEEP_POLICY) always pass the effective scheduling policy to
the hook rather than an internal sentinel. Because this is a shared hook
used across multiple paths, call sites that do not modify scheduling
attributes or CPU affinity pass NULL for the respective parameter. To
protect against unverified dereferences, both parameters are annotated
with __nullable in the LSM hook definition, ensuring the BPF verifier
mandates explicit NULL checks for attached eBPF programs.
Historically, SELinux has governed all scheduling alterations under a
single, monolithic PROCESS__SETSCHED access vector. By inspecting fields
such as attr->sched_policy or attr->sched_priority, as well as the
requested cpumask, a security module can introduce granular access
controls, such as differentiating between standard time-sharing policies
and latency-sensitive real-time policies.
This change updates all in-tree security modules (capabilities, SELinux,
and Smack) to accommodate the new parameters mechanically, while
providing LSMs with the necessary context to enforce strict scheduling
and affinity policies.
Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
Changes since v4:
- Rebased against v7.4-rc1
- In __sched_setscheduler(), populate a local copy of sched_attr with
the resolved scheduling policy before calling
security_task_setscheduler() so parameter-only updates (such as
sched_setparam() or sched_setattr() with SCHED_FLAG_KEEP_POLICY) pass
the effective policy rather than SETPARAM_POLICY (-1)
- Clarified the commit message to reflect that both the sched_attr and
cpumask parameters are introduced by this patch
- Link to v4: https://lore.kernel.org/all/20260720014156.459386-1-atomlin@atomlin.com/
Changes since v3:
- Expanded the task_setscheduler LSM hook to accept the 'sched_attr'
payload, enabling BPF and other LSMs to inspect scheduling attributes
(Peter Zijlstra)
- Updated all call sites for security_task_setscheduler() to correctly
pass the attr structure when available, or NULL otherwise
(Peter Zijlstra)
- Added the necessary forward declaration for struct sched_attr to
include/linux/security.h
- Link to v3: https://lore.kernel.org/lkml/20260526142838.774711-1-atomlin@atomlin.com/
Changes since v2:
- Dropped patch 1. This is to be addressed by the cgroup cpuset
maintainer (Waiman Long)
- Dropped patch 3. Will be submitted as a separate patch (Paul Moore)
- Link to v2: https://lore.kernel.org/lkml/20260509213803.968464-1-atomlin@atomlin.com/
Changes since v1:
- Reordered the allocation and user-copy of new_mask in the MIPS
architecture's mipsmt_sys_sched_setaffinity() to occur before the
LSM hook is invoked. This ensures the security modules evaluate a fully
populated mask rather than uninitialised memory, while cleanly handling
error unwinding
- Updated cpuset_can_fork() to pass the destination cpuset's effective CPU
mask instead of NULL
- Link to v1: https://lore.kernel.org/lkml/20260509164847.939294-1-atomlin@atomlin.com/
---
arch/mips/kernel/mips-mt-fpaff.c | 2 +-
fs/proc/base.c | 2 +-
include/linux/lsm_hook_defs.h | 4 +++-
include/linux/security.h | 15 +++++++++++----
kernel/cgroup/cpuset.c | 4 ++--
kernel/sched/syscalls.c | 7 +++++--
security/commoncap.c | 9 +++++++--
security/security.c | 14 +++++++++-----
security/selinux/hooks.c | 4 +++-
security/smack/smack_lsm.c | 13 +++++++++++--
10 files changed, 53 insertions(+), 21 deletions(-)
diff --git a/arch/mips/kernel/mips-mt-fpaff.c b/arch/mips/kernel/mips-mt-fpaff.c
index 4fead87d2f43..c7a5d55cf7ca 100644
--- a/arch/mips/kernel/mips-mt-fpaff.c
+++ b/arch/mips/kernel/mips-mt-fpaff.c
@@ -110,7 +110,7 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
goto out_unlock;
}
- retval = security_task_setscheduler(p);
+ retval = security_task_setscheduler(p, NULL, new_mask);
if (retval)
goto out_unlock;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index c9aebee0a338..d9a3c892586e 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2596,7 +2596,7 @@ static ssize_t timerslack_ns_write(struct file *file, const char __user *buf,
}
rcu_read_unlock();
- err = security_task_setscheduler(p);
+ err = security_task_setscheduler(p, NULL, NULL);
if (err) {
count = err;
goto out;
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 87d9d06710cb..411a5b977915 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -256,7 +256,9 @@ LSM_HOOK(int, 0, task_prlimit, const struct cred *cred,
const struct cred *tcred, unsigned int flags)
LSM_HOOK(int, 0, task_setrlimit, struct task_struct *p, unsigned int resource,
struct rlimit *new_rlim)
-LSM_HOOK(int, 0, task_setscheduler, struct task_struct *p)
+LSM_HOOK(int, 0, task_setscheduler, struct task_struct *p,
+ const struct sched_attr *attr__nullable,
+ const struct cpumask *in_mask__nullable)
LSM_HOOK(int, 0, task_getscheduler, struct task_struct *p)
LSM_HOOK(int, 0, task_movememory, struct task_struct *p)
LSM_HOOK(int, 0, task_kill, struct task_struct *p, struct kernel_siginfo *info,
diff --git a/include/linux/security.h b/include/linux/security.h
index 40831fab5470..b2c43e745447 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -84,6 +84,7 @@ struct audit_krule;
struct ns_common;
struct user_namespace;
struct timezone;
+struct sched_attr;
enum lsm_event {
LSM_POLICY_CHANGE,
@@ -198,7 +199,9 @@ extern int cap_mmap_addr(unsigned long addr);
extern int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags);
extern int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5);
-extern int cap_task_setscheduler(struct task_struct *p);
+extern int cap_task_setscheduler(struct task_struct *p,
+ const struct sched_attr *attr,
+ const struct cpumask *in_mask);
extern int cap_task_setioprio(struct task_struct *p, int ioprio);
extern int cap_task_setnice(struct task_struct *p, int nice);
extern int cap_vm_enough_memory(struct mm_struct *mm, long pages);
@@ -537,7 +540,9 @@ int security_task_prlimit(const struct cred *cred, const struct cred *tcred,
unsigned int flags);
int security_task_setrlimit(struct task_struct *p, unsigned int resource,
struct rlimit *new_rlim);
-int security_task_setscheduler(struct task_struct *p);
+int security_task_setscheduler(struct task_struct *p,
+ const struct sched_attr *attr,
+ const struct cpumask *in_mask);
int security_task_getscheduler(struct task_struct *p);
int security_task_movememory(struct task_struct *p);
int security_task_kill(struct task_struct *p, struct kernel_siginfo *info,
@@ -1408,9 +1413,11 @@ static inline int security_task_setrlimit(struct task_struct *p,
return 0;
}
-static inline int security_task_setscheduler(struct task_struct *p)
+static inline int security_task_setscheduler(struct task_struct *p,
+ const struct sched_attr *attr,
+ const struct cpumask *in_mask)
{
- return cap_task_setscheduler(p);
+ return cap_task_setscheduler(p, attr, in_mask);
}
static inline int security_task_getscheduler(struct task_struct *p)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 3219f927c35e..d122de948572 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -3267,7 +3267,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
attach_ctx.old_cs = task_cs(task);
if (setsched_check) {
- ret = security_task_setscheduler(task);
+ ret = security_task_setscheduler(task, NULL, cs->effective_cpus);
if (ret)
goto out_unlock;
}
@@ -3816,7 +3816,7 @@ static int cpuset_can_fork(struct task_struct *task, struct css_set *cset)
if (ret)
goto out_unlock;
- ret = security_task_setscheduler(task);
+ ret = security_task_setscheduler(task, NULL, cs->effective_cpus);
if (ret)
goto out_unlock;
diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c
index 45a127deeadd..1b3f81e6e18e 100644
--- a/kernel/sched/syscalls.c
+++ b/kernel/sched/syscalls.c
@@ -533,6 +533,8 @@ int __sched_setscheduler(struct task_struct *p,
return -EINVAL;
if (user) {
+ struct sched_attr lsm_attr = *attr;
+
retval = user_check_sched_setscheduler(p, attr, policy, reset_on_fork);
if (retval)
return retval;
@@ -540,7 +542,8 @@ int __sched_setscheduler(struct task_struct *p,
if (attr->sched_flags & SCHED_FLAG_SUGOV)
return -EINVAL;
- retval = security_task_setscheduler(p);
+ lsm_attr.sched_policy = policy;
+ retval = security_task_setscheduler(p, &lsm_attr, NULL);
if (retval)
return retval;
}
@@ -1215,7 +1218,7 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
return -EPERM;
}
- retval = security_task_setscheduler(p);
+ retval = security_task_setscheduler(p, NULL, in_mask);
if (retval)
return retval;
diff --git a/security/commoncap.c b/security/commoncap.c
index d47ab3022343..0c33f94a5707 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -1222,13 +1222,18 @@ static int cap_safe_nice(struct task_struct *p)
/**
* cap_task_setscheduler - Determine if scheduler policy change is permitted
* @p: The task to affect
+ * @attr: Requested scheduling attributes (ignored)
+ * @in_mask: Requested CPU affinity mask (ignored)
*
* Determine if the requested scheduler policy change is permitted for the
- * specified task.
+ * specified task. The capabilities security module does not evaluate the
+ * @attr or @in_mask parameters, relying solely on cap_safe_nice().
*
* Return: 0 if permission is granted, -ve if denied.
*/
-int cap_task_setscheduler(struct task_struct *p)
+int cap_task_setscheduler(struct task_struct *p,
+ const struct sched_attr *attr __always_unused,
+ const struct cpumask *in_mask __always_unused)
{
return cap_safe_nice(p);
}
diff --git a/security/security.c b/security/security.c
index f6583d507395..9ca3438ef96a 100644
--- a/security/security.c
+++ b/security/security.c
@@ -3261,17 +3261,21 @@ int security_task_setrlimit(struct task_struct *p, unsigned int resource,
}
/**
- * security_task_setscheduler() - Check if setting sched policy/param is allowed
+ * security_task_setscheduler() - Check if setting sched policy/param/affinity is allowed
* @p: target task
+ * @attr: requested scheduling attributes, or NULL if not changing parameters
+ * @in_mask: requested CPU affinity mask, or NULL if not changing affinity
*
- * Check permission before setting scheduling policy and/or parameters of
- * process @p.
+ * Check permission before setting the scheduling policy, parameters, and/or
+ * CPU affinity of process @p.
*
* Return: Returns 0 if permission is granted.
*/
-int security_task_setscheduler(struct task_struct *p)
+int security_task_setscheduler(struct task_struct *p,
+ const struct sched_attr *attr,
+ const struct cpumask *in_mask)
{
- return call_int_hook(task_setscheduler, p);
+ return call_int_hook(task_setscheduler, p, attr, in_mask);
}
/**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 6168dd5f91c2..4fc3e8c51035 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4690,7 +4690,9 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource,
return 0;
}
-static int selinux_task_setscheduler(struct task_struct *p)
+static int selinux_task_setscheduler(struct task_struct *p,
+ const struct sched_attr *attr __always_unused,
+ const struct cpumask *in_mask __always_unused)
{
return avc_has_perm(current_sid(), task_sid_obj(p), SECCLASS_PROCESS,
PROCESS__SETSCHED, NULL);
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 28a8e12603ce..a5f0de59dfc0 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -2350,10 +2350,19 @@ static int smack_task_getioprio(struct task_struct *p)
/**
* smack_task_setscheduler - Smack check on setting scheduler
* @p: the task object
+ * @attr: Requested scheduling attributes (ignored)
+ * @in_mask: Requested CPU affinity mask (ignored)
*
- * Return 0 if read access is permitted
+ * Evaluate whether the current task has write access to the target task @p
+ * to change its scheduling policy. The Smack security module relies
+ * strictly on label-based access control and does not evaluate CPU
+ * affinity masks or scheduling attributes.
+ *
+ * Return: 0 if write access is permitted
*/
-static int smack_task_setscheduler(struct task_struct *p)
+static int smack_task_setscheduler(struct task_struct *p,
+ const struct sched_attr *attr __always_unused,
+ const struct cpumask *in_mask __always_unused)
{
return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH v5] security: Expand task_setscheduler LSM hook
2026-11-05 17:22 [PATCH v5] security: Expand task_setscheduler LSM hook Aaron Tomlin
@ 2026-11-05 17:35 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-11-05 17:35 UTC (permalink / raw)
Cc: linux-security-module, selinux
> At present, the task_setscheduler LSM hook only accepts the target
> task_struct pointer. It lacks visibility into the requested scheduling
> attributes (sched_attr) and the requested CPU affinity mask (cpumask).
> Consequently, security modules are blind to both the target scheduling
> parameters and target CPUs, preventing granular access control decisions
> based on scheduling policy or spatial isolation.
>
> In modern multi-tenant and real-time environments, CPU and scheduling
> isolation are critical boundaries. The inability to audit or restrict
> specific scheduling policy changes or CPU pinning requests limits the
> effectiveness of security policies, particularly when attempting to
> shield isolated or cryptographic cores from unprivileged or compromised
> tasks.
>
> This patch expands the security_task_setscheduler() hook signature to
> [ ... ]
> providing LSMs with the necessary context to enforce strict scheduling
> and affinity policies.
>
> Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105172210.76706-1-atomlin@atomlin.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-05 17:35 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 17:22 [PATCH v5] security: Expand task_setscheduler LSM hook Aaron Tomlin
2026-11-05 17:35 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox