Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

selftests/hid: add tests for hid_hw_output_report HID-BPF hooks

We add 3 new tests:
- first, we make sure we can prevent the output_report to happen
- second, we make sure that we can detect that a given hidraw client
was actually doing the request, and for that client only, call ourself
hid_bpf_hw_output_report(), returning a custom value
- last, we ensure that we can not loop between hooks for
hid_hw_output_report() and manual calls to hid_bpf_hw_output_report()
from that same hook

Link: https://patch.msgid.link/20240626-hid_hw_req_bpf-v2-8-cfd60fb6c79f@kernel.org
Acked-by: Jiri Kosina <jkosina@suse.com>
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>

+165
+5
drivers/hid/bpf/hid_bpf_dispatch.c
··· 461 461 __bpf_kfunc int 462 462 hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) 463 463 { 464 + struct hid_bpf_ctx_kern *ctx_kern; 464 465 struct hid_device *hdev; 465 466 size_t size = buf__sz; 466 467 u8 *dma_data; 467 468 int ret; 469 + 470 + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); 471 + if (ctx_kern->from_bpf) 472 + return -EDEADLOCK; 468 473 469 474 /* check arguments */ 470 475 ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT);
+102
tools/testing/selftests/hid/hid_bpf.c
··· 1029 1029 } 1030 1030 1031 1031 /* 1032 + * Call hid_hw_output_report against the given uhid device, 1033 + * check that the program is called and prevents the 1034 + * call to uhid. 1035 + */ 1036 + TEST_F(hid_bpf, test_hid_filter_output_report_call) 1037 + { 1038 + const struct test_program progs[] = { 1039 + { .name = "hid_test_filter_output_report" }, 1040 + }; 1041 + __u8 buf[10] = {0}; 1042 + int err; 1043 + 1044 + LOAD_PROGRAMS(progs); 1045 + 1046 + /* first check that we did not attach to device_event */ 1047 + 1048 + /* inject one event */ 1049 + buf[0] = 1; 1050 + buf[1] = 42; 1051 + uhid_send_event(_metadata, self->uhid_fd, buf, 6); 1052 + 1053 + /* read the data from hidraw */ 1054 + memset(buf, 0, sizeof(buf)); 1055 + err = read(self->hidraw_fd, buf, sizeof(buf)); 1056 + ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 1057 + ASSERT_EQ(buf[0], 1); 1058 + ASSERT_EQ(buf[1], 42); 1059 + ASSERT_EQ(buf[2], 0) TH_LOG("leftovers_from_previous_test"); 1060 + 1061 + /* now check that our program is preventing hid_hw_output_report() */ 1062 + 1063 + buf[0] = 1; /* report ID */ 1064 + buf[1] = 2; 1065 + buf[2] = 42; 1066 + 1067 + err = write(self->hidraw_fd, buf, 3); 1068 + ASSERT_LT(err, 0) TH_LOG("unexpected success while sending hid_hw_output_report: %d", err); 1069 + ASSERT_EQ(errno, 25) TH_LOG("unexpected error code while sending hid_hw_output_report: %d", 1070 + errno); 1071 + 1072 + /* remove our bpf program and check that we can now emit commands */ 1073 + 1074 + /* detach the program */ 1075 + detach_bpf(self); 1076 + 1077 + self->hidraw_fd = open_hidraw(self->dev_id); 1078 + ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); 1079 + 1080 + err = write(self->hidraw_fd, buf, 3); 1081 + ASSERT_GE(err, 0) TH_LOG("error while sending hid_hw_output_report: %d", err); 1082 + } 1083 + 1084 + /* 1085 + * Call hid_hw_output_report against the given uhid device, 1086 + * check that the program is called and can issue the call 1087 + * to uhid and transform the answer. 1088 + */ 1089 + TEST_F(hid_bpf, test_hid_change_output_report_call) 1090 + { 1091 + const struct test_program progs[] = { 1092 + { .name = "hid_test_hidraw_output_report" }, 1093 + }; 1094 + __u8 buf[10] = {0}; 1095 + int err; 1096 + 1097 + LOAD_PROGRAMS(progs); 1098 + 1099 + /* emit hid_hw_output_report from hidraw */ 1100 + buf[0] = 1; /* report ID */ 1101 + buf[1] = 2; 1102 + buf[2] = 42; 1103 + 1104 + err = write(self->hidraw_fd, buf, 10); 1105 + ASSERT_EQ(err, 2) TH_LOG("unexpected returned size while sending hid_hw_output_report: %d", 1106 + err); 1107 + } 1108 + 1109 + /* 1110 + * Call hid_hw_output_report against the given uhid device, 1111 + * check that the program is not making infinite loops. 1112 + */ 1113 + TEST_F(hid_bpf, test_hid_infinite_loop_output_report_call) 1114 + { 1115 + const struct test_program progs[] = { 1116 + { .name = "hid_test_infinite_loop_output_report" }, 1117 + }; 1118 + __u8 buf[10] = {0}; 1119 + int err; 1120 + 1121 + LOAD_PROGRAMS(progs); 1122 + 1123 + /* emit hid_hw_output_report from hidraw */ 1124 + buf[0] = 1; /* report ID */ 1125 + buf[1] = 2; 1126 + buf[2] = 42; 1127 + 1128 + err = write(self->hidraw_fd, buf, 8); 1129 + ASSERT_EQ(err, 2) TH_LOG("unexpected returned size while sending hid_hw_output_report: %d", 1130 + err); 1131 + } 1132 + 1133 + /* 1032 1134 * Attach hid_insert{0,1,2} to the given uhid device, 1033 1135 * retrieve and open the matching hidraw node, 1034 1136 * inject one event in the uhid device,
+58
tools/testing/selftests/hid/progs/hid.c
··· 385 385 struct hid_bpf_ops test_infinite_loop_raw_request = { 386 386 .hid_hw_request = (void *)hid_test_infinite_loop_raw_request, 387 387 }; 388 + 389 + SEC("?struct_ops/hid_hw_output_report") 390 + int BPF_PROG(hid_test_filter_output_report, struct hid_bpf_ctx *hctx, unsigned char reportnum, 391 + enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) 392 + { 393 + return -25; 394 + } 395 + 396 + SEC(".struct_ops.link") 397 + struct hid_bpf_ops test_filter_output_report = { 398 + .hid_hw_output_report = (void *)hid_test_filter_output_report, 399 + }; 400 + 401 + SEC("?struct_ops.s/hid_hw_output_report") 402 + int BPF_PROG(hid_test_hidraw_output_report, struct hid_bpf_ctx *hctx, __u64 source) 403 + { 404 + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); 405 + int ret; 406 + 407 + if (!data) 408 + return 0; /* EPERM check */ 409 + 410 + /* check if the incoming request comes from our hidraw operation */ 411 + if (source == (__u64)current_file) 412 + return hid_bpf_hw_output_report(hctx, data, 2); 413 + 414 + return 0; 415 + } 416 + 417 + SEC(".struct_ops.link") 418 + struct hid_bpf_ops test_hidraw_output_report = { 419 + .hid_hw_output_report = (void *)hid_test_hidraw_output_report, 420 + }; 421 + 422 + SEC("?struct_ops.s/hid_hw_output_report") 423 + int BPF_PROG(hid_test_infinite_loop_output_report, struct hid_bpf_ctx *hctx, __u64 source) 424 + { 425 + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); 426 + int ret; 427 + 428 + if (!data) 429 + return 0; /* EPERM check */ 430 + 431 + /* always forward the request as-is to the device, hid-bpf should prevent 432 + * infinite loops. 433 + */ 434 + 435 + ret = hid_bpf_hw_output_report(hctx, data, 2); 436 + if (ret == 2) 437 + return 2; 438 + 439 + return 0; 440 + } 441 + 442 + SEC(".struct_ops.link") 443 + struct hid_bpf_ops test_infinite_loop_output_report = { 444 + .hid_hw_output_report = (void *)hid_test_infinite_loop_output_report, 445 + };