[PATCH 2/2] Input: applespi - fix use-after-free in applespi_remove()

From: Shih-Yuan Lee

Date: Sun Jul 12 2026 - 12:15:49 EST


applespi_remove() called applespi_drain_writes() to wait for in-flight
write transfers, then immediately called acpi_disable_gpe() and
acpi_remove_gpe_handler(). However it then called applespi_drain_reads()
*after* the GPE handler was removed, which races with any read SPI
completion callback that could still reference the applespi struct
already being torn down.

Moreover, the two drain helpers use separate wait paths that can miss
each other: a read completion arriving just after drain_writes() returns
but before drain_reads() is called will set read_active, and the
subsequent drain_reads() will then wait on a wait_queue that nobody will
ever wake because the GPE is already gone.

Fix by replacing the two separate drain calls with a single barrier
using the existing cancel_spi + wait_event_lock_irq mechanism:

- Set cancel_spi = true under the spinlock so that applespi_async()
immediately rejects new SPI submissions and wakes the wait queue
once all outstanding operations have drained.
- Wait for !applespi_async_outstanding() before proceeding with
teardown.
- Disable the GPE and remove its handler only after all in-flight SPI
transfers have completed, eliminating the use-after-free window.

Fixes: 0b7a8ac72fc1 ("Input: applespi - add driver for Apple SPI keyboard and touchpad")
Signed-off-by: Shih-Yuan Lee <fourdollars@xxxxxxxxxx>
---
drivers/input/keyboard/applespi.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
index c1065a6ba96c..1f5cc2b4dc30 100644
--- a/drivers/input/keyboard/applespi.c
+++ b/drivers/input/keyboard/applespi.c
@@ -1900,15 +1900,21 @@ static void applespi_drain_reads(struct applespi_data *applespi)
static void applespi_remove(struct spi_device *spi)
{
struct applespi_data *applespi = spi_get_drvdata(spi);
+ unsigned long flags;

- applespi_drain_writes(applespi);
+ /* Prevent any new SPI transfers and wait for outstanding ones */
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+ applespi->cancel_spi = true;
+ wait_event_lock_irq(applespi->wait_queue,
+ !applespi_async_outstanding(applespi),
+ applespi->cmd_msg_lock);
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);

+ /* Disable GPE and remove handler */
acpi_disable_gpe(NULL, applespi->gpe);
acpi_remove_gpe_handler(NULL, applespi->gpe, applespi_notify);
device_wakeup_disable(&spi->dev);

- applespi_drain_reads(applespi);
-
debugfs_remove_recursive(applespi->debugfs_root);
}

--
2.39.5