[PATCH] cgroup/cpuset: Skip security check for hotplug induced v1 task migration

Waiman Long posted 1 patch 5 days, 16 hours ago
kernel/cgroup/cpuset.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
[PATCH] cgroup/cpuset: Skip security check for hotplug induced v1 task migration
Posted by Waiman Long 5 days, 16 hours ago
When a CPU hot removal causes a v1 cpuset to lose all its CPUs, the
cpuset hotplug handler will schedule a work function to migrate tasks
in that cpuset with no CPU to its parent to enable those tasks to
continue running.

If a strict security policy is in place, however, the task migration
may fail when security_task_setscheduler() call in cpuset_can_attach()
returns a -EACCESS error. That will mean that those tasks will have
no CPU to run on. The system administrators will have to explicitly
intervene to either add CPUs to that cpuset or move the tasks elsewhere
if they are aware of it.

This problem was found by a reported test failure in the LTP's
cpuset_hotplug_test.sh. Fix this problem by treating this special case
as an exception to skip the setsched security check as it is initated
internally within the kernel itself instead of from user input. With that
patch applied, the cpuset_hotplug_test.sh test can be run successfully
without failure.

Signed-off-by: Waiman Long <longman@redhat.com>
---
 kernel/cgroup/cpuset.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index d21868455341..88ce7ed91cd1 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2989,6 +2989,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
 	struct cpuset *cs, *oldcs;
 	struct task_struct *task;
 	bool cpus_updated, mems_updated;
+	bool kthread_move_task_from_empty_cs;
 	int ret;
 
 	/* used later by cpuset_attach() */
@@ -3006,6 +3007,14 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
 	cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus);
 	mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems);
 
+	/*
+	 * Set to true if a kthread is moving tasks away from a v1 cpuset with
+	 * no CPUs
+	 */
+	kthread_move_task_from_empty_cs = !cpuset_v2() &&
+					  cpumask_empty(oldcs->effective_cpus) &&
+					  (current->flags & PF_KTHREAD);
+
 	cgroup_taskset_for_each(task, css, tset) {
 		ret = task_can_attach(task);
 		if (ret)
@@ -3015,8 +3024,15 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
 		 * Skip rights over task check in v2 when nothing changes,
 		 * migration permission derives from hierarchy ownership in
 		 * cgroup_procs_write_permission()).
+		 *
+		 * In the special case of forced cpuset1 task migration to
+		 * parent via workqueue because of empty cpuset.cpus caused by
+		 * hotplug, skip the task check to prevent restrictive security
+		 * policy from denying the task migration. Otherwise those
+		 * tasks will have no CPU to run on.
 		 */
-		if (!cpuset_v2() || (cpus_updated || mems_updated)) {
+		if (!kthread_move_task_from_empty_cs &&
+		   (!cpuset_v2() || cpus_updated || mems_updated)) {
 			ret = security_task_setscheduler(task);
 			if (ret)
 				goto out_unlock;
-- 
2.53.0
Re: [PATCH] cgroup/cpuset: Skip security check for hotplug induced v1 task migration
Posted by Chen Ridong 5 days, 11 hours ago

On 2026/3/28 4:15, Waiman Long wrote:
> When a CPU hot removal causes a v1 cpuset to lose all its CPUs, the
> cpuset hotplug handler will schedule a work function to migrate tasks
> in that cpuset with no CPU to its parent to enable those tasks to
> continue running.
> 
> If a strict security policy is in place, however, the task migration
> may fail when security_task_setscheduler() call in cpuset_can_attach()
> returns a -EACCESS error. That will mean that those tasks will have
> no CPU to run on. The system administrators will have to explicitly
> intervene to either add CPUs to that cpuset or move the tasks elsewhere
> if they are aware of it.
> 
> This problem was found by a reported test failure in the LTP's
> cpuset_hotplug_test.sh. Fix this problem by treating this special case
> as an exception to skip the setsched security check as it is initated
> internally within the kernel itself instead of from user input. With that
> patch applied, the cpuset_hotplug_test.sh test can be run successfully
> without failure.
> 
> Signed-off-by: Waiman Long <longman@redhat.com>

Should we add a Fixes tag?

> ---
>  kernel/cgroup/cpuset.c | 18 +++++++++++++++++-
>  1 file changed, 17 insertions(+), 1 deletion(-)
> 
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index d21868455341..88ce7ed91cd1 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -2989,6 +2989,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
>  	struct cpuset *cs, *oldcs;
>  	struct task_struct *task;
>  	bool cpus_updated, mems_updated;
> +	bool kthread_move_task_from_empty_cs;

This name is too long. How about `kthread_nocpus`?

>  	int ret;
>  
>  	/* used later by cpuset_attach() */
> @@ -3006,6 +3007,14 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
>  	cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus);
>  	mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems);
>  
> +	/*
> +	 * Set to true if a kthread is moving tasks away from a v1 cpuset with
> +	 * no CPUs
> +	 */
> +	kthread_move_task_from_empty_cs = !cpuset_v2() &&
> +					  cpumask_empty(oldcs->effective_cpus) &&
> +					  (current->flags & PF_KTHREAD);
> +
>  	cgroup_taskset_for_each(task, css, tset) {
>  		ret = task_can_attach(task);
>  		if (ret)
> @@ -3015,8 +3024,15 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
>  		 * Skip rights over task check in v2 when nothing changes,
>  		 * migration permission derives from hierarchy ownership in
>  		 * cgroup_procs_write_permission()).
> +		 *
> +		 * In the special case of forced cpuset1 task migration to
> +		 * parent via workqueue because of empty cpuset.cpus caused by
> +		 * hotplug, skip the task check to prevent restrictive security
> +		 * policy from denying the task migration. Otherwise those
> +		 * tasks will have no CPU to run on.
>  		 */
> -		if (!cpuset_v2() || (cpus_updated || mems_updated)) {
> +		if (!kthread_move_task_from_empty_cs &&
> +		   (!cpuset_v2() || cpus_updated || mems_updated)) {
>  			ret = security_task_setscheduler(task);
>  			if (ret)
>  				goto out_unlock;

-- 
Best regards,
Ridong
Re: [PATCH] cgroup/cpuset: Skip security check for hotplug induced v1 task migration
Posted by Waiman Long 3 days, 19 hours ago
On 3/27/26 9:39 PM, Chen Ridong wrote:
>
> On 2026/3/28 4:15, Waiman Long wrote:
>> When a CPU hot removal causes a v1 cpuset to lose all its CPUs, the
>> cpuset hotplug handler will schedule a work function to migrate tasks
>> in that cpuset with no CPU to its parent to enable those tasks to
>> continue running.
>>
>> If a strict security policy is in place, however, the task migration
>> may fail when security_task_setscheduler() call in cpuset_can_attach()
>> returns a -EACCESS error. That will mean that those tasks will have
>> no CPU to run on. The system administrators will have to explicitly
>> intervene to either add CPUs to that cpuset or move the tasks elsewhere
>> if they are aware of it.
>>
>> This problem was found by a reported test failure in the LTP's
>> cpuset_hotplug_test.sh. Fix this problem by treating this special case
>> as an exception to skip the setsched security check as it is initated
>> internally within the kernel itself instead of from user input. With that
>> patch applied, the cpuset_hotplug_test.sh test can be run successfully
>> without failure.
>>
>> Signed-off-by: Waiman Long <longman@redhat.com>
> Should we add a Fixes tag?

The call to security_task_setscheduler() was added a long time ago and 
the cpuset v1 task migration is more like a corner case that shouldn't 
normally happen. So I don't want to put a fixes tag that will force a 
backport to earlier stable releases.

Cheers,
Longman
Re: [PATCH] cgroup/cpuset: Skip security check for hotplug induced v1 task migration
Posted by Tejun Heo 5 days, 16 hours ago
On Fri, Mar 27, 2026 at 04:15:46PM -0400, Waiman Long wrote:
> +	/*
> +	 * Set to true if a kthread is moving tasks away from a v1 cpuset with
> +	 * no CPUs
> +	 */
> +	kthread_move_task_from_empty_cs = !cpuset_v2() &&
> +					  cpumask_empty(oldcs->effective_cpus) &&
> +					  (current->flags & PF_KTHREAD);

PF_KTHREAD test seems odd. Can't you pass this in as a flag from hotplug
handler?

Thanks.

-- 
tejun
Re: [PATCH] cgroup/cpuset: Skip security check for hotplug induced v1 task migration
Posted by Waiman Long 5 days, 10 hours ago
On 3/27/26 4:22 PM, Tejun Heo wrote:
> On Fri, Mar 27, 2026 at 04:15:46PM -0400, Waiman Long wrote:
>> +	/*
>> +	 * Set to true if a kthread is moving tasks away from a v1 cpuset with
>> +	 * no CPUs
>> +	 */
>> +	kthread_move_task_from_empty_cs = !cpuset_v2() &&
>> +					  cpumask_empty(oldcs->effective_cpus) &&
>> +					  (current->flags & PF_KTHREAD);
> PF_KTHREAD test seems odd. Can't you pass this in as a flag from hotplug
> handler?

Yes, I can added a new CS flag like CS_TASK_MIGRATION in the hotplug 
handler to indicate that task migration is allowed. Thanks for the 
suggestion. I will send a v2 with that change.

Cheers,
Longman