Re: [PATCH v4] security: Expand task_setscheduler LSM hook
Casey Schaufler <[email protected]> Tue, 21 Jul 2026 07:45:41 -0700
| Newsgroups | org.kernel.vger.linux-mips,org.kernel.vger.cgroups,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-security-module,org.kernel.vger.selinux |
|---|---|
| Message-ID | <[email protected]> |
On 7/19/2026 6:41 PM, Aaron Tomlin wrote: > At present, the task_setscheduler LSM hook provides security modules > with the opportunity to mediate changes to a task's scheduling policy by > inspecting the requested sched_attr. However, when invoked via > sched_setaffinity(), the hook lacks visibility into the actual CPU > affinity mask being requested. Consequently, security modules are > entirely blind to the target CPUs and cannot make granular access > control decisions based on spatial isolation. > > In modern multi-tenant and real-time environments, CPU isolation is a > critical boundary. The inability to audit or restrict specific 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 a pointer to the requested cpumask alongside the existing > sched_attr. Because this is a shared hook used for multiple scheduling > attribute changes, call sites that do not modify CPU affinity are > updated to safely pass NULL for the mask. To protect against unverified > dereferences, the parameter is 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. However, by > inspecting fields such as attr->sched_policy or attr->sched_priority, a > security module can introduce granular access controls. For instance, > differentiating between standard time-sharing policies and > latency-sensitive real-time policies. > > This change updates all in-tree security modules (SELinux and Smack) to > accommodate the new parameter mechanically, while providing LSMs with > the necessary context to enforce strict affinity policies. > > Signed-off-by: Aaron Tomlin <[email protected]> > --- > 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 > > - Linked to v3: https://lore.kernel.org/lkml/[email protected]/ > > 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) > > - Linked to v2: https://lore.kernel.org/lkml/[email protected]/ > > 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 > > - Linked to v1: https://lore.kernel.org/lkml/[email protected]/ > > Signed-off-by: Aaron Tomlin <[email protected]> For the (trivial) Smack changes: Reviewed-by: Casey Schaufler <[email protected]> > --- > 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 | 4 ++-- > security/commoncap.c | 9 +++++++-- > security/security.c | 14 +++++++++----- > security/selinux/hooks.c | 4 +++- > security/smack/smack_lsm.c | 13 +++++++++++-- > 10 files changed, 50 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 780f81259052..b14401030e5c 100644 > --- a/fs/proc/base.c > +++ b/fs/proc/base.c > @@ -2589,7 +2589,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 65c9609ec207..80ca02879620 100644 > --- a/include/linux/lsm_hook_defs.h > +++ b/include/linux/lsm_hook_defs.h > @@ -255,7 +255,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 153e9043058f..82dfe4e3f8d1 100644 > --- a/include/linux/security.h > +++ b/include/linux/security.h > @@ -82,6 +82,7 @@ struct ctl_table; > struct audit_krule; > struct user_namespace; > struct timezone; > +struct sched_attr; > > enum lsm_event { > LSM_POLICY_CHANGE, > @@ -196,7 +197,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); > @@ -531,7 +534,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, > @@ -1393,9 +1398,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 45944b3e31ca..6ab47cf5582b 100644 > --- a/kernel/cgroup/cpuset.c > +++ b/kernel/cgroup/cpuset.c > @@ -3042,7 +3042,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) > goto out_unlock; > > if (setsched_check) { > - ret = security_task_setscheduler(task); > + ret = security_task_setscheduler(task, NULL, cs->effective_cpus); > if (ret) > goto out_unlock; > } > @@ -3600,7 +3600,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 b215b0ead9a6..86490035e815 100644 > --- a/kernel/sched/syscalls.c > +++ b/kernel/sched/syscalls.c > @@ -540,7 +540,7 @@ int __sched_setscheduler(struct task_struct *p, > if (attr->sched_flags & SCHED_FLAG_SUGOV) > return -EINVAL; > > - retval = security_task_setscheduler(p); > + retval = security_task_setscheduler(p, attr, NULL); > if (retval) > return retval; > } > @@ -1213,7 +1213,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 3399535808fe..d83eee0459fe 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 71aea8fdf014..a6725dddc34f 100644 > --- a/security/security.c > +++ b/security/security.c > @@ -3240,17 +3240,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 8d6945edae7a..d77a8d5c364c 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -4559,7 +4559,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 ff115068c5c0..cf28ccc28b80 100644 > --- a/security/smack/smack_lsm.c > +++ b/security/smack/smack_lsm.c > @@ -2338,10 +2338,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__); > }