Thread (16 messages) 16 messages, 4 authors, 11h ago

Re: [PATCH] coresight: Fix scheduling while atomic in coresight_device_release()

From: Jie Gan <hidden>
Date: 2026-07-13 03:07:42
Also in: linux-rt-devel, lkml


On 7/13/2026 5:04 AM, Mohamed Ayman wrote:
Dropping the last reference to a coresight_device can trigger a kernel
panic on PREEMPT_RT builds due to a "scheduling while atomic" violation.

When the CPU enters an idle state, coresight_cpu_pm_notify() is invoked
with local interrupts disabled (atomic context). This function eventually
calls coresight_put_percpu_source_ref(), which drops the device reference
via put_device(). If this is the last reference, it triggers the release
chain:

   coresight_cpu_pm_notify() (IRQs off)
     -> coresight_put_percpu_source_ref()
       -> put_device()
         -> coresight_device_release()
           -> free_percpu()

On a PREEMPT_RT kernel, free_percpu() acquires pcpu_lock, which is
implemented as a sleeping rt-mutex. Sleeping while in an atomic context
causes a system crash.

Fix this by deferring the teardown of the coresight_device to process
context. Add a work_struct to `struct coresight_device` and use
schedule_work() inside coresight_device_release() to safely execute
free_percpu() and kfree() in a worker thread, away from the atomic PM
notifier path.

Additionally, remove the redundant raw_spinlock_irqsave guard in
The code diff doesnt include this description.
quoted hunk ↗ jump to hunk
coresight_put_percpu_source_ref(). The lock was originally intended to
protect the per-CPU pointer table, but dropping a reference does not
touch this table.

Signed-off-by: Mohamed Ayman <redacted>
---
  drivers/hwtracing/coresight/coresight-core.c | 28 +++++++++++++-------
  include/linux/coresight.h                    |  2 ++
  2 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 6d65c43d5..9dbdb2977 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -165,13 +165,6 @@ void coresight_put_percpu_source_ref(struct coresight_device *csdev)
  
  	guard(raw_spinlock_irqsave)(&coresight_dev_lock);
  
-	/*
-	 * TODO: coresight_device_release() is invoked to release resources when
-	 * the device's refcount reaches zero. It then calls free_percpu(),
-	 * which acquires pcpu_lock  a sleepable lock when PREEMPT_RT is
-	 * enabled. Since the raw spinlock coresight_dev_lock is held, this can
-	 * lead to a potential "scheduling while atomic" issue.
-	 */
  	put_device(&csdev->dev);
  }
  
@@ -1257,13 +1250,30 @@ static void coresight_clear_default_sink(struct coresight_device *csdev)
  	}
  }
  
+static void coresight_device_release_work(struct work_struct *work)
+{
+	struct coresight_device *csdev =
+		container_of(work, struct coresight_device, free_work);
+
+	free_percpu(csdev->perf_sink_id_map.cpu_map);
+	kfree(csdev);
+}
+
  static void coresight_device_release(struct device *dev)
  {
  	struct coresight_device *csdev = to_coresight_device(dev);
  
  	fwnode_handle_put(csdev->dev.fwnode);
-	free_percpu(csdev->perf_sink_id_map.cpu_map);
-	kfree(csdev);
+
+	/*
+	 * This release callback can run with the last reference dropped
+	 * from atomic/IRQs-off context (e.g. coresight_put_percpu_source_ref()
+	 * called from the CPU_PM notifier). free_percpu() takes pcpu_lock,
+	 * which is a sleeping lock under PREEMPT_RT, so defer the actual
+	 * teardown to process context.
+	 */
+	INIT_WORK(&csdev->free_work, coresight_device_release_work);
+	schedule_work(&csdev->free_work);
Consider the module unload process, the async work can lead to UAF:
For example:

1. unload ETM4X module:
coresight_unregister() -> device_unregister() -> put_device() -> ref 0
-> coresight_device_release() -> schedule_work(&csdev->free_work)

At this point, the release work is only queued, while the ETM4X module 
unload path proceeds as if the release operation has finished.

2. unload the coresight module without flushing the workqueue
3. A UAF occurs when the pending work is eventually executed and 
attempts to free csdev via coresight_device_release_work(), after the 
relevant code or resources have already been unloaded.

We must drain out the workqueue in coresight_exit to close the gap.

Thanks,
Jie
quoted hunk ↗ jump to hunk
  }
  
  static int coresight_orphan_match(struct device *dev, void *data)
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index ddf18c970..63253f6c4 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -13,6 +13,7 @@
  #include <linux/perf_event.h>
  #include <linux/sched.h>
  #include <linux/platform_device.h>
+#include <linux/workqueue.h>
  
  /* Peripheral id registers (0xFD0-0xFEC) */
  #define CORESIGHT_PERIPHIDR4	0xfd0
@@ -293,6 +294,7 @@ struct coresight_device {
  	struct csdev_access access;
  	struct device dev;
  	struct coresight_path *path;
+	struct work_struct free_work;
  	atomic_t mode;
  	int refcnt;
  	int cpu;
  
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help