* [PATCH v3 0/3] Input: applespi - fixes for DMA timeout, UAF, and NULL pointer dereference
@ 2026-07-11 9:20 Shih-Yuan Lee
2026-07-11 9:20 ` [PATCH v3 1/3] Input: applespi - force PIO mode on MacBook8,1 Shih-Yuan Lee
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Shih-Yuan Lee @ 2026-07-11 9:20 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, Shih-Yuan Lee
Hi Dmitry and the linux-input community,
This patch series addresses a long-standing DMA initialization timeout on the
early 2015 12" MacBook (MacBook8,1) on any boot (including cold boot), as well
as two pre-existing, high-severity UAF/NULL-pointer bugs in the applespi driver.
Changes in v3:
- Added a `force_pio` module parameter to applespi to allow users to manually
disable DMA for SPI transfers.
- Resolved the execute-after-free vulnerability by unconditionally restoring
the original can_dma callback (even if NULL) in the driver remove path.
- Fixed the probe timing issue by applying the can_dma override at the very
beginning of applespi_probe() so that all early initialization transfers
safely use PIO mode, and properly restoring it in all probe error paths.
- Documented the Bugzilla link in the commit message of Patch 1.
- Format the `force_pio` description style to match other parameters.
Changes in v2:
- Fixed an unbind/remove execute-after-free vulnerability by storing and
restoring the host controller's original can_dma callback in applespi_probe()
and applespi_remove().
- Split the fixes into a 3-patch logical series.
Patch 1 introduces a DMI quirk and a `force_pio` module parameter in applespi.c
to disable DMA. It overrides the shared controller's can_dma callback at the
very beginning of applespi_probe() and restores it in all failure and removal
paths.
Patch 2 fixes a pre-existing UAF vulnerability in the driver unbind path by
explicitly calling cancel_work_sync() on the asynchronous registration worker
work struct before devres frees the driver private data.
Patch 3 fixes a pre-existing race condition in the debugfs interface where
userspace could open the tp_dim file before the asynchronous worker has
finished initializing applespi->touchpad_input_dev, leading to a NULL
pointer dereference. We resolve this using smp_load_acquire() and checking
for NULL.
Best regards,
Shih-Yuan Lee
Shih-Yuan Lee (3):
Input: applespi - force PIO mode on MacBook8,1
Input: applespi - cancel pending work on driver remove
Input: applespi - fix NULL pointer dereference in tp_dim open
drivers/input/keyboard/applespi.c | 76 +++++++++++++++++++++++++------
1 file changed, 63 insertions(+), 13 deletions(-)
--
2.39.5
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v3 1/3] Input: applespi - force PIO mode on MacBook8,1
2026-07-11 9:20 [PATCH v3 0/3] Input: applespi - fixes for DMA timeout, UAF, and NULL pointer dereference Shih-Yuan Lee
@ 2026-07-11 9:20 ` Shih-Yuan Lee
2026-07-11 9:30 ` sashiko-bot
2026-07-11 9:20 ` [PATCH v3 2/3] Input: applespi - cancel pending work on driver remove Shih-Yuan Lee
2026-07-11 9:20 ` [PATCH v3 3/3] Input: applespi - fix NULL pointer dereference in tp_dim open Shih-Yuan Lee
2 siblings, 1 reply; 7+ messages in thread
From: Shih-Yuan Lee @ 2026-07-11 9:20 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, Shih-Yuan Lee
On MacBook8,1 (early 2015 12" MacBook), the LPSS SPI controller's DMA
handshake and interrupt routing frequently fail or time out on boot,
causing the keyboard and trackpad to become unresponsive.
Address this by introducing a DMI quirk and a `force_pio` module
parameter. If either is true, override the SPI controller's `can_dma`
callback to a helper that always returns false. This forces all SPI
transfers to fall back to the reliable PIO mode.
To ensure correct timing and architectural safety:
1. Perform the override at the very beginning of applespi_probe() so
that all early initialization transfers use PIO.
2. In case of probe failures or module unload (remove), unconditionally
restore the original `can_dma` callback (even if it was NULL) to
prevent any execute-after-free or dangling pointer vulnerability in
the shared host controller.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=108331
Signed-off-by: Shih-Yuan Lee <fourdollars@debian.org>
---
drivers/input/keyboard/applespi.c | 66 +++++++++++++++++++++++++------
1 file changed, 54 insertions(+), 12 deletions(-)
diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
index b5ff71cd5a70..79e5cb5001c7 100644
--- a/drivers/input/keyboard/applespi.c
+++ b/drivers/input/keyboard/applespi.c
@@ -46,6 +46,7 @@
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/efi.h>
+#include <linux/dmi.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/ktime.h>
@@ -110,6 +111,10 @@ module_param_string(touchpad_dimensions, touchpad_dimensions,
sizeof(touchpad_dimensions), 0444);
MODULE_PARM_DESC(touchpad_dimensions, "The pixel dimensions of the touchpad, as XxY+W+H .");
+static bool applespi_force_pio;
+module_param_named(force_pio, applespi_force_pio, bool, 0444);
+MODULE_PARM_DESC(force_pio, "Force PIO mode (disables DMA) for SPI transfers. ([0] = disabled, 1 = enabled)");
+
/**
* struct keyboard_protocol - keyboard message.
* message.type = 0x0110, message.length = 0x000a
@@ -431,6 +436,10 @@ struct applespi_data {
int tp_dim_max_x;
int tp_dim_min_y;
int tp_dim_max_y;
+
+ bool (*original_can_dma)(struct spi_controller *controller,
+ struct spi_device *spi,
+ struct spi_transfer *xfer);
};
static const unsigned char applespi_scancodes[] = {
@@ -1605,6 +1614,13 @@ static void applespi_save_bl_level(struct applespi_data *applespi,
"Error saving backlight level to EFI vars: 0x%lx\n", sts);
}
+static bool applespi_can_not_dma(struct spi_controller *controller,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ return false;
+}
+
static int applespi_probe(struct spi_device *spi)
{
struct applespi_data *applespi;
@@ -1612,6 +1628,7 @@ static int applespi_probe(struct spi_device *spi)
acpi_status acpi_sts;
int sts, i;
unsigned long long gpe, usb_status;
+ bool override_dma = applespi_force_pio || dmi_match(DMI_PRODUCT_NAME, "MacBook8,1");
/* check if the USB interface is present and enabled already */
acpi_sts = acpi_evaluate_integer(spi_handle, "UIST", NULL, &usb_status);
@@ -1628,6 +1645,13 @@ static int applespi_probe(struct spi_device *spi)
applespi->spi = spi;
+ /* Save original can_dma and override if requested or needed */
+ applespi->original_can_dma = spi->controller->can_dma;
+ if (override_dma) {
+ dev_info(&spi->dev, "Disabling DMA for SPI to force PIO mode\n");
+ spi->controller->can_dma = applespi_can_not_dma;
+ }
+
INIT_WORK(&applespi->work, applespi_worker);
/* store the driver data */
@@ -1645,8 +1669,10 @@ static int applespi_probe(struct spi_device *spi)
GFP_KERNEL);
if (!applespi->tx_buffer || !applespi->tx_status ||
- !applespi->rx_buffer || !applespi->msg_buf)
- return -ENOMEM;
+ !applespi->rx_buffer || !applespi->msg_buf) {
+ sts = -ENOMEM;
+ goto err_restore_dma;
+ }
/* set up our spi messages */
applespi_setup_read_txfrs(applespi);
@@ -1658,7 +1684,8 @@ static int applespi_probe(struct spi_device *spi)
dev_err(&applespi->spi->dev,
"Failed to get SIEN ACPI method handle: %s\n",
acpi_format_exception(acpi_sts));
- return -ENODEV;
+ sts = -ENODEV;
+ goto err_restore_dma;
}
acpi_sts = acpi_get_handle(spi_handle, "SIST", &applespi->sist);
@@ -1666,23 +1693,26 @@ static int applespi_probe(struct spi_device *spi)
dev_err(&applespi->spi->dev,
"Failed to get SIST ACPI method handle: %s\n",
acpi_format_exception(acpi_sts));
- return -ENODEV;
+ sts = -ENODEV;
+ goto err_restore_dma;
}
/* switch on the SPI interface */
sts = applespi_setup_spi(applespi);
if (sts)
- return sts;
+ goto err_restore_dma;
sts = applespi_enable_spi(applespi);
if (sts)
- return sts;
+ goto err_restore_dma;
/* setup the keyboard input dev */
applespi->keyboard_input_dev = devm_input_allocate_device(&spi->dev);
- if (!applespi->keyboard_input_dev)
- return -ENOMEM;
+ if (!applespi->keyboard_input_dev) {
+ sts = -ENOMEM;
+ goto err_restore_dma;
+ }
applespi->keyboard_input_dev->name = "Apple SPI Keyboard";
applespi->keyboard_input_dev->phys = "applespi/input0";
@@ -1717,7 +1747,7 @@ static int applespi_probe(struct spi_device *spi)
if (sts) {
dev_err(&applespi->spi->dev,
"Unable to register keyboard input device (%d)\n", sts);
- return -ENODEV;
+ goto err_restore_dma;
}
/*
@@ -1729,7 +1759,8 @@ static int applespi_probe(struct spi_device *spi)
dev_err(&applespi->spi->dev,
"Failed to obtain GPE for SPI slave device: %s\n",
acpi_format_exception(acpi_sts));
- return -ENODEV;
+ sts = -ENODEV;
+ goto err_restore_dma;
}
applespi->gpe = (int)gpe;
@@ -1740,7 +1771,8 @@ static int applespi_probe(struct spi_device *spi)
dev_err(&applespi->spi->dev,
"Failed to install GPE handler for GPE %d: %s\n",
applespi->gpe, acpi_format_exception(acpi_sts));
- return -ENODEV;
+ sts = -ENODEV;
+ goto err_restore_dma;
}
applespi->suspended = false;
@@ -1751,7 +1783,8 @@ static int applespi_probe(struct spi_device *spi)
"Failed to enable GPE handler for GPE %d: %s\n",
applespi->gpe, acpi_format_exception(acpi_sts));
acpi_remove_gpe_handler(NULL, applespi->gpe, applespi_notify);
- return -ENODEV;
+ sts = -ENODEV;
+ goto err_restore_dma;
}
/* trigger touchpad setup */
@@ -1789,6 +1822,11 @@ static int applespi_probe(struct spi_device *spi)
&applespi_tp_dim_fops);
return 0;
+
+err_restore_dma:
+ if (override_dma)
+ spi->controller->can_dma = applespi->original_can_dma;
+ return sts;
}
static void applespi_drain_writes(struct applespi_data *applespi)
@@ -1813,6 +1851,7 @@ 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);
+ bool override_dma = applespi_force_pio || dmi_match(DMI_PRODUCT_NAME, "MacBook8,1");
applespi_drain_writes(applespi);
@@ -1822,6 +1861,9 @@ static void applespi_remove(struct spi_device *spi)
applespi_drain_reads(applespi);
+ if (override_dma)
+ spi->controller->can_dma = applespi->original_can_dma;
+
debugfs_remove_recursive(applespi->debugfs_root);
}
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 2/3] Input: applespi - cancel pending work on driver remove
2026-07-11 9:20 [PATCH v3 0/3] Input: applespi - fixes for DMA timeout, UAF, and NULL pointer dereference Shih-Yuan Lee
2026-07-11 9:20 ` [PATCH v3 1/3] Input: applespi - force PIO mode on MacBook8,1 Shih-Yuan Lee
@ 2026-07-11 9:20 ` Shih-Yuan Lee
2026-07-11 9:36 ` sashiko-bot
2026-07-11 9:20 ` [PATCH v3 3/3] Input: applespi - fix NULL pointer dereference in tp_dim open Shih-Yuan Lee
2 siblings, 1 reply; 7+ messages in thread
From: Shih-Yuan Lee @ 2026-07-11 9:20 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, Shih-Yuan Lee
During driver removal in applespi_remove(), the managed private data
structure is freed by devres. However, the driver does not cancel the
asynchronous work applespi->work, which registers the touchpad input
device.
This creates a use-after-free (UAF) vulnerability if a pending or
running worker thread attempts to access the private data after the
remove function returns.
Fix this by explicitly calling cancel_work_sync(&applespi->work) in
applespi_remove() before cleanups.
Signed-off-by: Shih-Yuan Lee <fourdollars@debian.org>
---
drivers/input/keyboard/applespi.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
index 79e5cb5001c7..fd785dba1174 100644
--- a/drivers/input/keyboard/applespi.c
+++ b/drivers/input/keyboard/applespi.c
@@ -1861,6 +1861,8 @@ static void applespi_remove(struct spi_device *spi)
applespi_drain_reads(applespi);
+ cancel_work_sync(&applespi->work);
+
if (override_dma)
spi->controller->can_dma = applespi->original_can_dma;
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 3/3] Input: applespi - fix NULL pointer dereference in tp_dim open
2026-07-11 9:20 [PATCH v3 0/3] Input: applespi - fixes for DMA timeout, UAF, and NULL pointer dereference Shih-Yuan Lee
2026-07-11 9:20 ` [PATCH v3 1/3] Input: applespi - force PIO mode on MacBook8,1 Shih-Yuan Lee
2026-07-11 9:20 ` [PATCH v3 2/3] Input: applespi - cancel pending work on driver remove Shih-Yuan Lee
@ 2026-07-11 9:20 ` Shih-Yuan Lee
2026-07-11 9:39 ` sashiko-bot
2 siblings, 1 reply; 7+ messages in thread
From: Shih-Yuan Lee @ 2026-07-11 9:20 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, Shih-Yuan Lee
The tp_dim debugfs file is registered synchronously during driver probe
in applespi_probe(). However, the applespi->touchpad_input_dev is
initialized and registered asynchronously in the driver's worker thread.
If a userspace process opens the debugfs file before the worker thread
has completed initialization, applespi_tp_dim_open() will dereference
the NULL applespi->touchpad_input_dev pointer, causing a kernel panic.
Fix this by using smp_load_acquire() to safely load touchpad_input_dev
and return -ENODEV if it is not yet initialized.
Signed-off-by: Shih-Yuan Lee <fourdollars@debian.org>
---
drivers/input/keyboard/applespi.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
index fd785dba1174..e3e239f430bd 100644
--- a/drivers/input/keyboard/applespi.c
+++ b/drivers/input/keyboard/applespi.c
@@ -972,12 +972,18 @@ static void applespi_debug_update_dimensions(struct applespi_data *applespi,
static int applespi_tp_dim_open(struct inode *inode, struct file *file)
{
struct applespi_data *applespi = inode->i_private;
+ struct input_dev *touchpad;
file->private_data = applespi;
+ /* Pairs with smp_store_release in applespi_register_touchpad_device() */
+ touchpad = smp_load_acquire(&applespi->touchpad_input_dev);
+ if (!touchpad)
+ return -ENODEV;
+
snprintf(applespi->tp_dim_val, sizeof(applespi->tp_dim_val),
"0x%.4x %dx%d+%u+%u\n",
- applespi->touchpad_input_dev->id.product,
+ touchpad->id.product,
applespi->tp_dim_min_x, applespi->tp_dim_min_y,
applespi->tp_dim_max_x - applespi->tp_dim_min_x,
applespi->tp_dim_max_y - applespi->tp_dim_min_y);
--
2.39.5
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/3] Input: applespi - force PIO mode on MacBook8,1
2026-07-11 9:20 ` [PATCH v3 1/3] Input: applespi - force PIO mode on MacBook8,1 Shih-Yuan Lee
@ 2026-07-11 9:30 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-07-11 9:30 UTC (permalink / raw)
To: Shih-Yuan Lee; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] The child device driver (`applespi`) overwrites the parent SPI controller's `can_dma` callback, causing an architectural layering violation, data races (TOCTOU) with concurrent SPI transfers, and potential execute-after-free vulnerabilities upon module unload.
Pre-existing issues:
- [High] Race condition between manual cleanup and devres unregistration leads to a Use-After-Free via `applespi->work`.
--
--- Patch [1]: [PATCH v3 1/3] Input: applespi - force PIO mode on MacBook8,1 ---
commit fb42db4427f33df07132a7b2bd913031f55465ea
Author: Shih-Yuan Lee <fourdollars@debian.org>
Input: applespi - force PIO mode on MacBook8,1
On MacBook8,1 (early 2015 12" MacBook), the LPSS SPI controller's DMA
handshake and interrupt routing frequently fail or time out on boot,
causing the keyboard and trackpad to become unresponsive.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=108331
> diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
> --- a/drivers/input/keyboard/applespi.c
> +++ b/drivers/input/keyboard/applespi.c
[ ... ]
> @@ -1628,6 +1645,13 @@ static int applespi_probe(struct spi_device *spi)
>
> applespi->spi = spi;
>
> + /* Save original can_dma and override if requested or needed */
> + applespi->original_can_dma = spi->controller->can_dma;
> + if (override_dma) {
> + dev_info(&spi->dev, "Disabling DMA for SPI to force PIO mode\n");
> + spi->controller->can_dma = applespi_can_not_dma;
[Severity: High]
Is it safe for a child device driver to overwrite the parent SPI controller's
shared can_dma callback?
Modifying spi->controller->can_dma directly without a controller-wide lock
could cause a data race if the SPI worker thread concurrently dereferences
ctlr->can_dma for another device on the same bus.
> + }
> +
> INIT_WORK(&applespi->work, applespi_worker);
[ ... ]
> @@ -1822,6 +1861,9 @@ static void applespi_remove(struct spi_device *spi)
>
> applespi_drain_reads(applespi);
>
> cancel_work_sync(&applespi->work);
[Severity: High]
This is a pre-existing issue, but does this create a use-after-free window
during module unload?
In applespi_remove(), the work is canceled, but the input and LED devices
are managed by devm_ and aren't unregistered until the devres release phase
after applespi_remove() completes.
During this window, if a user writes to the LED sysfs interface, it will
call applespi_set_bl_level() which calls schedule_work(&applespi->work),
re-arming the worker. When devres subsequently frees the applespi structure,
the queued work might execute on freed memory.
> + if (override_dma)
> + spi->controller->can_dma = applespi->original_can_dma;
[Severity: High]
Could restoring the can_dma callback without synchronization leave a dangling
pointer if multiple instances bind or unbind?
The restore happens conditionally based on a dynamically recalculated
override_dma boolean. If the module is unloaded but fails to restore the
pointer properly, a dangling pointer will remain in the shared SPI controller.
The SPI core could then invoke the freed applespi_can_not_dma function inside
the unloaded module, resulting in an execute-after-free.
> +
> debugfs_remove_recursive(applespi->debugfs_root);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260711092054.13818-1-fourdollars@debian.org?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/3] Input: applespi - cancel pending work on driver remove
2026-07-11 9:20 ` [PATCH v3 2/3] Input: applespi - cancel pending work on driver remove Shih-Yuan Lee
@ 2026-07-11 9:36 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-07-11 9:36 UTC (permalink / raw)
To: Shih-Yuan Lee; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
Pre-existing issues:
- [High] Use-After-Free of `applespi` structure in SPI async read completion callback
- [High] Unbounded wait in `applespi_drain_writes` leading to deadlock on driver unbind or suspend
- [High] NULL pointer dereference in debugfs 'tp_dim' open handler
- [High] CRC error in SPI packet permanently leaks `read_active` flag, causing deadlock
- [High] Unsafe DMA allocation using `devm_kmalloc` leads to cacheline corruption
--
--- Patch [2]: [PATCH v3 2/3] Input: applespi - cancel pending work on driver remove ---
commit 600aa576cb85623de00e20bade1177db921cccac
Author: Shih-Yuan Lee <fourdollars@debian.org>
Input: applespi - cancel pending work on driver remove
During driver removal in applespi_remove(), the managed private data
structure is freed by devres. However, the driver does not cancel the
asynchronous work applespi->work, which registers the touchpad input
device.
> diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
> --- a/drivers/input/keyboard/applespi.c
> +++ b/drivers/input/keyboard/applespi.c
> @@ -1861,6 +1861,8 @@ static void applespi_remove(struct spi_device *spi)
>
> applespi_drain_reads(applespi);
[Severity: High]
This is a pre-existing issue, but does applespi_drain_reads() create a race
condition with the SPI async read completion callback?
If applespi_drain_reads() waits for read_active to become false, and the
callback applespi_async_read_complete() clears this flag via
applespi_msg_complete(), it unblocks the removal thread immediately:
applespi_async_read_complete() {
...
applespi_msg_complete(applespi, ...);
...
acpi_finish_gpe(NULL, applespi->gpe);
}
Can the removal thread finish applespi_remove() and free the applespi
structure via devres before the completion callback finishes executing,
leading to a use-after-free when acpi_finish_gpe() is called?
[Severity: High]
This is a pre-existing issue, but can this unbind or suspend path deadlock
due to unbounded waits?
Looking at applespi_drain_writes() called earlier in this path, it waits
for write_active to become false:
applespi_drain_writes() {
applespi->drain = true;
wait_event_lock_irq(applespi->drain_complete, !applespi->write_active,
applespi->cmd_msg_lock);
}
If the hardware fails to respond to a command, write_active remains true.
Because drain is set to true, the lazy timeout logic in
applespi_send_cmd_msg() is unreachable. Does this wait_event block
indefinitely without a fallback timeout if a write response is never received?
> + cancel_work_sync(&applespi->work);
> +
> if (override_dma)
> spi->controller->can_dma = applespi->original_can_dma;
>
[ ... ]
[Severity: High]
This is a pre-existing issue, but is there a risk of a NULL pointer
dereference when accessing the tp_dim debugfs file?
The tp_dim debugfs file is created synchronously during probe, but
applespi->touchpad_input_dev is allocated asynchronously in a workqueue.
If a user opens the debugfs file before the workqueue executes (or if it
never executes due to a hardware failure), applespi_tp_dim_open() will see
a NULL pointer:
applespi_tp_dim_open() {
...
applespi->touchpad_input_dev->id.product,
...
}
Can this unconditionally dereference touchpad_input_dev while it is NULL?
[Severity: High]
This is a pre-existing issue, but does a CRC error permanently leak the
read_active state?
In applespi_got_data(), if a packet fails CRC validation and drain is
false, it returns early:
applespi_got_data() {
if (!applespi_verify_crc(...)) {
if (applespi->drain) {
...
}
return;
}
...
}
This skips calling applespi_msg_complete(), leaving read_active set to
true. When the system later suspends or the driver is unbound, will
applespi_drain_reads() wait indefinitely for read_active to become
false, causing a deadlock?
[Severity: High]
This is a pre-existing issue, but are the SPI buffers safe for DMA transfers?
In applespi_probe(), the buffers are allocated using devm_kmalloc():
applespi_probe() {
...
applespi->tx_buffer = devm_kmalloc(&spi->dev, APPLESPI_PACKET_SIZE, ...);
applespi->rx_buffer = devm_kmalloc(&spi->dev, APPLESPI_PACKET_SIZE, ...);
...
}
Since devm allocations prepend a struct devres header immediately before
the returned pointer, the devres header shares the same cacheline as the
start of the buffer.
When the SPI core maps these buffers for DMA, will DMA cache invalidation
(especially DMA_FROM_DEVICE for Rx) invalidate the CPU cacheline and
corrupt the devres linked list headers?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260711092054.13818-1-fourdollars@debian.org?part=2
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 3/3] Input: applespi - fix NULL pointer dereference in tp_dim open
2026-07-11 9:20 ` [PATCH v3 3/3] Input: applespi - fix NULL pointer dereference in tp_dim open Shih-Yuan Lee
@ 2026-07-11 9:39 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-07-11 9:39 UTC (permalink / raw)
To: Shih-Yuan Lee; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] Repeated processing of expected or malicious hardware `PACKET_DEV_INFO` responses causes unbounded allocation and registration of `devm`-managed input devices.
- [High] Use-After-Free of `applespi` private data in the asynchronous SPI read callback during driver unbind.
- [High] Missing bounds check on keyboard release events causes an out-of-bounds read of kernel memory, leading to an information leak.
--
--- Patch [3]: [PATCH v3 3/3] Input: applespi - fix NULL pointer dereference in tp_dim open ---
commit 44778c849d055105696c2f1ad69bc1cf09ff47d0
Author: Shih-Yuan Lee <fourdollars@debian.org>
Input: applespi - fix NULL pointer dereference in tp_dim open
This commit fixes a NULL pointer dereference in applespi_tp_dim_open()
when userspace accesses the debugfs file before asynchronous initialization
completes. It uses smp_load_acquire() to safely load the touchpad input
device and returns -ENODEV if not initialized.
> diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
> index fd785dba1174e..e3e239f430bd9 100644
> --- a/drivers/input/keyboard/applespi.c
> +++ b/drivers/input/keyboard/applespi.c
> @@ -972,12 +972,18 @@ static void applespi_debug_update_dimensions(struct applespi_data *applespi,
> static int applespi_tp_dim_open(struct inode *inode, struct file *file)
> {
> struct applespi_data *applespi = inode->i_private;
> + struct input_dev *touchpad;
>
> file->private_data = applespi;
>
> + /* Pairs with smp_store_release in applespi_register_touchpad_device() */
> + touchpad = smp_load_acquire(&applespi->touchpad_input_dev);
> + if (!touchpad)
> + return -ENODEV;
> +
[Severity: High]
This isn't a bug introduced by this patch, but does the driver leak resources
if it repeatedly processes hardware device info responses?
If applespi_handle_cmd_response() receives this packet, it schedules the
worker:
applespi_handle_cmd_response() {
...
schedule_work(&applespi->work);
}
The worker unconditionally calls applespi_register_touchpad_device(), which
allocates a new devm-managed device:
applespi_register_touchpad_device() {
...
touchpad_input_dev = devm_input_allocate_device(&applespi->spi->dev);
...
}
Could this lead to unbounded allocation and registration of devm-managed input
devices, losing the reference to the old ones and eventually exhausting memory?
[Severity: High]
This is a pre-existing issue, but is there a potential use-after-free in the
asynchronous SPI read callback during driver unbind?
During driver removal, applespi_remove() calls applespi_drain_reads(), which
waits for read_active to become false:
applespi_remove() {
...
applespi_drain_reads(applespi);
...
}
In applespi_async_read_complete(), applespi_msg_complete() sets read_active
to false and wakes the waitqueue, then proceeds to use applespi->gpe:
applespi_async_read_complete() {
...
applespi_msg_complete(applespi, false, false);
...
acpi_finish_gpe(NULL, applespi->gpe);
}
If devres cleanup frees the applespi struct immediately after
applespi_drain_reads() returns, could accessing applespi->gpe cause a
use-after-free?
[Severity: High]
This isn't a bug introduced by this patch, but is there an out-of-bounds read
when processing keyboard release events?
In applespi_handle_keyboard_event(), the unvalidated keys_pressed array is
copied directly into last_keys_pressed:
applespi_handle_keyboard_event() {
...
memcpy(&applespi->last_keys_pressed, keyboard_protocol->keys_pressed,
sizeof(applespi->last_keys_pressed));
...
}
On a subsequent keyboard event, if a previously stored invalid keycode is
processed as released, it is passed to applespi_code_to_key():
applespi_handle_keyboard_event() {
...
key = applespi_code_to_key(applespi->last_keys_pressed[i],
applespi->last_keys_fn_pressed[i]);
...
}
applespi_code_to_key() uses this unvalidated keycode to index the
applespi_scancodes array:
applespi_code_to_key() {
...
unsigned int key = applespi_scancodes[code];
...
}
Could a maliciously crafted SPI packet cause an out-of-bounds read of
applespi_scancodes leading to an information leak?
> snprintf(applespi->tp_dim_val, sizeof(applespi->tp_dim_val),
> "0x%.4x %dx%d+%u+%u\n",
> - applespi->touchpad_input_dev->id.product,
> + touchpad->id.product,
> applespi->tp_dim_min_x, applespi->tp_dim_min_y,
> applespi->tp_dim_max_x - applespi->tp_dim_min_x,
> applespi->tp_dim_max_y - applespi->tp_dim_min_y);
>
> return nonseekable_open(inode, file);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260711092054.13818-1-fourdollars@debian.org?part=3
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-11 9:39 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-11 9:20 [PATCH v3 0/3] Input: applespi - fixes for DMA timeout, UAF, and NULL pointer dereference Shih-Yuan Lee
2026-07-11 9:20 ` [PATCH v3 1/3] Input: applespi - force PIO mode on MacBook8,1 Shih-Yuan Lee
2026-07-11 9:30 ` sashiko-bot
2026-07-11 9:20 ` [PATCH v3 2/3] Input: applespi - cancel pending work on driver remove Shih-Yuan Lee
2026-07-11 9:36 ` sashiko-bot
2026-07-11 9:20 ` [PATCH v3 3/3] Input: applespi - fix NULL pointer dereference in tp_dim open Shih-Yuan Lee
2026-07-11 9:39 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox