Messages in this thread Patch in this message | | | From | Waiman Long <> | Subject | [PATCH v3] sched: Fix NULL user_cpus_ptr check in dup_user_cpus_ptr() | Date | Thu, 24 Nov 2022 21:04:59 -0500 |
| |
In general, a non-null user_cpus_ptr will remain set until the task dies. A possible exception to this is the fact that do_set_cpus_allowed() will clear a non-null user_cpus_ptr. To allow this possible racing condition, we need to check for NULL user_cpus_ptr under the pi_lock before duping the user mask.
Fixes: 851a723e45d1 ("sched: Always clear user_cpus_ptr in do_set_cpus_allowed()") Signed-off-by: Waiman Long <longman@redhat.com> --- kernel/sched/core.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 8df51b08bb38..ee14aafeacba 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2624,19 +2624,44 @@ void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask) int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src, int node) { + cpumask_t *user_mask; unsigned long flags; + /* + * Always clear dst->user_cpus_ptr first as their user_cpus_ptr's + * may differ by now due to racing. + */ + dst->user_cpus_ptr = NULL; + + /* + * This check is racy and losing the race is a valid situation. + * It is not worth the extra overhead of taking the pi_lock on + * every fork/clone. + */ if (!src->user_cpus_ptr) return 0; - dst->user_cpus_ptr = kmalloc_node(cpumask_size(), GFP_KERNEL, node); - if (!dst->user_cpus_ptr) + user_mask = kmalloc_node(cpumask_size(), GFP_KERNEL, node); + if (!user_mask) return -ENOMEM; - /* Use pi_lock to protect content of user_cpus_ptr */ + /* + * Use pi_lock to protect content of user_cpus_ptr + * + * Though unlikely, user_cpus_ptr can be reset to NULL by a concurrent + * do_set_cpus_allowed(). When this happens, we need to clear + * dst->user_cpus_ptr and free the allocated memory afterward. + */ raw_spin_lock_irqsave(&src->pi_lock, flags); - cpumask_copy(dst->user_cpus_ptr, src->user_cpus_ptr); + if (src->user_cpus_ptr) { + swap(dst->user_cpus_ptr, user_mask); + cpumask_copy(dst->user_cpus_ptr, src->user_cpus_ptr); + } raw_spin_unlock_irqrestore(&src->pi_lock, flags); + + if (unlikely(user_mask)) + kfree(user_mask); + return 0; } -- 2.31.1
| |