Skip to content

Commit 9acbb7b

Browse files
author
Benjamin Tissoires
committed
HID: bpf: allow hid_device_event hooks to inject input reports on self
This is the same logic than hid_hw_raw_request or hid_hw_output_report: we can allow hid_bpf_try_input_report to be called from a hook on hid_input_report if we ensure that the call can not be made twice in a row. There is one extra subtlety in which there is a lock in hid_input_report. But given that we can detect if we are already in the hook, we can notify hid_input_report to not take the lock. This is done by checking if ctx_kern data is valid or null, and if it is equal to the dedicated incoming data buffer. In order to have more control on whether the lock needs to be taken or not we introduce a new kfunc for it: hid_bpf_try_input_report() Link: https://patch.msgid.link/[email protected] Acked-by: Jiri Kosina <[email protected]> Signed-off-by: Benjamin Tissoires <[email protected]>
1 parent fe8d561 commit 9acbb7b

File tree

4 files changed

+55
-14
lines changed

4 files changed

+55
-14
lines changed

Documentation/hid/hid-bpf.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ Available API that can be used in syscall HID-BPF programs or in sleepable HID-B
202202
-------------------------------------------------------------------------------------------------------
203203

204204
.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c
205-
:identifiers: hid_bpf_hw_request hid_bpf_hw_output_report hid_bpf_input_report hid_bpf_allocate_context hid_bpf_release_context
205+
:identifiers: hid_bpf_hw_request hid_bpf_hw_output_report hid_bpf_input_report hid_bpf_try_input_report hid_bpf_allocate_context hid_bpf_release_context
206206

207207
General overview of a HID-BPF program
208208
=====================================

drivers/hid/bpf/hid_bpf_dispatch.c

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ EXPORT_SYMBOL(hid_ops);
2424

2525
u8 *
2626
dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data,
27-
u32 *size, int interrupt, u64 source)
27+
u32 *size, int interrupt, u64 source, bool from_bpf)
2828
{
2929
struct hid_bpf_ctx_kern ctx_kern = {
3030
.ctx = {
@@ -33,6 +33,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type
3333
.size = *size,
3434
},
3535
.data = hdev->bpf.device_data,
36+
.from_bpf = from_bpf,
3637
};
3738
struct hid_bpf_ops *e;
3839
int ret;
@@ -488,6 +489,50 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz)
488489
return ret;
489490
}
490491

492+
static int
493+
__hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,
494+
size_t size, bool lock_already_taken)
495+
{
496+
struct hid_bpf_ctx_kern *ctx_kern;
497+
int ret;
498+
499+
ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
500+
if (ctx_kern->from_bpf)
501+
return -EDEADLOCK;
502+
503+
/* check arguments */
504+
ret = __hid_bpf_hw_check_params(ctx, buf, &size, type);
505+
if (ret)
506+
return ret;
507+
508+
return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx, true,
509+
lock_already_taken);
510+
}
511+
512+
/**
513+
* hid_bpf_try_input_report - Inject a HID report in the kernel from a HID device
514+
*
515+
* @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()
516+
* @type: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT)
517+
* @buf: a %PTR_TO_MEM buffer
518+
* @buf__sz: the size of the data to transfer
519+
*
520+
* Returns %0 on success, a negative error code otherwise. This function will immediately
521+
* fail if the device is not available, thus can be safely used in IRQ context.
522+
*/
523+
__bpf_kfunc int
524+
hid_bpf_try_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,
525+
const size_t buf__sz)
526+
{
527+
struct hid_bpf_ctx_kern *ctx_kern;
528+
bool from_hid_event_hook;
529+
530+
ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
531+
from_hid_event_hook = ctx_kern->data && ctx_kern->data == ctx->hid->bpf.device_data;
532+
533+
return __hid_bpf_input_report(ctx, type, buf, buf__sz, from_hid_event_hook);
534+
}
535+
491536
/**
492537
* hid_bpf_input_report - Inject a HID report in the kernel from a HID device
493538
*
@@ -504,20 +549,14 @@ __bpf_kfunc int
504549
hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,
505550
const size_t buf__sz)
506551
{
507-
size_t size = buf__sz;
508552
int ret;
509553

510554
ret = down_interruptible(&ctx->hid->driver_input_lock);
511555
if (ret)
512556
return ret;
513557

514558
/* check arguments */
515-
ret = __hid_bpf_hw_check_params(ctx, buf, &size, type);
516-
if (ret)
517-
return ret;
518-
519-
ret = hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx,
520-
true /* lock_already_taken */);
559+
ret = __hid_bpf_input_report(ctx, type, buf, buf__sz, true /* lock_already_taken */);
521560

522561
up(&ctx->hid->driver_input_lock);
523562

@@ -536,6 +575,7 @@ BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE | KF_SLEEPABLE)
536575
BTF_ID_FLAGS(func, hid_bpf_hw_request, KF_SLEEPABLE)
537576
BTF_ID_FLAGS(func, hid_bpf_hw_output_report, KF_SLEEPABLE)
538577
BTF_ID_FLAGS(func, hid_bpf_input_report, KF_SLEEPABLE)
578+
BTF_ID_FLAGS(func, hid_bpf_try_input_report)
539579
BTF_KFUNCS_END(hid_bpf_kfunc_ids)
540580

541581
static const struct btf_kfunc_id_set hid_bpf_kfunc_set = {

drivers/hid/hid-core.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,7 +2027,7 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event);
20272027

20282028

20292029
static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
2030-
u8 *data, u32 size, int interrupt, u64 source,
2030+
u8 *data, u32 size, int interrupt, u64 source, bool from_bpf,
20312031
bool lock_already_taken)
20322032
{
20332033
struct hid_report_enum *report_enum;
@@ -2053,7 +2053,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
20532053
report_enum = hid->report_enum + type;
20542054
hdrv = hid->driver;
20552055

2056-
data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source);
2056+
data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf);
20572057
if (IS_ERR(data)) {
20582058
ret = PTR_ERR(data);
20592059
goto unlock;
@@ -2105,6 +2105,7 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data
21052105
int interrupt)
21062106
{
21072107
return __hid_input_report(hid, type, data, size, interrupt, 0,
2108+
false, /* from_bpf */
21082109
false /* lock_already_taken */);
21092110
}
21102111
EXPORT_SYMBOL_GPL(hid_input_report);

include/linux/hid_bpf.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ struct hid_ops {
7272
int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len,
7373
__u64 source, bool from_bpf);
7474
int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type,
75-
u8 *data, u32 size, int interrupt, u64 source,
75+
u8 *data, u32 size, int interrupt, u64 source, bool from_bpf,
7676
bool lock_already_taken);
7777
struct module *owner;
7878
const struct bus_type *bus_type;
@@ -195,7 +195,7 @@ struct hid_bpf {
195195

196196
#ifdef CONFIG_HID_BPF
197197
u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
198-
u32 *size, int interrupt, u64 source);
198+
u32 *size, int interrupt, u64 source, bool from_bpf);
199199
int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
200200
unsigned char reportnum, __u8 *buf,
201201
u32 size, enum hid_report_type rtype,
@@ -211,7 +211,7 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *s
211211
#else /* CONFIG_HID_BPF */
212212
static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
213213
u8 *data, u32 *size, int interrupt,
214-
u64 source) { return data; }
214+
u64 source, bool from_bpf) { return data; }
215215
static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
216216
unsigned char reportnum, u8 *buf,
217217
u32 size, enum hid_report_type rtype,

0 commit comments

Comments
 (0)