brintos

brintos / linux-shallow public Read only

0
0
Text · 16.3 KiB · 8420c22 Raw
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