Skip to content

Commit 197f6ac

Browse files
committed
workqueue: Make sure struct worker is accessible for wq_worker_comm()
The worker struct could already be freed when wq_worker_comm() tries to access it for reporting. This patch protects PF_WQ_WORKER modifications with wq_pool_attach_mutex and makes wq_worker_comm() test the flag before dereferencing worker from kthread_data(), which ensures that it only dereferences when the worker struct is valid. Signed-off-by: Tejun Heo <[email protected]> Reported-by: Lai Jiangshan <[email protected]> Fixes: 6b59808 ("workqueue: Show the latest workqueue name in /proc/PID/{comm,stat,status}")
1 parent 6b59808 commit 197f6ac

File tree

1 file changed

+34
-24
lines changed

1 file changed

+34
-24
lines changed

kernel/workqueue.c

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2213,6 +2213,16 @@ static void process_scheduled_works(struct worker *worker)
22132213
}
22142214
}
22152215

2216+
static void set_pf_worker(bool val)
2217+
{
2218+
mutex_lock(&wq_pool_attach_mutex);
2219+
if (val)
2220+
current->flags |= PF_WQ_WORKER;
2221+
else
2222+
current->flags &= ~PF_WQ_WORKER;
2223+
mutex_unlock(&wq_pool_attach_mutex);
2224+
}
2225+
22162226
/**
22172227
* worker_thread - the worker thread function
22182228
* @__worker: self
@@ -2231,15 +2241,15 @@ static int worker_thread(void *__worker)
22312241
struct worker_pool *pool = worker->pool;
22322242

22332243
/* tell the scheduler that this is a workqueue worker */
2234-
worker->task->flags |= PF_WQ_WORKER;
2244+
set_pf_worker(true);
22352245
woke_up:
22362246
spin_lock_irq(&pool->lock);
22372247

22382248
/* am I supposed to die? */
22392249
if (unlikely(worker->flags & WORKER_DIE)) {
22402250
spin_unlock_irq(&pool->lock);
22412251
WARN_ON_ONCE(!list_empty(&worker->entry));
2242-
worker->task->flags &= ~PF_WQ_WORKER;
2252+
set_pf_worker(false);
22432253

22442254
set_task_comm(worker->task, "kworker/dying");
22452255
ida_simple_remove(&pool->worker_ida, worker->id);
@@ -2342,7 +2352,7 @@ static int rescuer_thread(void *__rescuer)
23422352
* Mark rescuer as worker too. As WORKER_PREP is never cleared, it
23432353
* doesn't participate in concurrency management.
23442354
*/
2345-
rescuer->task->flags |= PF_WQ_WORKER;
2355+
set_pf_worker(true);
23462356
repeat:
23472357
set_current_state(TASK_IDLE);
23482358

@@ -2434,7 +2444,7 @@ static int rescuer_thread(void *__rescuer)
24342444

24352445
if (should_stop) {
24362446
__set_current_state(TASK_RUNNING);
2437-
rescuer->task->flags &= ~PF_WQ_WORKER;
2447+
set_pf_worker(false);
24382448
return 0;
24392449
}
24402450

@@ -4580,37 +4590,37 @@ void show_workqueue_state(void)
45804590
/* used to show worker information through /proc/PID/{comm,stat,status} */
45814591
void wq_worker_comm(char *buf, size_t size, struct task_struct *task)
45824592
{
4583-
struct worker *worker;
4584-
struct worker_pool *pool;
45854593
int off;
45864594

45874595
/* always show the actual comm */
45884596
off = strscpy(buf, task->comm, size);
45894597
if (off < 0)
45904598
return;
45914599

4592-
/* stabilize worker pool association */
4600+
/* stabilize PF_WQ_WORKER and worker pool association */
45934601
mutex_lock(&wq_pool_attach_mutex);
45944602

4595-
worker = kthread_data(task);
4596-
pool = worker->pool;
4603+
if (task->flags & PF_WQ_WORKER) {
4604+
struct worker *worker = kthread_data(task);
4605+
struct worker_pool *pool = worker->pool;
45974606

4598-
if (pool) {
4599-
spin_lock_irq(&pool->lock);
4600-
/*
4601-
* ->desc tracks information (wq name or set_worker_desc())
4602-
* for the latest execution. If current, prepend '+',
4603-
* otherwise '-'.
4604-
*/
4605-
if (worker->desc[0] != '\0') {
4606-
if (worker->current_work)
4607-
scnprintf(buf + off, size - off, "+%s",
4608-
worker->desc);
4609-
else
4610-
scnprintf(buf + off, size - off, "-%s",
4611-
worker->desc);
4607+
if (pool) {
4608+
spin_lock_irq(&pool->lock);
4609+
/*
4610+
* ->desc tracks information (wq name or
4611+
* set_worker_desc()) for the latest execution. If
4612+
* current, prepend '+', otherwise '-'.
4613+
*/
4614+
if (worker->desc[0] != '\0') {
4615+
if (worker->current_work)
4616+
scnprintf(buf + off, size - off, "+%s",
4617+
worker->desc);
4618+
else
4619+
scnprintf(buf + off, size - off, "-%s",
4620+
worker->desc);
4621+
}
4622+
spin_unlock_irq(&pool->lock);
46124623
}
4613-
spin_unlock_irq(&pool->lock);
46144624
}
46154625

46164626
mutex_unlock(&wq_pool_attach_mutex);

0 commit comments

Comments
 (0)