Skip to content

Commit 3a7956e

Browse files
author
Peter Zijlstra
committed
kthread: Fix PF_KTHREAD vs to_kthread() race
The kthread_is_per_cpu() construct relies on only being called on PF_KTHREAD tasks (per the WARN in to_kthread). This gives rise to the following usage pattern: if ((p->flags & PF_KTHREAD) && kthread_is_per_cpu(p)) However, as reported by syzcaller, this is broken. The scenario is: CPU0 CPU1 (running p) (p->flags & PF_KTHREAD) // true begin_new_exec() me->flags &= ~(PF_KTHREAD|...); kthread_is_per_cpu(p) to_kthread(p) WARN(!(p->flags & PF_KTHREAD) <-- *SPLAT* Introduce __to_kthread() that omits the WARN and is sure to check both values. Use this to remove the problematic pattern for kthread_is_per_cpu() and fix a number of other kthread_*() functions that have similar issues but are currently not used in ways that would expose the problem. Notably kthread_func() is only ever called on 'current', while kthread_probe_data() is only used for PF_WQ_WORKER, which implies the task is from kthread_create*(). Fixes: ac687e6 ("kthread: Extract KTHREAD_IS_PER_CPU") Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Reviewed-by: Valentin Schneider <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent ad789f8 commit 3a7956e

File tree

3 files changed

+29
-8
lines changed

3 files changed

+29
-8
lines changed

kernel/kthread.c

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,25 @@ static inline struct kthread *to_kthread(struct task_struct *k)
8484
return (__force void *)k->set_child_tid;
8585
}
8686

87+
/*
88+
* Variant of to_kthread() that doesn't assume @p is a kthread.
89+
*
90+
* Per construction; when:
91+
*
92+
* (p->flags & PF_KTHREAD) && p->set_child_tid
93+
*
94+
* the task is both a kthread and struct kthread is persistent. However
95+
* PF_KTHREAD on it's own is not, kernel_thread() can exec() (See umh.c and
96+
* begin_new_exec()).
97+
*/
98+
static inline struct kthread *__to_kthread(struct task_struct *p)
99+
{
100+
void *kthread = (__force void *)p->set_child_tid;
101+
if (kthread && !(p->flags & PF_KTHREAD))
102+
kthread = NULL;
103+
return kthread;
104+
}
105+
87106
void free_kthread_struct(struct task_struct *k)
88107
{
89108
struct kthread *kthread;
@@ -168,8 +187,9 @@ EXPORT_SYMBOL_GPL(kthread_freezable_should_stop);
168187
*/
169188
void *kthread_func(struct task_struct *task)
170189
{
171-
if (task->flags & PF_KTHREAD)
172-
return to_kthread(task)->threadfn;
190+
struct kthread *kthread = __to_kthread(task);
191+
if (kthread)
192+
return kthread->threadfn;
173193
return NULL;
174194
}
175195
EXPORT_SYMBOL_GPL(kthread_func);
@@ -199,10 +219,11 @@ EXPORT_SYMBOL_GPL(kthread_data);
199219
*/
200220
void *kthread_probe_data(struct task_struct *task)
201221
{
202-
struct kthread *kthread = to_kthread(task);
222+
struct kthread *kthread = __to_kthread(task);
203223
void *data = NULL;
204224

205-
copy_from_kernel_nofault(&data, &kthread->data, sizeof(data));
225+
if (kthread)
226+
copy_from_kernel_nofault(&data, &kthread->data, sizeof(data));
206227
return data;
207228
}
208229

@@ -514,9 +535,9 @@ void kthread_set_per_cpu(struct task_struct *k, int cpu)
514535
set_bit(KTHREAD_IS_PER_CPU, &kthread->flags);
515536
}
516537

517-
bool kthread_is_per_cpu(struct task_struct *k)
538+
bool kthread_is_per_cpu(struct task_struct *p)
518539
{
519-
struct kthread *kthread = to_kthread(k);
540+
struct kthread *kthread = __to_kthread(p);
520541
if (!kthread)
521542
return false;
522543

kernel/sched/core.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7667,7 +7667,7 @@ static void balance_push(struct rq *rq)
76677667
* histerical raisins.
76687668
*/
76697669
if (rq->idle == push_task ||
7670-
((push_task->flags & PF_KTHREAD) && kthread_is_per_cpu(push_task)) ||
7670+
kthread_is_per_cpu(push_task) ||
76717671
is_migration_disabled(push_task)) {
76727672

76737673
/*

kernel/sched/fair.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7612,7 +7612,7 @@ int can_migrate_task(struct task_struct *p, struct lb_env *env)
76127612
return 0;
76137613

76147614
/* Disregard pcpu kthreads; they are where they need to be. */
7615-
if ((p->flags & PF_KTHREAD) && kthread_is_per_cpu(p))
7615+
if (kthread_is_per_cpu(p))
76167616
return 0;
76177617

76187618
if (!cpumask_test_cpu(env->dst_cpu, p->cpus_ptr)) {

0 commit comments

Comments
 (0)