From: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>
To: "Yifan Zhang" <yfzhang23@stu.pku.edu.cn>,
"Jiri Kosina" <jikos@kernel.org>,
"Benjamin Tissoires" <bentiss@kernel.org>,
<linux-input@vger.kernel.org>
Cc: "Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
<bpf@vger.kernel.org>, <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2 1/2] HID: bpf: synchronize hardware operations with device destruction
Date: Tue, 03 Nov 2026 03:00:07 +0000 [thread overview]
Message-ID: <DML8NIYTERIZ.1GLZ1R1VJPLUG@gmail.com> (raw)
In-Reply-To: <179367391951.8187.17327354719079930667@stu.pku.edu.cn>
On Mon, Nov 02, 2026 at 06:45 PM Yifan Zhang <yfzhang23@stu.pku.edu.cn> wrote:
> Add a lifetime lock that serializes admission of HID-BPF hardware
> operations with device destruction. Take an SRCU read lock while holding
> the lifetime lock before accessing the report tables or the low-level
> driver. Destruction marks the device destroyed under the same lock and
> waits for the SRCU readers before cleaning up the SRCU state.
The lifetime lock is needed only because hid_bpf_destroy_device() calls
cleanup_srcu_struct(). After that a reference to hid_device is not
enough to call srcu_read_lock().
The splat in patch 2 has the same cause. So does
commit 578e1b96fad7 ("HID: bpf: abort dispatch if device destroyed").
Lets keep hdev->bpf.srcu valid until hiddev_free().
Then the check of 'destroyed' goes inside the SRCU section.
No need for the 2nd mutex.
The dispatchers have the same race and this patch leaves it.
hid_bpf_destroy_device() runs before hidraw_disconnect():
hidraw ioctl hid_destroy_device()
dispatch_hid_bpf_raw_requests()
destroyed == false
destroyed = true
synchronize_srcu()
cleanup_srcu_struct()
srcu_read_lock() <- use after free
hid_close_report() also runs on unbind and on device_reprobe().
SRCU doesn't cover that. Neither this patch nor the diff below fixes it.
Instead of patches 1 and 2:
and Pls wait for Benjamin before sending v3.
diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index 6b467c7..6c2b136 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -99,13 +99,15 @@ int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
struct hid_bpf_ops *e;
int ret, idx;
- if (unlikely(hdev->bpf.destroyed))
- return -ENODEV;
-
if (rtype >= HID_REPORT_TYPES)
return -EINVAL;
idx = srcu_read_lock(&hdev->bpf.srcu);
+ if (unlikely(hdev->bpf.destroyed)) {
+ ret = -ENODEV;
+ goto out;
+ }
+
list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list,
srcu_read_lock_held(&hdev->bpf.srcu)) {
if (!e->hid_hw_request)
@@ -139,10 +141,12 @@ int dispatch_hid_bpf_output_report(struct hid_device *hdev,
struct hid_bpf_ops *e;
int ret, idx;
- if (unlikely(hdev->bpf.destroyed))
- return -ENODEV;
-
idx = srcu_read_lock(&hdev->bpf.srcu);
+ if (unlikely(hdev->bpf.destroyed)) {
+ ret = -ENODEV;
+ goto out;
+ }
+
list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list,
srcu_read_lock_held(&hdev->bpf.srcu)) {
if (!e->hid_hw_output_report)
@@ -423,6 +427,11 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
if (ctx_kern->from_bpf)
return -EDEADLOCK;
+ /* hid_bpf_destroy_device() waits for us before the transport goes away */
+ guard(srcu)(&ctx->hid->bpf.srcu);
+ if (ctx->hid->bpf.destroyed)
+ return -ENODEV;
+
/* check arguments */
ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype, true);
if (ret)
@@ -483,6 +492,10 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz)
if (ctx_kern->from_bpf)
return -EDEADLOCK;
+ guard(srcu)(&ctx->hid->bpf.srcu);
+ if (ctx->hid->bpf.destroyed)
+ return -ENODEV;
+
/* check arguments */
ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT, true);
if (ret)
@@ -641,13 +654,9 @@ 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;
-
__hid_bpf_ops_destroy_device(hdev);
synchronize_srcu(&hdev->bpf.srcu);
- cleanup_srcu_struct(&hdev->bpf.srcu);
}
EXPORT_SYMBOL_GPL(hid_bpf_destroy_device);
@@ -659,6 +668,13 @@ int hid_bpf_device_init(struct hid_device *hdev)
}
EXPORT_SYMBOL_GPL(hid_bpf_device_init);
+/* A reference to hdev is enough to use hdev->bpf.srcu */
+void hid_bpf_device_free(struct hid_device *hdev)
+{
+ cleanup_srcu_struct(&hdev->bpf.srcu);
+}
+EXPORT_SYMBOL_GPL(hid_bpf_device_free);
+
static int __init hid_bpf_init(void)
{
int err;
diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c
index c90b689..f3bbe11 100644
--- a/drivers/hid/bpf/hid_bpf_struct_ops.c
+++ b/drivers/hid/bpf/hid_bpf_struct_ops.c
@@ -197,10 +197,13 @@ static int hid_bpf_reg(void *kdata, struct bpf_link *link)
if (IS_ERR(hdev))
return PTR_ERR(hdev);
- ops->hdev = hdev;
-
mutex_lock(&hdev->bpf.prog_list_lock);
+ if (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;
@@ -222,6 +225,7 @@ static int hid_bpf_reg(void *kdata, struct bpf_link *link)
goto out_unlock;
}
+ ops->hdev = hdev;
if (ops->flags & BPF_F_BEFORE)
list_add_rcu(&ops->list, &hdev->bpf.prog_list);
else
@@ -324,6 +328,8 @@ void __hid_bpf_ops_destroy_device(struct hid_device *hdev)
int count = 0;
mutex_lock(&hdev->bpf.prog_list_lock);
+ /* mark the device as destroyed in bpf so we don't reattach it */
+ hdev->bpf.destroyed = true;
list_for_each_entry(e, &hdev->bpf.prog_list, list) {
e->hdev = NULL;
count++;
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index b4fd482..827a0ee 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -768,6 +768,7 @@ void hiddev_free(struct kref *ref)
hid_close_report(hid);
hid_free_bpf_rdesc(hid);
+ hid_bpf_device_free(hid);
kfree(hid->dev_rdesc);
kfree(hid);
}
diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h
index 19fffa4..0db7f67 100644
--- a/include/linux/hid_bpf.h
+++ b/include/linux/hid_bpf.h
@@ -213,6 +213,7 @@ int hid_bpf_connect_device(struct hid_device *hdev);
void hid_bpf_disconnect_device(struct hid_device *hdev);
void hid_bpf_destroy_device(struct hid_device *hid);
int hid_bpf_device_init(struct hid_device *hid);
+void hid_bpf_device_free(struct hid_device *hid);
const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size);
#else /* CONFIG_HID_BPF */
static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
@@ -232,6 +233,7 @@ static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; }
static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {}
static inline void hid_bpf_destroy_device(struct hid_device *hid) {}
static inline int hid_bpf_device_init(struct hid_device *hid) { return 0; }
+static inline void hid_bpf_device_free(struct hid_device *hid) {}
static inline const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc,
unsigned int *size) { return rdesc; }
pw-bot: cr
next prev parent reply other threads:[~2026-11-03 3:00 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 2:45 [PATCH v2 0/2] HID: bpf: fix device destruction races Yifan Zhang
2026-11-03 2:45 ` [PATCH v2 1/2] HID: bpf: synchronize hardware operations with device destruction Yifan Zhang
2026-11-03 3:00 ` Alexei Starovoitov [this message]
2026-11-03 2:45 ` [PATCH v2 2/2] HID: bpf: reject struct_ops registration during " Yifan Zhang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DML8NIYTERIZ.1GLZ1R1VJPLUG@gmail.com \
--to=alexei.starovoitov@gmail.com \
--cc=bentiss@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=jikos@kernel.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=yfzhang23@stu.pku.edu.cn \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®