* [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue
@ 2025-10-20 9:06 Xiaoqi Zhuang
2025-10-20 10:18 ` Jie Gan
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Xiaoqi Zhuang @ 2025-10-20 9:06 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
Xiaoqi Zhuang
When ETR is enabled as CS_MODE_SYSFS, if the buffer size is changed
and enabled again, currently sysfs_buf will point to the newly
allocated memory(buf_new) and free the old memory(buf_old). But the
etr_buf that is being used by the ETR remains pointed to buf_old, not
updated to buf_new. In this case, it will result in a memory
use-after-free issue.
Fix this by checking ETR's mode before updating and releasing buf_old,
if the mode is CS_MODE_SYSFS, then skip updating and releasing it.
Signed-off-by: Xiaoqi Zhuang <xiaoqi.zhuang@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-tmc-etr.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index b07fcdb3fe1a..3e73cf2c38a3 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1268,6 +1268,13 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
goto out;
}
+ /*
+ * Since this sink is already enabled, the existing buffer should not
+ * be released even if the buffer size has changed.
+ */
+ if (coresight_get_mode(csdev) == CS_MODE_SYSFS)
+ goto out;
+
/*
* If we don't have a buffer or it doesn't match the requested size,
* use the buffer allocated above. Otherwise reuse the existing buffer.
---
base-commit: 98ac9cc4b4452ed7e714eddc8c90ac4ae5da1a09
change-id: 20251020-fix_etr_issue-02c706dbc899
Best regards,
--
Xiaoqi Zhuang <xiaoqi.zhuang@oss.qualcomm.com>
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue 2025-10-20 9:06 [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue Xiaoqi Zhuang @ 2025-10-20 10:18 ` Jie Gan 2025-10-20 10:57 ` Suzuki K Poulose 2025-10-20 14:37 ` Leo Yan 2025-10-21 7:43 ` Jie Gan 2 siblings, 1 reply; 8+ messages in thread From: Jie Gan @ 2025-10-20 10:18 UTC (permalink / raw) To: Xiaoqi Zhuang, Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm On 10/20/2025 5:06 PM, Xiaoqi Zhuang wrote: > When ETR is enabled as CS_MODE_SYSFS, if the buffer size is changed > and enabled again, currently sysfs_buf will point to the newly > allocated memory(buf_new) and free the old memory(buf_old). But the > etr_buf that is being used by the ETR remains pointed to buf_old, not > updated to buf_new. In this case, it will result in a memory > use-after-free issue. > > Fix this by checking ETR's mode before updating and releasing buf_old, > if the mode is CS_MODE_SYSFS, then skip updating and releasing it. > I think we need a fix tag for the fix patch: Fixes: de5461970b3e ("coresight: tmc: allocating memory when needed") minor comment: Since ETR is enabled, we can't switch the sysfs buffer, can we exit earlier by checking the CS_MODE to avoid allocating memory unnecessarily? Besides, Looks good to me. Thanks, Jie > Signed-off-by: Xiaoqi Zhuang <xiaoqi.zhuang@oss.qualcomm.com> > --- > drivers/hwtracing/coresight/coresight-tmc-etr.c | 7 +++++++ > 1 file changed, 7 insertions(+) > > diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c > index b07fcdb3fe1a..3e73cf2c38a3 100644 > --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c > +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c > @@ -1268,6 +1268,13 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) > goto out; > } > > + /* > + * Since this sink is already enabled, the existing buffer should not > + * be released even if the buffer size has changed. > + */ > + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) > + goto out; > + > /* > * If we don't have a buffer or it doesn't match the requested size, > * use the buffer allocated above. Otherwise reuse the existing buffer. > > --- > base-commit: 98ac9cc4b4452ed7e714eddc8c90ac4ae5da1a09 > change-id: 20251020-fix_etr_issue-02c706dbc899 > > Best regards, ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue 2025-10-20 10:18 ` Jie Gan @ 2025-10-20 10:57 ` Suzuki K Poulose 0 siblings, 0 replies; 8+ messages in thread From: Suzuki K Poulose @ 2025-10-20 10:57 UTC (permalink / raw) To: Jie Gan, Xiaoqi Zhuang, Mike Leach, James Clark, Alexander Shishkin Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm On 20/10/2025 11:18, Jie Gan wrote: > > > On 10/20/2025 5:06 PM, Xiaoqi Zhuang wrote: >> When ETR is enabled as CS_MODE_SYSFS, if the buffer size is changed >> and enabled again, currently sysfs_buf will point to the newly >> allocated memory(buf_new) and free the old memory(buf_old). But the >> etr_buf that is being used by the ETR remains pointed to buf_old, not >> updated to buf_new. In this case, it will result in a memory >> use-after-free issue. >> >> Fix this by checking ETR's mode before updating and releasing buf_old, >> if the mode is CS_MODE_SYSFS, then skip updating and releasing it. >> > > I think we need a fix tag for the fix patch: > Fixes: de5461970b3e ("coresight: tmc: allocating memory when needed") > > minor comment: > Since ETR is enabled, we can't switch the sysfs buffer, can we exit > earlier by checking the CS_MODE to avoid allocating memory unnecessarily? +1 Something like this: diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index b07fcdb3fe1a..a9ddb05c10d9 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1252,6 +1252,12 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) raw_spin_lock_irqsave(&drvdata->spinlock, flags); sysfs_buf = READ_ONCE(drvdata->sysfs_buf); if (!sysfs_buf || (sysfs_buf->size != drvdata->size)) { + /* + * If the ETR is already enabled, continue with the existing + * buffer. + */ + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) + goto out; raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); /* Allocate memory with the locks released */ Suzuki> > Besides, > Looks good to me. > > Thanks, > Jie > >> Signed-off-by: Xiaoqi Zhuang <xiaoqi.zhuang@oss.qualcomm.com> >> --- >> drivers/hwtracing/coresight/coresight-tmc-etr.c | 7 +++++++ >> 1 file changed, 7 insertions(+) >> >> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/ >> drivers/hwtracing/coresight/coresight-tmc-etr.c >> index b07fcdb3fe1a..3e73cf2c38a3 100644 >> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c >> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c >> @@ -1268,6 +1268,13 @@ static struct etr_buf >> *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) >> goto out; >> } >> + /* >> + * Since this sink is already enabled, the existing buffer should >> not >> + * be released even if the buffer size has changed. >> + */ >> + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) >> + goto out; >> + >> /* >> * If we don't have a buffer or it doesn't match the requested >> size, >> * use the buffer allocated above. Otherwise reuse the existing >> buffer. >> >> --- >> base-commit: 98ac9cc4b4452ed7e714eddc8c90ac4ae5da1a09 >> change-id: 20251020-fix_etr_issue-02c706dbc899 >> >> Best regards, > ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue 2025-10-20 9:06 [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue Xiaoqi Zhuang 2025-10-20 10:18 ` Jie Gan @ 2025-10-20 14:37 ` Leo Yan 2025-10-21 1:56 ` Jie Gan 2025-10-21 10:07 ` hejunhao 2025-10-21 7:43 ` Jie Gan 2 siblings, 2 replies; 8+ messages in thread From: Leo Yan @ 2025-10-20 14:37 UTC (permalink / raw) To: Xiaoqi Zhuang Cc: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin, coresight, linux-arm-kernel, linux-kernel, linux-arm-msm On Mon, Oct 20, 2025 at 05:06:46PM +0800, Xiaoqi Zhuang wrote: > When ETR is enabled as CS_MODE_SYSFS, if the buffer size is changed > and enabled again, currently sysfs_buf will point to the newly > allocated memory(buf_new) and free the old memory(buf_old). But the > etr_buf that is being used by the ETR remains pointed to buf_old, not > updated to buf_new. In this case, it will result in a memory > use-after-free issue. I struggled to understand how to reproduce the issue under the condition "if the buffer size is changed and enabled again." I don't think the flow below where the trace is re-enabled would cause an issue: - Step 1: Enable trace path between ETM0 -> ETR0; - Step 2: Change the buffer size for ETR0; - Step 3: Disable trace path between ETM0 -> ETR0; - Step 4: Enable again trace path between ETM0 -> ETR0. In this case, step3 releases the buffer and update "drvdata->etr_buf" to NULL, and step 4 allocates a new buffer and assign it to "drvdata->etr_buf". The problem should occur when operating on two trace paths, E.g., - Step 1: Enable trace path between ETM0 -> ETR0; - Step 2: Change the buffer size for ETR0; - Step 3: Enable trace path between ETM1 -> ETR0; In step3, the driver releases the existed buffer and allocate a new one. At the meantime, "drvdata->etr_buf" still holds the buffer allocated in step 1. > Fix this by checking ETR's mode before updating and releasing buf_old, > if the mode is CS_MODE_SYSFS, then skip updating and releasing it. Given that we now have a couple of reported issues related to ETR mode, I'd like to refactor the ETR mode handling and its reference counting thoroughly. I've drafted a large change (it's quite big, but we can split it into small patches if we agree to proceed). Thanks for reporting the issue! Leo ---8<--- diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index b07fcdb3fe1a..d0fac958c614 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1241,6 +1241,8 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etr_buf *sysfs_buf = NULL, *new_buf = NULL, *free_buf = NULL; + WARN_ON(coresight_get_mode(csdev) != CS_MODE_SYSFS); + /* * If we are enabling the ETR from disabled state, we need to make * sure we have a buffer with the right size. The etr_buf is not reset @@ -1263,7 +1265,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) raw_spin_lock_irqsave(&drvdata->spinlock, flags); } - if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) { + if (drvdata->reading) { ret = -EBUSY; goto out; } @@ -1292,30 +1294,14 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) int ret = 0; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - struct etr_buf *sysfs_buf = tmc_etr_get_sysfs_buffer(csdev); + struct etr_buf *sysfs_buf; + sysfs_buf = tmc_etr_get_sysfs_buffer(csdev); if (IS_ERR(sysfs_buf)) return PTR_ERR(sysfs_buf); raw_spin_lock_irqsave(&drvdata->spinlock, flags); - - /* - * In sysFS mode we can have multiple writers per sink. Since this - * sink is already enabled no memory is needed and the HW need not be - * touched, even if the buffer size has changed. - */ - if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { - csdev->refcnt++; - goto out; - } - ret = tmc_etr_enable_hw(drvdata, sysfs_buf); - if (!ret) { - coresight_set_mode(csdev, CS_MODE_SYSFS); - csdev->refcnt++; - } - -out: raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); if (!ret) @@ -1735,11 +1721,6 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) struct etr_perf_buffer *etr_perf = etm_perf_sink_config(handle); raw_spin_lock_irqsave(&drvdata->spinlock, flags); - /* Don't use this sink if it is already claimed by sysFS */ - if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { - rc = -EBUSY; - goto unlock_out; - } if (WARN_ON(!etr_perf || !etr_perf->etr_buf)) { rc = -EINVAL; @@ -1759,18 +1740,14 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) * No HW configuration is needed if the sink is already in * use for this session. */ - if (drvdata->pid == pid) { - csdev->refcnt++; + if (drvdata->pid == pid) goto unlock_out; - } rc = tmc_etr_enable_hw(drvdata, etr_perf->etr_buf); if (!rc) { /* Associate with monitored process. */ drvdata->pid = pid; - coresight_set_mode(csdev, CS_MODE_PERF); drvdata->perf_buf = etr_perf->etr_buf; - csdev->refcnt++; } unlock_out: @@ -1778,17 +1755,76 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) return rc; } +static int tmc_acquire_mode(struct coresight_device *csdev, enum cs_mode mode) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (mode != CS_MODE_SYSFS && mode != CS_MODE_PERF) + return -EINVAL; + + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock); + + if (coresight_get_mode(csdev) == CS_MODE_DISABLED) { + if (!csdev->refcnt) + coresight_set_mode(csdev, mode); + csdev->refcnt++; + } else if (coresight_get_mode(csdev) != mode) { + ret = -EBUSY; + } + + return csdev->refcnt; +} + +static void tmc_release_mode(struct coresight_device *csdev, enum cs_mode mode) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock); + + if (WARN_ON(coresight_get_mode(csdev) != mode)) + return; + + csdev->refcnt--; + if (!csdev->refcnt) + coresight_set_mode(csdev, CS_MODE_DISABLED); +} + static int tmc_enable_etr_sink(struct coresight_device *csdev, enum cs_mode mode, void *data) { + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + int ret; + + ret = tmc_acquire_mode(csdev, mode); + if (ret < 0) + return ret; + + /* + * For sysfs mode, the higher level mutex ensures exclusively + * enabling sink, it is safe to bail out if this is not the + * first time to enable sink. + * + * A perf session can enable the same sink simultaneously, fall + * through to call tmc_enable_etr_sink_perf() to ensure the sink + * has been enabled. + */ + if (mode == CS_MODE_SYSFS && ret > 1) + return 0; + switch (mode) { case CS_MODE_SYSFS: - return tmc_enable_etr_sink_sysfs(csdev); + ret = tmc_enable_etr_sink_sysfs(csdev); case CS_MODE_PERF: - return tmc_enable_etr_sink_perf(csdev, data); + ret = tmc_enable_etr_sink_perf(csdev, data); default: - return -EINVAL; + ret = -EINVAL; } + + if (ret) + tmc_release_mode(csdev, mode); + + return ret; } static int tmc_disable_etr_sink(struct coresight_device *csdev) @@ -1796,30 +1832,20 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev) unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - raw_spin_lock_irqsave(&drvdata->spinlock, flags); + tmc_release_mode(csdev, mode); - if (drvdata->reading) { - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); - return -EBUSY; - } + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock); - csdev->refcnt--; - if (csdev->refcnt) { - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); + if (csdev->refcnt || drvdata->reading) return -EBUSY; - } - /* Complain if we (somehow) got out of sync */ - WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED); + if (drvdata->pid == -1) + return 0; + tmc_etr_disable_hw(drvdata); - /* Dissociate from monitored process. */ - drvdata->pid = -1; - coresight_set_mode(csdev, CS_MODE_DISABLED); /* Reset perf specific data */ drvdata->perf_buf = NULL; - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_dbg(&csdev->dev, "TMC-ETR disabled\n"); return 0; } ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue 2025-10-20 14:37 ` Leo Yan @ 2025-10-21 1:56 ` Jie Gan 2025-10-21 7:08 ` Leo Yan 2025-10-21 10:07 ` hejunhao 1 sibling, 1 reply; 8+ messages in thread From: Jie Gan @ 2025-10-21 1:56 UTC (permalink / raw) To: Leo Yan, Xiaoqi Zhuang Cc: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin, coresight, linux-arm-kernel, linux-kernel, linux-arm-msm On 10/20/2025 10:37 PM, Leo Yan wrote: > On Mon, Oct 20, 2025 at 05:06:46PM +0800, Xiaoqi Zhuang wrote: >> When ETR is enabled as CS_MODE_SYSFS, if the buffer size is changed >> and enabled again, currently sysfs_buf will point to the newly >> allocated memory(buf_new) and free the old memory(buf_old). But the >> etr_buf that is being used by the ETR remains pointed to buf_old, not >> updated to buf_new. In this case, it will result in a memory >> use-after-free issue. > > I struggled to understand how to reproduce the issue under the condition > "if the buffer size is changed and enabled again." > > I don't think the flow below where the trace is re-enabled would cause > an issue: > > - Step 1: Enable trace path between ETM0 -> ETR0; > - Step 2: Change the buffer size for ETR0; > - Step 3: Disable trace path between ETM0 -> ETR0; > - Step 4: Enable again trace path between ETM0 -> ETR0. > > In this case, step3 releases the buffer and update "drvdata->etr_buf" to > NULL, and step 4 allocates a new buffer and assign it to > "drvdata->etr_buf". > > The problem should occur when operating on two trace paths, E.g., > > - Step 1: Enable trace path between ETM0 -> ETR0; > - Step 2: Change the buffer size for ETR0; > - Step 3: Enable trace path between ETM1 -> ETR0; > > In step3, the driver releases the existed buffer and allocate a new one. > At the meantime, "drvdata->etr_buf" still holds the buffer allocated in > step 1. That's the scenario of the issue. The system will report a segmentation error when the driver try to sync the freed etr_buf. > >> Fix this by checking ETR's mode before updating and releasing buf_old, >> if the mode is CS_MODE_SYSFS, then skip updating and releasing it. I agree. We should bail out as earlier as possible to gain some performance efficiency. > > Given that we now have a couple of reported issues related to ETR mode, > I'd like to refactor the ETR mode handling and its reference counting > thoroughly. I've drafted a large change (it's quite big, but we can > split it into small patches if we agree to proceed). > > Thanks for reporting the issue! > > Leo > > ---8<--- > > diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c > index b07fcdb3fe1a..d0fac958c614 100644 > --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c > +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c > @@ -1241,6 +1241,8 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) > struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > struct etr_buf *sysfs_buf = NULL, *new_buf = NULL, *free_buf = NULL; > > + WARN_ON(coresight_get_mode(csdev) != CS_MODE_SYSFS); I think we should check the WARN_ON result and exit if there is an error? > + > /* > * If we are enabling the ETR from disabled state, we need to make > * sure we have a buffer with the right size. The etr_buf is not reset > @@ -1263,7 +1265,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) > raw_spin_lock_irqsave(&drvdata->spinlock, flags); > } > > - if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) { > + if (drvdata->reading) { > ret = -EBUSY; > goto out; > } > @@ -1292,30 +1294,14 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) > int ret = 0; > unsigned long flags; > struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > - struct etr_buf *sysfs_buf = tmc_etr_get_sysfs_buffer(csdev); > + struct etr_buf *sysfs_buf; > > + sysfs_buf = tmc_etr_get_sysfs_buffer(csdev); > if (IS_ERR(sysfs_buf)) > return PTR_ERR(sysfs_buf); > > raw_spin_lock_irqsave(&drvdata->spinlock, flags); > - > - /* > - * In sysFS mode we can have multiple writers per sink. Since this > - * sink is already enabled no memory is needed and the HW need not be > - * touched, even if the buffer size has changed. > - */ > - if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { > - csdev->refcnt++; > - goto out; > - } > - > ret = tmc_etr_enable_hw(drvdata, sysfs_buf); > - if (!ret) { > - coresight_set_mode(csdev, CS_MODE_SYSFS); > - csdev->refcnt++; > - } > - > -out: > raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); > > if (!ret) > @@ -1735,11 +1721,6 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) > struct etr_perf_buffer *etr_perf = etm_perf_sink_config(handle); > > raw_spin_lock_irqsave(&drvdata->spinlock, flags); > - /* Don't use this sink if it is already claimed by sysFS */ > - if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { > - rc = -EBUSY; > - goto unlock_out; > - } > > if (WARN_ON(!etr_perf || !etr_perf->etr_buf)) { > rc = -EINVAL; > @@ -1759,18 +1740,14 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) > * No HW configuration is needed if the sink is already in > * use for this session. > */ > - if (drvdata->pid == pid) { > - csdev->refcnt++; > + if (drvdata->pid == pid) > goto unlock_out; > - } > > rc = tmc_etr_enable_hw(drvdata, etr_perf->etr_buf); > if (!rc) { > /* Associate with monitored process. */ > drvdata->pid = pid; > - coresight_set_mode(csdev, CS_MODE_PERF); > drvdata->perf_buf = etr_perf->etr_buf; > - csdev->refcnt++; > } > > unlock_out: > @@ -1778,17 +1755,76 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) > return rc; > } > > +static int tmc_acquire_mode(struct coresight_device *csdev, enum cs_mode mode) > +{ > + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > + > + if (mode != CS_MODE_SYSFS && mode != CS_MODE_PERF) > + return -EINVAL; > + > + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock); > + > + if (coresight_get_mode(csdev) == CS_MODE_DISABLED) { > + if (!csdev->refcnt) > + coresight_set_mode(csdev, mode); > + csdev->refcnt++; > + } else if (coresight_get_mode(csdev) != mode) { > + ret = -EBUSY; > + } > + > + return csdev->refcnt; > +} > + > +static void tmc_release_mode(struct coresight_device *csdev, enum cs_mode mode) > +{ > + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > + > + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock); > + > + if (WARN_ON(coresight_get_mode(csdev) != mode)) > + return; the mode here could be set to any CS_MODE, so I think it's possible to encounter the secenario below: coresight_get_mode(csdev) == CS_MODE_DISABLED, mode == CS_MODE_DISABLED, With the condition, the csdev->refcnt will go to negative number? > + > + csdev->refcnt--; > + if (!csdev->refcnt) > + coresight_set_mode(csdev, CS_MODE_DISABLED); > +} > + > static int tmc_enable_etr_sink(struct coresight_device *csdev, > enum cs_mode mode, void *data) > { > + unsigned long flags; > + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > + int ret; > + > + ret = tmc_acquire_mode(csdev, mode); > + if (ret < 0) > + return ret; > + > + /* > + * For sysfs mode, the higher level mutex ensures exclusively > + * enabling sink, it is safe to bail out if this is not the > + * first time to enable sink. > + * > + * A perf session can enable the same sink simultaneously, fall > + * through to call tmc_enable_etr_sink_perf() to ensure the sink > + * has been enabled. > + */ > + if (mode == CS_MODE_SYSFS && ret > 1) > + return 0; > + > switch (mode) { > case CS_MODE_SYSFS: > - return tmc_enable_etr_sink_sysfs(csdev); > + ret = tmc_enable_etr_sink_sysfs(csdev); > case CS_MODE_PERF: > - return tmc_enable_etr_sink_perf(csdev, data); > + ret = tmc_enable_etr_sink_perf(csdev, data); > default: > - return -EINVAL; > + ret = -EINVAL; > } > + > + if (ret) > + tmc_release_mode(csdev, mode); > + > + return ret; > } > > static int tmc_disable_etr_sink(struct coresight_device *csdev) > @@ -1796,30 +1832,20 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev) > unsigned long flags; > struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > > - raw_spin_lock_irqsave(&drvdata->spinlock, flags); > + tmc_release_mode(csdev, mode); mode here is undefined? Thanks, Jie > > - if (drvdata->reading) { > - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); > - return -EBUSY; > - } > + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock); > > - csdev->refcnt--; > - if (csdev->refcnt) { > - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); > + if (csdev->refcnt || drvdata->reading) > return -EBUSY; > - } > > - /* Complain if we (somehow) got out of sync */ > - WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED); > + if (drvdata->pid == -1) > + return 0; > + > tmc_etr_disable_hw(drvdata); > - /* Dissociate from monitored process. */ > - drvdata->pid = -1; > - coresight_set_mode(csdev, CS_MODE_DISABLED); > /* Reset perf specific data */ > drvdata->perf_buf = NULL; > > - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); > - > dev_dbg(&csdev->dev, "TMC-ETR disabled\n"); > return 0; > } > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue 2025-10-21 1:56 ` Jie Gan @ 2025-10-21 7:08 ` Leo Yan 0 siblings, 0 replies; 8+ messages in thread From: Leo Yan @ 2025-10-21 7:08 UTC (permalink / raw) To: Jie Gan Cc: Xiaoqi Zhuang, Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin, coresight, linux-arm-kernel, linux-kernel, linux-arm-msm On Tue, Oct 21, 2025 at 09:56:43AM +0800, Jie Gan wrote: [...] > > diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c > > index b07fcdb3fe1a..d0fac958c614 100644 > > --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c > > +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c > > @@ -1241,6 +1241,8 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) > > struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > > struct etr_buf *sysfs_buf = NULL, *new_buf = NULL, *free_buf = NULL; > > + WARN_ON(coresight_get_mode(csdev) != CS_MODE_SYSFS); > > I think we should check the WARN_ON result and exit if there is an error? When run at here, it should be in Sysfs mode. Here the check is for debugging purpose in case any mismatch. [...] > > +static void tmc_release_mode(struct coresight_device *csdev, enum cs_mode mode) > > +{ > > + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > > + > > + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock); > > + > > + if (WARN_ON(coresight_get_mode(csdev) != mode)) > > + return; > > the mode here could be set to any CS_MODE, so I think it's possible to > encounter the secenario below: > > coresight_get_mode(csdev) == CS_MODE_DISABLED, mode == CS_MODE_DISABLED, > > With the condition, the csdev->refcnt will go to negative number? The parameter "mode" might cause complexity, will drop it. The correctness will be ensured by the callers. Thanks for review! Leo ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue 2025-10-20 14:37 ` Leo Yan 2025-10-21 1:56 ` Jie Gan @ 2025-10-21 10:07 ` hejunhao 1 sibling, 0 replies; 8+ messages in thread From: hejunhao @ 2025-10-21 10:07 UTC (permalink / raw) To: Leo Yan Cc: Xiaoqi Zhuang, Mike Leach, James Clark, Alexander Shishkin, coresight, linux-arm-kernel, linux-kernel, linux-arm-msm, Linuxarm On 2025/10/20 22:37, Leo Yan via CoreSight wrote: > On Mon, Oct 20, 2025 at 05:06:46PM +0800, Xiaoqi Zhuang wrote: >> When ETR is enabled as CS_MODE_SYSFS, if the buffer size is changed >> and enabled again, currently sysfs_buf will point to the newly >> allocated memory(buf_new) and free the old memory(buf_old). But the >> etr_buf that is being used by the ETR remains pointed to buf_old, not >> updated to buf_new. In this case, it will result in a memory >> use-after-free issue. > I struggled to understand how to reproduce the issue under the condition > "if the buffer size is changed and enabled again." > > I don't think the flow below where the trace is re-enabled would cause > an issue: > > - Step 1: Enable trace path between ETM0 -> ETR0; > - Step 2: Change the buffer size for ETR0; > - Step 3: Disable trace path between ETM0 -> ETR0; > - Step 4: Enable again trace path between ETM0 -> ETR0. > > In this case, step3 releases the buffer and update "drvdata->etr_buf" to > NULL, and step 4 allocates a new buffer and assign it to > "drvdata->etr_buf". > > The problem should occur when operating on two trace paths, E.g., > > - Step 1: Enable trace path between ETM0 -> ETR0; > - Step 2: Change the buffer size for ETR0; > - Step 3: Enable trace path between ETM1 -> ETR0; > > In step3, the driver releases the existed buffer and allocate a new one. > At the meantime, "drvdata->etr_buf" still holds the buffer allocated in > step 1. > >> Fix this by checking ETR's mode before updating and releasing buf_old, >> if the mode is CS_MODE_SYSFS, then skip updating and releasing it. > Given that we now have a couple of reported issues related to ETR mode, > I'd like to refactor the ETR mode handling and its reference counting > thoroughly. I've drafted a large change (it's quite big, but we can > split it into small patches if we agree to proceed). > > Thanks for reporting the issue! > > Leo > > ---8<--- > > diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c > index b07fcdb3fe1a..d0fac958c614 100644 > --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c > +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c > @@ -1241,6 +1241,8 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) > struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > struct etr_buf *sysfs_buf = NULL, *new_buf = NULL, *free_buf = NULL; > > + WARN_ON(coresight_get_mode(csdev) != CS_MODE_SYSFS); > + > /* > * If we are enabling the ETR from disabled state, we need to make > * sure we have a buffer with the right size. The etr_buf is not reset > @@ -1263,7 +1265,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) > raw_spin_lock_irqsave(&drvdata->spinlock, flags); > } > > - if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) { > + if (drvdata->reading) { > ret = -EBUSY; > goto out; > } > @@ -1292,30 +1294,14 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) > int ret = 0; > unsigned long flags; > struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > - struct etr_buf *sysfs_buf = tmc_etr_get_sysfs_buffer(csdev); > + struct etr_buf *sysfs_buf; > > + sysfs_buf = tmc_etr_get_sysfs_buffer(csdev); > if (IS_ERR(sysfs_buf)) > return PTR_ERR(sysfs_buf); > > raw_spin_lock_irqsave(&drvdata->spinlock, flags); > - > - /* > - * In sysFS mode we can have multiple writers per sink. Since this > - * sink is already enabled no memory is needed and the HW need not be > - * touched, even if the buffer size has changed. > - */ > - if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { > - csdev->refcnt++; > - goto out; > - } > - > ret = tmc_etr_enable_hw(drvdata, sysfs_buf); > - if (!ret) { > - coresight_set_mode(csdev, CS_MODE_SYSFS); > - csdev->refcnt++; > - } > - > -out: > raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); > > if (!ret) > @@ -1735,11 +1721,6 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) > struct etr_perf_buffer *etr_perf = etm_perf_sink_config(handle); > > raw_spin_lock_irqsave(&drvdata->spinlock, flags); > - /* Don't use this sink if it is already claimed by sysFS */ > - if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { > - rc = -EBUSY; > - goto unlock_out; > - } > > if (WARN_ON(!etr_perf || !etr_perf->etr_buf)) { > rc = -EINVAL; > @@ -1759,18 +1740,14 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) > * No HW configuration is needed if the sink is already in > * use for this session. > */ > - if (drvdata->pid == pid) { > - csdev->refcnt++; > + if (drvdata->pid == pid) > goto unlock_out; > - } > > rc = tmc_etr_enable_hw(drvdata, etr_perf->etr_buf); > if (!rc) { > /* Associate with monitored process. */ > drvdata->pid = pid; > - coresight_set_mode(csdev, CS_MODE_PERF); > drvdata->perf_buf = etr_perf->etr_buf; > - csdev->refcnt++; > } > > unlock_out: > @@ -1778,17 +1755,76 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) > return rc; > } > > +static int tmc_acquire_mode(struct coresight_device *csdev, enum cs_mode mode) > +{ > + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > + > + if (mode != CS_MODE_SYSFS && mode != CS_MODE_PERF) > + return -EINVAL; > + > + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock); > + > + if (coresight_get_mode(csdev) == CS_MODE_DISABLED) { > + if (!csdev->refcnt) > + coresight_set_mode(csdev, mode); > + csdev->refcnt++; Hi,Leo Reference counting only increases above, and after ETR has been enabled, no other place was found where the reference count is incremented. According to the previous review comment [1], the reference count should only be increased after ETR is enabled. I've send the v3 [2] of the ETR mode refactoring, maybe could also take a look at this, and provide some review~~ [1]: https://lore.kernel.org/linux-arm-kernel/20250702164729.GA1039028@e132581.arm.com/ [2]: https://lore.kernel.org/linux-arm-kernel/20250818080600.418425-3-hejunhao3@huawei.com/ Best regards, Junhao. > + } else if (coresight_get_mode(csdev) != mode) { > + ret = -EBUSY; > + } > + > + return csdev->refcnt; > +} > + > +static void tmc_release_mode(struct coresight_device *csdev, enum cs_mode mode) > +{ > + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > + > + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock); > + > + if (WARN_ON(coresight_get_mode(csdev) != mode)) > + return; > + > + csdev->refcnt--; > + if (!csdev->refcnt) > + coresight_set_mode(csdev, CS_MODE_DISABLED); > +} > + > static int tmc_enable_etr_sink(struct coresight_device *csdev, > enum cs_mode mode, void *data) > { > + unsigned long flags; > + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > + int ret; > + > + ret = tmc_acquire_mode(csdev, mode); > + if (ret < 0) > + return ret; > + > + /* > + * For sysfs mode, the higher level mutex ensures exclusively > + * enabling sink, it is safe to bail out if this is not the > + * first time to enable sink. > + * > + * A perf session can enable the same sink simultaneously, fall > + * through to call tmc_enable_etr_sink_perf() to ensure the sink > + * has been enabled. > + */ > + if (mode == CS_MODE_SYSFS && ret > 1) > + return 0; > + > switch (mode) { > case CS_MODE_SYSFS: > - return tmc_enable_etr_sink_sysfs(csdev); > + ret = tmc_enable_etr_sink_sysfs(csdev); > case CS_MODE_PERF: > - return tmc_enable_etr_sink_perf(csdev, data); > + ret = tmc_enable_etr_sink_perf(csdev, data); > default: > - return -EINVAL; > + ret = -EINVAL; > } > + > + if (ret) > + tmc_release_mode(csdev, mode); > + > + return ret; > } > > static int tmc_disable_etr_sink(struct coresight_device *csdev) > @@ -1796,30 +1832,20 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev) > unsigned long flags; > struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > > - raw_spin_lock_irqsave(&drvdata->spinlock, flags); > + tmc_release_mode(csdev, mode); > > - if (drvdata->reading) { > - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); > - return -EBUSY; > - } > + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock); > > - csdev->refcnt--; > - if (csdev->refcnt) { > - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); > + if (csdev->refcnt || drvdata->reading) > return -EBUSY; > - } > > - /* Complain if we (somehow) got out of sync */ > - WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED); > + if (drvdata->pid == -1) > + return 0; > + > tmc_etr_disable_hw(drvdata); > - /* Dissociate from monitored process. */ > - drvdata->pid = -1; > - coresight_set_mode(csdev, CS_MODE_DISABLED); > /* Reset perf specific data */ > drvdata->perf_buf = NULL; > > - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); > - > dev_dbg(&csdev->dev, "TMC-ETR disabled\n"); > return 0; > } > _______________________________________________ > CoreSight mailing list -- coresight@lists.linaro.org > To unsubscribe send an email to coresight-leave@lists.linaro.org > . > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue 2025-10-20 9:06 [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue Xiaoqi Zhuang 2025-10-20 10:18 ` Jie Gan 2025-10-20 14:37 ` Leo Yan @ 2025-10-21 7:43 ` Jie Gan 2 siblings, 0 replies; 8+ messages in thread From: Jie Gan @ 2025-10-21 7:43 UTC (permalink / raw) To: Xiaoqi Zhuang, Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm On 10/20/2025 5:06 PM, Xiaoqi Zhuang wrote: > When ETR is enabled as CS_MODE_SYSFS, if the buffer size is changed > and enabled again, currently sysfs_buf will point to the newly > allocated memory(buf_new) and free the old memory(buf_old). But the > etr_buf that is being used by the ETR remains pointed to buf_old, not > updated to buf_new. In this case, it will result in a memory > use-after-free issue. > > Fix this by checking ETR's mode before updating and releasing buf_old, > if the mode is CS_MODE_SYSFS, then skip updating and releasing it. > missed fix tag: Fixes: de5461970b3e ("coresight: tmc: allocating memory when needed") Thanks, Jie > Signed-off-by: Xiaoqi Zhuang <xiaoqi.zhuang@oss.qualcomm.com> > --- > drivers/hwtracing/coresight/coresight-tmc-etr.c | 7 +++++++ > 1 file changed, 7 insertions(+) > > diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c > index b07fcdb3fe1a..3e73cf2c38a3 100644 > --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c > +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c > @@ -1268,6 +1268,13 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) > goto out; > } > > + /* > + * Since this sink is already enabled, the existing buffer should not > + * be released even if the buffer size has changed. > + */ > + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) > + goto out; > + > /* > * If we don't have a buffer or it doesn't match the requested size, > * use the buffer allocated above. Otherwise reuse the existing buffer. > > --- > base-commit: 98ac9cc4b4452ed7e714eddc8c90ac4ae5da1a09 > change-id: 20251020-fix_etr_issue-02c706dbc899 > > Best regards, ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-10-21 10:07 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-10-20 9:06 [PATCH] coresight: ETR: Fix ETR buffer use-after-free issue Xiaoqi Zhuang 2025-10-20 10:18 ` Jie Gan 2025-10-20 10:57 ` Suzuki K Poulose 2025-10-20 14:37 ` Leo Yan 2025-10-21 1:56 ` Jie Gan 2025-10-21 7:08 ` Leo Yan 2025-10-21 10:07 ` hejunhao 2025-10-21 7:43 ` Jie Gan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox