679 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 3/*4 * HID-BPF support for Linux5 *6 * Copyright (c) 2022-2024 Benjamin Tissoires7 */8 9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt10#include <linux/bitops.h>11#include <linux/btf.h>12#include <linux/btf_ids.h>13#include <linux/filter.h>14#include <linux/hid.h>15#include <linux/hid_bpf.h>16#include <linux/init.h>17#include <linux/kfifo.h>18#include <linux/minmax.h>19#include <linux/module.h>20#include "hid_bpf_dispatch.h"21 22struct hid_ops *hid_ops;23EXPORT_SYMBOL(hid_ops);24 25u8 *26dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data,27 u32 *size, int interrupt, u64 source, bool from_bpf)28{29 struct hid_bpf_ctx_kern ctx_kern = {30 .ctx = {31 .hid = hdev,32 .allocated_size = hdev->bpf.allocated_data,33 .size = *size,34 },35 .data = hdev->bpf.device_data,36 .from_bpf = from_bpf,37 };38 struct hid_bpf_ops *e;39 int ret;40 41 if (type >= HID_REPORT_TYPES)42 return ERR_PTR(-EINVAL);43 44 /* no program has been attached yet */45 if (!hdev->bpf.device_data)46 return data;47 48 memset(ctx_kern.data, 0, hdev->bpf.allocated_data);49 memcpy(ctx_kern.data, data, *size);50 51 rcu_read_lock();52 list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {53 if (e->hid_device_event) {54 ret = e->hid_device_event(&ctx_kern.ctx, type, source);55 if (ret < 0) {56 rcu_read_unlock();57 return ERR_PTR(ret);58 }59 60 if (ret)61 ctx_kern.ctx.size = ret;62 }63 }64 rcu_read_unlock();65 66 ret = ctx_kern.ctx.size;67 if (ret) {68 if (ret > ctx_kern.ctx.allocated_size)69 return ERR_PTR(-EINVAL);70 71 *size = ret;72 }73 74 return ctx_kern.data;75}76EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event);77 78int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,79 unsigned char reportnum, u8 *buf,80 u32 size, enum hid_report_type rtype,81 enum hid_class_request reqtype,82 u64 source, bool from_bpf)83{84 struct hid_bpf_ctx_kern ctx_kern = {85 .ctx = {86 .hid = hdev,87 .allocated_size = size,88 .size = size,89 },90 .data = buf,91 .from_bpf = from_bpf,92 };93 struct hid_bpf_ops *e;94 int ret, idx;95 96 if (rtype >= HID_REPORT_TYPES)97 return -EINVAL;98 99 idx = srcu_read_lock(&hdev->bpf.srcu);100 list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list,101 srcu_read_lock_held(&hdev->bpf.srcu)) {102 if (!e->hid_hw_request)103 continue;104 105 ret = e->hid_hw_request(&ctx_kern.ctx, reportnum, rtype, reqtype, source);106 if (ret)107 goto out;108 }109 ret = 0;110 111out:112 srcu_read_unlock(&hdev->bpf.srcu, idx);113 return ret;114}115EXPORT_SYMBOL_GPL(dispatch_hid_bpf_raw_requests);116 117int dispatch_hid_bpf_output_report(struct hid_device *hdev,118 __u8 *buf, u32 size, u64 source,119 bool from_bpf)120{121 struct hid_bpf_ctx_kern ctx_kern = {122 .ctx = {123 .hid = hdev,124 .allocated_size = size,125 .size = size,126 },127 .data = buf,128 .from_bpf = from_bpf,129 };130 struct hid_bpf_ops *e;131 int ret, idx;132 133 idx = srcu_read_lock(&hdev->bpf.srcu);134 list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list,135 srcu_read_lock_held(&hdev->bpf.srcu)) {136 if (!e->hid_hw_output_report)137 continue;138 139 ret = e->hid_hw_output_report(&ctx_kern.ctx, source);140 if (ret)141 goto out;142 }143 ret = 0;144 145out:146 srcu_read_unlock(&hdev->bpf.srcu, idx);147 return ret;148}149EXPORT_SYMBOL_GPL(dispatch_hid_bpf_output_report);150 151u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size)152{153 int ret;154 struct hid_bpf_ctx_kern ctx_kern = {155 .ctx = {156 .hid = hdev,157 .size = *size,158 .allocated_size = HID_MAX_DESCRIPTOR_SIZE,159 },160 };161 162 if (!hdev->bpf.rdesc_ops)163 goto ignore_bpf;164 165 ctx_kern.data = kzalloc(ctx_kern.ctx.allocated_size, GFP_KERNEL);166 if (!ctx_kern.data)167 goto ignore_bpf;168 169 memcpy(ctx_kern.data, rdesc, min_t(unsigned int, *size, HID_MAX_DESCRIPTOR_SIZE));170 171 ret = hdev->bpf.rdesc_ops->hid_rdesc_fixup(&ctx_kern.ctx);172 if (ret < 0)173 goto ignore_bpf;174 175 if (ret) {176 if (ret > ctx_kern.ctx.allocated_size)177 goto ignore_bpf;178 179 *size = ret;180 }181 182 return krealloc(ctx_kern.data, *size, GFP_KERNEL);183 184 ignore_bpf:185 kfree(ctx_kern.data);186 return kmemdup(rdesc, *size, GFP_KERNEL);187}188EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup);189 190static int device_match_id(struct device *dev, const void *id)191{192 struct hid_device *hdev = to_hid_device(dev);193 194 return hdev->id == *(int *)id;195}196 197struct hid_device *hid_get_device(unsigned int hid_id)198{199 struct device *dev;200 201 if (!hid_ops)202 return ERR_PTR(-EINVAL);203 204 dev = bus_find_device(hid_ops->bus_type, NULL, &hid_id, device_match_id);205 if (!dev)206 return ERR_PTR(-EINVAL);207 208 return to_hid_device(dev);209}210 211void hid_put_device(struct hid_device *hid)212{213 put_device(&hid->dev);214}215 216static int __hid_bpf_allocate_data(struct hid_device *hdev, u8 **data, u32 *size)217{218 u8 *alloc_data;219 unsigned int i, j, max_report_len = 0;220 size_t alloc_size = 0;221 222 /* compute the maximum report length for this device */223 for (i = 0; i < HID_REPORT_TYPES; i++) {224 struct hid_report_enum *report_enum = hdev->report_enum + i;225 226 for (j = 0; j < HID_MAX_IDS; j++) {227 struct hid_report *report = report_enum->report_id_hash[j];228 229 if (report)230 max_report_len = max(max_report_len, hid_report_len(report));231 }232 }233 234 /*235 * Give us a little bit of extra space and some predictability in the236 * buffer length we create. This way, we can tell users that they can237 * work on chunks of 64 bytes of memory without having the bpf verifier238 * scream at them.239 */240 alloc_size = DIV_ROUND_UP(max_report_len, 64) * 64;241 242 alloc_data = kzalloc(alloc_size, GFP_KERNEL);243 if (!alloc_data)244 return -ENOMEM;245 246 *data = alloc_data;247 *size = alloc_size;248 249 return 0;250}251 252int hid_bpf_allocate_event_data(struct hid_device *hdev)253{254 /* hdev->bpf.device_data is already allocated, abort */255 if (hdev->bpf.device_data)256 return 0;257 258 return __hid_bpf_allocate_data(hdev, &hdev->bpf.device_data, &hdev->bpf.allocated_data);259}260 261int hid_bpf_reconnect(struct hid_device *hdev)262{263 if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status))264 return device_reprobe(&hdev->dev);265 266 return 0;267}268 269/* Disables missing prototype warnings */270__bpf_kfunc_start_defs();271 272/**273 * hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx274 *275 * @ctx: The HID-BPF context276 * @offset: The offset within the memory277 * @rdwr_buf_size: the const size of the buffer278 *279 * @returns %NULL on error, an %__u8 memory pointer on success280 */281__bpf_kfunc __u8 *282hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size)283{284 struct hid_bpf_ctx_kern *ctx_kern;285 286 if (!ctx)287 return NULL;288 289 ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);290 291 if (rdwr_buf_size + offset > ctx->allocated_size)292 return NULL;293 294 return ctx_kern->data + offset;295}296 297/**298 * hid_bpf_allocate_context - Allocate a context to the given HID device299 *300 * @hid_id: the system unique identifier of the HID device301 *302 * @returns A pointer to &struct hid_bpf_ctx on success, %NULL on error.303 */304__bpf_kfunc struct hid_bpf_ctx *305hid_bpf_allocate_context(unsigned int hid_id)306{307 struct hid_device *hdev;308 struct hid_bpf_ctx_kern *ctx_kern = NULL;309 310 hdev = hid_get_device(hid_id);311 if (IS_ERR(hdev))312 return NULL;313 314 ctx_kern = kzalloc(sizeof(*ctx_kern), GFP_KERNEL);315 if (!ctx_kern) {316 hid_put_device(hdev);317 return NULL;318 }319 320 ctx_kern->ctx.hid = hdev;321 322 return &ctx_kern->ctx;323}324 325/**326 * hid_bpf_release_context - Release the previously allocated context @ctx327 *328 * @ctx: the HID-BPF context to release329 *330 */331__bpf_kfunc void332hid_bpf_release_context(struct hid_bpf_ctx *ctx)333{334 struct hid_bpf_ctx_kern *ctx_kern;335 struct hid_device *hid;336 337 ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);338 hid = (struct hid_device *)ctx_kern->ctx.hid; /* ignore const */339 340 kfree(ctx_kern);341 342 /* get_device() is called by bus_find_device() */343 hid_put_device(hid);344}345 346static int347__hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz,348 enum hid_report_type rtype)349{350 struct hid_report_enum *report_enum;351 struct hid_report *report;352 struct hid_device *hdev;353 u32 report_len;354 355 /* check arguments */356 if (!ctx || !hid_ops || !buf)357 return -EINVAL;358 359 switch (rtype) {360 case HID_INPUT_REPORT:361 case HID_OUTPUT_REPORT:362 case HID_FEATURE_REPORT:363 break;364 default:365 return -EINVAL;366 }367 368 if (*buf__sz < 1)369 return -EINVAL;370 371 hdev = (struct hid_device *)ctx->hid; /* discard const */372 373 report_enum = hdev->report_enum + rtype;374 report = hid_ops->hid_get_report(report_enum, buf);375 if (!report)376 return -EINVAL;377 378 report_len = hid_report_len(report);379 380 if (*buf__sz > report_len)381 *buf__sz = report_len;382 383 return 0;384}385 386/**387 * hid_bpf_hw_request - Communicate with a HID device388 *389 * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()390 * @buf: a %PTR_TO_MEM buffer391 * @buf__sz: the size of the data to transfer392 * @rtype: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT)393 * @reqtype: the type of the request (%HID_REQ_GET_REPORT, %HID_REQ_SET_REPORT, ...)394 *395 * @returns %0 on success, a negative error code otherwise.396 */397__bpf_kfunc int398hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,399 enum hid_report_type rtype, enum hid_class_request reqtype)400{401 struct hid_bpf_ctx_kern *ctx_kern;402 struct hid_device *hdev;403 size_t size = buf__sz;404 u8 *dma_data;405 int ret;406 407 ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);408 409 if (ctx_kern->from_bpf)410 return -EDEADLOCK;411 412 /* check arguments */413 ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype);414 if (ret)415 return ret;416 417 switch (reqtype) {418 case HID_REQ_GET_REPORT:419 case HID_REQ_GET_IDLE:420 case HID_REQ_GET_PROTOCOL:421 case HID_REQ_SET_REPORT:422 case HID_REQ_SET_IDLE:423 case HID_REQ_SET_PROTOCOL:424 break;425 default:426 return -EINVAL;427 }428 429 hdev = (struct hid_device *)ctx->hid; /* discard const */430 431 dma_data = kmemdup(buf, size, GFP_KERNEL);432 if (!dma_data)433 return -ENOMEM;434 435 ret = hid_ops->hid_hw_raw_request(hdev,436 dma_data[0],437 dma_data,438 size,439 rtype,440 reqtype,441 (u64)(long)ctx,442 true); /* prevent infinite recursions */443 444 if (ret > 0)445 memcpy(buf, dma_data, ret);446 447 kfree(dma_data);448 return ret;449}450 451/**452 * hid_bpf_hw_output_report - Send an output report to a HID device453 *454 * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()455 * @buf: a %PTR_TO_MEM buffer456 * @buf__sz: the size of the data to transfer457 *458 * Returns the number of bytes transferred on success, a negative error code otherwise.459 */460__bpf_kfunc int461hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz)462{463 struct hid_bpf_ctx_kern *ctx_kern;464 struct hid_device *hdev;465 size_t size = buf__sz;466 u8 *dma_data;467 int ret;468 469 ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);470 if (ctx_kern->from_bpf)471 return -EDEADLOCK;472 473 /* check arguments */474 ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT);475 if (ret)476 return ret;477 478 hdev = (struct hid_device *)ctx->hid; /* discard const */479 480 dma_data = kmemdup(buf, size, GFP_KERNEL);481 if (!dma_data)482 return -ENOMEM;483 484 ret = hid_ops->hid_hw_output_report(hdev, dma_data, size, (u64)(long)ctx, true);485 486 kfree(dma_data);487 return ret;488}489 490static int491__hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,492 size_t size, bool lock_already_taken)493{494 struct hid_bpf_ctx_kern *ctx_kern;495 int ret;496 497 ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);498 if (ctx_kern->from_bpf)499 return -EDEADLOCK;500 501 /* check arguments */502 ret = __hid_bpf_hw_check_params(ctx, buf, &size, type);503 if (ret)504 return ret;505 506 return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (u64)(long)ctx, true,507 lock_already_taken);508}509 510/**511 * hid_bpf_try_input_report - Inject a HID report in the kernel from a HID device512 *513 * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()514 * @type: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT)515 * @buf: a %PTR_TO_MEM buffer516 * @buf__sz: the size of the data to transfer517 *518 * Returns %0 on success, a negative error code otherwise. This function will immediately519 * fail if the device is not available, thus can be safely used in IRQ context.520 */521__bpf_kfunc int522hid_bpf_try_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,523 const size_t buf__sz)524{525 struct hid_bpf_ctx_kern *ctx_kern;526 bool from_hid_event_hook;527 528 ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);529 from_hid_event_hook = ctx_kern->data && ctx_kern->data == ctx->hid->bpf.device_data;530 531 return __hid_bpf_input_report(ctx, type, buf, buf__sz, from_hid_event_hook);532}533 534/**535 * hid_bpf_input_report - Inject a HID report in the kernel from a HID device536 *537 * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()538 * @type: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT)539 * @buf: a %PTR_TO_MEM buffer540 * @buf__sz: the size of the data to transfer541 *542 * Returns %0 on success, a negative error code otherwise. This function will wait for the543 * device to be available before injecting the event, thus needs to be called in sleepable544 * context.545 */546__bpf_kfunc int547hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,548 const size_t buf__sz)549{550 int ret;551 552 ret = down_interruptible(&ctx->hid->driver_input_lock);553 if (ret)554 return ret;555 556 /* check arguments */557 ret = __hid_bpf_input_report(ctx, type, buf, buf__sz, true /* lock_already_taken */);558 559 up(&ctx->hid->driver_input_lock);560 561 return ret;562}563__bpf_kfunc_end_defs();564 565/*566 * The following set contains all functions we agree BPF programs567 * can use.568 */569BTF_KFUNCS_START(hid_bpf_kfunc_ids)570BTF_ID_FLAGS(func, hid_bpf_get_data, KF_RET_NULL)571BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)572BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE | KF_SLEEPABLE)573BTF_ID_FLAGS(func, hid_bpf_hw_request, KF_SLEEPABLE)574BTF_ID_FLAGS(func, hid_bpf_hw_output_report, KF_SLEEPABLE)575BTF_ID_FLAGS(func, hid_bpf_input_report, KF_SLEEPABLE)576BTF_ID_FLAGS(func, hid_bpf_try_input_report)577BTF_KFUNCS_END(hid_bpf_kfunc_ids)578 579static const struct btf_kfunc_id_set hid_bpf_kfunc_set = {580 .owner = THIS_MODULE,581 .set = &hid_bpf_kfunc_ids,582};583 584/* for syscall HID-BPF */585BTF_KFUNCS_START(hid_bpf_syscall_kfunc_ids)586BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL)587BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE)588BTF_ID_FLAGS(func, hid_bpf_hw_request)589BTF_ID_FLAGS(func, hid_bpf_hw_output_report)590BTF_ID_FLAGS(func, hid_bpf_input_report)591BTF_KFUNCS_END(hid_bpf_syscall_kfunc_ids)592 593static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {594 .owner = THIS_MODULE,595 .set = &hid_bpf_syscall_kfunc_ids,596};597 598int hid_bpf_connect_device(struct hid_device *hdev)599{600 bool need_to_allocate = false;601 struct hid_bpf_ops *e;602 603 rcu_read_lock();604 list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {605 if (e->hid_device_event) {606 need_to_allocate = true;607 break;608 }609 }610 rcu_read_unlock();611 612 /* only allocate BPF data if there are programs attached */613 if (!need_to_allocate)614 return 0;615 616 return hid_bpf_allocate_event_data(hdev);617}618EXPORT_SYMBOL_GPL(hid_bpf_connect_device);619 620void hid_bpf_disconnect_device(struct hid_device *hdev)621{622 kfree(hdev->bpf.device_data);623 hdev->bpf.device_data = NULL;624 hdev->bpf.allocated_data = 0;625}626EXPORT_SYMBOL_GPL(hid_bpf_disconnect_device);627 628void hid_bpf_destroy_device(struct hid_device *hdev)629{630 if (!hdev)631 return;632 633 /* mark the device as destroyed in bpf so we don't reattach it */634 hdev->bpf.destroyed = true;635 636 __hid_bpf_ops_destroy_device(hdev);637 638 synchronize_srcu(&hdev->bpf.srcu);639 cleanup_srcu_struct(&hdev->bpf.srcu);640}641EXPORT_SYMBOL_GPL(hid_bpf_destroy_device);642 643int hid_bpf_device_init(struct hid_device *hdev)644{645 INIT_LIST_HEAD(&hdev->bpf.prog_list);646 mutex_init(&hdev->bpf.prog_list_lock);647 return init_srcu_struct(&hdev->bpf.srcu);648}649EXPORT_SYMBOL_GPL(hid_bpf_device_init);650 651static int __init hid_bpf_init(void)652{653 int err;654 655 /* Note: if we exit with an error any time here, we would entirely break HID, which656 * is probably not something we want. So we log an error and return success.657 *658 * This is not a big deal: nobody will be able to use the functionality.659 */660 661 err = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &hid_bpf_kfunc_set);662 if (err) {663 pr_warn("error while setting HID BPF tracing kfuncs: %d", err);664 return 0;665 }666 667 err = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &hid_bpf_syscall_kfunc_set);668 if (err) {669 pr_warn("error while setting HID BPF syscall kfuncs: %d", err);670 return 0;671 }672 673 return 0;674}675 676late_initcall(hid_bpf_init);677MODULE_AUTHOR("Benjamin Tissoires");678MODULE_LICENSE("GPL");679