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

HID: bpf: allow to change the report descriptor

Add a new tracepoint hid_bpf_rdesc_fixup() so we can trigger a
report descriptor fixup in the bpf world.

Whenever the program gets attached/detached, the device is reconnected
meaning that userspace will see it disappearing and reappearing with
the new report descriptor.

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Benjamin Tissoires and committed by
Jiri Kosina
ad190df1 4f7153cf

+97 -2
+79 -1
drivers/hid/bpf/hid_bpf_dispatch.c
··· 15 15 #include <linux/hid_bpf.h> 16 16 #include <linux/init.h> 17 17 #include <linux/kfifo.h> 18 + #include <linux/minmax.h> 18 19 #include <linux/module.h> 19 20 #include <linux/workqueue.h> 20 21 #include "hid_bpf_dispatch.h" ··· 85 84 return ctx_kern.data; 86 85 } 87 86 EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event); 87 + 88 + /** 89 + * hid_bpf_rdesc_fixup - Called when the probe function parses the report 90 + * descriptor of the HID device 91 + * 92 + * @ctx: The HID-BPF context 93 + * 94 + * @return 0 on success and keep processing; a positive value to change the 95 + * incoming size buffer; a negative error code to interrupt the processing 96 + * of this event 97 + * 98 + * Declare an %fmod_ret tracing bpf program to this function and attach this 99 + * program through hid_bpf_attach_prog() to have this helper called before any 100 + * parsing of the report descriptor by HID. 101 + */ 102 + /* never used by the kernel but declared so we can load and attach a tracepoint */ 103 + __weak noinline int hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx) 104 + { 105 + return 0; 106 + } 107 + ALLOW_ERROR_INJECTION(hid_bpf_rdesc_fixup, ERRNO); 108 + 109 + u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size) 110 + { 111 + int ret; 112 + struct hid_bpf_ctx_kern ctx_kern = { 113 + .ctx = { 114 + .hid = hdev, 115 + .size = *size, 116 + .allocated_size = HID_MAX_DESCRIPTOR_SIZE, 117 + }, 118 + }; 119 + 120 + ctx_kern.data = kzalloc(ctx_kern.ctx.allocated_size, GFP_KERNEL); 121 + if (!ctx_kern.data) 122 + goto ignore_bpf; 123 + 124 + memcpy(ctx_kern.data, rdesc, min_t(unsigned int, *size, HID_MAX_DESCRIPTOR_SIZE)); 125 + 126 + ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_RDESC_FIXUP, &ctx_kern); 127 + if (ret < 0) 128 + goto ignore_bpf; 129 + 130 + if (ret) { 131 + if (ret > ctx_kern.ctx.allocated_size) 132 + goto ignore_bpf; 133 + 134 + *size = ret; 135 + } 136 + 137 + rdesc = krealloc(ctx_kern.data, *size, GFP_KERNEL); 138 + 139 + return rdesc; 140 + 141 + ignore_bpf: 142 + kfree(ctx_kern.data); 143 + return kmemdup(rdesc, *size, GFP_KERNEL); 144 + } 145 + EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup); 88 146 89 147 /** 90 148 * hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx ··· 236 176 return __hid_bpf_allocate_data(hdev, &hdev->bpf.device_data, &hdev->bpf.allocated_data); 237 177 } 238 178 179 + int hid_bpf_reconnect(struct hid_device *hdev) 180 + { 181 + if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) 182 + return device_reprobe(&hdev->dev); 183 + 184 + return 0; 185 + } 186 + 239 187 /** 240 188 * hid_bpf_attach_prog - Attach the given @prog_fd to the given HID device 241 189 * ··· 285 217 return err; 286 218 } 287 219 288 - return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); 220 + err = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); 221 + if (err) 222 + return err; 223 + 224 + if (prog_type == HID_BPF_PROG_TYPE_RDESC_FIXUP) { 225 + err = hid_bpf_reconnect(hdev); 226 + if (err) 227 + return err; 228 + } 229 + 230 + return 0; 289 231 } 290 232 291 233 /**
+1
drivers/hid/bpf/hid_bpf_dispatch.h
··· 18 18 void __hid_bpf_destroy_device(struct hid_device *hdev); 19 19 int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type, 20 20 struct hid_bpf_ctx_kern *ctx_kern); 21 + int hid_bpf_reconnect(struct hid_device *hdev); 21 22 22 23 struct bpf_prog; 23 24
+7
drivers/hid/bpf/hid_bpf_jmp_table.c
··· 58 58 59 59 BTF_ID_LIST(hid_bpf_btf_ids) 60 60 BTF_ID(func, hid_bpf_device_event) /* HID_BPF_PROG_TYPE_DEVICE_EVENT */ 61 + BTF_ID(func, hid_bpf_rdesc_fixup) /* HID_BPF_PROG_TYPE_RDESC_FIXUP */ 61 62 62 63 static int hid_bpf_max_programs(enum hid_bpf_prog_type type) 63 64 { 64 65 switch (type) { 65 66 case HID_BPF_PROG_TYPE_DEVICE_EVENT: 66 67 return HID_BPF_MAX_PROGS_PER_DEV; 68 + case HID_BPF_PROG_TYPE_RDESC_FIXUP: 69 + return 1; 67 70 default: 68 71 return -EINVAL; 69 72 } ··· 236 233 if (next->hdev == hdev && next->type == type) 237 234 next->hdev = NULL; 238 235 } 236 + 237 + /* if type was rdesc fixup, reconnect device */ 238 + if (type == HID_BPF_PROG_TYPE_RDESC_FIXUP) 239 + hid_bpf_reconnect(hdev); 239 240 } 240 241 } 241 242
+2 -1
drivers/hid/hid-core.c
··· 1218 1218 return -ENODEV; 1219 1219 size = device->dev_rsize; 1220 1220 1221 - buf = kmemdup(start, size, GFP_KERNEL); 1221 + /* call_hid_bpf_rdesc_fixup() ensures we work on a copy of rdesc */ 1222 + buf = call_hid_bpf_rdesc_fixup(device, start, &size); 1222 1223 if (buf == NULL) 1223 1224 return -ENOMEM; 1224 1225
+8
include/linux/hid_bpf.h
··· 74 74 75 75 /* Following functions are tracepoints that BPF programs can attach to */ 76 76 int hid_bpf_device_event(struct hid_bpf_ctx *ctx); 77 + int hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx); 77 78 78 79 /* Following functions are kfunc that we export to BPF programs */ 79 80 /* available everywhere in HID-BPF */ ··· 101 100 enum hid_bpf_prog_type { 102 101 HID_BPF_PROG_TYPE_UNDEF = -1, 103 102 HID_BPF_PROG_TYPE_DEVICE_EVENT, /* an event is emitted from the device */ 103 + HID_BPF_PROG_TYPE_RDESC_FIXUP, 104 104 HID_BPF_PROG_TYPE_MAX, 105 105 }; 106 106 ··· 145 143 void hid_bpf_disconnect_device(struct hid_device *hdev); 146 144 void hid_bpf_destroy_device(struct hid_device *hid); 147 145 void hid_bpf_device_init(struct hid_device *hid); 146 + u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size); 148 147 #else /* CONFIG_HID_BPF */ 149 148 static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, 150 149 u8 *data, u32 *size, int interrupt) { return NULL; } ··· 153 150 static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {} 154 151 static inline void hid_bpf_destroy_device(struct hid_device *hid) {} 155 152 static inline void hid_bpf_device_init(struct hid_device *hid) {} 153 + static inline u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size) 154 + { 155 + return kmemdup(rdesc, *size, GFP_KERNEL); 156 + } 157 + 156 158 #endif /* CONFIG_HID_BPF */ 157 159 158 160 #endif /* __HID_BPF_H */