Skip to content

Commit 7f970ec

Browse files
dlechbroonie
authored andcommitted
spi: axi-spi-engine: move msg state to new struct
This moves the message state in the AXI SPI Engine driver to a new struct spi_engine_msg_state. Previously, the driver state contained various pointers that pointed to memory owned by a struct spi_message. However, it did not set any of these pointers to NULL when a message was completed. This could lead to use after free bugs. Example of how this could happen: 1. SPI core calls into spi_engine_transfer_one_message() with msg1. 2. Assume something was misconfigured and spi_engine_tx_next() is not called enough times in interrupt callbacks for msg1 such that spi_engine->tx_xfer is never set to NULL before the msg1 completes. 3. SYNC interrupt is received and spi_finalize_current_message() is called for msg1. spi_engine->msg is set to NULL but no other message-specific state is reset. 4. Caller that sent msg1 is notified of the completion and frees msg1 and the associated xfers and tx/rx buffers. 4. SPI core calls into spi_engine_transfer_one_message() with msg2. 5. When spi_engine_tx_next() is called for msg2, spi_engine->tx_xfer is still be pointing to an xfer from msg1, which was already freed. spi_engine_xfer_next() tries to access xfer->transfer_list of one of the freed xfers and we get a segfault or undefined behavior. To avoid issues like this, instead of putting per-message state in the driver state struct, we can make use of the struct spi_message::state field to store a pointer to a new struct spi_engine_msg_state. This way, all of the state that belongs to specific message stays with that message and we don't have to remember to manually reset all aspects of the message state when a message is completed. Rather, a new state is allocated for each message. Most of the changes are just renames where the state is accessed. One place where this wasn't straightforward was the sync_id member. This has been changed to use ida_alloc_range() since we needed to separate the per-message sync_id from the per-controller next available sync_id. Signed-off-by: David Lechner <dlechner@baylibre.com> Link: https://lore.kernel.org/r/20231117-axi-spi-engine-series-1-v1-9-cc59db999b87@baylibre.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent e6d5eb8 commit 7f970ec

1 file changed

Lines changed: 96 additions & 54 deletions

File tree

drivers/spi/spi-axi-spi-engine.c

Lines changed: 96 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
#include <linux/clk.h>
9+
#include <linux/idr.h>
910
#include <linux/interrupt.h>
1011
#include <linux/io.h>
1112
#include <linux/of.h>
@@ -78,28 +79,42 @@ struct spi_engine_program {
7879
uint16_t instructions[];
7980
};
8081

