* [PATCH v2] irq_work: Fix use-after-free in irq_work_single on PREEMPT_RT
@ 2026-03-30 7:32 Jiayuan Chen
2026-04-02 6:50 ` Sebastian Andrzej Siewior
2026-05-11 14:31 ` [tip: irq/urgent] irq_work: Fix use-after-free in irq_work_single() " tip-bot2 for Jiayuan Chen
0 siblings, 2 replies; 6+ messages in thread
From: Jiayuan Chen @ 2026-03-30 7:32 UTC (permalink / raw)
To: linux-rt-devel
Cc: Jiayuan Chen, Sebastian Andrzej Siewior, Steven Rostedt,
Clark Williams, Peter Zijlstra (Intel), linux-kernel
On PREEMPT_RT, non-HARD irq_work runs in per-CPU kthreads via
run_irq_workd(), so irq_work_sync() uses rcuwait to wait for
BUSY==0.
After irq_work_single() clears BUSY via atomic_cmpxchg(), it still
dereferences @work for irq_work_is_hard() and rcuwait_wake_up().
An irq_work_sync() caller on another CPU that enters after BUSY is
cleared can observe BUSY==0 immediately, return, and free the work
before those accesses complete — causing a use-after-free.
Fix this by wrapping run_irq_workd() in guard(rcu)() so that the
entire irq_work_single() execution is within an RCU read-side
critical section. Then add synchronize_rcu() in irq_work_sync()
after rcuwait_wait_event() to ensure the caller waits for the RCU
grace period before returning, preventing premature frees.
Fixes: 810979682ccc ("irq_work: Allow irq_work_sync() to sleep if irq_work() no IRQ support.")
Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Suggested-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
---
kernel/irq_work.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/kernel/irq_work.c b/kernel/irq_work.c
index 73f7e1fd4ab4..bf411656c316 100644
--- a/kernel/irq_work.c
+++ b/kernel/irq_work.c
@@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work)
!arch_irq_work_has_interrupt()) {
rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work),
TASK_UNINTERRUPTIBLE);
+ /*
+ * Ensure irq_work_single() does not access @work
+ * after removing IRQ_WORK_BUSY. It is always
+ * accessed within a RCU-read section.
+ */
+ synchronize_rcu();
return;
}
@@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync);
static void run_irq_workd(unsigned int cpu)
{
+ guard(rcu)();
irq_work_run_list(this_cpu_ptr(&lazy_list));
}
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2] irq_work: Fix use-after-free in irq_work_single on PREEMPT_RT
2026-03-30 7:32 [PATCH v2] irq_work: Fix use-after-free in irq_work_single on PREEMPT_RT Jiayuan Chen
@ 2026-04-02 6:50 ` Sebastian Andrzej Siewior
2026-06-05 10:31 ` Xi Ruoyao
2026-05-11 14:31 ` [tip: irq/urgent] irq_work: Fix use-after-free in irq_work_single() " tip-bot2 for Jiayuan Chen
1 sibling, 1 reply; 6+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-04-02 6:50 UTC (permalink / raw)
To: Jiayuan Chen
Cc: linux-rt-devel, Steven Rostedt, Clark Williams,
Peter Zijlstra (Intel), linux-kernel
On 2026-03-30 15:32:29 [+0800], Jiayuan Chen wrote:
> On PREEMPT_RT, non-HARD irq_work runs in per-CPU kthreads via
> run_irq_workd(), so irq_work_sync() uses rcuwait to wait for
> BUSY==0.
>
> After irq_work_single() clears BUSY via atomic_cmpxchg(), it still
> dereferences @work for irq_work_is_hard() and rcuwait_wake_up().
> An irq_work_sync() caller on another CPU that enters after BUSY is
> cleared can observe BUSY==0 immediately, return, and free the work
> before those accesses complete — causing a use-after-free.
>
> Fix this by wrapping run_irq_workd() in guard(rcu)() so that the
> entire irq_work_single() execution is within an RCU read-side
> critical section. Then add synchronize_rcu() in irq_work_sync()
> after rcuwait_wait_event() to ensure the caller waits for the RCU
> grace period before returning, preventing premature frees.
>
> Fixes: 810979682ccc ("irq_work: Allow irq_work_sync() to sleep if irq_work() no IRQ support.")
> Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> Suggested-by: Steven Rostedt <rostedt@goodmis.org>
> Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Sebastian
^ permalink raw reply [flat|nested] 6+ messages in thread
* [tip: irq/urgent] irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT
2026-03-30 7:32 [PATCH v2] irq_work: Fix use-after-free in irq_work_single on PREEMPT_RT Jiayuan Chen
2026-04-02 6:50 ` Sebastian Andrzej Siewior
@ 2026-05-11 14:31 ` tip-bot2 for Jiayuan Chen
1 sibling, 0 replies; 6+ messages in thread
From: tip-bot2 for Jiayuan Chen @ 2026-05-11 14:31 UTC (permalink / raw)
To: linux-tip-commits
Cc: Sebastian Andrzej Siewior, Steven Rostedt, Jiayuan Chen,
Thomas Gleixner, x86, linux-kernel, maz
The following commit has been merged into the irq/urgent branch of tip:
Commit-ID: 91840be8f710370607f949a627e070896faeddb8
Gitweb: https://git.kernel.org/tip/91840be8f710370607f949a627e070896faeddb8
Author: Jiayuan Chen <jiayuan.chen@linux.dev>
AuthorDate: Mon, 30 Mar 2026 15:32:29 +08:00
Committer: Thomas Gleixner <tglx@kernel.org>
CommitterDate: Mon, 11 May 2026 16:28:04 +02:00
irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT
On PREEMPT_RT, non-HARD irq_work runs in per-CPU kthreads via
run_irq_workd(), so irq_work_sync() uses rcuwait() to wait for BUSY==0.
After irq_work_single() clears BUSY via atomic_cmpxchg(), it still
dereferences @work for irq_work_is_hard() and rcuwait_wake_up().
An irq_work_sync() caller on another CPU that enters after BUSY is cleared
can observe BUSY==0 immediately, return, and free the work before those
accesses complete — causing a use-after-free.
Fix this by wrapping run_irq_workd() in guard(rcu)() so that the entire
irq_work_single() execution is within an RCU read-side critical
section. Then add synchronize_rcu() in irq_work_sync() after
rcuwait_wait_event() to ensure the caller waits for the RCU grace period
before returning, preventing premature frees.
Fixes: 810979682ccc ("irq_work: Allow irq_work_sync() to sleep if irq_work() no IRQ support.")
Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Suggested-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev
---
kernel/irq_work.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/kernel/irq_work.c b/kernel/irq_work.c
index 120fd73..f7e2dc2 100644
--- a/kernel/irq_work.c
+++ b/kernel/irq_work.c
@@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work)
!arch_irq_work_has_interrupt()) {
rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work),
TASK_UNINTERRUPTIBLE);
+ /*
+ * Ensure irq_work_single() does not access @work
+ * after removing IRQ_WORK_BUSY. It is always
+ * accessed within a RCU-read section.
+ */
+ synchronize_rcu();
return;
}
@@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync);
static void run_irq_workd(unsigned int cpu)
{
+ guard(rcu)();
irq_work_run_list(this_cpu_ptr(&lazy_list));
}
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2] irq_work: Fix use-after-free in irq_work_single on PREEMPT_RT
2026-04-02 6:50 ` Sebastian Andrzej Siewior
@ 2026-06-05 10:31 ` Xi Ruoyao
2026-06-05 10:45 ` Sebastian Andrzej Siewior
0 siblings, 1 reply; 6+ messages in thread
From: Xi Ruoyao @ 2026-06-05 10:31 UTC (permalink / raw)
To: Sebastian Andrzej Siewior, Jiayuan Chen
Cc: linux-rt-devel, Steven Rostedt, Clark Williams,
Peter Zijlstra (Intel), linux-kernel, Mingcong Bai, Henry Chen,
Huacai Chen
[-- Attachment #1: Type: text/plain, Size: 2074 bytes --]
On Thu, 2026-04-02 at 08:50 +0200, Sebastian Andrzej Siewior wrote:
> On 2026-03-30 15:32:29 [+0800], Jiayuan Chen wrote:
> > On PREEMPT_RT, non-HARD irq_work runs in per-CPU kthreads via
> > run_irq_workd(), so irq_work_sync() uses rcuwait to wait for
> > BUSY==0.
> >
> > After irq_work_single() clears BUSY via atomic_cmpxchg(), it still
> > dereferences @work for irq_work_is_hard() and rcuwait_wake_up().
> > An irq_work_sync() caller on another CPU that enters after BUSY is
> > cleared can observe BUSY==0 immediately, return, and free the work
> > before those accesses complete — causing a use-after-free.
> >
> > Fix this by wrapping run_irq_workd() in guard(rcu)() so that the
> > entire irq_work_single() execution is within an RCU read-side
> > critical section. Then add synchronize_rcu() in irq_work_sync()
> > after rcuwait_wait_event() to ensure the caller waits for the RCU
> > grace period before returning, preventing premature frees.
> >
> > Fixes: 810979682ccc ("irq_work: Allow irq_work_sync() to sleep if irq_work() no IRQ support.")
> > Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> > Suggested-by: Steven Rostedt <rostedt@goodmis.org>
> > Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
>
> Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
>
> Sebastian
Hi,
This commit significantly slows down execve() (and perhaps other things)
on MIPS. A very simple program:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int x = argc > 1 ? atoi(argv[1]) : 114;
if (!x)
exit(0);
char buf[10];
sprintf(buf, "%d", x - 1);
execl("/proc/self/exe", argv[0], buf, NULL);
__builtin_trap();
}
now spends several (2 to 6) seconds on a Loongson 3A4000, with the
commit reverted it only spends 0.06 second.
I noticed https://lore.kernel.org/all/20260604182407.3109536-1-
jelonek.jonas@gmail.com/ but it doesn't help.
The kernel configuration is attached. Any thoughts?
--
Xi Ruoyao <xry111@xry111.site>
[-- Attachment #2: config-mips.zst --]
[-- Type: application/zstd, Size: 25907 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2] irq_work: Fix use-after-free in irq_work_single on PREEMPT_RT
2026-06-05 10:31 ` Xi Ruoyao
@ 2026-06-05 10:45 ` Sebastian Andrzej Siewior
2026-06-05 17:26 ` Xi Ruoyao
0 siblings, 1 reply; 6+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-06-05 10:45 UTC (permalink / raw)
To: Xi Ruoyao
Cc: Jiayuan Chen, linux-rt-devel, Steven Rostedt, Clark Williams,
Peter Zijlstra (Intel), linux-kernel, Mingcong Bai, Henry Chen,
Huacai Chen
On 2026-06-05 18:31:41 [+0800], Xi Ruoyao wrote:
> Hi,
>
> This commit significantly slows down execve() (and perhaps other things)
> on MIPS. A very simple program:
This is probably originating from sched_mm_cid_exit(). Can you check?
Sebastian
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2] irq_work: Fix use-after-free in irq_work_single on PREEMPT_RT
2026-06-05 10:45 ` Sebastian Andrzej Siewior
@ 2026-06-05 17:26 ` Xi Ruoyao
0 siblings, 0 replies; 6+ messages in thread
From: Xi Ruoyao @ 2026-06-05 17:26 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: Jiayuan Chen, linux-rt-devel, Steven Rostedt, Clark Williams,
Peter Zijlstra (Intel), linux-kernel, Mingcong Bai, Henry Chen,
Huacai Chen, Yao Zi
On Fri, 2026-06-05 at 12:45 +0200, Sebastian Andrzej Siewior wrote:
> On 2026-06-05 18:31:41 [+0800], Xi Ruoyao wrote:
> > Hi,
> >
> > This commit significantly slows down execve() (and perhaps other things)
> > on MIPS. A very simple program:
>
> This is probably originating from sched_mm_cid_exit(). Can you check?
I've not checked that yet (I have to admit I don't really know how).
On the other hand, Yao Zi told me she speculated this might be related
to the lack of arch_irq_work_raise on MIPS. For mips/loongson64 the IPI
support is already there and we only need 16 lines to implement
arch_irq_work_raise:
diff --git a/arch/mips/include/asm/irq_work.h b/arch/mips/include/asm/irq_work.h
new file mode 100644
index 000000000000..2695e0f94f2f
--- /dev/null
+++ b/arch/mips/include/asm/irq_work.h
@@ -0,0 +1,4 @@
+static inline bool arch_irq_work_has_interrupt(void)
+{
+ return IS_ENABLED(CONFIG_MACH_LOONGSON64) && IS_ENABLED(CONFIG_SMP);
+}
diff --git a/arch/mips/include/asm/smp.h b/arch/mips/include/asm/smp.h
index 2427d76f953f..30a036e5bfc4 100644
--- a/arch/mips/include/asm/smp.h
+++ b/arch/mips/include/asm/smp.h
@@ -50,6 +50,8 @@ extern int __cpu_logical_map[NR_CPUS];
#define SMP_CALL_FUNCTION 0x2
/* Octeon - Tell another core to flush its icache */
#define SMP_ICACHE_FLUSH 0x4
+/* Loongson 3 - IRQ work */
+#define SMP_IRQ_WORK 0x8
/* Mask of CPUs which are currently definitely operating coherently */
extern cpumask_t cpu_coherent_mask;
diff --git a/arch/mips/loongson64/smp.c b/arch/mips/loongson64/smp.c
index 147acd972a07..e584299d0fde 100644
--- a/arch/mips/loongson64/smp.c
+++ b/arch/mips/loongson64/smp.c
@@ -381,6 +381,13 @@ loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action)
ipi_write_action(cpu_logical_map(i), (u32)action);
}
+#ifdef CONFIG_IRQ_WORK
+void arch_irq_work_raise(void)
+{
+ loongson3_send_ipi_single(smp_processor_id(), SMP_IRQ_WORK);
+}
+#endif
+
static irqreturn_t loongson3_ipi_interrupt(int irq, void *dev_id)
{
int cpu = smp_processor_id();
@@ -397,6 +404,9 @@ static irqreturn_t loongson3_ipi_interrupt(int irq, void *dev_id)
irq_exit();
}
+ if (action & SMP_IRQ_WORK)
+ irq_work_run();
+
return IRQ_HANDLED;
}
With this the performance indeed becomes normal.
--
Xi Ruoyao <xry111@xry111.site>
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-06-05 17:27 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-30 7:32 [PATCH v2] irq_work: Fix use-after-free in irq_work_single on PREEMPT_RT Jiayuan Chen
2026-04-02 6:50 ` Sebastian Andrzej Siewior
2026-06-05 10:31 ` Xi Ruoyao
2026-06-05 10:45 ` Sebastian Andrzej Siewior
2026-06-05 17:26 ` Xi Ruoyao
2026-05-11 14:31 ` [tip: irq/urgent] irq_work: Fix use-after-free in irq_work_single() " tip-bot2 for Jiayuan Chen
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.