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

Configure Feed

Select the types of activity you want to include in your feed.

HID: bpf: add HID-BPF hooks for hid_hw_output_report

Same story than hid_hw_raw_requests:

This allows to intercept and prevent or change the behavior of
hid_hw_output_report() from a bpf program.

The intent is to solve a couple of use case:
- firewalling a HID device: a firewall can monitor who opens the hidraw
nodes and then prevent or allow access to write operations on that
hidraw node.
- change the behavior of a device and emulate a new HID feature request

The hook is allowed to be run as sleepable so it can itself call
hid_hw_output_report(), which allows to "convert" one feature request into
another or even call the feature request on a different HID device on the
same physical device.

Link: https://patch.msgid.link/20240626-hid_hw_req_bpf-v2-7-cfd60fb6c79f@kernel.org
Acked-by: Jiri Kosina <jkosina@suse.com>
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>

+70 -9
+35 -4
drivers/hid/bpf/hid_bpf_dispatch.c
··· 113 113 } 114 114 EXPORT_SYMBOL_GPL(dispatch_hid_bpf_raw_requests); 115 115 116 + int dispatch_hid_bpf_output_report(struct hid_device *hdev, 117 + __u8 *buf, u32 size, __u64 source, 118 + bool from_bpf) 119 + { 120 + struct hid_bpf_ctx_kern ctx_kern = { 121 + .ctx = { 122 + .hid = hdev, 123 + .allocated_size = size, 124 + .size = size, 125 + }, 126 + .data = buf, 127 + .from_bpf = from_bpf, 128 + }; 129 + struct hid_bpf_ops *e; 130 + int ret, idx; 131 + 132 + idx = srcu_read_lock(&hdev->bpf.srcu); 133 + list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list, 134 + srcu_read_lock_held(&hdev->bpf.srcu)) { 135 + if (!e->hid_hw_output_report) 136 + continue; 137 + 138 + ret = e->hid_hw_output_report(&ctx_kern.ctx, source); 139 + if (ret) 140 + goto out; 141 + } 142 + ret = 0; 143 + 144 + out: 145 + srcu_read_unlock(&hdev->bpf.srcu, idx); 146 + return ret; 147 + } 148 + EXPORT_SYMBOL_GPL(dispatch_hid_bpf_output_report); 149 + 116 150 u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size) 117 151 { 118 152 int ret; ··· 477 443 if (!dma_data) 478 444 return -ENOMEM; 479 445 480 - ret = hid_ops->hid_hw_output_report(hdev, 481 - dma_data, 482 - size, 483 - (__u64)ctx); 446 + ret = hid_ops->hid_hw_output_report(hdev, dma_data, size, (__u64)ctx, true); 484 447 485 448 kfree(dma_data); 486 449 return ret;
+1
drivers/hid/bpf/hid_bpf_struct_ops.c
··· 45 45 switch (moff) { 46 46 case offsetof(struct hid_bpf_ops, hid_rdesc_fixup): 47 47 case offsetof(struct hid_bpf_ops, hid_hw_request): 48 + case offsetof(struct hid_bpf_ops, hid_hw_output_report): 48 49 break; 49 50 default: 50 51 if (prog->sleepable)
+8 -2
drivers/hid/hid-core.c
··· 2445 2445 } 2446 2446 EXPORT_SYMBOL_GPL(hid_hw_raw_request); 2447 2447 2448 - int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source) 2448 + int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source, 2449 + bool from_bpf) 2449 2450 { 2450 2451 unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; 2452 + int ret; 2451 2453 2452 2454 if (hdev->ll_driver->max_buffer_size) 2453 2455 max_buffer_size = hdev->ll_driver->max_buffer_size; 2454 2456 2455 2457 if (len < 1 || len > max_buffer_size || !buf) 2456 2458 return -EINVAL; 2459 + 2460 + ret = dispatch_hid_bpf_output_report(hdev, buf, len, source, from_bpf); 2461 + if (ret) 2462 + return ret; 2457 2463 2458 2464 if (hdev->ll_driver->output_report) 2459 2465 return hdev->ll_driver->output_report(hdev, buf, len); ··· 2478 2472 */ 2479 2473 int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len) 2480 2474 { 2481 - return __hid_hw_output_report(hdev, buf, len, 0); 2475 + return __hid_hw_output_report(hdev, buf, len, 0, false); 2482 2476 } 2483 2477 EXPORT_SYMBOL_GPL(hid_hw_output_report); 2484 2478
+1 -1
drivers/hid/hidraw.c
··· 140 140 141 141 if ((report_type == HID_OUTPUT_REPORT) && 142 142 !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) { 143 - ret = __hid_hw_output_report(dev, buf, count, (__u64)file); 143 + ret = __hid_hw_output_report(dev, buf, count, (__u64)file, false); 144 144 /* 145 145 * compatibility with old implementation of USB-HID and I2C-HID: 146 146 * if the device does not support receiving output reports,
+2 -1
include/linux/hid.h
··· 1130 1130 size_t len, enum hid_report_type rtype, 1131 1131 enum hid_class_request reqtype, 1132 1132 __u64 source, bool from_bpf); 1133 - int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source); 1133 + int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source, 1134 + bool from_bpf); 1134 1135 int hid_hw_raw_request(struct hid_device *hdev, 1135 1136 unsigned char reportnum, __u8 *buf, 1136 1137 size_t len, enum hid_report_type rtype,
+23 -1
include/linux/hid_bpf.h
··· 70 70 enum hid_class_request reqtype, 71 71 __u64 source, bool from_bpf); 72 72 int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len, 73 - __u64 source); 73 + __u64 source, bool from_bpf); 74 74 int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type, 75 75 u8 *data, u32 size, int interrupt, u64 source); 76 76 struct module *owner; ··· 154 154 enum hid_report_type rtype, enum hid_class_request reqtype, 155 155 __u64 source); 156 156 157 + /** 158 + * @hid_hw_output_report: called whenever a hid_hw_output_report() call is emitted 159 + * on the HID device 160 + * 161 + * It has the following arguments: 162 + * 163 + * ``ctx``: The HID-BPF context as &struct hid_bpf_ctx 164 + * ``source``: a u64 referring to a uniq but identifiable source. If %0, the 165 + * kernel itself emitted that call. For hidraw, ``source`` is set 166 + * to the associated ``struct file *``. 167 + * 168 + * Return: %0 to keep processing the request by hid-core; any other value 169 + * stops hid-core from processing that event. A positive value should be 170 + * returned with the number of bytes written to the device; a negative error 171 + * code interrupts the processing of this call. 172 + */ 173 + int (*hid_hw_output_report)(struct hid_bpf_ctx *ctx, __u64 source); 174 + 157 175 158 176 /* private: do not show up in the docs */ 159 177 struct hid_device *hdev; ··· 200 182 u32 size, enum hid_report_type rtype, 201 183 enum hid_class_request reqtype, 202 184 __u64 source, bool from_bpf); 185 + int dispatch_hid_bpf_output_report(struct hid_device *hdev, __u8 *buf, u32 size, 186 + __u64 source, bool from_bpf); 203 187 int hid_bpf_connect_device(struct hid_device *hdev); 204 188 void hid_bpf_disconnect_device(struct hid_device *hdev); 205 189 void hid_bpf_destroy_device(struct hid_device *hid); ··· 216 196 u32 size, enum hid_report_type rtype, 217 197 enum hid_class_request reqtype, 218 198 u64 source, bool from_bpf) { return 0; } 199 + static inline int dispatch_hid_bpf_output_report(struct hid_device *hdev, __u8 *buf, u32 size, 200 + __u64 source, bool from_bpf) { return 0; } 219 201 static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; } 220 202 static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {} 221 203 static inline void hid_bpf_destroy_device(struct hid_device *hid) {}