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 prevents the
552 * call to uhid.
553 */
TEST_F(hid_bpf,test_hid_filter_raw_request_call)554 TEST_F(hid_bpf, test_hid_filter_raw_request_call)
555 {
556 const struct test_program progs[] = {
557 { .name = "hid_test_filter_raw_request" },
558 };
559 __u8 buf[MAX_BUF_SIZE] = {0};
560 int err;
561
562 LOAD_PROGRAMS(progs);
563
564 /* first check that we did not attach to device_event */
565
566 /* inject one event */
567 buf[0] = 1;
568 buf[1] = 42;
569 uhid_send_event(_metadata, &self->hid, buf, 6);
570
571 /* read the data from hidraw */
572 memset(buf, 0, sizeof(buf));
573 err = read(self->hidraw_fd, buf, sizeof(buf));
574 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
575 ASSERT_EQ(buf[0], 1);
576 ASSERT_EQ(buf[1], 42);
577 ASSERT_EQ(buf[2], 0) TH_LOG("leftovers_from_previous_test");
578
579 /* now check that our program is preventing hid_hw_raw_request() */
580
581 /* emit hid_hw_raw_request from hidraw */
582 /* Get Feature */
583 memset(buf, 0, sizeof(buf));
584 buf[0] = 0x1; /* Report Number */
585 err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
586 ASSERT_LT(err, 0) TH_LOG("unexpected success while reading HIDIOCGFEATURE: %d", err);
587 ASSERT_EQ(errno, 20) TH_LOG("unexpected error code while reading HIDIOCGFEATURE: %d",
588 errno);
589
590 /* remove our bpf program and check that we can now emit commands */
591
592 /* detach the program */
593 detach_bpf(self);
594
595 self->hidraw_fd = open_hidraw(&self->hid);
596 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
597
598 err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
599 ASSERT_GE(err, 0) TH_LOG("error while reading HIDIOCGFEATURE: %d", err);
600 }
601
602 /*
603 * Call hid_hw_raw_request against the given uhid device,
604 * check that the program is called and can issue the call
605 * to uhid and transform the answer.
606 */
TEST_F(hid_bpf,test_hid_change_raw_request_call)607 TEST_F(hid_bpf, test_hid_change_raw_request_call)
608 {
609 const struct test_program progs[] = {
610 { .name = "hid_test_hidraw_raw_request" },
611 };
612 __u8 buf[MAX_BUF_SIZE] = {0};
613 int err;
614
615 LOAD_PROGRAMS(progs);
616
617 /* emit hid_hw_raw_request from hidraw */
618 /* Get Feature */
619 memset(buf, 0, sizeof(buf));
620 buf[0] = 0x1; /* Report Number */
621 err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
622 ASSERT_EQ(err, 3) TH_LOG("unexpected returned size while reading HIDIOCGFEATURE: %d", err);
623
624 ASSERT_EQ(buf[0], 2);
625 ASSERT_EQ(buf[1], 3);
626 ASSERT_EQ(buf[2], 4);
627 }
628
629 /*
630 * Call hid_hw_raw_request against the given uhid device,
631 * check that the program is not making infinite loops.
632 */
TEST_F(hid_bpf,test_hid_infinite_loop_raw_request_call)633 TEST_F(hid_bpf, test_hid_infinite_loop_raw_request_call)
634 {
635 const struct test_program progs[] = {
636 { .name = "hid_test_infinite_loop_raw_request" },
637 };
638 __u8 buf[MAX_BUF_SIZE] = {0};
639 int err;
640
641 LOAD_PROGRAMS(progs);
642
643 /* emit hid_hw_raw_request from hidraw */
644 /* Get Feature */
645 memset(buf, 0, sizeof(buf));
646 buf[0] = 0x1; /* Report Number */
647 err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
648 ASSERT_EQ(err, 3) TH_LOG("unexpected returned size while reading HIDIOCGFEATURE: %d", err);
649 }
650
651 /*
652 * Call hid_hw_output_report against the given uhid device,
653 * check that the program is called and prevents the
654 * call to uhid.
655 */
TEST_F(hid_bpf,test_hid_filter_output_report_call)656 TEST_F(hid_bpf, test_hid_filter_output_report_call)
657 {
658 const struct test_program progs[] = {
659 { .name = "hid_test_filter_output_report" },
660 };
661 __u8 buf[MAX_BUF_SIZE] = {0};
662 int err;
663
664 LOAD_PROGRAMS(progs);
665
666 /* first check that we did not attach to device_event */
667
668 /* inject one event */
669 buf[0] = 1;
670 buf[1] = 42;
671 uhid_send_event(_metadata, &self->hid, buf, 6);
672
673 /* read the data from hidraw */
674 memset(buf, 0, sizeof(buf));
675 err = read(self->hidraw_fd, buf, sizeof(buf));
676 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
677 ASSERT_EQ(buf[0], 1);
678 ASSERT_EQ(buf[1], 42);
679 ASSERT_EQ(buf[2], 0) TH_LOG("leftovers_from_previous_test");
680
681 /* now check that our program is preventing hid_hw_output_report() */
682
683 buf[0] = 1; /* report ID */
684 buf[1] = 2;
685 buf[2] = 42;
686
687 err = write(self->hidraw_fd, buf, 3);
688 ASSERT_LT(err, 0) TH_LOG("unexpected success while sending hid_hw_output_report: %d", err);
689 ASSERT_EQ(errno, 25) TH_LOG("unexpected error code while sending hid_hw_output_report: %d",
690 errno);
691
692 /* remove our bpf program and check that we can now emit commands */
693
694 /* detach the program */
695 detach_bpf(self);
696
697 self->hidraw_fd = open_hidraw(&self->hid);
698 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
699
700 err = write(self->hidraw_fd, buf, 3);
701 ASSERT_GE(err, 0) TH_LOG("error while sending hid_hw_output_report: %d", err);
702 }
703
704 /*
705 * Call hid_hw_output_report against the given uhid device,
706 * check that the program is called and can issue the call
707 * to uhid and transform the answer.
708 */
TEST_F(hid_bpf,test_hid_change_output_report_call)709 TEST_F(hid_bpf, test_hid_change_output_report_call)
710 {
711 const struct test_program progs[] = {
712 { .name = "hid_test_hidraw_output_report" },
713 };
714 __u8 buf[MAX_BUF_SIZE] = {0};
715 int err;
716
717 LOAD_PROGRAMS(progs);
718
719 /* emit hid_hw_output_report from hidraw */
720 buf[0] = 1; /* report ID */
721 buf[1] = 2;
722 buf[2] = 42;
723
724 err = write(self->hidraw_fd, buf, 10);
725 ASSERT_EQ(err, 2) TH_LOG("unexpected returned size while sending hid_hw_output_report: %d",
726 err);
727 }
728
729 /*
730 * Call hid_hw_output_report against the given uhid device,
731 * check that the program is not making infinite loops.
732 */
TEST_F(hid_bpf,test_hid_infinite_loop_output_report_call)733 TEST_F(hid_bpf, test_hid_infinite_loop_output_report_call)
734 {
735 const struct test_program progs[] = {
736 { .name = "hid_test_infinite_loop_output_report" },
737 };
738 __u8 buf[MAX_BUF_SIZE] = {0};
739 int err;
740
741 LOAD_PROGRAMS(progs);
742
743 /* emit hid_hw_output_report from hidraw */
744 buf[0] = 1; /* report ID */
745 buf[1] = 2;
746 buf[2] = 42;
747
748 err = write(self->hidraw_fd, buf, 8);
749 ASSERT_EQ(err, 2) TH_LOG("unexpected returned size while sending hid_hw_output_report: %d",
750 err);
751 }
752
753 /*
754 * Attach hid_multiply_event_wq to the given uhid device,
755 * retrieve and open the matching hidraw node,
756 * inject one event in the uhid device,
757 * check that the program sees it and can add extra data
758 */
TEST_F(hid_bpf,test_multiply_events_wq)759 TEST_F(hid_bpf, test_multiply_events_wq)
760 {
761 const struct test_program progs[] = {
762 { .name = "hid_test_multiply_events_wq" },
763 };
764 __u8 buf[MAX_BUF_SIZE] = {0};
765 int err;
766
767 LOAD_PROGRAMS(progs);
768
769 /* inject one event */
770 buf[0] = 1;
771 buf[1] = 42;
772 uhid_send_event(_metadata, &self->hid, buf, 6);
773
774 /* read the data from hidraw */
775 memset(buf, 0, sizeof(buf));
776 err = read(self->hidraw_fd, buf, sizeof(buf));
777 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
778 ASSERT_EQ(buf[0], 1);
779 ASSERT_EQ(buf[1], 47);
780
781 usleep(100000);
782
783 /* read the data from hidraw */
784 memset(buf, 0, sizeof(buf));
785 err = read(self->hidraw_fd, buf, sizeof(buf));
786 ASSERT_EQ(err, 9) TH_LOG("read_hidraw");
787 ASSERT_EQ(buf[0], 2);
788 ASSERT_EQ(buf[1], 3);
789 }
790
791 /*
792 * Attach hid_multiply_event to the given uhid device,
793 * retrieve and open the matching hidraw node,
794 * inject one event in the uhid device,
795 * check that the program sees it and can add extra data
796 */
TEST_F(hid_bpf,test_multiply_events)797 TEST_F(hid_bpf, test_multiply_events)
798 {
799 const struct test_program progs[] = {
800 { .name = "hid_test_multiply_events" },
801 };
802 __u8 buf[MAX_BUF_SIZE] = {0};
803 int err;
804
805 LOAD_PROGRAMS(progs);
806
807 /* inject one event */
808 buf[0] = 1;
809 buf[1] = 42;
810 uhid_send_event(_metadata, &self->hid, buf, 6);
811
812 /* read the data from hidraw */
813 memset(buf, 0, sizeof(buf));
814 err = read(self->hidraw_fd, buf, sizeof(buf));
815 ASSERT_EQ(err, 9) TH_LOG("read_hidraw");
816 ASSERT_EQ(buf[0], 2);
817 ASSERT_EQ(buf[1], 47);
818
819 /* read the data from hidraw */
820 memset(buf, 0, sizeof(buf));
821 err = read(self->hidraw_fd, buf, sizeof(buf));
822 ASSERT_EQ(err, 9) TH_LOG("read_hidraw");
823 ASSERT_EQ(buf[0], 2);
824 ASSERT_EQ(buf[1], 52);
825 }
826
827 /*
828 * Call hid_bpf_input_report against the given uhid device,
829 * check that the program is not making infinite loops.
830 */
TEST_F(hid_bpf,test_hid_infinite_loop_input_report_call)831 TEST_F(hid_bpf, test_hid_infinite_loop_input_report_call)
832 {
833 const struct test_program progs[] = {
834 { .name = "hid_test_infinite_loop_input_report" },
835 };
836 __u8 buf[MAX_BUF_SIZE] = {0};
837 int err;
838
839 LOAD_PROGRAMS(progs);
840
841 /* emit hid_hw_output_report from hidraw */
842 buf[0] = 1; /* report ID */
843 buf[1] = 2;
844 buf[2] = 42;
845
846 uhid_send_event(_metadata, &self->hid, buf, 6);
847
848 /* read the data from hidraw */
849 memset(buf, 0, sizeof(buf));
850 err = read(self->hidraw_fd, buf, sizeof(buf));
851 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
852 ASSERT_EQ(buf[0], 1);
853 ASSERT_EQ(buf[1], 3);
854
855 /* read the data from hidraw: hid_bpf_try_input_report should work exactly one time */
856 memset(buf, 0, sizeof(buf));
857 err = read(self->hidraw_fd, buf, sizeof(buf));
858 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
859 ASSERT_EQ(buf[0], 1);
860 ASSERT_EQ(buf[1], 4);
861
862 /* read the data from hidraw: there should be none */
863 memset(buf, 0, sizeof(buf));
864 err = read(self->hidraw_fd, buf, sizeof(buf));
865 ASSERT_EQ(err, -1) TH_LOG("read_hidraw");
866 }
867
868 /*
869 * Attach hid_insert{0,1,2} to the given uhid device,
870 * retrieve and open the matching hidraw node,
871 * inject one event in the uhid device,
872 * check that the programs have been inserted in the correct order.
873 */
TEST_F(hid_bpf,test_hid_attach_flags)874 TEST_F(hid_bpf, test_hid_attach_flags)
875 {
876 const struct test_program progs[] = {
877 {
878 .name = "hid_test_insert2",
879 .insert_head = 0,
880 },
881 {
882 .name = "hid_test_insert1",
883 .insert_head = 1,
884 },
885 {
886 .name = "hid_test_insert3",
887 .insert_head = 0,
888 },
889 };
890 __u8 buf[MAX_BUF_SIZE] = {0};
891 int err;
892
893 LOAD_PROGRAMS(progs);
894
895 /* inject one event */
896 buf[0] = 1;
897 uhid_send_event(_metadata, &self->hid, buf, 6);
898
899 /* read the data from hidraw */
900 memset(buf, 0, sizeof(buf));
901 err = read(self->hidraw_fd, buf, sizeof(buf));
902 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
903 ASSERT_EQ(buf[1], 1);
904 ASSERT_EQ(buf[2], 2);
905 ASSERT_EQ(buf[3], 3);
906 }
907
908 /*
909 * Attach hid_rdesc_fixup to the given uhid device,
910 * retrieve and open the matching hidraw node,
911 * check that the hidraw report descriptor has been updated.
912 */
TEST_F(hid_bpf,test_rdesc_fixup)913 TEST_F(hid_bpf, test_rdesc_fixup)
914 {
915 struct hidraw_report_descriptor rpt_desc = {0};
916 const struct test_program progs[] = {
917 { .name = "hid_rdesc_fixup" },
918 };
919 int err, desc_size;
920
921 if (variant->rdesc != rdesc)
922 SKIP(return, "not compatible report descriptor");
923
924 LOAD_PROGRAMS(progs);
925
926 /* check that hid_rdesc_fixup() was executed */
927 ASSERT_EQ(self->skel->data->callback2_check, 0x21);
928
929 /* read the exposed report descriptor from hidraw */
930 err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size);
931 ASSERT_GE(err, 0) TH_LOG("error while reading HIDIOCGRDESCSIZE: %d", err);
932
933 /* ensure the new size of the rdesc is bigger than the old one */
934 ASSERT_GT(desc_size, sizeof(rdesc));
935
936 rpt_desc.size = desc_size;
937 err = ioctl(self->hidraw_fd, HIDIOCGRDESC, &rpt_desc);
938 ASSERT_GE(err, 0) TH_LOG("error while reading HIDIOCGRDESC: %d", err);
939
940 ASSERT_EQ(rpt_desc.value[4], 0x42);
941 }
942
TEST_F(hid_bpf,test_rdesc_fixup_get_data_overflow)943 TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow)
944 {
945 const struct test_program progs[] = {
946 { .name = "hid_rdesc_fixup_get_data_overflow" },
947 };
948
949 /* newer verifier can detect the overflow at load time */
950 if (LOAD_PROGRAMS_MAY_FAIL(progs))
951 return;
952
953 ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1);
954 }
955
TEST_F(hid_bpf,test_rdesc_fixup_change_uniq_name_phys)956 TEST_F(hid_bpf, test_rdesc_fixup_change_uniq_name_phys)
957 {
958 const struct test_program progs[] = {
959 { .name = "hid_rdesc_fixup_change_uniq_name_phys" },
960 };
961 char expected[256], buf[256] = {};
962 int err;
963
964 LOAD_PROGRAMS(progs);
965
966 err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(buf)), buf);
967 ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWNAME");
968 ASSERT_STREQ("name coming from bpf", buf);
969
970 snprintf(expected, sizeof(expected), "%d phys:coming:from:bpf", self->hid.dev_id);
971
972 err = ioctl(self->hidraw_fd, HIDIOCGRAWPHYS(sizeof(buf)), buf);
973 ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWPHYS");
974 ASSERT_STREQ(expected, buf);
975
976 err = ioctl(self->hidraw_fd, HIDIOCGRAWUNIQ(sizeof(buf)), buf);
977 ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWUNIQ");
978 ASSERT_STREQ("uniq:coming:from:bpf", buf);
979
980 }
981
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)982 static int libbpf_print_fn(enum libbpf_print_level level,
983 const char *format, va_list args)
984 {
985 char buf[1024];
986
987 if (level == LIBBPF_DEBUG)
988 return 0;
989
990 snprintf(buf, sizeof(buf), "# %s", format);
991
992 vfprintf(stdout, buf, args);
993 return 0;
994 }
995
main(int argc,char ** argv)996 int main(int argc, char **argv)
997 {
998 /* Use libbpf 1.0 API mode */
999 libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
1000 libbpf_set_print(libbpf_print_fn);
1001
1002 return test_harness_run(argc, argv);
1003 }
1004