CVE-2026-46228
Description
In the Linux kernel, the following vulnerability has been resolved:
spi: ch341: fix devres lifetime
USB drivers bind to USB interfaces and any device managed resources should have their lifetime tied to the interface rather than parent USB device. This avoids issues like memory leaks when drivers are unbound without their devices being physically disconnected (e.g. on probe deferral or configuration changes).
Fix the controller and driver data lifetime so that they are released on driver unbind.
Note that this also makes sure that the SPI controller is placed correctly under the USB interface in the device tree.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In Linux kernel spi:ch341 driver, a devres lifetime bug ties resources to the USB device instead of the interface, causing memory leaks on unbind.
Vulnerability
A lifetime management flaw exists in the Linux kernel's spi/ch341 driver. The driver incorrectly ties device-managed resources (devres) to the parent USB device structure rather than the USB interface. This affects the SPI controller and driver data allocation, causing resources to persist beyond driver unbind. The issue is present in all kernel versions featuring the spi-ch341 driver [1].
Exploitation
To reach the vulnerable code path, an attacker must be able to unbind the driver from a ch341 USB interface (e.g., through sysfs manipulation on an affected system, or by physically disconnecting/reconnecting the device while the driver is in use). No special authentication beyond local user access to the device files is required, though root privileges are typically needed to trigger driver unbind operations [1].
Impact
On driver unbind, the SPI controller and related data structures are not released, resulting in a memory leak. Over time, this can exhaust kernel memory, leading to denial of service. No other CIA impact (no direct data disclosure, corruption, or privilege escalation) is described in the available references [1].
Mitigation
The fix is committed in the Linux kernel stable tree as commit abe572f630bc1f0e77041012ab075869036ede4f [1]. Users should apply the patch or update to a kernel version that includes this commit. No workaround is documented in the reference [1].
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
6108a64b27a52spi: ch341: fix devres lifetime
2 files changed · +6 −10
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
4422fc2411cbspi: ch341: fix devres lifetime
2 files changed · +6 −10
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
abe572f630bcspi: ch341: fix devres lifetime
2 files changed · +6 −10
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
108a64b27a52spi: ch341: fix devres lifetime
2 files changed · +6 −10
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
4422fc2411cbspi: ch341: fix devres lifetime
2 files changed · +6 −10
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
abe572f630bcspi: ch341: fix devres lifetime
2 files changed · +6 −10
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
drivers/spi/spi-ch341.c+3 −5 modifieddiff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index ded09356626058..3eaa8f176f63aa 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -152,7 +152,7 @@ static int ch341_probe(struct usb_interface *intf, if (ret) return ret; - ctrl = devm_spi_alloc_host(&udev->dev, sizeof(struct ch341_spi_dev)); + ctrl = devm_spi_alloc_host(&intf->dev, sizeof(struct ch341_spi_dev)); if (!ctrl) return -ENOMEM; @@ -163,7 +163,7 @@ static int ch341_probe(struct usb_interface *intf, ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in)); ch341->rx_len = usb_endpoint_maxp(in); - ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL); + ch341->rx_buf = devm_kzalloc(&intf->dev, ch341->rx_len, GFP_KERNEL); if (!ch341->rx_buf) return -ENOMEM; @@ -171,8 +171,7 @@ static int ch341_probe(struct usb_interface *intf, if (!ch341->rx_urb) return -ENOMEM; - ch341->tx_buf = - devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL); + ch341->tx_buf = devm_kzalloc(&intf->dev, CH341_PACKET_LENGTH, GFP_KERNEL); if (!ch341->tx_buf) { ret = -ENOMEM; goto err_free_urb; -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Device-managed resources in the ch341 SPI driver are tied to the parent USB device instead of the USB interface, causing memory leaks and incorrect device tree placement on driver unbind."
Attack vector
An attacker with physical access to the USB port can plug in a CH341-based SPI adapter. When the driver is unbound (e.g., due to probe deferral, configuration changes, or driver unbind) without the device being physically disconnected, the devm-managed resources tied to the parent USB device are not released, causing memory leaks [patch_id=2897592]. The incorrect lifetime also means the SPI controller is placed under the wrong device in the device tree, which can lead to resource management issues on driver unbind.
Affected code
The vulnerability is in `drivers/spi/spi-ch341.c` in the `ch341_probe()` function. The SPI controller (allocated via `devm_spi_alloc_host`) and driver data buffers (`rx_buf`, `tx_buf`, allocated via `devm_kzalloc`) were incorrectly tied to the parent USB device (`&udev->dev`) instead of the USB interface (`&intf->dev`) [patch_id=2897592][patch_id=2897593].
What the fix does
The patch changes three `devm_*` calls in `ch341_probe()` to use `&intf->dev` (the USB interface device) instead of `&udev->dev` (the parent USB device) [patch_id=2897592]. The SPI controller allocation (`devm_spi_alloc_host`) and the two receive/transmit buffer allocations (`devm_kzalloc`) are now tied to the interface's lifetime. This ensures that when the driver is unbound from the interface, all resources are properly released, preventing memory leaks and ensuring the SPI controller appears under the correct device in the device tree.
Preconditions
- inputPhysical access to a USB port to connect a CH341-based SPI adapter
- configThe ch341 SPI driver must be loaded and bound to the device
- inputDriver unbind must occur (e.g., probe deferral, configuration change, or manual unbind) without physical disconnection
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
3News mentions
0No linked articles in our index yet.