81-
struct spi_engine {
82-
struct clk *clk;
83-
struct clk *ref_clk;
84-
85-
spinlock_t lock;
86-
87-
void __iomem *base;
88-
89-
struct spi_message *msg;
82+
/**
83+
* struct spi_engine_message_state - SPI engine per-message state
84+
*/
85+
struct spi_engine_message_state {
86+
/** Instructions for executing this message. */
9087
struct spi_engine_program *p;
88+
/** Number of elements in cmd_buf array. */
9189
unsigned cmd_length;
90+
/** Array of commands not yet written to CMD FIFO. */
9291
const uint16_t *cmd_buf;
93-
92+
/** Next xfer with tx_buf not yet fully written to TX FIFO. */
9493
struct spi_transfer *tx_xfer;
94+
/** Size of tx_buf in bytes. */
9595
unsigned int tx_length;
96+
/** Bytes not yet written to TX FIFO. */
9697
const uint8_t *tx_buf;
97-
98+
/** Next xfer with rx_buf not yet fully written to RX FIFO. */
9899
struct spi_transfer *rx_xfer;
100+
/** Size of tx_buf in bytes. */
99101
unsigned int rx_length;
102+
/** Bytes not yet written to the RX FIFO. */
100103
uint8_t *rx_buf;
104+
/** ID to correlate SYNC interrupts with this message. */
105+
u8 sync_id;
106+
};
107+
108+
struct spi_engine {
109+
struct clk *clk;
110+
struct clk *ref_clk;
101111

102-
unsigned int sync_id;
112+
spinlock_t lock;
113+
114+
void __iomem *base;
115+
116+
struct spi_message *msg;
117+
struct ida sync_ida;
103118
unsigned int completed_id;
104119

105120
unsigned int int_enable;
@@ -258,100 +273,105 @@ static void spi_engine_xfer_next(struct spi_engine *spi_engine,
258273

259274
static void spi_engine_tx_next(struct spi_engine *spi_engine)
260275
{
261-
struct spi_transfer *xfer = spi_engine->tx_xfer;
276+
struct spi_engine_message_state *st = spi_engine->msg->state;
277+
struct spi_transfer *xfer = st->tx_xfer;
262278

263279
do {
264280
spi_engine_xfer_next(spi_engine, &xfer);
265281
} while (xfer && !xfer->tx_buf);
266282

267-
spi_engine->tx_xfer = xfer;
283+
st->tx_xfer = xfer;
268284
if (xfer) {
269-
spi_engine->tx_length = xfer->len;
270-
spi_engine->tx_buf = xfer->tx_buf;
285+
st->tx_length = xfer->len;
286+
st->tx_buf = xfer->tx_buf;
271287
} else {
272-
spi_engine->tx_buf = NULL;
288+
st->tx_buf = NULL;
273289
}
274290
}
275291

276292
static void spi_engine_rx_next(struct spi_engine *spi_engine)
277293
{
278-
struct spi_transfer *xfer = spi_engine->rx_xfer;
294+
struct spi_engine_message_state *st = spi_engine->msg->state;
295+
struct spi_transfer *xfer = st->rx_xfer;
279296

280297
do {
281298
spi_engine_xfer_next(spi_engine, &xfer);
282299
} while (xfer && !xfer->rx_buf);
283300

284-
spi_engine->rx_xfer = xfer;
301+
st->rx_xfer = xfer;
285302
if (xfer) {
286-
spi_engine->rx_length = xfer->len;
287-
spi_engine->rx_buf = xfer->rx_buf;
303+
st->rx_length = xfer->len;
304+
st->rx_buf = xfer->rx_buf;
288305
} else {
289-
spi_engine->rx_buf = NULL;
306+
st->rx_buf = NULL;
290307
}
291308
}
292309

293310
static bool spi_engine_write_cmd_fifo(struct spi_engine *spi_engine)
294311
{
295312
void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_CMD_FIFO;
313+
struct spi_engine_message_state *st = spi_engine->msg->state;
296314
unsigned int n, m, i;
297315
const uint16_t *buf;
298316

299317
n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_CMD_FIFO_ROOM);
300-
while (n && spi_engine->cmd_length) {
301-
m = min(n, spi_engine->cmd_length);
302-
buf = spi_engine->cmd_buf;
318+
while (n && st->cmd_length) {
319+
m = min(n, st->cmd_length);
320+
buf = st->cmd_buf;
303321
for (i = 0; i < m; i++)
304322
writel_relaxed(buf[i], addr);
305-
spi_engine->cmd_buf += m;
306-
spi_engine->cmd_length -= m;
323+
st->cmd_buf += m;
324+
st->cmd_length -= m;
307325
n -= m;
308326
}
309327

310-
return spi_engine->cmd_length != 0;
328+
return st->cmd_length != 0;
311329
}
312330

313331
static bool spi_engine_write_tx_fifo(struct spi_engine *spi_engine)
314332
{
315333
void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDO_DATA_FIFO;
334+
struct spi_engine_message_state *st = spi_engine->msg->state;
316335
unsigned int n, m, i;
317336
const uint8_t *buf;
318337

319338
n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDO_FIFO_ROOM);
320-
while (n && spi_engine->tx_length) {
321-
m = min(n, spi_engine->tx_length);
322-
buf = spi_engine->tx_buf;
339+
while (n && st->tx_length) {
340+
m = min(n, st->tx_length);
341+
buf = st->tx_buf;
323342
for (i = 0; i < m; i++)
324343
writel_relaxed(buf[i], addr);
325-
spi_engine->tx_buf += m;
326-
spi_engine->tx_length -= m;
344+
st->tx_buf += m;
345+
st->tx_length -= m;
327346
n -= m;
328-
if (spi_engine->tx_length == 0)
347+
if (st->tx_length == 0)
329348
spi_engine_tx_next(spi_engine);
330349
}
331350

332-
return spi_engine->tx_length != 0;
351+
return st->tx_length != 0;
333352
}
334353

335354
static bool spi_engine_read_rx_fifo(struct spi_engine *spi_engine)
336355
{
337356
void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDI_DATA_FIFO;
357+
struct spi_engine_message_state *st = spi_engine->msg->state;
338358
unsigned int n, m, i;
339359
uint8_t *buf;
340360

341361
n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDI_FIFO_LEVEL);
342-
while (n && spi_engine->rx_length) {
343-
m = min(n, spi_engine->rx_length);
344-
buf = spi_engine->rx_buf;
362+
while (n && st->rx_length) {
363+
m = min(n, st->rx_length);
364+
buf = st->rx_buf;
345365
for (i = 0; i < m; i++)
346366
buf[i] = readl_relaxed(addr);
347-
spi_engine->rx_buf += m;
348-
spi_engine->rx_length -= m;
367+
st->rx_buf += m;
368+
st->rx_length -= m;
349369
n -= m;
350-
if (spi_engine->rx_length == 0)
370+
if (st->rx_length == 0)
351371
spi_engine_rx_next(spi_engine);
352372
}
353373

354-
return spi_engine->rx_length != 0;
374+
return st->rx_length != 0;
355375
}
356376

357377
static irqreturn_t spi_engine_irq(int irq, void *devid)
@@ -387,12 +407,16 @@ static irqreturn_t spi_engine_irq(int irq, void *devid)
387407
disable_int |= SPI_ENGINE_INT_SDI_ALMOST_FULL;
388408
}
389409

390-
if (pending & SPI_ENGINE_INT_SYNC) {
391-
if (spi_engine->msg &&
392-
spi_engine->completed_id == spi_engine->sync_id) {
410+
if (pending & SPI_ENGINE_INT_SYNC && spi_engine->msg) {
411+
struct spi_engine_message_state *st = spi_engine->msg->state;
412+
413+
if (spi_engine->completed_id == st->sync_id) {
393414
struct spi_message *msg = spi_engine->msg;
415+
struct spi_engine_message_state *st = msg->state;
394416

395-
kfree(spi_engine->p);
417+
ida_free(&spi_engine->sync_ida, st->sync_id);
418+
kfree(st->p);
419+
kfree(st);
396420
msg->status = 0;
397421
msg->actual_length = msg->frame_length;
398422
spi_engine->msg = NULL;
@@ -417,29 +441,46 @@ static int spi_engine_transfer_one_message(struct spi_controller *host,
417441
{
418442
struct spi_engine_program p_dry, *p;
419443
struct spi_engine *spi_engine = spi_controller_get_devdata(host);
444+
struct spi_engine_message_state *st;
420445
unsigned int int_enable = 0;
421446
unsigned long flags;
422447
size_t size;
448+
int ret;
449+
450+
st = kzalloc(sizeof(*st), GFP_KERNEL);
451+
if (!st)
452+
return -ENOMEM;
423453

424454
p_dry.length = 0;
425455
spi_engine_compile_message(spi_engine, msg, true, &p_dry);
426456

427457
size = sizeof(*p->instructions) * (p_dry.length + 1);
428458
p = kzalloc(sizeof(*p) + size, GFP_KERNEL);
429-
if (!p)
459+
if (!p) {
460+
kfree(st);
430461
return -ENOMEM;
462+
}
463+
464+
ret = ida_alloc_range(&spi_engine->sync_ida, 0, U8_MAX, GFP_KERNEL);
465+
if (ret < 0) {
466+
kfree(p);
467+
kfree(st);
468+
return ret;
469+
}
470+
471+
st->sync_id = ret;
472+
431473
spi_engine_compile_message(spi_engine, msg, false, p);
432474

433475
spin_lock_irqsave(&spi_engine->lock, flags);
434-
spi_engine->sync_id = (spi_engine->sync_id + 1) & 0xff;
435-
spi_engine_program_add_cmd(p, false,
436-
SPI_ENGINE_CMD_SYNC(spi_engine->sync_id));
476+
spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(st->sync_id));
437477

478+
msg->state = st;
438479
spi_engine->msg = msg;
439-
spi_engine->p = p;
480+
st->p = p;
440481

441-
spi_engine->cmd_buf = p->instructions;
442-
spi_engine->cmd_length = p->length;
482+
st->cmd_buf = p->instructions;
483+
st->cmd_length = p->length;
443484
if (spi_engine_write_cmd_fifo(spi_engine))
444485
int_enable |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY;
445486

@@ -448,7 +489,7 @@ static int spi_engine_transfer_one_message(struct spi_controller *host,
448489
int_enable |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY;
449490

450491
spi_engine_rx_next(spi_engine);
451-
if (spi_engine->rx_length != 0)
492+
if (st->rx_length != 0)
452493
int_enable |= SPI_ENGINE_INT_SDI_ALMOST_FULL;
453494

454495
int_enable |= SPI_ENGINE_INT_SYNC;
@@ -489,6 +530,7 @@ static int spi_engine_probe(struct platform_device *pdev)
489530
spi_engine = spi_controller_get_devdata(host);
490531

491532
spin_lock_init(&spi_engine->lock);
533+
ida_init(&spi_engine->sync_ida);
492534

493535
spi_engine->clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
494536
if (IS_ERR(spi_engine->clk))

0 commit comments

Comments
 (0)