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