1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2022-2024 Red Hat */
3 #include "hid.skel.h"
4 #include "hid_common.h"
5 #include <bpf/bpf.h>
6
7 struct hid_hw_request_syscall_args {
8 __u8 data[MAX_BUF_SIZE];
9 unsigned int hid;
10 int retval;
11 size_t size;
12 enum hid_report_type type;
13 __u8 request_type;
14 };
15
FIXTURE(hid_bpf)16 FIXTURE(hid_bpf) {
17 struct uhid_device hid;
18 int hidraw_fd;
19 struct hid *skel;
20 struct bpf_link *hid_links[3]; /* max number of programs loaded in a single test */
21 };
detach_bpf(FIXTURE_DATA (hid_bpf)* self)22 static void detach_bpf(FIXTURE_DATA(hid_bpf) * self)
23 {
24 int i;
25
26 if (self->hidraw_fd)
27 close(self->hidraw_fd);
28 self->hidraw_fd = 0;
29
30 if (!self->skel)
31 return;
32
33 hid__detach(self->skel);
34
35 for (i = 0; i < ARRAY_SIZE(self->hid_links); i++) {
36 if (self->hid_links[i])
37 bpf_link__destroy(self->hid_links[i]);
38 }
39
40 hid__destroy(self->skel);
41 self->skel = NULL;
42 }
43
FIXTURE_TEARDOWN(hid_bpf)44 FIXTURE_TEARDOWN(hid_bpf) {
45 void *uhid_err;
46
47 uhid_destroy(_metadata, &self->hid);
48
49 detach_bpf(self);
50 pthread_join(self->hid.tid, &uhid_err);
51 }
52 #define TEARDOWN_LOG(fmt, ...) do { \
53 TH_LOG(fmt, ##__VA_ARGS__); \
54 hid_bpf_teardown(_metadata, self, variant); \
55 } while (0)
56
FIXTURE_VARIANT(hid_bpf)57 FIXTURE_VARIANT(hid_bpf) {
58 __u8 *rdesc;
59 size_t rdesc_size;
60 };
61
FIXTURE_VARIANT_ADD(hid_bpf,numbered)62 FIXTURE_VARIANT_ADD(hid_bpf, numbered) {
63 .rdesc = rdesc,
64 .rdesc_size = sizeof(rdesc),
65 };
66
FIXTURE_VARIANT_ADD(hid_bpf,unnumbered)67 FIXTURE_VARIANT_ADD(hid_bpf, unnumbered) {
68 .rdesc = fido2_rdesc,
69 .rdesc_size = sizeof(fido2_rdesc),
70 };
71
FIXTURE_SETUP(hid_bpf)72 FIXTURE_SETUP(hid_bpf)
73 {
74 int err;
75
76 err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a36,
77 variant->rdesc, variant->rdesc_size);
78 ASSERT_OK(err);
79 }
80
81 struct test_program {
82 const char *name;
83 int insert_head;
84 };
85 #define LOAD_PROGRAMS(progs) \
86 load_programs(progs, ARRAY_SIZE(progs), false, _metadata, self, variant)
87 #define LOAD_PROGRAMS_MAY_FAIL(progs) \
88 load_programs(progs, ARRAY_SIZE(progs), true, _metadata, self, variant)
89 #define LOAD_BPF \
90 load_programs(NULL, 0, false, _metadata, self, variant)
load_programs(const struct test_program programs[],const size_t progs_count,bool load_may_fail,struct __test_metadata * _metadata,FIXTURE_DATA (hid_bpf)* self,const FIXTURE_VARIANT (hid_bpf)* variant)91 static int load_programs(const struct test_program programs[],
92 const size_t progs_count,
93 bool load_may_fail,
94 struct __test_metadata *_metadata,
95 FIXTURE_DATA(hid_bpf) * self,
96 const FIXTURE_VARIANT(hid_bpf) * variant)
97 {
98 struct bpf_map *iter_map;
99 int err = -EINVAL;
100
101 ASSERT_LE(progs_count, ARRAY_SIZE(self->hid_links))
102 TH_LOG("too many programs are to be loaded");
103
104 /* open the bpf file */
105 self->skel = hid__open();
106 ASSERT_OK_PTR(self->skel) TEARDOWN_LOG("Error while calling hid__open");
107
108 /*
109 * Disable all struct_ops maps by default so libbpf does not autoload
110 * programs referenced by maps that are unrelated to the current test.
111 */
112 bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) {
113 if (bpf_map__type(iter_map) == BPF_MAP_TYPE_STRUCT_OPS) {
114 err = bpf_map__set_autocreate(iter_map, false);
115 ASSERT_OK(err) TH_LOG("can not disable struct_ops map '%s'",
116 bpf_map__name(iter_map));
117 }
118
119 bpf_map__set_autoattach(iter_map, false);
120 }
121
122 for (int i = 0; i < progs_count; i++) {
123 struct bpf_program *prog;
124 struct bpf_map *map;
125 int *ops_hid_id;
126
127 prog = bpf_object__find_program_by_name(*self->skel->skeleton->obj,
128 programs[i].name);
129 ASSERT_OK_PTR(prog) TH_LOG("can not find program by name '%s'", programs[i].name);
130
131 bpf_program__set_autoload(prog, true);
132
133 map = bpf_object__find_map_by_name(*self->skel->skeleton->obj,
134 programs[i].name + 4);
135 ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'",
136 programs[i].name + 4);
137
138 err = bpf_map__set_autocreate(map, true);
139 ASSERT_OK(err) TH_LOG("can not enable struct_ops map '%s'",
140 programs[i].name + 4);
141
142 /* hid_id is the first field of struct hid_bpf_ops */
143 ops_hid_id = bpf_map__initial_value(map, NULL);
144 ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data");
145
146 *ops_hid_id = self->hid.hid_id;
147 }
148
149 err = hid__load(self->skel);
150 if (err && load_may_fail)
151 return err;
152
153 ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err);
154
155 for (int i = 0; i < progs_count; i++) {
156 struct bpf_map *map;
157
158 map = bpf_object__find_map_by_name(*self->skel->skeleton->obj,
159 programs[i].name + 4);
160 ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'",
161 programs[i].name + 4);
162
163 self->hid_links[i] = bpf_map__attach_struct_ops(map);
164 ASSERT_OK_PTR(self->hid_links[i]) TH_LOG("failed to attach struct ops '%s'",
165 programs[i].name + 4);
166 }
167
168 hid__attach(self->skel);
169
170 self->hidraw_fd = open_hidraw(&self->hid);
171 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
172 return 0;
173 }
174
175 /*
176 * A simple test to see if the fixture is working fine.
177 * If this fails, none of the other tests will pass.
178 */
TEST_F(hid_bpf,test_create_uhid)179 TEST_F(hid_bpf, test_create_uhid)
180 {
181 }
182
183 /*
184 * Attach hid_first_event to the given uhid device,
185 * retrieve and open the matching hidraw node,
186 * inject one event in the uhid device,
187 * check that the program sees it and can change the data
188 */
TEST_F(hid_bpf,raw_event)189 TEST_F(hid_bpf, raw_event)
190 {
191 const struct test_program progs[] = {
192 { .name = "hid_first_event" },
193 };
194 __u8 buf[MAX_BUF_SIZE] = {0};
195 int err;
196
197 LOAD_PROGRAMS(progs);
198
199 /* check that the program is correctly loaded */
200 ASSERT_EQ(self->skel->data->callback_check, 52) TH_LOG("callback_check1");
201 ASSERT_EQ(self->skel->data->callback2_check, 52) TH_LOG("callback2_check1");
202
203 /* inject one event */
204 buf[0] = 1;
205 buf[1] = 42;
206 uhid_send_event(_metadata, &self->hid, buf, 6);
207
208 /* check that hid_first_event() was executed */
209 ASSERT_EQ(self->skel->data->callback_check, 42) TH_LOG("callback_check1");
210
211 /* read the data from hidraw */
212 memset(buf, 0, sizeof(buf));
213 err = read(self->hidraw_fd, buf, sizeof(buf));
214 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
215 ASSERT_EQ(buf[0], 1);
216 ASSERT_EQ(buf[2], 47);
217
218 /* inject another event */
219 memset(buf, 0, sizeof(buf));
220 buf[0] = 1;
221 buf[1] = 47;
222 uhid_send_event(_metadata, &self->hid, buf, 6);
223
224 /* check that hid_first_event() was executed */
225 ASSERT_EQ(self->skel->data->callback_check, 47) TH_LOG("callback_check1");
226
227 /* read the data from hidraw */
228 memset(buf, 0, sizeof(buf));
229 err = read(self->hidraw_fd, buf, sizeof(buf));
230 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
231 ASSERT_EQ(buf[2], 52);
232 }
233
234 /*
235 * Attach hid_first_event to the given uhid device,
236 * retrieve and open the matching hidraw node,
237 * inject one event in the uhid device,
238 * check that the program sees it and can change the data
239 */
TEST_F(hid_bpf,subprog_raw_event)240 TEST_F(hid_bpf, subprog_raw_event)
241 {
242 const struct test_program progs[] = {
243 { .name = "hid_subprog_first_event" },
244 };
245 __u8 buf[MAX_BUF_SIZE] = {0};
246 int err;
247
248 LOAD_PROGRAMS(progs);
249
250 /* inject one event */
251 buf[0] = 1;
252 buf[1] = 42;
253 uhid_send_event(_metadata, &self->hid, buf, 6);
254
255 /* read the data from hidraw */
256 memset(buf, 0, sizeof(buf));
257 err = read(self->hidraw_fd, buf, sizeof(buf));
258 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
259 ASSERT_EQ(buf[0], 1);
260 ASSERT_EQ(buf[2], 47);
261
262 /* inject another event */
263 memset(buf, 0, sizeof(buf));
264 buf[0] = 1;
265 buf[1] = 47;
266 uhid_send_event(_metadata, &self->hid, buf, 6);
267
268 /* read the data from hidraw */
269 memset(buf, 0, sizeof(buf));
270 err = read(self->hidraw_fd, buf, sizeof(buf));
271 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
272 ASSERT_EQ(buf[2], 52);
273 }
274
275 /*
276 * Attach hid_first_event to the given uhid device,
277 * attempt at re-attaching it, we should not lock and
278 * return an invalid struct bpf_link
279 */
TEST_F(hid_bpf,multiple_attach)280 TEST_F(hid_bpf, multiple_attach)
281 {
282 const struct test_program progs[] = {
283 { .name = "hid_first_event" },
284 };
285 struct bpf_link *link;
286
287 LOAD_PROGRAMS(progs);
288
289 link = bpf_map__attach_struct_ops(self->skel->maps.first_event);
290 ASSERT_NULL(link) TH_LOG("unexpected return value when re-attaching the struct_ops");
291 }
292
293 /*
294 * Ensures that we can attach/detach programs
295 */
TEST_F(hid_bpf,test_attach_detach)296 TEST_F(hid_bpf, test_attach_detach)
297 {
298 const struct test_program progs[] = {
299 { .name = "hid_first_event" },
300 { .name = "hid_second_event" },
301 };
302 struct bpf_link *link;
303 __u8 buf[MAX_BUF_SIZE] = {0};
304 int err, link_fd;
305
306 LOAD_PROGRAMS(progs);
307
308 link = self->hid_links[0];
309 ASSERT_OK_PTR(link) TH_LOG("HID-BPF link not created");
310
311 link_fd = bpf_link__fd(link);
312 ASSERT_GE(link_fd, 0) TH_LOG("HID-BPF link FD not valid");
313
314 /* inject one event */
315 buf[0] = 1;
316 buf[1] = 42;
317 uhid_send_event(_metadata, &self->hid, buf, 6);
318
319 /* read the data from hidraw */
320 memset(buf, 0, sizeof(buf));
321 err = read(self->hidraw_fd, buf, sizeof(buf));
322 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
323 ASSERT_EQ(buf[0], 1);
324 ASSERT_EQ(buf[2], 47);
325
326 /* make sure both programs are run */
327 ASSERT_EQ(buf[3], 52);
328
329 /* pin the first program and immediately unpin it */
330 #define PIN_PATH "/sys/fs/bpf/hid_first_event"
331 err = bpf_obj_pin(link_fd, PIN_PATH);
332 ASSERT_OK(err) TH_LOG("error while calling bpf_obj_pin");
333 remove(PIN_PATH);
334 #undef PIN_PATH
335 usleep(100000);
336
337 /* detach the program */
338 detach_bpf(self);
339
340 self->hidraw_fd = open_hidraw(&self->hid);
341 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
342
343 /* inject another event */
344 memset(buf, 0, sizeof(buf));
345 buf[0] = 1;
346 buf[1] = 47;
347 uhid_send_event(_metadata, &self->hid, buf, 6);
348
349 /* read the data from hidraw */
350 memset(buf, 0, sizeof(buf));
351 err = read(self->hidraw_fd, buf, sizeof(buf));
352 ASSERT_EQ(err, 6) TH_LOG("read_hidraw_no_bpf");
353 ASSERT_EQ(buf[0], 1);
354 ASSERT_EQ(buf[1], 47);
355 ASSERT_EQ(buf[2], 0);
356 ASSERT_EQ(buf[3], 0);
357
358 /* re-attach our program */
359
360 LOAD_PROGRAMS(progs);
361
362 /* inject one event */
363 memset(buf, 0, sizeof(buf));
364 buf[0] = 1;
365 buf[1] = 42;
366 uhid_send_event(_metadata, &self->hid, buf, 6);
367
368 /* read the data from hidraw */
369 memset(buf, 0, sizeof(buf));
370 err = read(self->hidraw_fd, buf, sizeof(buf));
371 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
372 ASSERT_EQ(buf[0], 1);
373 ASSERT_EQ(buf[2], 47);
374 ASSERT_EQ(buf[3], 52);
375 }
376
377 /*
378 * Attach hid_change_report_id to the given uhid device,
379 * retrieve and open the matching hidraw node,
380 * inject one event in the uhid device,
381 * check that the program sees it and can change the data
382 */
TEST_F(hid_bpf,test_hid_change_report)383 TEST_F(hid_bpf, test_hid_change_report)
384 {
385 const struct test_program progs[] = {
386 { .name = "hid_change_report_id" },
387 };
388 __u8 buf[MAX_BUF_SIZE] = {0};
389 int err;
390
391 LOAD_PROGRAMS(progs);
392
393 /* inject one event */
394 buf[0] = 1;
395 buf[1] = 42;
396 uhid_send_event(_metadata, &self->hid, buf, 6);
397
398 /* read the data from hidraw */
399 memset(buf, 0, sizeof(buf));
400 err = read(self->hidraw_fd, buf, sizeof(buf));
401 ASSERT_EQ(err, 9) TH_LOG("read_hidraw");
402 ASSERT_EQ(buf[0], 2);
403 ASSERT_EQ(buf[1], 42);
404 ASSERT_EQ(buf[2], 0) TH_LOG("leftovers_from_previous_test");
405 }
406
407 /*
408 * Call hid_bpf_input_report against the given uhid device,
409 * check that the program is called and does the expected.
410 */
TEST_F(hid_bpf,test_hid_user_input_report_call)411 TEST_F(hid_bpf, test_hid_user_input_report_call)
412 {
413 struct hid_hw_request_syscall_args args = {
414 .retval = -1,
415 .size = MAX_BUF_SIZE,
416 };
417 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
418 .ctx_in = &args,
419 .ctx_size_in = sizeof(args),
420 );
421 __u8 buf[MAX_BUF_SIZE] = {0};
422 int err, prog_fd;
423
424 LOAD_BPF;
425
426 args.hid = self->hid.hid_id;
427 args.data[0] = 1; /* report ID */
428 args.data[1] = 2;
429 args.data[2] = 42;
430
431 if (variant->rdesc == fido2_rdesc)
432 args.data[0] = 0;
433
434 prog_fd = bpf_program__fd(self->skel->progs.hid_user_input_report);
435
436 /* check that there is no data to read from hidraw */
437 memset(buf, 0, sizeof(buf));
438 err = read(self->hidraw_fd, buf, sizeof(buf));
439 ASSERT_EQ(err, -1) TH_LOG("read_hidraw");
440
441 err = bpf_prog_test_run_opts(prog_fd, &tattrs);
442
443 ASSERT_OK(err) TH_LOG("error while calling bpf_prog_test_run_opts");
444
445 ASSERT_EQ(args.retval, 0);
446
447 /* read the data from hidraw */
448 memset(buf, 0, sizeof(buf));
449 err = read(self->hidraw_fd, buf, sizeof(buf));
450 if (variant->rdesc == rdesc) {
451 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
452 } else {
453 ASSERT_EQ(err, 64)
454 TH_LOG("read_hidraw");
455 }
456 ASSERT_EQ(buf[0], args.data[0]);
457 ASSERT_EQ(buf[1], 2);
458 ASSERT_EQ(buf[2], 42);
459 }
460
461 /*
462 * Call hid_bpf_hw_output_report against the given uhid device,
463 * check that the program is called and does the expected.
464 */
TEST_F(hid_bpf,test_hid_user_output_report_call)465 TEST_F(hid_bpf, test_hid_user_output_report_call)
466 {
467 struct hid_hw_request_syscall_args args = {
468 .retval = -1,
469 .size = MAX_BUF_SIZE,
470 };
471 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
472 .ctx_in = &args,
473 .ctx_size_in = sizeof(args),
474 );
475 int err, cond_err, prog_fd;
476 struct timespec time_to_wait;
477
478 LOAD_BPF;
479
480 args.hid = self->hid.hid_id;
481 args.data[0] = 1; /* report ID */
482 args.data[1] = 2;
483 args.data[2] = 42;
484
485 if (variant->rdesc == fido2_rdesc)
486 args.data[0] = 0;
487
488 prog_fd = bpf_program__fd(self->skel->progs.hid_user_output_report);
489
490 pthread_mutex_lock(&uhid_output_mtx);
491
492 memset(output_report, 0, sizeof(output_report));
493 clock_gettime(CLOCK_REALTIME, &time_to_wait);
494 time_to_wait.tv_sec += 2;
495
496 err = bpf_prog_test_run_opts(prog_fd, &tattrs);
497 cond_err = pthread_cond_timedwait(&uhid_output_cond, &uhid_output_mtx, &time_to_wait);
498
499 ASSERT_OK(err) TH_LOG("error while calling bpf_prog_test_run_opts");
500 ASSERT_OK(cond_err) TH_LOG("error while calling waiting for the condition");
501
502 if (variant->rdesc == rdesc) {
503 ASSERT_EQ(args.retval, 3);
504 } else if (variant->rdesc == fido2_rdesc) {
505 ASSERT_EQ(args.retval, 65)
506 TH_LOG("report size error, should have 64 + 1 extra byte for the report ID 0");
507 }
508
509 ASSERT_EQ(output_report[0], args.data[0]);
510 ASSERT_EQ(output_report[1], 2);
511 ASSERT_EQ(output_report[2], 42);
512
513 pthread_mutex_unlock(&uhid_output_mtx);
514 }
515
516 /*
517 * Call hid_hw_raw_request against the given uhid device,
518 * check that the program is called and does the expected.
519 */
TEST_F(hid_bpf,test_hid_user_raw_request_call)520 TEST_F(hid_bpf, test_hid_user_raw_request_call)
521 {
522 struct hid_hw_request_syscall_args args = {
523 .retval = -1,
524 .type = HID_FEATURE_REPORT,
525 .request_type = HID_REQ_GET_REPORT,
526 .size = MAX_BUF_SIZE,
527 };
528 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
529 .ctx_in = &args,
530 .ctx_size_in = sizeof(args),
531 );
532 int err, prog_fd;
533
534 LOAD_BPF;
535
536 args.hid = self->hid.hid_id;
537 args.data[0] = 1; /* report ID */
538
539 prog_fd = bpf_program__fd(self->skel->progs.hid_user_raw_request);
540
541 err = bpf_prog_test_run_opts(prog_fd, &tattrs);
542 ASSERT_OK(err) TH_LOG("error while calling bpf_prog_test_run_opts");
543
544 ASSERT_EQ(args.retval, 2);
545
546 ASSERT_EQ(args.data[1], 2);
547 }
548
549 /*
550 * Call hid_hw_raw_request against the given uhid device,
551 * check that the program is called and does the expected.
552 */
TEST_F(hid_bpf,test_hid_user_raw_request_call_eio)553 TEST_F(hid_bpf, test_hid_user_raw_request_call_eio)
554 {
555 struct hid_hw_request_syscall_args args = {
556 .retval = -1,
557 .type = HID_FEATURE_REPORT,
558 .request_type = HID_REQ_GET_PROTOCOL,
559 .size = MAX_BUF_SIZE,
560 };
561 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
562 .ctx_in = &args,
563 .ctx_size_in = sizeof(args),
564 );
565 int err, prog_fd;
566
567 LOAD_BPF;
568
569 args.hid = self->hid.hid_id;
570 args.data[0] = 1; /* report ID */
571
572 prog_fd = bpf_program__fd(self->skel->progs.hid_user_raw_request);
573
574 err = bpf_prog_test_run_opts(prog_fd, &tattrs);
575 ASSERT_OK(err) TH_LOG("error while calling bpf_prog_test_run_opts");
576
577 ASSERT_EQ(args.retval, -EIO);
578 }
579
580 /*
581 * Call hid_hw_raw_request against the given uhid device,
582 * check that the program is called and prevents the
583 * call to uhid.
584 */
TEST_F(hid_bpf,test_hid_filter_raw_request_call)585 TEST_F(hid_bpf, test_hid_filter_raw_request_call)
586 {
587 const struct test_program progs[] = {
588 { .name = "hid_test_filter_raw_request" },
589 };
590 __u8 buf[MAX_BUF_SIZE] = {0};
591 int err;
592
593 LOAD_PROGRAMS(progs);
594
595 /* first check that we did not attach to device_event */
596
597 /* inject one event */
598 buf[0] = 1;
599 buf[1] = 42;
600 uhid_send_event(_metadata, &self->hid, buf, 6);
601
602 /* read the data from hidraw */
603 memset(buf, 0, sizeof(buf));
604 err = read(self->hidraw_fd, buf, sizeof(buf));
605 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
606 ASSERT_EQ(buf[0], 1);
607 ASSERT_EQ(buf[1], 42);
608 ASSERT_EQ(buf[2], 0) TH_LOG("leftovers_from_previous_test");
609
610 /* now check that our program is preventing hid_hw_raw_request() */
611
612 /* emit hid_hw_raw_request from hidraw */
613 /* Get Feature */
614 memset(buf, 0, sizeof(buf));
615 buf[0] = 0x1; /* Report Number */
616 err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
617 ASSERT_LT(err, 0) TH_LOG("unexpected success while reading HIDIOCGFEATURE: %d", err);
618 ASSERT_EQ(errno, 20) TH_LOG("unexpected error code while reading HIDIOCGFEATURE: %d",
619 errno);
620
621 /* remove our bpf program and check that we can now emit commands */
622
623 /* detach the program */
624 detach_bpf(self);
625
626 self->hidraw_fd = open_hidraw(&self->hid);
627 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
628
629 err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
630 ASSERT_GE(err, 0) TH_LOG("error while reading HIDIOCGFEATURE: %d", err);
631 }
632
633 /*
634 * Call hid_hw_raw_request against the given uhid device,
635 * check that the program is called and can issue the call
636 * to uhid and transform the answer.
637 */
TEST_F(hid_bpf,test_hid_change_raw_request_call)638 TEST_F(hid_bpf, test_hid_change_raw_request_call)
639 {
640 const struct test_program progs[] = {
641 { .name = "hid_test_hidraw_raw_request" },
642 };
643 __u8 buf[MAX_BUF_SIZE] = {0};
644 int err;
645
646 LOAD_PROGRAMS(progs);
647
648 /* emit hid_hw_raw_request from hidraw */
649 /* Get Feature */
650 memset(buf, 0, sizeof(buf));
651 buf[0] = 0x1; /* Report Number */
652 err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
653 ASSERT_EQ(err, 3) TH_LOG("unexpected returned size while reading HIDIOCGFEATURE: %d", err);
654
655 ASSERT_EQ(buf[0], 2);
656 ASSERT_EQ(buf[1], 3);
657 ASSERT_EQ(buf[2], 4);
658 }
659
660 /*
661 * Call hid_hw_raw_request against the given uhid device,
662 * check that the program is not making infinite loops.
663 */
TEST_F(hid_bpf,test_hid_infinite_loop_raw_request_call)664 TEST_F(hid_bpf, test_hid_infinite_loop_raw_request_call)
665 {
666 const struct test_program progs[] = {
667 { .name = "hid_test_infinite_loop_raw_request" },
668 };
669 __u8 buf[MAX_BUF_SIZE] = {0};
670 int err;
671
672 LOAD_PROGRAMS(progs);
673
674 /* emit hid_hw_raw_request from hidraw */
675 /* Get Feature */
676 memset(buf, 0, sizeof(buf));
677 buf[0] = 0x1; /* Report Number */
678 err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
679 ASSERT_EQ(err, 3) TH_LOG("unexpected returned size while reading HIDIOCGFEATURE: %d", err);
680 }
681
682 /*
683 * Call hid_hw_output_report against the given uhid device,
684 * check that the program is called and prevents the
685 * call to uhid.
686 */
TEST_F(hid_bpf,test_hid_filter_output_report_call)687 TEST_F(hid_bpf, test_hid_filter_output_report_call)
688 {
689 const struct test_program progs[] = {
690 { .name = "hid_test_filter_output_report" },
691 };
692 __u8 buf[MAX_BUF_SIZE] = {0};
693 int err;
694
695 LOAD_PROGRAMS(progs);
696
697 /* first check that we did not attach to device_event */
698
699 /* inject one event */
700 buf[0] = 1;
701 buf[1] = 42;
702 uhid_send_event(_metadata, &self->hid, buf, 6);
703
704 /* read the data from hidraw */
705 memset(buf, 0, sizeof(buf));
706 err = read(self->hidraw_fd, buf, sizeof(buf));
707 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
708 ASSERT_EQ(buf[0], 1);
709 ASSERT_EQ(buf[1], 42);
710 ASSERT_EQ(buf[2], 0) TH_LOG("leftovers_from_previous_test");
711
712 /* now check that our program is preventing hid_hw_output_report() */
713
714 buf[0] = 1; /* report ID */
715 buf[1] = 2;
716 buf[2] = 42;
717
718 err = write(self->hidraw_fd, buf, 3);
719 ASSERT_LT(err, 0) TH_LOG("unexpected success while sending hid_hw_output_report: %d", err);
720 ASSERT_EQ(errno, 25) TH_LOG("unexpected error code while sending hid_hw_output_report: %d",
721 errno);
722
723 /* remove our bpf program and check that we can now emit commands */
724
725 /* detach the program */
726 detach_bpf(self);
727
728 self->hidraw_fd = open_hidraw(&self->hid);
729 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
730
731 err = write(self->hidraw_fd, buf, 3);
732 ASSERT_GE(err, 0) TH_LOG("error while sending hid_hw_output_report: %d", err);
733 }
734
735 /*
736 * Call hid_hw_output_report against the given uhid device,
737 * check that the program is called and can issue the call
738 * to uhid and transform the answer.
739 */
TEST_F(hid_bpf,test_hid_change_output_report_call)740 TEST_F(hid_bpf, test_hid_change_output_report_call)
741 {
742 const struct test_program progs[] = {
743 { .name = "hid_test_hidraw_output_report" },
744 };
745 __u8 buf[MAX_BUF_SIZE] = {0};
746 int err;
747
748 LOAD_PROGRAMS(progs);
749
750 /* emit hid_hw_output_report from hidraw */
751 buf[0] = 1; /* report ID */
752 buf[1] = 2;
753 buf[2] = 42;
754
755 err = write(self->hidraw_fd, buf, 10);
756 ASSERT_EQ(err, 2) TH_LOG("unexpected returned size while sending hid_hw_output_report: %d",
757 err);
758 }
759
760 /*
761 * Call hid_hw_output_report against the given uhid device,
762 * check that the program is not making infinite loops.
763 */
TEST_F(hid_bpf,test_hid_infinite_loop_output_report_call)764 TEST_F(hid_bpf, test_hid_infinite_loop_output_report_call)
765 {
766 const struct test_program progs[] = {
767 { .name = "hid_test_infinite_loop_output_report" },
768 };
769 __u8 buf[MAX_BUF_SIZE] = {0};
770 int err;
771
772 LOAD_PROGRAMS(progs);
773
774 /* emit hid_hw_output_report from hidraw */
775 buf[0] = 1; /* report ID */
776 buf[1] = 2;
777 buf[2] = 42;
778
779 err = write(self->hidraw_fd, buf, 8);
780 ASSERT_EQ(err, 2) TH_LOG("unexpected returned size while sending hid_hw_output_report: %d",
781 err);
782 }
783
784 /*
785 * Attach hid_multiply_event_wq to the given uhid device,
786 * retrieve and open the matching hidraw node,
787 * inject one event in the uhid device,
788 * check that the program sees it and can add extra data
789 */
TEST_F(hid_bpf,test_multiply_events_wq)790 TEST_F(hid_bpf, test_multiply_events_wq)
791 {
792 const struct test_program progs[] = {
793 { .name = "hid_test_multiply_events_wq" },
794 };
795 __u8 buf[MAX_BUF_SIZE] = {0};
796 int err;
797
798 LOAD_PROGRAMS(progs);
799
800 /* inject one event */
801 buf[0] = 1;
802 buf[1] = 42;
803 uhid_send_event(_metadata, &self->hid, buf, 6);
804
805 /* read the data from hidraw */
806 memset(buf, 0, sizeof(buf));
807 err = read(self->hidraw_fd, buf, sizeof(buf));
808 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
809 ASSERT_EQ(buf[0], 1);
810 ASSERT_EQ(buf[1], 47);
811
812 usleep(100000);
813
814 /* read the data from hidraw */
815 memset(buf, 0, sizeof(buf));
816 err = read(self->hidraw_fd, buf, sizeof(buf));
817 ASSERT_EQ(err, 9) TH_LOG("read_hidraw");
818 ASSERT_EQ(buf[0], 2);
819 ASSERT_EQ(buf[1], 3);
820 }
821
822 /*
823 * Attach hid_multiply_event to the given uhid device,
824 * retrieve and open the matching hidraw node,
825 * inject one event in the uhid device,
826 * check that the program sees it and can add extra data
827 */
TEST_F(hid_bpf,test_multiply_events)828 TEST_F(hid_bpf, test_multiply_events)
829 {
830 const struct test_program progs[] = {
831 { .name = "hid_test_multiply_events" },
832 };
833 __u8 buf[MAX_BUF_SIZE] = {0};
834 int err;
835
836 LOAD_PROGRAMS(progs);
837
838 /* inject one event */
839 buf[0] = 1;
840 buf[1] = 42;
841 uhid_send_event(_metadata, &self->hid, buf, 6);
842
843 /* read the data from hidraw */
844 memset(buf, 0, sizeof(buf));
845 err = read(self->hidraw_fd, buf, sizeof(buf));
846 ASSERT_EQ(err, 9) TH_LOG("read_hidraw");
847 ASSERT_EQ(buf[0], 2);
848 ASSERT_EQ(buf[1], 47);
849
850 /* read the data from hidraw */
851 memset(buf, 0, sizeof(buf));
852 err = read(self->hidraw_fd, buf, sizeof(buf));
853 ASSERT_EQ(err, 9) TH_LOG("read_hidraw");
854 ASSERT_EQ(buf[0], 2);
855 ASSERT_EQ(buf[1], 52);
856 }
857
858 /*
859 * Call hid_bpf_input_report against the given uhid device,
860 * check that the program is not making infinite loops.
861 */
TEST_F(hid_bpf,test_hid_infinite_loop_input_report_call)862 TEST_F(hid_bpf, test_hid_infinite_loop_input_report_call)
863 {
864 const struct test_program progs[] = {
865 { .name = "hid_test_infinite_loop_input_report" },
866 };
867 __u8 buf[MAX_BUF_SIZE] = {0};
868 int err;
869
870 LOAD_PROGRAMS(progs);
871
872 /* emit hid_hw_output_report from hidraw */
873 buf[0] = 1; /* report ID */
874 buf[1] = 2;
875 buf[2] = 42;
876
877 uhid_send_event(_metadata, &self->hid, buf, 6);
878
879 /* read the data from hidraw */
880 memset(buf, 0, sizeof(buf));
881 err = read(self->hidraw_fd, buf, sizeof(buf));
882 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
883 ASSERT_EQ(buf[0], 1);
884 ASSERT_EQ(buf[1], 3);
885
886 /* read the data from hidraw: hid_bpf_try_input_report should work exactly one time */
887 memset(buf, 0, sizeof(buf));
888 err = read(self->hidraw_fd, buf, sizeof(buf));
889 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
890 ASSERT_EQ(buf[0], 1);
891 ASSERT_EQ(buf[1], 4);
892
893 /* read the data from hidraw: there should be none */
894 memset(buf, 0, sizeof(buf));
895 err = read(self->hidraw_fd, buf, sizeof(buf));
896 ASSERT_EQ(err, -1) TH_LOG("read_hidraw");
897 }
898
899 /*
900 * Attach hid_insert{0,1,2} to the given uhid device,
901 * retrieve and open the matching hidraw node,
902 * inject one event in the uhid device,
903 * check that the programs have been inserted in the correct order.
904 */
TEST_F(hid_bpf,test_hid_attach_flags)905 TEST_F(hid_bpf, test_hid_attach_flags)
906 {
907 const struct test_program progs[] = {
908 {
909 .name = "hid_test_insert2",
910 .insert_head = 0,
911 },
912 {
913 .name = "hid_test_insert1",
914 .insert_head = 1,
915 },
916 {
917 .name = "hid_test_insert3",
918 .insert_head = 0,
919 },
920 };
921 __u8 buf[MAX_BUF_SIZE] = {0};
922 int err;
923
924 LOAD_PROGRAMS(progs);
925
926 /* inject one event */
927 buf[0] = 1;
928 uhid_send_event(_metadata, &self->hid, buf, 6);
929
930 /* read the data from hidraw */
931 memset(buf, 0, sizeof(buf));
932 err = read(self->hidraw_fd, buf, sizeof(buf));
933 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
934 ASSERT_EQ(buf[1], 1);
935 ASSERT_EQ(buf[2], 2);
936 ASSERT_EQ(buf[3], 3);
937 }
938
939 /*
940 * Attach hid_rdesc_fixup to the given uhid device,
941 * retrieve and open the matching hidraw node,
942 * check that the hidraw report descriptor has been updated.
943 */
TEST_F(hid_bpf,test_rdesc_fixup)944 TEST_F(hid_bpf, test_rdesc_fixup)
945 {
946 struct hidraw_report_descriptor rpt_desc = {0};
947 const struct test_program progs[] = {
948 { .name = "hid_rdesc_fixup" },
949 };
950 int err, desc_size;
951
952 if (variant->rdesc != rdesc)
953 SKIP(return, "not compatible report descriptor");
954
955 LOAD_PROGRAMS(progs);
956
957 /* check that hid_rdesc_fixup() was executed */
958 ASSERT_EQ(self->skel->data->callback2_check, 0x21);
959
960 /* read the exposed report descriptor from hidraw */
961 err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size);
962 ASSERT_GE(err, 0) TH_LOG("error while reading HIDIOCGRDESCSIZE: %d", err);
963
964 /* ensure the new size of the rdesc is bigger than the old one */
965 ASSERT_GT(desc_size, sizeof(rdesc));
966
967 rpt_desc.size = desc_size;
968 err = ioctl(self->hidraw_fd, HIDIOCGRDESC, &rpt_desc);
969 ASSERT_GE(err, 0) TH_LOG("error while reading HIDIOCGRDESC: %d", err);
970
971 ASSERT_EQ(rpt_desc.value[4], 0x42);
972 }
973
TEST_F(hid_bpf,test_rdesc_fixup_get_data_overflow)974 TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow)
975 {
976 const struct test_program progs[] = {
977 { .name = "hid_rdesc_fixup_get_data_overflow" },
978 };
979
980 /* newer verifier can detect the overflow at load time */
981 if (LOAD_PROGRAMS_MAY_FAIL(progs))
982 return;
983
984 ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1);
985 }
986
TEST_F(hid_bpf,test_rdesc_fixup_change_uniq_name_phys)987 TEST_F(hid_bpf, test_rdesc_fixup_change_uniq_name_phys)
988 {
989 const struct test_program progs[] = {
990 { .name = "hid_rdesc_fixup_change_uniq_name_phys" },
991 };
992 char expected[256], buf[256] = {};
993 int err;
994
995 LOAD_PROGRAMS(progs);
996
997 err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(buf)), buf);
998 ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWNAME");
999 ASSERT_STREQ("name coming from bpf", buf);
1000
1001 snprintf(expected, sizeof(expected), "%d phys:coming:from:bpf", self->hid.dev_id);
1002
1003 err = ioctl(self->hidraw_fd, HIDIOCGRAWPHYS(sizeof(buf)), buf);
1004 ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWPHYS");
1005 ASSERT_STREQ(expected, buf);
1006
1007 err = ioctl(self->hidraw_fd, HIDIOCGRAWUNIQ(sizeof(buf)), buf);
1008 ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWUNIQ");
1009 ASSERT_STREQ("uniq:coming:from:bpf", buf);
1010
1011 }
1012
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)1013 static int libbpf_print_fn(enum libbpf_print_level level,
1014 const char *format, va_list args)
1015 {
1016 char buf[1024];
1017
1018 if (level == LIBBPF_DEBUG)
1019 return 0;
1020
1021 snprintf(buf, sizeof(buf), "# %s", format);
1022
1023 vfprintf(stdout, buf, args);
1024 return 0;
1025 }
1026
main(int argc,char ** argv)1027 int main(int argc, char **argv)
1028 {
1029 /* Use libbpf 1.0 API mode */
1030 libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
1031 libbpf_set_print(libbpf_print_fn);
1032
1033 return test_harness_run(argc, argv);
1034 }
1035