From bb63374e2ffcb269b3ca565e4bd18ee2edb70d9f Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 10 Oct 2016 04:57:33 +0000 Subject: [PATCH] While the thread is sleeping in taskqueue_drain_all() it is posible that the queue entry it is looking at is removed from the queue, but we make no effort to account for this. when we wake up we need to check it's still there. PR: 209580 Sponsored by: Panzura inc Differential Revision: D8160 git-svn-id: svn://svn.freebsd.org/base/stable/10@306935 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- sys/kern/subr_taskqueue.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/sys/kern/subr_taskqueue.c b/sys/kern/subr_taskqueue.c index bfc941ee3..c9addb19d 100644 --- a/sys/kern/subr_taskqueue.c +++ b/sys/kern/subr_taskqueue.c @@ -454,9 +454,26 @@ taskqueue_drain_all(struct taskqueue *queue) TQ_LOCK(queue); task = STAILQ_LAST(&queue->tq_queue, task, ta_link); - if (task != NULL) - while (task->ta_pending != 0) - TQ_SLEEP(queue, task, &queue->tq_mutex, PWAIT, "-", 0); + while (task != NULL && task->ta_pending != 0) { + struct task *oldtask; + TQ_SLEEP(queue, task, &queue->tq_mutex, PWAIT, "-", 0); + /* + * While we were asleeep the last entry may have been freed. + * We need to check if it's still even in the queue. + * Not perfect, but it's better than referencing bad memory. + * first guess is the current 'end of queue' but if a new + * item has been added we need to take the expensive path + * Better fix in 11. + */ + oldtask = task; + if (oldtask != + (task = STAILQ_LAST(&queue->tq_queue, task, ta_link))) { + STAILQ_FOREACH(task, &queue->tq_queue, ta_link) { + if (task == oldtask) + break; + } + } + } taskqueue_drain_running(queue); KASSERT(STAILQ_EMPTY(&queue->tq_queue), ("taskqueue queue is not empty after draining")); -- 2.45.0