* [PATCH 1/2] HID: bpf: serialize hardware operations with device destruction
2026-11-02 11:58 [PATCH 0/2] HID: bpf: close device teardown races Yifan Zhang
@ 2026-11-02 11:58 ` Yifan Zhang
2026-11-02 16:21 ` Alexei Starovoitov
2026-11-02 11:58 ` [PATCH 2/2] HID: bpf: reject struct_ops registration during " Yifan Zhang
1 sibling, 1 reply; 4+ messages in thread
From: Yifan Zhang @ 2026-11-02 11:58 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input
Cc: Greg Kroah-Hartman, Alexei Starovoitov, bpf, linux-kernel
A HID-BPF syscall context pins struct hid_device, but it does not pin the
transport-private data referenced by the low-level driver. A hardware
request can therefore sleep in the UHID report-wait path while closing the
UHID file destroys the HID device, wakes the waiter, and frees struct
uhid_device. The awakened request then accesses the freed object.
KASAN reports:
BUG: KASAN: use-after-free in __uhid_report_queue_and_wait
Read of size 1
uhid_hid_raw_request
__hid_hw_raw_request
hid_bpf_hw_request
bpf_prog_test_run_syscall
Hold prog_list_lock across HID-BPF hardware operations. Device destruction
sets destroyed while holding the same lock, so it waits for an operation
that has already started. Later operations return -ENODEV before reaching
the low-level driver. Apply the same lifetime rule to output reports.
The reproducer completed 100 close-versus-request trials without a KASAN
report with this change.
Fixes: 91a7f802d185 ("HID: bpf: introduce hid_hw_request()")
Closes: https://lore.kernel.org/linux-input/179317460423.87432.1451672332362541157@stu.pku.edu.cn/
Signed-off-by: Yifan Zhang <yfzhang23@stu.pku.edu.cn>
---
drivers/hid/bpf/hid_bpf_dispatch.c | 36 +++++++++++++++++++++---------
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index 6b467c74601cb..4fea42dd2f392 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -444,14 +444,20 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
if (!dma_data)
return -ENOMEM;
- ret = hid_ops->hid_hw_raw_request(ctx->hid,
- dma_data[0],
- dma_data,
- size,
- rtype,
- reqtype,
- (u64)(long)ctx,
- true); /* prevent infinite recursions */
+ mutex_lock(&ctx->hid->bpf.prog_list_lock);
+ if (READ_ONCE(ctx->hid->bpf.destroyed)) {
+ ret = -ENODEV;
+ } else {
+ ret = hid_ops->hid_hw_raw_request(ctx->hid,
+ dma_data[0],
+ dma_data,
+ size,
+ rtype,
+ reqtype,
+ (u64)(long)ctx,
+ true); /* prevent infinite recursions */
+ }
+ mutex_unlock(&ctx->hid->bpf.prog_list_lock);
if (ret > (ssize_t)size)
ret = size;
@@ -492,7 +498,13 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz)
if (!dma_data)
return -ENOMEM;
- ret = hid_ops->hid_hw_output_report(ctx->hid, dma_data, size, (u64)(long)ctx, true);
+ mutex_lock(&ctx->hid->bpf.prog_list_lock);
+ if (READ_ONCE(ctx->hid->bpf.destroyed))
+ ret = -ENODEV;
+ else
+ ret = hid_ops->hid_hw_output_report(ctx->hid, dma_data, size,
+ (u64)(long)ctx, true);
+ mutex_unlock(&ctx->hid->bpf.prog_list_lock);
kfree(dma_data);
return ret;
@@ -641,8 +653,10 @@ void hid_bpf_destroy_device(struct hid_device *hdev)
if (!hdev)
return;
- /* mark the device as destroyed in bpf so we don't reattach it */
- hdev->bpf.destroyed = true;
+ /* Block new BPF hardware operations before transport teardown. */
+ mutex_lock(&hdev->bpf.prog_list_lock);
+ WRITE_ONCE(hdev->bpf.destroyed, true);
+ mutex_unlock(&hdev->bpf.prog_list_lock);
__hid_bpf_ops_destroy_device(hdev);
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH 2/2] HID: bpf: reject struct_ops registration during device destruction
2026-11-02 11:58 [PATCH 0/2] HID: bpf: close device teardown races Yifan Zhang
2026-11-02 11:58 ` [PATCH 1/2] HID: bpf: serialize hardware operations with device destruction Yifan Zhang
@ 2026-11-02 11:58 ` Yifan Zhang
1 sibling, 0 replies; 4+ messages in thread
From: Yifan Zhang @ 2026-11-02 11:58 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input
Cc: Greg Kroah-Hartman, Alexei Starovoitov, bpf, linux-kernel
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. The preceding teardown
serialization change sets the flag while holding the same lock, so either a
registration completes before destruction starts or it fails with -ENODEV
without accessing SRCU.
The reproducer no longer triggers a KASAN report with this change.
Fixes: ebc0d8093e8c ("HID: bpf: implement HID-BPF through bpf_struct_ops")
Closes: https://lore.kernel.org/linux-input/179317460937.87455.1799017093083467190@stu.pku.edu.cn/
Signed-off-by: Yifan Zhang <yfzhang23@stu.pku.edu.cn>
---
drivers/hid/bpf/hid_bpf_struct_ops.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c
index c90b68956cb3b..4c433f419c85e 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;
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 4+ messages in thread