Commit ef1ea98c authored by Peter Zijlstra's avatar Peter Zijlstra
Browse files

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!)

Fixes: 466e4d80 ("task_work: Add TWA_NMI_CURRENT as an additional notify mode.")
Reported-by: default avatarJosh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
Link: https://patch.msgid.link/20250924080118.425949403@infradead.org
parent 34976eaf
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -9,7 +9,12 @@ static struct callback_head work_exited; /* all we need is ->next == NULL */
#ifdef CONFIG_IRQ_WORK
static void task_work_set_notify_irq(struct irq_work *entry)
{
	test_and_set_tsk_thread_flag(current, TIF_NOTIFY_RESUME);
	/*
	 * no-op IPI
	 *
	 * TWA_NMI_CURRENT will already have set the TIF flag, all
	 * this interrupt does it tickle the return-to-user path.
	 */
}
static DEFINE_PER_CPU(struct irq_work, irq_work_NMI_resume) =
	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,
		break;
#ifdef CONFIG_IRQ_WORK
	case TWA_NMI_CURRENT:
		set_tsk_thread_flag(current, TIF_NOTIFY_RESUME);
		irq_work_queue(this_cpu_ptr(&irq_work_NMI_resume));
		break;
#endif