Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

workqueue: unlink pwqs from wq->pwqs list in alloc_and_link_pwqs() error path

When alloc_and_link_pwqs() fails partway through the per-cpu allocation
loop, some pool_workqueues may have already been linked into wq->pwqs
via link_pwq(). The error path frees these pwqs with kmem_cache_free()
but never removes them from the wq->pwqs list, leaving dangling pointers
in the list.

Currently this is not exploitable because the workqueue was never added
to the global workqueues list and the caller frees the wq immediately
after. However, this makes sure that alloc_and_link_pwqs() doesn't leave
any half-baked structure, which may have side effects if not properly
cleaned up.

Fix this by unlinking each pwq from wq->pwqs before freeing it. No
locking is needed as the workqueue has not been published yet, thus
no concurrency is possible.

Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Tejun Heo <tj@kernel.org>

authored by

Breno Leitao and committed by
Tejun Heo
afeaa9f2 48718378

+9 -1
+9 -1
kernel/workqueue.c
··· 5624 5624 for_each_possible_cpu(cpu) { 5625 5625 struct pool_workqueue *pwq = *per_cpu_ptr(wq->cpu_pwq, cpu); 5626 5626 5627 - if (pwq) 5627 + if (pwq) { 5628 + /* 5629 + * Unlink pwq from wq->pwqs since link_pwq() 5630 + * may have already added it. wq->mutex is not 5631 + * needed as the wq has not been published yet. 5632 + */ 5633 + if (!list_empty(&pwq->pwqs_node)) 5634 + list_del_rcu(&pwq->pwqs_node); 5628 5635 kmem_cache_free(pwq_cache, pwq); 5636 + } 5629 5637 } 5630 5638 free_percpu(wq->cpu_pwq); 5631 5639 wq->cpu_pwq = NULL;