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")
BPF_PROG(hid_first_event,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)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
__hid_subprog_first_event(struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)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")
BPF_PROG(hid_subprog_first_event,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)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")
BPF_PROG(hid_second_event,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)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")
BPF_PROG(hid_change_report_id,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)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")
hid_user_raw_request(struct hid_hw_request_syscall_args * args)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")
hid_user_output_report(struct hid_hw_request_syscall_args * args)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")
hid_user_input_report(struct hid_hw_request_syscall_args * args)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")
BPF_PROG(hid_rdesc_fixup,struct hid_bpf_ctx * hid_ctx)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")
BPF_PROG(hid_rdesc_fixup_get_data_overflow,struct hid_bpf_ctx * hid_ctx)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")
BPF_PROG(hid_test_insert1,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)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")
BPF_PROG(hid_test_insert2,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)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")
BPF_PROG(hid_test_insert3,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)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")
BPF_PROG(hid_test_filter_raw_request,struct hid_bpf_ctx * hctx,unsigned char reportnum,enum hid_report_type rtype,enum hid_class_request reqtype,__u64 source)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")
BPF_PROG(hidraw_open,struct inode * inode,struct file * file)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")
BPF_PROG(hid_test_hidraw_raw_request,struct hid_bpf_ctx * hctx,unsigned char reportnum,enum hid_report_type rtype,enum hid_class_request reqtype,__u64 source)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")
BPF_PROG(hid_test_infinite_loop_raw_request,struct hid_bpf_ctx * hctx,unsigned char reportnum,enum hid_report_type rtype,enum hid_class_request reqtype,__u64 source)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")
BPF_PROG(hid_test_filter_output_report,struct hid_bpf_ctx * hctx,unsigned char reportnum,enum hid_report_type rtype,enum hid_class_request reqtype,__u64 source)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")
BPF_PROG(hid_test_hidraw_output_report,struct hid_bpf_ctx * hctx,__u64 source)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")
BPF_PROG(hid_test_infinite_loop_output_report,struct hid_bpf_ctx * hctx,__u64 source)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
wq_cb_sleepable(void * map,int * key,void * work)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
test_inject_input_report_callback(int * key)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")
BPF_PROG(hid_test_multiply_events_wq,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)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")
BPF_PROG(hid_test_multiply_events,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)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")
BPF_PROG(hid_test_infinite_loop_input_report,struct hid_bpf_ctx * hctx,enum hid_report_type report_type,__u64 source)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