brintos

brintos / linux-shallow public Read only

0
0
Text · 13.6 KiB · 5ecc845 Raw
601 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (c) 2022 Red hat */3#include "hid_bpf_helpers.h"4 5char _license[] SEC("license") = "GPL";6 7struct attach_prog_args {8	int prog_fd;9	unsigned int hid;10	int retval;11	int insert_head;12};13 14__u64 callback_check = 52;15__u64 callback2_check = 52;16 17SEC("?struct_ops/hid_device_event")18int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)19{20	__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);21 22	if (!rw_data)23		return 0; /* EPERM check */24 25	callback_check = rw_data[1];26 27	rw_data[2] = rw_data[1] + 5;28 29	return hid_ctx->size;30}31 32SEC(".struct_ops.link")33struct hid_bpf_ops first_event = {34	.hid_device_event = (void *)hid_first_event,35	.hid_id = 2,36};37 38int __hid_subprog_first_event(struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)39{40	__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);41 42	if (!rw_data)43		return 0; /* EPERM check */44 45	rw_data[2] = rw_data[1] + 5;46 47	return hid_ctx->size;48}49 50SEC("?struct_ops/hid_device_event")51int BPF_PROG(hid_subprog_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)52{53	return __hid_subprog_first_event(hid_ctx, type);54}55 56SEC(".struct_ops.link")57struct hid_bpf_ops subprog_first_event = {58	.hid_device_event = (void *)hid_subprog_first_event,59	.hid_id = 2,60};61 62SEC("?struct_ops/hid_device_event")63int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)64{65	__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);66 67	if (!rw_data)68		return 0; /* EPERM check */69 70	rw_data[3] = rw_data[2] + 5;71 72	return hid_ctx->size;73}74 75SEC(".struct_ops.link")76struct hid_bpf_ops second_event = {77	.hid_device_event = (void *)hid_second_event,78};79 80SEC("?struct_ops/hid_device_event")81int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)82{83	__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);84 85	if (!rw_data)86		return 0; /* EPERM check */87 88	rw_data[0] = 2;89 90	return 9;91}92 93SEC(".struct_ops.link")94struct hid_bpf_ops change_report_id = {95	.hid_device_event = (void *)hid_change_report_id,96};97 98struct hid_hw_request_syscall_args {99	/* data needs to come at offset 0 so we can use it in calls */100	__u8 data[10];101	unsigned int hid;102	int retval;103	size_t size;104	enum hid_report_type type;105	__u8 request_type;106};107 108SEC("syscall")109int hid_user_raw_request(struct hid_hw_request_syscall_args *args)110{111	struct hid_bpf_ctx *ctx;112	const size_t size = args->size;113	int i, ret = 0;114 115	if (size > sizeof(args->data))116		return -7; /* -E2BIG */117 118	ctx = hid_bpf_allocate_context(args->hid);119	if (!ctx)120		return -1; /* EPERM check */121 122	ret = hid_bpf_hw_request(ctx,123				 args->data,124				 size,125				 args->type,126				 args->request_type);127	args->retval = ret;128 129	hid_bpf_release_context(ctx);130 131	return 0;132}133 134SEC("syscall")135int hid_user_output_report(struct hid_hw_request_syscall_args *args)136{137	struct hid_bpf_ctx *ctx;138	const size_t size = args->size;139	int i, ret = 0;140 141	if (size > sizeof(args->data))142		return -7; /* -E2BIG */143 144	ctx = hid_bpf_allocate_context(args->hid);145	if (!ctx)146		return -1; /* EPERM check */147 148	ret = hid_bpf_hw_output_report(ctx,149				       args->data,150				       size);151	args->retval = ret;152 153	hid_bpf_release_context(ctx);154 155	return 0;156}157 158SEC("syscall")159int hid_user_input_report(struct hid_hw_request_syscall_args *args)160{161	struct hid_bpf_ctx *ctx;162	const size_t size = args->size;163	int i, ret = 0;164 165	if (size > sizeof(args->data))166		return -7; /* -E2BIG */167 168	ctx = hid_bpf_allocate_context(args->hid);169	if (!ctx)170		return -1; /* EPERM check */171 172	ret = hid_bpf_input_report(ctx, HID_INPUT_REPORT, args->data, size);173	args->retval = ret;174 175	hid_bpf_release_context(ctx);176 177	return 0;178}179 180static const __u8 rdesc[] = {181	0x05, 0x01,				/* USAGE_PAGE (Generic Desktop) */182	0x09, 0x32,				/* USAGE (Z) */183	0x95, 0x01,				/* REPORT_COUNT (1) */184	0x81, 0x06,				/* INPUT (Data,Var,Rel) */185 186	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */187	0x19, 0x01,				/* USAGE_MINIMUM (1) */188	0x29, 0x03,				/* USAGE_MAXIMUM (3) */189	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */190	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */191	0x95, 0x03,				/* REPORT_COUNT (3) */192	0x75, 0x01,				/* REPORT_SIZE (1) */193	0x91, 0x02,				/* Output (Data,Var,Abs) */194	0x95, 0x01,				/* REPORT_COUNT (1) */195	0x75, 0x05,				/* REPORT_SIZE (5) */196	0x91, 0x01,				/* Output (Cnst,Var,Abs) */197 198	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */199	0x19, 0x06,				/* USAGE_MINIMUM (6) */200	0x29, 0x08,				/* USAGE_MAXIMUM (8) */201	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */202	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */203	0x95, 0x03,				/* REPORT_COUNT (3) */204	0x75, 0x01,				/* REPORT_SIZE (1) */205	0xb1, 0x02,				/* Feature (Data,Var,Abs) */206	0x95, 0x01,				/* REPORT_COUNT (1) */207	0x75, 0x05,				/* REPORT_SIZE (5) */208	0x91, 0x01,				/* Output (Cnst,Var,Abs) */209 210	0xc0,				/* END_COLLECTION */211	0xc0,			/* END_COLLECTION */212};213 214/*215 * the following program is marked as sleepable (struct_ops.s).216 * This is not strictly mandatory but is a nice test for217 * sleepable struct_ops218 */219SEC("?struct_ops.s/hid_rdesc_fixup")220int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx)221{222	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */);223 224	if (!data)225		return 0; /* EPERM check */226 227	callback2_check = data[4];228 229	/* insert rdesc at offset 73 */230	__builtin_memcpy(&data[73], rdesc, sizeof(rdesc));231 232	/* Change Usage Vendor globally */233	data[4] = 0x42;234 235	return sizeof(rdesc) + 73;236}237 238SEC(".struct_ops.link")239struct hid_bpf_ops rdesc_fixup = {240	.hid_rdesc_fixup = (void *)hid_rdesc_fixup,241};242 243SEC("?struct_ops/hid_device_event")244int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)245{246	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);247 248	if (!data)249		return 0; /* EPERM check */250 251	/* we need to be run first */252	if (data[2] || data[3])253		return -1;254 255	data[1] = 1;256 257	return 0;258}259 260SEC(".struct_ops.link")261struct hid_bpf_ops test_insert1 = {262	.hid_device_event = (void *)hid_test_insert1,263	.flags = BPF_F_BEFORE,264};265 266SEC("?struct_ops/hid_device_event")267int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)268{269	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);270 271	if (!data)272		return 0; /* EPERM check */273 274	/* after insert0 and before insert2 */275	if (!data[1] || data[3])276		return -1;277 278	data[2] = 2;279 280	return 0;281}282 283SEC(".struct_ops.link")284struct hid_bpf_ops test_insert2 = {285	.hid_device_event = (void *)hid_test_insert2,286};287 288SEC("?struct_ops/hid_device_event")289int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)290{291	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);292 293	if (!data)294		return 0; /* EPERM check */295 296	/* at the end */297	if (!data[1] || !data[2])298		return -1;299 300	data[3] = 3;301 302	return 0;303}304 305SEC(".struct_ops.link")306struct hid_bpf_ops test_insert3 = {307	.hid_device_event = (void *)hid_test_insert3,308};309 310SEC("?struct_ops/hid_hw_request")311int BPF_PROG(hid_test_filter_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,312	     enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)313{314	return -20;315}316 317SEC(".struct_ops.link")318struct hid_bpf_ops test_filter_raw_request = {319	.hid_hw_request = (void *)hid_test_filter_raw_request,320};321 322static struct file *current_file;323 324SEC("fentry/hidraw_open")325int BPF_PROG(hidraw_open, struct inode *inode, struct file *file)326{327	current_file = file;328	return 0;329}330 331SEC("?struct_ops.s/hid_hw_request")332int BPF_PROG(hid_test_hidraw_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,333	     enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)334{335	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);336	int ret;337 338	if (!data)339		return 0; /* EPERM check */340 341	/* check if the incoming request comes from our hidraw operation */342	if (source == (__u64)current_file) {343		data[0] = reportnum;344 345		ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);346		if (ret != 2)347			return -1;348		data[0] = reportnum + 1;349		data[1] = reportnum + 2;350		data[2] = reportnum + 3;351		return 3;352	}353 354	return 0;355}356 357SEC(".struct_ops.link")358struct hid_bpf_ops test_hidraw_raw_request = {359	.hid_hw_request = (void *)hid_test_hidraw_raw_request,360};361 362SEC("?struct_ops.s/hid_hw_request")363int BPF_PROG(hid_test_infinite_loop_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,364	     enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)365{366	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);367	int ret;368 369	if (!data)370		return 0; /* EPERM check */371 372	/* always forward the request as-is to the device, hid-bpf should prevent373	 * infinite loops.374	 */375	data[0] = reportnum;376 377	ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);378	if (ret == 2)379		return 3;380 381	return 0;382}383 384SEC(".struct_ops.link")385struct hid_bpf_ops test_infinite_loop_raw_request = {386	.hid_hw_request = (void *)hid_test_infinite_loop_raw_request,387};388 389SEC("?struct_ops/hid_hw_output_report")390int BPF_PROG(hid_test_filter_output_report, struct hid_bpf_ctx *hctx, unsigned char reportnum,391	     enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)392{393	return -25;394}395 396SEC(".struct_ops.link")397struct hid_bpf_ops test_filter_output_report = {398	.hid_hw_output_report = (void *)hid_test_filter_output_report,399};400 401SEC("?struct_ops.s/hid_hw_output_report")402int BPF_PROG(hid_test_hidraw_output_report, struct hid_bpf_ctx *hctx, __u64 source)403{404	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);405	int ret;406 407	if (!data)408		return 0; /* EPERM check */409 410	/* check if the incoming request comes from our hidraw operation */411	if (source == (__u64)current_file)412		return hid_bpf_hw_output_report(hctx, data, 2);413 414	return 0;415}416 417SEC(".struct_ops.link")418struct hid_bpf_ops test_hidraw_output_report = {419	.hid_hw_output_report = (void *)hid_test_hidraw_output_report,420};421 422SEC("?struct_ops.s/hid_hw_output_report")423int BPF_PROG(hid_test_infinite_loop_output_report, struct hid_bpf_ctx *hctx, __u64 source)424{425	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);426	int ret;427 428	if (!data)429		return 0; /* EPERM check */430 431	/* always forward the request as-is to the device, hid-bpf should prevent432	 * infinite loops.433	 */434 435	ret = hid_bpf_hw_output_report(hctx, data, 2);436	if (ret == 2)437		return 2;438 439	return 0;440}441 442SEC(".struct_ops.link")443struct hid_bpf_ops test_infinite_loop_output_report = {444	.hid_hw_output_report = (void *)hid_test_infinite_loop_output_report,445};446 447struct elem {448	struct bpf_wq work;449};450 451struct {452	__uint(type, BPF_MAP_TYPE_HASH);453	__uint(max_entries, 1);454	__type(key, int);455	__type(value, struct elem);456} hmap SEC(".maps");457 458static int wq_cb_sleepable(void *map, int *key, void *work)459{460	__u8 buf[9] = {2, 3, 4, 5, 6, 7, 8, 9, 10};461	struct hid_bpf_ctx *hid_ctx;462 463	hid_ctx = hid_bpf_allocate_context(*key);464	if (!hid_ctx)465		return 0; /* EPERM check */466 467	hid_bpf_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));468 469	hid_bpf_release_context(hid_ctx);470 471	return 0;472}473 474static int test_inject_input_report_callback(int *key)475{476	struct elem init = {}, *val;477	struct bpf_wq *wq;478 479	if (bpf_map_update_elem(&hmap, key, &init, 0))480		return -1;481 482	val = bpf_map_lookup_elem(&hmap, key);483	if (!val)484		return -2;485 486	wq = &val->work;487	if (bpf_wq_init(wq, &hmap, 0) != 0)488		return -3;489 490	if (bpf_wq_set_callback(wq, wq_cb_sleepable, 0))491		return -4;492 493	if (bpf_wq_start(wq, 0))494		return -5;495 496	return 0;497}498 499SEC("?struct_ops/hid_device_event")500int BPF_PROG(hid_test_multiply_events_wq, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)501{502	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);503	int hid = hid_ctx->hid->id;504	int ret;505 506	if (!data)507		return 0; /* EPERM check */508 509	if (data[0] != 1)510		return 0;511 512	ret = test_inject_input_report_callback(&hid);513	if (ret)514		return ret;515 516	data[1] += 5;517 518	return 0;519}520 521SEC(".struct_ops.link")522struct hid_bpf_ops test_multiply_events_wq = {523	.hid_device_event = (void *)hid_test_multiply_events_wq,524};525 526SEC("?struct_ops/hid_device_event")527int BPF_PROG(hid_test_multiply_events, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)528{529	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);530	__u8 buf[9];531	int ret;532 533	if (!data)534		return 0; /* EPERM check */535 536	if (data[0] != 1)537		return 0;538 539	/*540	 * we have to use an intermediate buffer as hid_bpf_input_report541	 * will memset data to \0542	 */543	__builtin_memcpy(buf, data, sizeof(buf));544 545	buf[0] = 2;546	buf[1] += 5;547	ret = hid_bpf_try_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));548	if (ret < 0)549		return ret;550 551	/*552	 * In real world we should reset the original buffer as data might be garbage now,553	 * but it actually now has the content of 'buf'554	 */555	data[1] += 5;556 557	return 9;558}559 560SEC(".struct_ops.link")561struct hid_bpf_ops test_multiply_events = {562	.hid_device_event = (void *)hid_test_multiply_events,563};564 565SEC("?struct_ops/hid_device_event")566int BPF_PROG(hid_test_infinite_loop_input_report, struct hid_bpf_ctx *hctx,567	     enum hid_report_type report_type, __u64 source)568{569	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 6 /* size */);570	__u8 buf[6];571 572	if (!data)573		return 0; /* EPERM check */574 575	/*576	 * we have to use an intermediate buffer as hid_bpf_input_report577	 * will memset data to \0578	 */579	__builtin_memcpy(buf, data, sizeof(buf));580 581	/* always forward the request as-is to the device, hid-bpf should prevent582	 * infinite loops.583	 * the return value is ignored so the event is passing to userspace.584	 */585 586	hid_bpf_try_input_report(hctx, report_type, buf, sizeof(buf));587 588	/* each time we process the event, we increment by one data[1]:589	 * after each successful call to hid_bpf_try_input_report, buf590	 * has been memcopied into data by the kernel.591	 */592	data[1] += 1;593 594	return 0;595}596 597SEC(".struct_ops.link")598struct hid_bpf_ops test_infinite_loop_input_report = {599	.hid_device_event = (void *)hid_test_infinite_loop_input_report,600};601