ZeroHour

CVE-2026-89738

niche

Use-After-Free in Linux Kernel at91_udc USB Gadget Driver (Polled-VBUS Mode)

CVSS 3.1
7.8 high
EPSS
Published
()
Modified
AI analysis

The Linux kernel's at91_udc USB gadget driver contains a use-after-free: in polled-VBUS mode (board.vbus_pin with board.vbus_polled), probe arms a self-restarting cycle in which at91_vbus_timer() schedules vbus_timer_work and the work handler re-arms the timer via mod_timer(), with both callbacks recovering and dereferencing the devm-allocated udc structure on every iteration — and neither the remove path nor the probe-failure path cancels this cycle. Because devres frees the udc after at91udc_remove() returns (or when a failed probe unwinds), a timer callback or work item that is pending or running at that moment dereferences freed memory, and this can occur during a completely normal driver unbind with no fault required. A local attacker with low privileges who can trigger or race the unbind could leverage the freed-memory access for kernel memory corruption, consistent with the CVSS 3.1 score of 7.8 (high impact to confidentiality, integrity, and availability — effectively local privilege escalation or a system crash). Only systems built on Atmel AT91 / Microchip SAM SoCs that use this gadget driver with a polled-VBUS board configuration are affected; the flaw was found by static analysis, no public PoC exists, and there is no evidence of exploitation in the wild. The fix adds at91_udc_shutdown_vbus_timer(), which calls timer_shutdown_sync() followed by cancel_work_sync() from at91udc_remove() and the usb_add_gadget_udc() failure path in probe, permanently preventing the work handler from re-arming the timer.

What to do: Update to a kernel containing the upstream fix (the commit adding at91_udc_shutdown_vbus_timer() in at91udc_remove() and the probe failure path) or your SoC vendor's BSP backport. Check whether your boards actually run in polled-VBUS mode — IRQ-VBUS configurations never initialize the timer/work and are unaffected. Since exploitation requires local access racing a driver unbind, restrict unprivileged local accounts and avoid unnecessary driver unbinds or module unloads on production embedded devices.

Affected
Linux kernel (drivers/usb/gadget/udc/at91_udc)All kernel versions containing the at91_udc driver prior to the fix commit that adds at91_udc_shutdown_vbus_timer(); the source data does not specify exact vers
Estimated exposure
nicheunknown; plausibly at most on the order of tens of thousands of embedded AT91/SAM-based devices, with no public counts available — The at91_udc driver exists only for Atmel AT91 / Microchip SAM ARM SoCs and the vulnerable code path further requires boards wired for polled VBUS, a small subset of embedded Linux deployments for which no install or internet-scan…

Order-of-magnitude estimate by the model from install counts, market share and public scan data it knows; verify before quoting.

Description

In the Linux kernel, the following vulnerability has been resolved: usb: gadget: at91_udc: drain polled-VBUS timer/work before udc is freed In polled-VBUS mode (board.vbus_pin && board.vbus_polled), probe arms a self-restarting cycle: at91_vbus_timer() schedules vbus_timer_work, and at91_vbus_timer_work() calls at91_vbus_update() and re-arms the timer via mod_timer(). Both recover the same udc through container_of and dereference it on every iteration. Neither teardown path cancels this cycle. udc is devm-allocated, so it is freed after at91udc_remove() returns, and is likewise freed when probe fails and devres runs. A timer callback or work item that is pending or running at either point dereferences the freed udc. Add at91_udc_shutdown_vbus_timer() and call it from at91udc_remove() and from the usb_add_gadget_udc() failure path in probe; the remaining probe error paths fail before the timer is armed. timer_shutdown_sync() waits for a running callback and clears timer->function, which makes the work handler's mod_timer() a permanent no-op; cancel_work_sync() then drains any pending or running work whose re-arm attempt now does nothing. The timer must be shut down first, since cancelling the work alone would let the timer re-queue it. The guard mirrors probe: in IRQ mode the timer and work_struct are never initialized. This does not require a fault; a normal driver unbind can interleave with an already queued work item. This issue was found by an in-house static analysis tool.

Vector
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

In the news

No ingested article mentions this CVE yet.