xref: /linux/tools/testing/selftests/hid/progs/hid.c (revision eebbef7c468a5cb58c4772849ee5066441166cf0)
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/hid_device_event")
259 int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
260 {
261 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
262 
263 	if (!data)
264 		return 0; /* EPERM check */
265 
266 	/* we need to be run first */
267 	if (data[2] || data[3])
268 		return -1;
269 
270 	data[1] = 1;
271 
272 	return 0;
273 }
274 
275 SEC(".struct_ops.link")
276 struct hid_bpf_ops test_insert1 = {
277 	.hid_device_event = (void *)hid_test_insert1,
278 	.flags = BPF_F_BEFORE,
279 };
280 
281 SEC("?struct_ops/hid_device_event")
282 int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
283 {
284 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
285 
286 	if (!data)
287 		return 0; /* EPERM check */
288 
289 	/* after insert0 and before insert2 */
290 	if (!data[1] || data[3])
291 		return -1;
292 
293 	data[2] = 2;
294 
295 	return 0;
296 }
297 
298 SEC(".struct_ops.link")
299 struct hid_bpf_ops test_insert2 = {
300 	.hid_device_event = (void *)hid_test_insert2,
301 };
302 
303 SEC("?struct_ops/hid_device_event")
304 int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
305 {
306 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
307 
308 	if (!data)
309 		return 0; /* EPERM check */
310 
311 	/* at the end */
312 	if (!data[1] || !data[2])
313 		return -1;
314 
315 	data[3] = 3;
316 
317 	return 0;
318 }
319 
320 SEC(".struct_ops.link")
321 struct hid_bpf_ops test_insert3 = {
322 	.hid_device_event = (void *)hid_test_insert3,
323 };
324 
325 SEC("?struct_ops/hid_hw_request")
326 int BPF_PROG(hid_test_filter_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
327 	     enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
328 {
329 	return -20;
330 }
331 
332 SEC(".struct_ops.link")
333 struct hid_bpf_ops test_filter_raw_request = {
334 	.hid_hw_request = (void *)hid_test_filter_raw_request,
335 };
336 
337 static struct file *current_file;
338 
339 SEC("fentry/hidraw_open")
340 int BPF_PROG(hidraw_open, struct inode *inode, struct file *file)
341 {
342 	current_file = file;
343 	return 0;
344 }
345 
346 SEC("?struct_ops.s/hid_hw_request")
347 int BPF_PROG(hid_test_hidraw_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
348 	     enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
349 {
350 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
351 	int ret;
352 
353 	if (!data)
354 		return 0; /* EPERM check */
355 
356 	/* check if the incoming request comes from our hidraw operation */
357 	if (source == (__u64)current_file) {
358 		data[0] = reportnum;
359 
360 		ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);
361 		if (ret != 2)
362 			return -1;
363 		data[0] = reportnum + 1;
364 		data[1] = reportnum + 2;
365 		data[2] = reportnum + 3;
366 		return 3;
367 	}
368 
369 	return 0;
370 }
371 
372 SEC(".struct_ops.link")
373 struct hid_bpf_ops test_hidraw_raw_request = {
374 	.hid_hw_request = (void *)hid_test_hidraw_raw_request,
375 };
376 
377 SEC("?struct_ops.s/hid_hw_request")
378 int BPF_PROG(hid_test_infinite_loop_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
379 	     enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
380 {
381 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
382 	int ret;
383 
384 	if (!data)
385 		return 0; /* EPERM check */
386 
387 	/* always forward the request as-is to the device, hid-bpf should prevent
388 	 * infinite loops.
389 	 */
390 	data[0] = reportnum;
391 
392 	ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);
393 	if (ret == 2)
394 		return 3;
395 
396 	return 0;
397 }
398 
399 SEC(".struct_ops.link")
400 struct hid_bpf_ops test_infinite_loop_raw_request = {
401 	.hid_hw_request = (void *)hid_test_infinite_loop_raw_request,
402 };
403 
404 SEC("?struct_ops/hid_hw_output_report")
405 int BPF_PROG(hid_test_filter_output_report, struct hid_bpf_ctx *hctx, unsigned char reportnum,
406 	     enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
407 {
408 	return -25;
409 }
410 
411 SEC(".struct_ops.link")
412 struct hid_bpf_ops test_filter_output_report = {
413 	.hid_hw_output_report = (void *)hid_test_filter_output_report,
414 };
415 
416 SEC("?struct_ops.s/hid_hw_output_report")
417 int BPF_PROG(hid_test_hidraw_output_report, struct hid_bpf_ctx *hctx, __u64 source)
418 {
419 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
420 	int ret;
421 
422 	if (!data)
423 		return 0; /* EPERM check */
424 
425 	/* check if the incoming request comes from our hidraw operation */
426 	if (source == (__u64)current_file)
427 		return hid_bpf_hw_output_report(hctx, data, 2);
428 
429 	return 0;
430 }
431 
432 SEC(".struct_ops.link")
433 struct hid_bpf_ops test_hidraw_output_report = {
434 	.hid_hw_output_report = (void *)hid_test_hidraw_output_report,
435 };
436 
437 SEC("?struct_ops.s/hid_hw_output_report")
438 int BPF_PROG(hid_test_infinite_loop_output_report, struct hid_bpf_ctx *hctx, __u64 source)
439 {
440 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
441 	int ret;
442 
443 	if (!data)
444 		return 0; /* EPERM check */
445 
446 	/* always forward the request as-is to the device, hid-bpf should prevent
447 	 * infinite loops.
448 	 */
449 
450 	ret = hid_bpf_hw_output_report(hctx, data, 2);
451 	if (ret == 2)
452 		return 2;
453 
454 	return 0;
455 }
456 
457 SEC(".struct_ops.link")
458 struct hid_bpf_ops test_infinite_loop_output_report = {
459 	.hid_hw_output_report = (void *)hid_test_infinite_loop_output_report,
460 };
461 
462 struct elem {
463 	struct bpf_wq work;
464 };
465 
466 struct {
467 	__uint(type, BPF_MAP_TYPE_HASH);
468 	__uint(max_entries, 1);
469 	__type(key, int);
470 	__type(value, struct elem);
471 } hmap SEC(".maps");
472 
473 static int wq_cb_sleepable(void *map, int *key, void *work)
474 {
475 	__u8 buf[9] = {2, 3, 4, 5, 6, 7, 8, 9, 10};
476 	struct hid_bpf_ctx *hid_ctx;
477 
478 	hid_ctx = hid_bpf_allocate_context(*key);
479 	if (!hid_ctx)
480 		return 0; /* EPERM check */
481 
482 	hid_bpf_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));
483 
484 	hid_bpf_release_context(hid_ctx);
485 
486 	return 0;
487 }
488 
489 static int test_inject_input_report_callback(int *key)
490 {
491 	struct elem init = {}, *val;
492 	struct bpf_wq *wq;
493 
494 	if (bpf_map_update_elem(&hmap, key, &init, 0))
495 		return -1;
496 
497 	val = bpf_map_lookup_elem(&hmap, key);
498 	if (!val)
499 		return -2;
500 
501 	wq = &val->work;
502 	if (bpf_wq_init(wq, &hmap, 0) != 0)
503 		return -3;
504 
505 	if (bpf_wq_set_callback(wq, wq_cb_sleepable, 0))
506 		return -4;
507 
508 	if (bpf_wq_start(wq, 0))
509 		return -5;
510 
511 	return 0;
512 }
513 
514 SEC("?struct_ops/hid_device_event")
515 int BPF_PROG(hid_test_multiply_events_wq, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
516 {
517 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);
518 	int hid = hid_ctx->hid->id;
519 	int ret;
520 
521 	if (!data)
522 		return 0; /* EPERM check */
523 
524 	if (data[0] != 1)
525 		return 0;
526 
527 	ret = test_inject_input_report_callback(&hid);
528 	if (ret)
529 		return ret;
530 
531 	data[1] += 5;
532 
533 	return 0;
534 }
535 
536 SEC(".struct_ops.link")
537 struct hid_bpf_ops test_multiply_events_wq = {
538 	.hid_device_event = (void *)hid_test_multiply_events_wq,
539 };
540 
541 SEC("?struct_ops/hid_device_event")
542 int BPF_PROG(hid_test_multiply_events, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
543 {
544 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);
545 	__u8 buf[9];
546 	int ret;
547 
548 	if (!data)
549 		return 0; /* EPERM check */
550 
551 	if (data[0] != 1)
552 		return 0;
553 
554 	/*
555 	 * we have to use an intermediate buffer as hid_bpf_input_report
556 	 * will memset data to \0
557 	 */
558 	__builtin_memcpy(buf, data, sizeof(buf));
559 
560 	buf[0] = 2;
561 	buf[1] += 5;
562 	ret = hid_bpf_try_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));
563 	if (ret < 0)
564 		return ret;
565 
566 	/*
567 	 * In real world we should reset the original buffer as data might be garbage now,
568 	 * but it actually now has the content of 'buf'
569 	 */
570 	data[1] += 5;
571 
572 	return 9;
573 }
574 
575 SEC(".struct_ops.link")
576 struct hid_bpf_ops test_multiply_events = {
577 	.hid_device_event = (void *)hid_test_multiply_events,
578 };
579 
580 SEC("?struct_ops/hid_device_event")
581 int BPF_PROG(hid_test_infinite_loop_input_report, struct hid_bpf_ctx *hctx,
582 	     enum hid_report_type report_type, __u64 source)
583 {
584 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 6 /* size */);
585 	__u8 buf[6];
586 
587 	if (!data)
588 		return 0; /* EPERM check */
589 
590 	/*
591 	 * we have to use an intermediate buffer as hid_bpf_input_report
592 	 * will memset data to \0
593 	 */
594 	__builtin_memcpy(buf, data, sizeof(buf));
595 
596 	/* always forward the request as-is to the device, hid-bpf should prevent
597 	 * infinite loops.
598 	 * the return value is ignored so the event is passing to userspace.
599 	 */
600 
601 	hid_bpf_try_input_report(hctx, report_type, buf, sizeof(buf));
602 
603 	/* each time we process the event, we increment by one data[1]:
604 	 * after each successful call to hid_bpf_try_input_report, buf
605 	 * has been memcopied into data by the kernel.
606 	 */
607 	data[1] += 1;
608 
609 	return 0;
610 }
611 
612 SEC(".struct_ops.link")
613 struct hid_bpf_ops test_infinite_loop_input_report = {
614 	.hid_device_event = (void *)hid_test_infinite_loop_input_report,
615 };
616