Skip to content

Commit 034dc9b

Browse files
committed
can: mcp25xxfd: fix concerns about spi data on stack
reimplement mcp25xxfd_spi_write_then_read and mcp25xxfd_spi_write_then_write Signed-off-by: Martin Sperl <[email protected]>
1 parent 3713fca commit 034dc9b

File tree

2 files changed

+58
-35
lines changed

2 files changed

+58
-35
lines changed

drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
#include <linux/debugfs.h>
1414
#include <linux/device.h>
1515
#include <linux/jiffies.h>
16+
#include <linux/mutex.h>
1617
#include <linux/regulator/consumer.h>
1718
#include <linux/spi/spi.h>
19+
#include <linux/spinlock.h>
1820

1921
/* some constants derived from the datasheets */
2022
#define MCP25XXFD_OST_DELAY_MS 3
@@ -143,7 +145,7 @@ struct mcp25xxfd_priv {
143145
/* spi-tx/rx buffers for efficient transfers
144146
* used during setup and irq
145147
*/
146-
struct mutex spi_rxtx_lock;
148+
spinlock_t spi_rxtx_lock; /* protects use of spi_tx/rx */
147149
u8 spi_tx[MCP25XXFD_BUFFER_TXRX_SIZE];
148150
u8 spi_rx[MCP25XXFD_BUFFER_TXRX_SIZE];
149151

drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -145,45 +145,60 @@ static int mcp25xxfd_write_then_read(struct spi_device *spi,
145145
{
146146
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
147147
struct spi_transfer xfer[2];
148-
u8 single_reg_data_tx[6];
149-
u8 single_reg_data_rx[6];
148+
u8 *spi_tx, *spi_rx;
149+
int xfers;
150150
int ret;
151151

152+
/* use allocated buffer when too big or already in use */
153+
if ((tx_len + rx_len > MCP25XXFD_BUFFER_TXRX_SIZE) ||
154+
!spin_trylock(&priv->spi_rxtx_lock)) {
155+
spi_tx = kzalloc(2 * (tx_len + rx_len), GFP_KERNEL);
156+
spi_rx = spi_tx + tx_len + rx_len;
157+
} else {
158+
spi_tx = priv->spi_tx;
159+
spi_rx = priv->spi_rx;
160+
}
161+
162+
/* clear the xfers */
152163
memset(xfer, 0, sizeof(xfer));
153164

154-
/* when using a halfduplex controller or to big for buffer */
165+
/* when using a halfduplex controller still copy the buffers to
166+
* avoid stack objects
167+
*/
155168
if ((spi->master->flags & SPI_MASTER_HALF_DUPLEX) ||
156169
(tx_len + rx_len > sizeof(priv->spi_tx))) {
157-
xfer[0].tx_buf = tx_buf;
170+
xfers = 2;
171+
xfer[0].tx_buf = priv->spi_tx;
158172
xfer[0].len = tx_len;
159173

160-
xfer[1].rx_buf = rx_buf;
174+
xfer[1].rx_buf = priv->spi_rx + tx_len;
161175
xfer[1].len = rx_len;
162-
163-
return mcp25xxfd_sync_transfer(spi, xfer, 2);
164-
}
165-
166-
/* full duplex optimization */
167-
xfer[0].len = tx_len + rx_len;
168-
if (xfer[0].len > sizeof(single_reg_data_tx)) {
169-
mutex_lock(&priv->spi_rxtx_lock);
176+
} else {
177+
xfers = 1;
178+
xfer[0].len = tx_len + rx_len;
170179
xfer[0].tx_buf = priv->spi_tx;
171180
xfer[0].rx_buf = priv->spi_rx;
172-
} else {
173-
xfer[0].tx_buf = single_reg_data_tx;
174-
xfer[0].rx_buf = single_reg_data_rx;
181+
182+
/* clean the data after the write block */
183+
memset((u8 *)xfer[0].tx_buf + tx_len, 0, rx_len);
175184
}
176185

177-
/* copy and clean */
186+
/* copy especially to avoid buffers from stack */
178187
memcpy((u8 *)xfer[0].tx_buf, tx_buf, tx_len);
179-
memset((u8 *)xfer[0].tx_buf + tx_len, 0, rx_len);
180188

181-
ret = mcp25xxfd_sync_transfer(spi, xfer, 1);
182-
if (!ret)
183-
memcpy(rx_buf, xfer[0].rx_buf + tx_len, rx_len);
189+
/* do the transfer */
190+
ret = mcp25xxfd_sync_transfer(spi, xfer, xfers);
191+
if (ret)
192+
goto out;
193+
194+
/* copy result back */
195+
memcpy(rx_buf, xfer[0].rx_buf + tx_len, rx_len);
184196

185-
if (xfer[0].len > sizeof(single_reg_data_tx))
186-
mutex_unlock(&priv->spi_rxtx_lock);
197+
out:
198+
if (spi_tx == priv->spi_tx)
199+
spin_unlock(&priv->spi_rxtx_lock);
200+
else
201+
kfree(spi_tx);
187202

188203
return ret;
189204
}
@@ -196,29 +211,33 @@ static int mcp25xxfd_write_then_write(struct spi_device *spi,
196211
{
197212
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
198213
struct spi_transfer xfer;
199-
u8 single_reg_data[6];
200214
int ret;
201215

202-
if (tx_len + tx2_len > MCP25XXFD_BUFFER_TXRX_SIZE)
203-
return -EINVAL;
204-
216+
/* clear the xfers */
205217
memset(&xfer, 0, sizeof(xfer));
206-
207218
xfer.len = tx_len + tx2_len;
208-
if (xfer.len > sizeof(single_reg_data)) {
209-
mutex_lock(&priv->spi_rxtx_lock);
210-
xfer.tx_buf = priv->spi_tx;
219+
xfer.tx_buf = priv->spi_tx;
220+
221+
/* use allocated buffer when too big or already in use */
222+
if ((tx_len + tx2_len > MCP25XXFD_BUFFER_TXRX_SIZE) ||
223+
!spin_trylock(&priv->spi_rxtx_lock)) {
224+
xfer.tx_buf = kzalloc(tx_len + tx2_len, GFP_KERNEL);
211225
} else {
212-
xfer.tx_buf = single_reg_data;
226+
xfer.tx_buf = priv->spi_tx;
213227
}
214228

229+
/* copy data to location */
215230
memcpy((u8 *)xfer.tx_buf, tx_buf, tx_len);
216231
memcpy((u8 *)xfer.tx_buf + tx_len, tx2_buf, tx2_len);
217232

233+
/* run the transfer */
218234
ret = mcp25xxfd_sync_transfer(spi, &xfer, 1);
219235

220-
if (xfer.len > sizeof(single_reg_data))
221-
mutex_unlock(&priv->spi_rxtx_lock);
236+
/* release lock */
237+
if (xfer.tx_buf == priv->spi_tx)
238+
spin_unlock(&priv->spi_rxtx_lock);
239+
else
240+
kfree(xfer.tx_buf);
222241

223242
return ret;
224243
}
@@ -978,8 +997,10 @@ static int mcp25xxfd_probe(struct spi_device *spi)
978997

979998
spi_set_drvdata(spi, priv);
980999
priv->spi = spi;
1000+
priv->clk = clk;
9811001

9821002
mutex_init(&priv->clk_user_lock);
1003+
spin_lock_init(&priv->spi_rxtx_lock);
9831004

9841005
/* enable the clock and mark as enabled */
9851006
ret = clk_prepare_enable(clk);

0 commit comments

Comments
 (0)