[PATCH v2 2/2] HID: bpf: reject struct_ops registration during device destruction
From: Yifan Zhang
Date: Mon Nov 02 2026 - 21:46:19 EST
hid_bpf_reg() can obtain a device reference before hid_destroy_device()
removes the device from the bus, then block on prog_list_lock while HID-BPF
destruction cleans the device's SRCU state. The registration path does not
check the destroyed flag after taking the lock, so it can publish a new
struct_ops entry and call synchronize_srcu() on the cleaned SRCU object.
KASAN reports:
BUG: KASAN: slab-use-after-free in check_init_srcu_struct
Read of size 8
check_init_srcu_struct
synchronize_srcu
hid_bpf_reg
bpf_struct_ops_link_create
Check destroyed while holding prog_list_lock. Device destruction sets the
flag while holding the same lock, so a registration either completes before
destruction starts or fails with -ENODEV without accessing the SRCU state.
Also clear ops->hdev on every registration error before dropping the device
reference. This keeps a failed struct_ops instance reusable and prevents a
dangling device pointer from reaching a later unregister operation.
Fixes: ebc0d8093e8c ("HID: bpf: implement HID-BPF through bpf_struct_ops")
Closes: https://lore.kernel.org/linux-input/179317460937.87455.1799017093083467190@xxxxxxxxxxxxxx/
Signed-off-by: Yifan Zhang <yfzhang23@xxxxxxxxxxxxxx>
---
drivers/hid/bpf/hid_bpf_struct_ops.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c
index c90b68956cb3b..31e2cb146b92c 100644
--- a/drivers/hid/bpf/hid_bpf_struct_ops.c
+++ b/drivers/hid/bpf/hid_bpf_struct_ops.c
@@ -201,6 +201,11 @@ static int hid_bpf_reg(void *kdata, struct bpf_link *link)
mutex_lock(&hdev->bpf.prog_list_lock);
+ if (READ_ONCE(hdev->bpf.destroyed)) {
+ err = -ENODEV;
+ goto out_unlock;
+ }
+
count = list_count_nodes(&hdev->bpf.prog_list);
if (count >= HID_BPF_MAX_PROGS_PER_DEV) {
err = -E2BIG;
@@ -229,11 +234,14 @@ static int hid_bpf_reg(void *kdata, struct bpf_link *link)
synchronize_srcu(&hdev->bpf.srcu);
out_unlock:
- mutex_unlock(&hdev->bpf.prog_list_lock);
-
if (err) {
if (hdev->bpf.rdesc_ops == ops)
hdev->bpf.rdesc_ops = NULL;
+ ops->hdev = NULL;
+ }
+ mutex_unlock(&hdev->bpf.prog_list_lock);
+
+ if (err) {
hid_put_device(hdev);
} else if (ops->hid_rdesc_fixup) {
hid_bpf_reconnect(hdev);
--
2.39.5 (Apple Git-154)