From: Linus Torvalds Date: Thu, 18 Jul 2024 00:28:31 +0000 (-0700) Subject: Merge tag 'for-linus-2024071601' of git://git.kernel.org/pub/scm/linux/kernel/git... X-Git-Tag: v6.11-rc1~148 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6e504d2c61244a01226c5100c835e44fb9b85ca8;p=thirdparty%2Fkernel%2Flinux.git Merge tag 'for-linus-2024071601' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid Pull HID updates from Benjamin Tissoires: - rewrite of the HID-BPF internal implementation to use bpf struct_ops instead of a tracing endpoint (Benjamin Tissoires) - add two new HID-BPF hooks to be able to intercept userspace calls targeting a HID device and filtering them (Benjamin Tissoires) - add support for various new devices through HID-BPF filters (Benjamin Tissoires) - add support for the magic keyboard backlight (Orlando Chamberlain) - add the missing MODULE_DESCRIPTION() macros in HID drivers (Jeff Johnson) - use of kvzalloc in case memory gets too fragmented (Hailong Liu) - retrieve the device firmware node in the child HID device (Danny Kaehn) - some hid-uclogic improvements (José Expósito) - some more typos, trivial fixes, kernel doctext and unused functions cleanups * tag 'for-linus-2024071601' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (60 commits) HID: hid-steam: Fix typo in goto label HID: mcp2221: Remove unnecessary semicolon HID: Fix spelling mistakes "Kensigton" -> "Kensington" HID: add more missing MODULE_DESCRIPTION() macros HID: samples: fix the 2 struct_ops definitions HID: fix for amples in for-6.11/bpf HID: apple: Add support for magic keyboard backlight on T2 Macs HID: bpf: Thrustmaster TCA Yoke Boeing joystick fix HID: bpf: Add Huion Dial 2 bpf fixup HID: bpf: Add support for the XP-PEN Deco Mini 4 HID: bpf: move the BIT() macro to hid_bpf_helpers.h HID: bpf: add a driver for the Huion Inspiroy 2S (H641P) HID: bpf: Add a HID report composition helper macros HID: bpf: doc fixes for hid_hw_request() hooks HID: bpf: doc fixes for hid_hw_request() hooks HID: bpf: fix gcc warning and unify __u64 into u64 selftests/hid: ensure CKI can compile our new tests on old kernels selftests/hid: add an infinite loop test for hid_bpf_try_input_report selftests/hid: add another test for injecting an event from an event hook HID: bpf: allow hid_device_event hooks to inject input reports on self ... --- 6e504d2c61244a01226c5100c835e44fb9b85ca8 diff --cc drivers/hid/bpf/hid_bpf_struct_ops.c index 0000000000000,b03b44ad3458f..f59cce6e437f7 mode 000000,100644..100644 --- a/drivers/hid/bpf/hid_bpf_struct_ops.c +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@@ -1,0 -1,307 +1,307 @@@ + // SPDX-License-Identifier: GPL-2.0-only + + /* + * HID-BPF support for Linux + * + * Copyright (c) 2024 Benjamin Tissoires + */ + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include "hid_bpf_dispatch.h" + + static struct btf *hid_bpf_ops_btf; + + static int hid_bpf_ops_init(struct btf *btf) + { + hid_bpf_ops_btf = btf; + return 0; + } + + static bool hid_bpf_ops_is_valid_access(int off, int size, + enum bpf_access_type type, + const struct bpf_prog *prog, + struct bpf_insn_access_aux *info) + { + return bpf_tracing_btf_ctx_access(off, size, type, prog, info); + } + + static int hid_bpf_ops_check_member(const struct btf_type *t, + const struct btf_member *member, + const struct bpf_prog *prog) + { + u32 moff = __btf_member_bit_offset(t, member) / 8; + + switch (moff) { + case offsetof(struct hid_bpf_ops, hid_rdesc_fixup): + case offsetof(struct hid_bpf_ops, hid_hw_request): + case offsetof(struct hid_bpf_ops, hid_hw_output_report): + break; + default: + if (prog->sleepable) + return -EINVAL; + } + + return 0; + } + + struct hid_bpf_offset_write_range { + const char *struct_name; + u32 struct_length; + u32 start; + u32 end; + }; + + static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log, + const struct bpf_reg_state *reg, + int off, int size) + { + #define WRITE_RANGE(_name, _field, _is_string) \ + { \ + .struct_name = #_name, \ + .struct_length = sizeof(struct _name), \ + .start = offsetof(struct _name, _field), \ + .end = offsetofend(struct _name, _field) - !!(_is_string), \ + } + + const struct hid_bpf_offset_write_range write_ranges[] = { + WRITE_RANGE(hid_bpf_ctx, retval, false), + WRITE_RANGE(hid_device, name, true), + WRITE_RANGE(hid_device, uniq, true), + WRITE_RANGE(hid_device, phys, true), + }; + #undef WRITE_RANGE + const struct btf_type *state = NULL; + const struct btf_type *t; + const char *cur = NULL; + int i; + + t = btf_type_by_id(reg->btf, reg->btf_id); + + for (i = 0; i < ARRAY_SIZE(write_ranges); i++) { + const struct hid_bpf_offset_write_range *write_range = &write_ranges[i]; + s32 type_id; + + /* we already found a writeable struct, but there is a + * new one, let's break the loop. + */ + if (t == state && write_range->struct_name != cur) + break; + + /* new struct to look for */ + if (write_range->struct_name != cur) { + type_id = btf_find_by_name_kind(reg->btf, write_range->struct_name, + BTF_KIND_STRUCT); + if (type_id < 0) + return -EINVAL; + + state = btf_type_by_id(reg->btf, type_id); + } + + /* this is not the struct we are looking for */ + if (t != state) { + cur = write_range->struct_name; + continue; + } + + /* first time we see this struct, check for out of bounds */ + if (cur != write_range->struct_name && + off + size > write_range->struct_length) { + bpf_log(log, "write access for struct %s at off %d with size %d\n", + write_range->struct_name, off, size); + return -EACCES; + } + + /* now check if we are in our boundaries */ + if (off >= write_range->start && off + size <= write_range->end) + return NOT_INIT; + + cur = write_range->struct_name; + } + + + if (t != state) + bpf_log(log, "write access to this struct is not supported\n"); + else + bpf_log(log, + "write access at off %d with size %d on read-only part of %s\n", + off, size, cur); + + return -EACCES; + } + + static const struct bpf_verifier_ops hid_bpf_verifier_ops = { + .get_func_proto = bpf_base_func_proto, + .is_valid_access = hid_bpf_ops_is_valid_access, + .btf_struct_access = hid_bpf_ops_btf_struct_access, + }; + + static int hid_bpf_ops_init_member(const struct btf_type *t, + const struct btf_member *member, + void *kdata, const void *udata) + { + const struct hid_bpf_ops *uhid_bpf_ops; + struct hid_bpf_ops *khid_bpf_ops; + u32 moff; + + uhid_bpf_ops = (const struct hid_bpf_ops *)udata; + khid_bpf_ops = (struct hid_bpf_ops *)kdata; + + moff = __btf_member_bit_offset(t, member) / 8; + + switch (moff) { + case offsetof(struct hid_bpf_ops, hid_id): + /* For hid_id and flags fields, this function has to copy it + * and return 1 to indicate that the data has been handled by + * the struct_ops type, or the verifier will reject the map if + * the value of those fields is not zero. + */ + khid_bpf_ops->hid_id = uhid_bpf_ops->hid_id; + return 1; + case offsetof(struct hid_bpf_ops, flags): + if (uhid_bpf_ops->flags & ~BPF_F_BEFORE) + return -EINVAL; + khid_bpf_ops->flags = uhid_bpf_ops->flags; + return 1; + } + return 0; + } + -static int hid_bpf_reg(void *kdata) ++static int hid_bpf_reg(void *kdata, struct bpf_link *link) + { + struct hid_bpf_ops *ops = kdata; + struct hid_device *hdev; + int count, err = 0; + + hdev = hid_get_device(ops->hid_id); + if (IS_ERR(hdev)) + return PTR_ERR(hdev); + + ops->hdev = hdev; + + mutex_lock(&hdev->bpf.prog_list_lock); + + count = list_count_nodes(&hdev->bpf.prog_list); + if (count >= HID_BPF_MAX_PROGS_PER_DEV) { + err = -E2BIG; + goto out_unlock; + } + + if (ops->hid_rdesc_fixup) { + if (hdev->bpf.rdesc_ops) { + err = -EINVAL; + goto out_unlock; + } + + hdev->bpf.rdesc_ops = ops; + } + + if (ops->hid_device_event) { + err = hid_bpf_allocate_event_data(hdev); + if (err) + goto out_unlock; + } + + if (ops->flags & BPF_F_BEFORE) + list_add_rcu(&ops->list, &hdev->bpf.prog_list); + else + list_add_tail_rcu(&ops->list, &hdev->bpf.prog_list); + synchronize_srcu(&hdev->bpf.srcu); + + out_unlock: + mutex_unlock(&hdev->bpf.prog_list_lock); + + if (err) { + if (hdev->bpf.rdesc_ops == ops) + hdev->bpf.rdesc_ops = NULL; + hid_put_device(hdev); + } else if (ops->hid_rdesc_fixup) { + hid_bpf_reconnect(hdev); + } + + return err; + } + -static void hid_bpf_unreg(void *kdata) ++static void hid_bpf_unreg(void *kdata, struct bpf_link *link) + { + struct hid_bpf_ops *ops = kdata; + struct hid_device *hdev; + bool reconnect = false; + + hdev = ops->hdev; + + /* check if __hid_bpf_ops_destroy_device() has been called */ + if (!hdev) + return; + + mutex_lock(&hdev->bpf.prog_list_lock); + + list_del_rcu(&ops->list); + synchronize_srcu(&hdev->bpf.srcu); + + reconnect = hdev->bpf.rdesc_ops == ops; + if (reconnect) + hdev->bpf.rdesc_ops = NULL; + + mutex_unlock(&hdev->bpf.prog_list_lock); + + if (reconnect) + hid_bpf_reconnect(hdev); + + hid_put_device(hdev); + } + + static int __hid_bpf_device_event(struct hid_bpf_ctx *ctx, enum hid_report_type type, u64 source) + { + return 0; + } + + static int __hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx) + { + return 0; + } + + static struct hid_bpf_ops __bpf_hid_bpf_ops = { + .hid_device_event = __hid_bpf_device_event, + .hid_rdesc_fixup = __hid_bpf_rdesc_fixup, + }; + + static struct bpf_struct_ops bpf_hid_bpf_ops = { + .verifier_ops = &hid_bpf_verifier_ops, + .init = hid_bpf_ops_init, + .check_member = hid_bpf_ops_check_member, + .init_member = hid_bpf_ops_init_member, + .reg = hid_bpf_reg, + .unreg = hid_bpf_unreg, + .name = "hid_bpf_ops", + .cfi_stubs = &__bpf_hid_bpf_ops, + .owner = THIS_MODULE, + }; + + void __hid_bpf_ops_destroy_device(struct hid_device *hdev) + { + struct hid_bpf_ops *e; + + rcu_read_lock(); + list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) { + hid_put_device(hdev); + e->hdev = NULL; + } + rcu_read_unlock(); + } + + static int __init hid_bpf_struct_ops_init(void) + { + return register_bpf_struct_ops(&bpf_hid_bpf_ops, hid_bpf_ops); + } + late_initcall(hid_bpf_struct_ops_init);