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_raw_request HID-BPF hooks

We add 3 new tests:
- first, we make sure we can prevent the raw_request 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_request(), returning a custom value
- last, we ensure that we can not loop between hooks for
hid_hw_raw_request() and manual calls to hid_bpf_hw_request() from that
hook

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

+188
+109
tools/testing/selftests/hid/hid_bpf.c
··· 470 470 close(self->hidraw_fd); 471 471 self->hidraw_fd = 0; 472 472 473 + if (!self->skel) 474 + return; 475 + 476 + hid__detach(self->skel); 477 + 473 478 for (i = 0; i < ARRAY_SIZE(self->hid_links); i++) { 474 479 if (self->hid_links[i]) 475 480 bpf_link__destroy(self->hid_links[i]); ··· 579 574 ASSERT_OK_PTR(self->hid_links[i]) TH_LOG("failed to attach struct ops '%s'", 580 575 programs[i].name + 4); 581 576 } 577 + 578 + hid__attach(self->skel); 582 579 583 580 self->hidraw_fd = open_hidraw(self->dev_id); 584 581 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); ··· 924 917 ASSERT_EQ(args.retval, 2); 925 918 926 919 ASSERT_EQ(args.data[1], 2); 920 + } 921 + 922 + /* 923 + * Call hid_hw_raw_request against the given uhid device, 924 + * check that the program is called and prevents the 925 + * call to uhid. 926 + */ 927 + TEST_F(hid_bpf, test_hid_filter_raw_request_call) 928 + { 929 + const struct test_program progs[] = { 930 + { .name = "hid_test_filter_raw_request" }, 931 + }; 932 + __u8 buf[10] = {0}; 933 + int err; 934 + 935 + LOAD_PROGRAMS(progs); 936 + 937 + /* first check that we did not attach to device_event */ 938 + 939 + /* inject one event */ 940 + buf[0] = 1; 941 + buf[1] = 42; 942 + uhid_send_event(_metadata, self->uhid_fd, buf, 6); 943 + 944 + /* read the data from hidraw */ 945 + memset(buf, 0, sizeof(buf)); 946 + err = read(self->hidraw_fd, buf, sizeof(buf)); 947 + ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 948 + ASSERT_EQ(buf[0], 1); 949 + ASSERT_EQ(buf[1], 42); 950 + ASSERT_EQ(buf[2], 0) TH_LOG("leftovers_from_previous_test"); 951 + 952 + /* now check that our program is preventing hid_hw_raw_request() */ 953 + 954 + /* emit hid_hw_raw_request from hidraw */ 955 + /* Get Feature */ 956 + memset(buf, 0, sizeof(buf)); 957 + buf[0] = 0x1; /* Report Number */ 958 + err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); 959 + ASSERT_LT(err, 0) TH_LOG("unexpected success while reading HIDIOCGFEATURE: %d", err); 960 + ASSERT_EQ(errno, 20) TH_LOG("unexpected error code while reading HIDIOCGFEATURE: %d", 961 + errno); 962 + 963 + /* remove our bpf program and check that we can now emit commands */ 964 + 965 + /* detach the program */ 966 + detach_bpf(self); 967 + 968 + self->hidraw_fd = open_hidraw(self->dev_id); 969 + ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); 970 + 971 + err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); 972 + ASSERT_GE(err, 0) TH_LOG("error while reading HIDIOCGFEATURE: %d", err); 973 + } 974 + 975 + /* 976 + * Call hid_hw_raw_request against the given uhid device, 977 + * check that the program is called and can issue the call 978 + * to uhid and transform the answer. 979 + */ 980 + TEST_F(hid_bpf, test_hid_change_raw_request_call) 981 + { 982 + const struct test_program progs[] = { 983 + { .name = "hid_test_hidraw_raw_request" }, 984 + }; 985 + __u8 buf[10] = {0}; 986 + int err; 987 + 988 + LOAD_PROGRAMS(progs); 989 + 990 + /* emit hid_hw_raw_request from hidraw */ 991 + /* Get Feature */ 992 + memset(buf, 0, sizeof(buf)); 993 + buf[0] = 0x1; /* Report Number */ 994 + err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); 995 + ASSERT_EQ(err, 3) TH_LOG("unexpected returned size while reading HIDIOCGFEATURE: %d", err); 996 + 997 + ASSERT_EQ(buf[0], 2); 998 + ASSERT_EQ(buf[1], 3); 999 + ASSERT_EQ(buf[2], 4); 1000 + } 1001 + 1002 + /* 1003 + * Call hid_hw_raw_request against the given uhid device, 1004 + * check that the program is not making infinite loops. 1005 + */ 1006 + TEST_F(hid_bpf, test_hid_infinite_loop_raw_request_call) 1007 + { 1008 + const struct test_program progs[] = { 1009 + { .name = "hid_test_infinite_loop_raw_request" }, 1010 + }; 1011 + __u8 buf[10] = {0}; 1012 + int err; 1013 + 1014 + LOAD_PROGRAMS(progs); 1015 + 1016 + /* emit hid_hw_raw_request from hidraw */ 1017 + /* Get Feature */ 1018 + memset(buf, 0, sizeof(buf)); 1019 + buf[0] = 0x1; /* Report Number */ 1020 + err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); 1021 + ASSERT_EQ(err, 3) TH_LOG("unexpected returned size while reading HIDIOCGFEATURE: %d", err); 927 1022 } 928 1023 929 1024 /*
+79
tools/testing/selftests/hid/progs/hid.c
··· 306 306 struct hid_bpf_ops test_insert3 = { 307 307 .hid_device_event = (void *)hid_test_insert3, 308 308 }; 309 + 310 + SEC("?struct_ops/hid_hw_request") 311 + int BPF_PROG(hid_test_filter_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum, 312 + enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) 313 + { 314 + return -20; 315 + } 316 + 317 + SEC(".struct_ops.link") 318 + struct hid_bpf_ops test_filter_raw_request = { 319 + .hid_hw_request = (void *)hid_test_filter_raw_request, 320 + }; 321 + 322 + static struct file *current_file; 323 + 324 + SEC("fentry/hidraw_open") 325 + int BPF_PROG(hidraw_open, struct inode *inode, struct file *file) 326 + { 327 + current_file = file; 328 + return 0; 329 + } 330 + 331 + SEC("?struct_ops.s/hid_hw_request") 332 + int BPF_PROG(hid_test_hidraw_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum, 333 + enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) 334 + { 335 + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); 336 + int ret; 337 + 338 + if (!data) 339 + return 0; /* EPERM check */ 340 + 341 + /* check if the incoming request comes from our hidraw operation */ 342 + if (source == (__u64)current_file) { 343 + data[0] = reportnum; 344 + 345 + ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype); 346 + if (ret != 2) 347 + return -1; 348 + data[0] = reportnum + 1; 349 + data[1] = reportnum + 2; 350 + data[2] = reportnum + 3; 351 + return 3; 352 + } 353 + 354 + return 0; 355 + } 356 + 357 + SEC(".struct_ops.link") 358 + struct hid_bpf_ops test_hidraw_raw_request = { 359 + .hid_hw_request = (void *)hid_test_hidraw_raw_request, 360 + }; 361 + 362 + SEC("?struct_ops.s/hid_hw_request") 363 + int BPF_PROG(hid_test_infinite_loop_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum, 364 + enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) 365 + { 366 + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); 367 + int ret; 368 + 369 + if (!data) 370 + return 0; /* EPERM check */ 371 + 372 + /* always forward the request as-is to the device, hid-bpf should prevent 373 + * infinite loops. 374 + */ 375 + data[0] = reportnum; 376 + 377 + ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype); 378 + if (ret == 2) 379 + return 3; 380 + 381 + return 0; 382 + } 383 + 384 + SEC(".struct_ops.link") 385 + struct hid_bpf_ops test_infinite_loop_raw_request = { 386 + .hid_hw_request = (void *)hid_test_infinite_loop_raw_request, 387 + };