ZeroHour

CVE-2026-90001

niche

Use-after-free race in Linux kernel HID BPF struct_ops device teardown

CVSS 3.1
7.8 high
EPSS
Published
()
Modified
AI analysis

CVE-2026-90001 is a race condition in the Linux kernel's HID BPF subsystem in which the device-destruction path (__hid_bpf_ops_destroy_device) and the BPF unregistration path (hid_bpf_unreg) can both release the same reference to a HID device, double-putting it and freeing the device while hid_destroy_device() is still using it, causing a use-after-free. It is triggered locally when a HID BPF program attached via the struct_ops mechanism is torn down concurrently with device destruction — for example, a BPF map delete or link release racing with the HID device's destroy traversal. A local attacker with low privileges (in practice, the ability to attach BPF programs, which normally requires CAP_BPF/CAP_SYS_ADMIN) can crash the kernel and, given the freed-memory window during teardown, potentially compromise confidentiality, integrity and availability as reflected in the 7.8 CVSS score. Only systems running kernels that include the HID BPF struct_ops destroy path and with HID BPF programs actively attached are affected. No public proof-of-concept is known, there are no reports of in-the-wild exploitation, and the issue is not in CISA KEV.

What to do: Update the kernel to a version that includes the fix serializing the remove/NULL decision under hdev->bpf.prog_list_lock once it is merged in mainline and backported to stable branches for your kernel series. As an interim mitigation, do not load HID BPF struct_ops programs on multi-user or exposed systems and restrict CAP_BPF/CAP_SYS_ADMIN to trusted processes. Audit hosts with bpftool or similar to check whether any HID BPF struct_ops programs or links are currently attached.

Affected
Linux kernel (HID BPF struct_ops subsystem, HID: bpf)
Estimated exposure
nicheunknown; plausibly only thousands of systems at most (HID BPF struct_ops is a newly merged, rarely deployed feature and attaching such programs requires… — No public telemetry or internet-scan counts exist for HID BPF struct_ops usage, so the estimate assumes only recent kernels with HID BPF programs actually attached and a local user able to attach BPF programs are exposed.

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: HID: bpf: serialize device reference release in struct_ops destroy path __hid_bpf_ops_destroy_device() and hid_bpf_unreg() can race on the same registration reference, double-putting struct hid_device and freeing it while hid_destroy_device() still uses it. Serialize the remove/NULL decision under hdev->bpf.prog_list_lock so exactly one path releases each registration reference: unreg re-checks ops->hdev under the lock and returns without putting when the destroy path already cleared it; all put_device() calls happen after the lock is dropped, which is safe because a concurrent unreg then observes ops->hdev == NULL under the lock. Background: each successful attach (hid_bpf_ops_reg) acquires one device reference (hid_get_device()). Two paths can release it: - device destruction: hid_destroy_device() -> hid_bpf_destroy_device() -> __hid_bpf_ops_destroy_device(), which walks hdev->bpf.prog_list under rcu_read_lock() and drops one reference per attached program; - BPF link release: bpf map delete (no BPF_F_LINK) synchronously calls st_ops->unreg() -> hid_bpf_unreg(), which drops the reference for its own registration. The coordination handshake (e->hdev = NULL on the destroy side vs "if (!hdev) return" on the unreg side) is a TOCTOU check: the two paths run under different lock domains (rcu_read_lock vs prog_list_lock), so a concurrent unreg can read ops->hdev as non-NULL, block on prog_list_lock, and then proceed while the destroy traversal executes - both paths then drop the same reference. The refcount reaches zero legitimately (each decrement is individually valid), so no refcount_t saturation fires: the device is simply freed while the transport is still inside hid_destroy_device(), and subsequent teardown touches freed memory. The fix serializes the remove/NULL decision under prog_list_lock on both sides and moves the destroy-side puts outside the lock. With the lock held, plain reads/writes of ops->hdev are sufficient; no READ_ONCE/WRITE_ONCE are added, keeping the patch minimal. Unlocked-read safety: the unlocked read of ops->hdev at the top of hid_bpf_unreg() cannot touch a freed device, because the unreg path itself still holds this registration's reference (released only by its own hid_put_device() after the lock is dropped), and a destroy traversal that already cleared ops->hdev makes the lock-internal re-check return early without any put. At most one of the two paths releases each registration reference.

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.