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