File Index
Files and directories
Sign in
FILE

hid_bpf_dispatch.c

File preview with syntax highlighting.

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