History

task_work: Fix NMI race condition

__schedule() // disable irqs <NMI> task_work_add(current, work, TWA_NMI_CURRENT); </NMI> // current = next; // enable irqs <IRQ> task_work_set_notify_irq() test_and_set_tsk_thread_flag(current, TIF_NOTIFY_RESUME); // wrong task! </IRQ> // original task skips task work on its next return to user (or exit!)

Peter Zijlstra committed 1 year ago

Commit ef1ea981 parent 34976ea1 file · +7 −1Browse files

Unified · Split

kernel/task_work.c+7−1
@@ -9,7 +9,12 @@ static struct callback_head work_exited; /* all we need is ->next == NULL */ · 8 unmodified lines
99 #ifdef CONFIG_IRQ_WORK
1010 static void task_work_set_notify_irq(struct irq_work *entry)
1111 {
12− test_and_set_tsk_thread_flag(current, TIF_NOTIFY_RESUME);
12+ /*
13+ * no-op IPI
14+ *
15+ * TWA_NMI_CURRENT will already have set the TIF flag, all
16+ * this interrupt does it tickle the return-to-user path.
17+ */
1318 }
1419 static DEFINE_PER_CPU(struct irq_work, irq_work_NMI_resume) =
1520 IRQ_WORK_INIT_HARD(task_work_set_notify_irq);
@@ -86,6 +91,7 @@ int task_work_add(struct task_struct *task, struct callback_head *work, · 70 unmodified lines
8691 break;
8792 #ifdef CONFIG_IRQ_WORK
8893 case TWA_NMI_CURRENT:
94+ set_tsk_thread_flag(current, TIF_NOTIFY_RESUME);
8995 irq_work_queue(this_cpu_ptr(&irq_work_NMI_resume));
9096 break;
9197 #endif