[PATCH v2 05/10] soundwire: intel_ace2x: Protect bus.bpt_stream with bus_lock
From: Lee Jones
Date: Tue Nov 03 2026 - 05:18:18 EST
intel_ace2x_bpt_open_stream() checks and assigns cdns->bus.bpt_stream
without holding cdns->bus.bus_lock. Concurrent BPT open requests can
both observe a NULL cdns->bus.bpt_stream pointer, allocate separate
streams, and overwrite cdns->bus.bpt_stream, leaking the first stream.
In addition, failures after sdw_slave_bpt_stream_add() jump past
sdw_stream_remove_master(), leaving the master runtime allocated by
sdw_stream_add_slave() on the bus list.
Hold cdns->bus.bus_lock when checking, assigning, and clearing
cdns->bus.bpt_stream, and remove both slave and master stream runtimes
on open failure.
Closes: https://sashiko.dev/#/bug/linux-88535ed9-b7e9-4ede-93c7-59950c524c0b
Signed-off-by: Lee Jones <lee@xxxxxxxxxx>
---
v1 => v2:
- Replace cleanup.h guards with explicit mutex_lock() and mutex_unlock() in intel_ace2x_bpt_{open,close}_stream()
- Drop Fixes and Stable tags
drivers/soundwire/intel_ace2x.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c
index 642b33ab5526..c964dd4a7c22 100644
--- a/drivers/soundwire/intel_ace2x.c
+++ b/drivers/soundwire/intel_ace2x.c
@@ -83,16 +83,21 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *
int len;
int i;
+ mutex_lock(&cdns->bus.bus_lock);
if (cdns->bus.bpt_stream) {
+ mutex_unlock(&cdns->bus.bus_lock);
dev_err(cdns->dev, "%s: BPT stream already exists\n", __func__);
return -EAGAIN;
}
stream = sdw_alloc_stream("BPT", SDW_STREAM_BPT);
- if (!stream)
+ if (!stream) {
+ mutex_unlock(&cdns->bus.bus_lock);
return -ENOMEM;
+ }
cdns->bus.bpt_stream = stream;
+ mutex_unlock(&cdns->bus.bus_lock);
ret = sdw_slave_bpt_stream_add(slave, stream);
if (ret < 0)
@@ -105,7 +110,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *
if (!pdi0) {
dev_err(cdns->dev, "%s: sdw_cdns_alloc_pdi0 failed\n", __func__);
ret = -EINVAL;
- goto remove_slave;
+ goto remove_master;
}
sdw_cdns_config_stream(cdns, 1, dir, pdi0);
@@ -117,7 +122,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *
if (!pdi1) {
dev_err(cdns->dev, "%s: sdw_cdns_alloc_pdi1 failed\n", __func__);
ret = -EINVAL;
- goto remove_slave;
+ goto remove_master;
}
sdw_cdns_config_stream(cdns, 1, dir, pdi1);
@@ -136,7 +141,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *
pconfig = kzalloc_objs(*pconfig, 2);
if (!pconfig) {
ret = -ENOMEM;
- goto remove_slave;
+ goto remove_master;
}
for (i = 0; i < 2 /* num_pdi */; i++) {
@@ -149,7 +154,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *
if (ret < 0) {
dev_err(cdns->dev, "add master to stream failed:%d\n", ret);
- goto remove_slave;
+ goto remove_master;
}
ret = sdw_prepare_stream(cdns->bus.bpt_stream);
@@ -293,15 +298,16 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *
dev_err(cdns->dev, "%s: remove master failed: %d\n",
__func__, ret1);
-remove_slave:
ret1 = sdw_stream_remove_slave(slave, cdns->bus.bpt_stream);
if (ret1 < 0)
dev_err(cdns->dev, "%s: remove slave failed: %d\n",
__func__, ret1);
release_stream:
+ mutex_lock(&cdns->bus.bus_lock);
sdw_release_stream(cdns->bus.bpt_stream);
cdns->bus.bpt_stream = NULL;
+ mutex_unlock(&cdns->bus.bus_lock);
return ret;
}
@@ -335,8 +341,10 @@ static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave
dev_err(cdns->dev, "%s: remove slave failed: %d\n",
__func__, ret);
+ mutex_lock(&cdns->bus.bus_lock);
sdw_release_stream(cdns->bus.bpt_stream);
cdns->bus.bpt_stream = NULL;
+ mutex_unlock(&cdns->bus.bus_lock);
}
#define INTEL_BPT_MSG_BYTE_MIN 16
--
2.56.0.385.gac671c819a-goog