Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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/20240626-hid_hw_req_bpf-v2-11-cfd60fb6c79f@kernel.org
Acked-by: Jiri Kosina <jkosina@suse.com>
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>

+55 -14
+1 -1
Documentation/hid/hid-bpf.rst
··· 202 202 ------------------------------------------------------------------------------------------------------- 203 203 204 204 .. 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 206 206 207 207 General overview of a HID-BPF program 208 208 =====================================
+48 -8
drivers/hid/bpf/hid_bpf_dispatch.c
··· 24 24 25 25 u8 * 26 26 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) 28 28 { 29 29 struct hid_bpf_ctx_kern ctx_kern = { 30 30 .ctx = { ··· 33 33 .size = *size, 34 34 }, 35 35 .data = hdev->bpf.device_data, 36 + .from_bpf = from_bpf, 36 37 }; 37 38 struct hid_bpf_ops *e; 38 39 int ret; ··· 489 488 return ret; 490 489 } 491 490 491 + static int 492 + __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, 493 + size_t size, bool lock_already_taken) 494 + { 495 + struct hid_bpf_ctx_kern *ctx_kern; 496 + int ret; 497 + 498 + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); 499 + if (ctx_kern->from_bpf) 500 + return -EDEADLOCK; 501 + 502 + /* check arguments */ 503 + ret = __hid_bpf_hw_check_params(ctx, buf, &size, type); 504 + if (ret) 505 + return ret; 506 + 507 + return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx, true, 508 + lock_already_taken); 509 + } 510 + 511 + /** 512 + * hid_bpf_try_input_report - Inject a HID report in the kernel from a HID device 513 + * 514 + * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context() 515 + * @type: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT) 516 + * @buf: a %PTR_TO_MEM buffer 517 + * @buf__sz: the size of the data to transfer 518 + * 519 + * Returns %0 on success, a negative error code otherwise. This function will immediately 520 + * fail if the device is not available, thus can be safely used in IRQ context. 521 + */ 522 + __bpf_kfunc int 523 + hid_bpf_try_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, 524 + const size_t buf__sz) 525 + { 526 + struct hid_bpf_ctx_kern *ctx_kern; 527 + bool from_hid_event_hook; 528 + 529 + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); 530 + from_hid_event_hook = ctx_kern->data && ctx_kern->data == ctx->hid->bpf.device_data; 531 + 532 + return __hid_bpf_input_report(ctx, type, buf, buf__sz, from_hid_event_hook); 533 + } 534 + 492 535 /** 493 536 * hid_bpf_input_report - Inject a HID report in the kernel from a HID device 494 537 * ··· 549 504 hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, 550 505 const size_t buf__sz) 551 506 { 552 - size_t size = buf__sz; 553 507 int ret; 554 508 555 509 ret = down_interruptible(&ctx->hid->driver_input_lock); ··· 556 512 return ret; 557 513 558 514 /* check arguments */ 559 - ret = __hid_bpf_hw_check_params(ctx, buf, &size, type); 560 - if (ret) 561 - return ret; 562 - 563 - ret = hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx, 564 - true /* lock_already_taken */); 515 + ret = __hid_bpf_input_report(ctx, type, buf, buf__sz, true /* lock_already_taken */); 565 516 566 517 up(&ctx->hid->driver_input_lock); 567 518 ··· 575 536 BTF_ID_FLAGS(func, hid_bpf_hw_request, KF_SLEEPABLE) 576 537 BTF_ID_FLAGS(func, hid_bpf_hw_output_report, KF_SLEEPABLE) 577 538 BTF_ID_FLAGS(func, hid_bpf_input_report, KF_SLEEPABLE) 539 + BTF_ID_FLAGS(func, hid_bpf_try_input_report) 578 540 BTF_KFUNCS_END(hid_bpf_kfunc_ids) 579 541 580 542 static const struct btf_kfunc_id_set hid_bpf_kfunc_set = {
+3 -2
drivers/hid/hid-core.c
··· 2027 2027 2028 2028 2029 2029 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, 2031 2031 bool lock_already_taken) 2032 2032 { 2033 2033 struct hid_report_enum *report_enum; ··· 2053 2053 report_enum = hid->report_enum + type; 2054 2054 hdrv = hid->driver; 2055 2055 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); 2057 2057 if (IS_ERR(data)) { 2058 2058 ret = PTR_ERR(data); 2059 2059 goto unlock; ··· 2105 2105 int interrupt) 2106 2106 { 2107 2107 return __hid_input_report(hid, type, data, size, interrupt, 0, 2108 + false, /* from_bpf */ 2108 2109 false /* lock_already_taken */); 2109 2110 } 2110 2111 EXPORT_SYMBOL_GPL(hid_input_report);
+3 -3
include/linux/hid_bpf.h
··· 72 72 int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len, 73 73 __u64 source, bool from_bpf); 74 74 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, 76 76 bool lock_already_taken); 77 77 struct module *owner; 78 78 const struct bus_type *bus_type; ··· 195 195 196 196 #ifdef CONFIG_HID_BPF 197 197 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); 199 199 int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, 200 200 unsigned char reportnum, __u8 *buf, 201 201 u32 size, enum hid_report_type rtype, ··· 211 211 #else /* CONFIG_HID_BPF */ 212 212 static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, 213 213 u8 *data, u32 *size, int interrupt, 214 - u64 source) { return data; } 214 + u64 source, bool from_bpf) { return data; } 215 215 static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, 216 216 unsigned char reportnum, u8 *buf, 217 217 u32 size, enum hid_report_type rtype,