Skip to content

Commit e928d5c

Browse files
ujfalusibardliao
authored andcommitted
soundwire: bus: serialize BPT/BRA transfers per bus
On Intel ACE2+ platforms, each SoundWire link uses a single pair of PDIs (PDI0 for TX, PDI1 for RX) and associated DMA resources for Bulk Port Transfers. When two codecs on the same link initiate BRA transfers concurrently (e.g. during firmware download), the second sdw_bpt_send_async() call races with the first: - intel_ace2x_bpt_open_stream() has a TOCTOU on the bpt_stream pointer: both callers see it as NULL and proceed - The second open overwrites the first's stream allocation, leaking the original sdw_stream_runtime - PCMSyCM mappings for the first transfer get overwritten, causing chain DMA to operate with wrong PDI assignments - IOC timeouts, use-after-free, and double-free follow on close Add a per-bus bpt_lock mutex held across the send_async/wait span to serialize BPT transfers. The lock is acquired in sdw_bpt_send_async() before calling the master ops and released in sdw_bpt_wait() after the transfer completes. On send_async failure, the lock is released immediately. Cross-link transfers (different sdw_bus instances) remain concurrent since each bus has its own lock. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
1 parent f6602b8 commit e928d5c

2 files changed

Lines changed: 19 additions & 2 deletions

File tree

  • drivers/soundwire
  • include/linux/soundwire

drivers/soundwire/bus.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
8787
lockdep_register_key(&bus->bus_lock_key);
8888
__mutex_init(&bus->bus_lock, "bus_lock", &bus->bus_lock_key);
8989

90+
mutex_init(&bus->bpt_lock);
91+
9092
INIT_LIST_HEAD(&bus->slaves);
9193
INIT_LIST_HEAD(&bus->m_rt_list);
9294

@@ -2094,6 +2096,7 @@ EXPORT_SYMBOL(sdw_clear_slave_status);
20942096
int sdw_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg)
20952097
{
20962098
int len = 0;
2099+
int ret;
20972100
int i;
20982101

20992102
for (i = 0; i < msg->sections; i++)
@@ -2118,13 +2121,26 @@ int sdw_bpt_send_async(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_
21182121
return -EOPNOTSUPP;
21192122
}
21202123

2121-
return bus->ops->bpt_send_async(bus, slave, msg);
2124+
/* Serialize BPT/BRA transfers per bus: PDIs and DMA resources are shared */
2125+
mutex_lock(&bus->bpt_lock);
2126+
2127+
ret = bus->ops->bpt_send_async(bus, slave, msg);
2128+
if (ret < 0)
2129+
mutex_unlock(&bus->bpt_lock);
2130+
2131+
/* on success the lock is held until sdw_bpt_wait() */
2132+
return ret;
21222133
}
21232134
EXPORT_SYMBOL(sdw_bpt_send_async);
21242135

21252136
int sdw_bpt_wait(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_bpt_msg *msg)
21262137
{
2127-
return bus->ops->bpt_wait(bus, slave, msg);
2138+
int ret;
2139+
2140+
ret = bus->ops->bpt_wait(bus, slave, msg);
2141+
mutex_unlock(&bus->bpt_lock);
2142+
2143+
return ret;
21282144
}
21292145
EXPORT_SYMBOL(sdw_bpt_wait);
21302146

include/linux/soundwire/sdw.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,7 @@ struct sdw_bus {
10441044
int stream_refcount;
10451045
int bpt_stream_refcount;
10461046
struct sdw_stream_runtime *bpt_stream;
1047+
struct mutex bpt_lock; /* serialize BPT/BRA transfers per bus */
10471048
const struct sdw_master_ops *ops;
10481049
const struct sdw_master_port_ops *port_ops;
10491050
struct sdw_master_prop prop;

0 commit comments

Comments
 (0)