From: "Serge E. Hallyn" <serge@hallyn.com>
To: Aaron Tomlin <atomlin@atomlin.com>
Cc: tsbogend@alpha.franken.de, paul@paul-moore.com,
jmorris@namei.org, mingo@redhat.com, peterz@infradead.org,
juri.lelli@redhat.com, vincent.guittot@linaro.org,
stephen.smalley.work@gmail.com, casey@schaufler-ca.com,
longman@redhat.com, tj@kernel.org, hannes@cmpxchg.org,
mkoutny@suse.com, chenridong@huaweicloud.com,
dietmar.eggemann@arm.com, rostedt@goodmis.org,
bsegall@google.com, mgorman@suse.de, vschneid@redhat.com,
kprateek.nayak@amd.com, omosnace@redhat.com, kees@kernel.org,
neelx@suse.com, sean@ashe.io, chjohnst@gmail.com, steve@abita.co,
mproche@gmail.com, nick.lange@gmail.com, cgroups@vger.kernel.org,
linux-mips@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org, selinux@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH v5] security: Expand task_setscheduler LSM hook
Date: Fri, 6 Nov 2026 13:21:34 -0600 [thread overview]
Message-ID: <au4ovp/Ghd25rtfZ@hallyn.com> (raw)
In-Reply-To: <20261105172210.76706-1-atomlin@atomlin.com>
On Thu, Nov 05, 2026 at 12:22:10PM -0500, Aaron Tomlin wrote:
> 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>
This seems misleading - Casey explicitly said "for the smack bits". This
makes it seem like he reviewed and approved of the whole set.
> Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
> ---
I see no issues with the commoncap.c and security.c changes, but as
Paul said two versions ago in
https://lore.kernel.org/lkml/CAHC9VhSROg6RGUN4_ZVBoEwYjRnKvyjnkbx2D88c09KiTgY3KQ@mail.gmail.com/
nothing appaers to be using these. Where is the user?
> 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
next prev parent reply other threads:[~2026-11-06 19:21 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 17:22 Aaron Tomlin
2026-11-06 19:21 ` Serge E. Hallyn [this message]
2026-11-06 19:50 ` Aaron Tomlin
2026-11-06 19:59 ` Serge E. Hallyn
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=au4ovp/Ghd25rtfZ@hallyn.com \
--to=serge@hallyn.com \
--cc=atomlin@atomlin.com \
--cc=bsegall@google.com \
--cc=casey@schaufler-ca.com \
--cc=cgroups@vger.kernel.org \
--cc=chenridong@huaweicloud.com \
--cc=chjohnst@gmail.com \
--cc=dietmar.eggemann@arm.com \
--cc=hannes@cmpxchg.org \
--cc=jmorris@namei.org \
--cc=juri.lelli@redhat.com \
--cc=kees@kernel.org \
--cc=kprateek.nayak@amd.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mips@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=longman@redhat.com \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=mkoutny@suse.com \
--cc=mproche@gmail.com \
--cc=neelx@suse.com \
--cc=nick.lange@gmail.com \
--cc=omosnace@redhat.com \
--cc=paul@paul-moore.com \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
--cc=sean@ashe.io \
--cc=selinux@vger.kernel.org \
--cc=stephen.smalley.work@gmail.com \
--cc=steve@abita.co \
--cc=tj@kernel.org \
--cc=tsbogend@alpha.franken.de \
--cc=vincent.guittot@linaro.org \
--cc=vschneid@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®