DragonFlyBSD Kernel Audit
DF-2849 / fix.diff
← back to finding ↓ download raw
--- a/sys/kern/subr_gtaskqueue.c
+++ b/sys/kern/subr_gtaskqueue.c
@@ -200,6 +200,12 @@
 int
 grouptaskqueue_enqueue(struct gtaskqueue *queue, struct gtask *gtask)
 {
+	/*
+	 * A detached grouptask has gt_taskqueue == NULL; fail gracefully
+	 * instead of panicking (INVARIANTS) or faulting on TQ_LOCK(NULL).
+	 */
+	if (queue == NULL)
+		return (EINVAL);
 #ifdef INVARIANTS
 	if (queue == NULL) {
 		gtask_dump(gtask);
@@ -659,6 +665,15 @@
 	qgroup->tqg_queue[qid].tgc_cnt++;
 	LIST_INSERT_HEAD(&qgroup->tqg_queue[qid].tgc_tasks, gtask, gt_list);
 	gtask->gt_taskqueue = qgroup->tqg_queue[qid].tgc_taskq;
+	/* re-arm the task under the queue lock (detach leaves
+	 * TASK_NOENQUEUE set) so the flag RMW cannot race enqueue */
+	{
+		struct gtaskqueue *gtq = gtask->gt_taskqueue;
+
+		TQ_LOCK(gtq);
+		gtask->gt_task.ta_flags &= ~TASK_NOENQUEUE;
+		TQ_UNLOCK(gtq);
+	}
 	if (dev != NULL && irq != NULL) {
 		cpu = qgroup->tqg_queue[qid].tgc_cpu;
 		gtask->gt_cpu = cpu;
@@ -707,6 +722,15 @@
 	qgroup->tqg_queue[qid].tgc_cnt++;
 	LIST_INSERT_HEAD(&qgroup->tqg_queue[qid].tgc_tasks, gtask, gt_list);
 	gtask->gt_taskqueue = qgroup->tqg_queue[qid].tgc_taskq;
+	/* re-arm the task under the queue lock (detach leaves
+	 * TASK_NOENQUEUE set) so the flag RMW cannot race enqueue */
+	{
+		struct gtaskqueue *gtq = gtask->gt_taskqueue;
+
+		TQ_LOCK(gtq);
+		gtask->gt_task.ta_flags &= ~TASK_NOENQUEUE;
+		TQ_UNLOCK(gtq);
+	}
 	cpu = qgroup->tqg_queue[qid].tgc_cpu;
 	lockmgr(&qgroup->tqg_lock, LK_RELEASE);
 
@@ -744,7 +768,12 @@
 	LIST_REMOVE(gtask, gt_list);
 	lockmgr(&qgroup->tqg_lock, LK_RELEASE);
 	gtask->gt_taskqueue = NULL;
-	gtask->gt_task.ta_flags &= ~TASK_NOENQUEUE;
+	/*
+	 * Leave TASK_NOENQUEUE set while the task is detached: a racing
+	 * grouptaskqueue_enqueue() fails with EAGAIN (stale queue pointer)
+	 * or EINVAL (NULL queue) instead of corrupting the queue list or
+	 * panicking.  attach re-arms the flag under the queue lock.
+	 */
 }
 
 static void