Skip to content

Commit 33e3f0a

Browse files
richardxnuclarkhtejun
authored andcommitted
workqueue: Add a new flag to spot the potential UAF error
Currently if the user queues a new work item unintentionally into a wq after the destroy_workqueue(wq), the work still can be queued and scheduled without any noticeable kernel message before the end of a RCU grace period. As a debug-aid facility, this commit adds a new flag __WQ_DESTROYING to spot that issue by triggering a kernel WARN message. Signed-off-by: Richard Clark <[email protected]> Reviewed-by: Lai Jiangshan <[email protected]> Signed-off-by: Tejun Heo <[email protected]>
1 parent 512dee0 commit 33e3f0a

File tree

2 files changed

+13
-3
lines changed

2 files changed

+13
-3
lines changed

include/linux/workqueue.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ enum {
335335
*/
336336
WQ_POWER_EFFICIENT = 1 << 7,
337337

338+
__WQ_DESTROYING = 1 << 15, /* internal: workqueue is destroying */
338339
__WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */
339340
__WQ_ORDERED = 1 << 17, /* internal: workqueue is ordered */
340341
__WQ_LEGACY = 1 << 18, /* internal: create*_workqueue() */

kernel/workqueue.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,9 +1433,13 @@ static void __queue_work(int cpu, struct workqueue_struct *wq,
14331433
lockdep_assert_irqs_disabled();
14341434

14351435

1436-
/* if draining, only works from the same workqueue are allowed */
1437-
if (unlikely(wq->flags & __WQ_DRAINING) &&
1438-
WARN_ON_ONCE(!is_chained_work(wq)))
1436+
/*
1437+
* For a draining wq, only works from the same workqueue are
1438+
* allowed. The __WQ_DESTROYING helps to spot the issue that
1439+
* queues a new work item to a wq after destroy_workqueue(wq).
1440+
*/
1441+
if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) &&
1442+
WARN_ON_ONCE(!is_chained_work(wq))))
14391443
return;
14401444
rcu_read_lock();
14411445
retry:
@@ -4414,6 +4418,11 @@ void destroy_workqueue(struct workqueue_struct *wq)
44144418
*/
44154419
workqueue_sysfs_unregister(wq);
44164420

4421+
/* mark the workqueue destruction is in progress */
4422+
mutex_lock(&wq->mutex);
4423+
wq->flags |= __WQ_DESTROYING;
4424+
mutex_unlock(&wq->mutex);
4425+
44174426
/* drain it before proceeding with destruction */
44184427
drain_workqueue(wq);
44194428

0 commit comments

Comments
 (0)