FILE
hid.c
File preview with syntax highlighting.
linux-6.19.14/tools/testing/selftests/hid/progs/hid.c
1// SPDX-License-Identifier: GPL-2.0 2/* Copyright (c) 2022 Red hat */ 3#include "hid_bpf_helpers.h" 4 5char _license[] SEC("license") = "GPL"; 6 7struct attach_prog_args { 8 int prog_fd; 9 unsigned int hid; 10 int retval; 11 int insert_head; 12}; 13 14__u64 callback_check = 52; 15__u64 callback2_check = 52; 16 17SEC("?struct_ops/hid_device_event") 18int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 19{ 20 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */); 21 22 if (!rw_data) 23 return 0; /* EPERM check */ 24 25 callback_check = rw_data[1]; 26 27 rw_data[2] = rw_data[1] + 5; 28 29 return hid_ctx->size; 30} 31 32SEC(".struct_ops.link") 33struct hid_bpf_ops first_event = { 34 .hid_device_event = (void *)hid_first_event, 35 .hid_id = 2, 36}; 37 38int __hid_subprog_first_event(struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 39{ 40 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */); 41 42 if (!rw_data) 43 return 0; /* EPERM check */ 44 45 rw_data[2] = rw_data[1] + 5; 46 47 return hid_ctx->size; 48} 49 50SEC("?struct_ops/hid_device_event") 51int BPF_PROG(hid_subprog_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 52{ 53 return __hid_subprog_first_event(hid_ctx, type); 54} 55 56SEC(".struct_ops.link") 57struct hid_bpf_ops subprog_first_event = { 58 .hid_device_event = (void *)hid_subprog_first_event, 59 .hid_id = 2, 60}; 61 62SEC("?struct_ops/hid_device_event") 63int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 64{ 65 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 66 67 if (!rw_data) 68 return 0; /* EPERM check */ 69 70 rw_data[3] = rw_data[2] + 5; 71 72 return hid_ctx->size; 73} 74 75SEC(".struct_ops.link") 76struct hid_bpf_ops second_event = { 77 .hid_device_event = (void *)hid_second_event, 78}; 79 80SEC("?struct_ops/hid_device_event") 81int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 82{ 83 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */); 84 85 if (!rw_data) 86 return 0; /* EPERM check */ 87 88 rw_data[0] = 2; 89 90 return 9; 91} 92 93SEC(".struct_ops.link") 94struct hid_bpf_ops change_report_id = { 95 .hid_device_event = (void *)hid_change_report_id, 96}; 97 98struct hid_hw_request_syscall_args { 99 /* data needs to come at offset 0 so we can use it in calls */ 100 __u8 data[10]; 101 unsigned int hid; 102 int retval; 103 size_t size; 104 enum hid_report_type type; 105 __u8 request_type; 106}; 107 108SEC("syscall") 109int hid_user_raw_request(struct hid_hw_request_syscall_args *args) 110{ 111 struct hid_bpf_ctx *ctx; 112 const size_t size = args->size; 113 int i, ret = 0; 114 115 if (size > sizeof(args->data)) 116 return -7; /* -E2BIG */ 117 118 ctx = hid_bpf_allocate_context(args->hid); 119 if (!ctx) 120 return -1; /* EPERM check */ 121 122 ret = hid_bpf_hw_request(ctx, 123 args->data, 124 size, 125 args->type, 126 args->request_type); 127 args->retval = ret; 128 129 hid_bpf_release_context(ctx); 130 131 return 0; 132} 133 134SEC("syscall") 135int hid_user_output_report(struct hid_hw_request_syscall_args *args) 136{ 137 struct hid_bpf_ctx *ctx; 138 const size_t size = args->size; 139 int i, ret = 0; 140 141 if (size > sizeof(args->data)) 142 return -7; /* -E2BIG */ 143 144 ctx = hid_bpf_allocate_context(args->hid); 145 if (!ctx) 146 return -1; /* EPERM check */ 147 148 ret = hid_bpf_hw_output_report(ctx, 149 args->data, 150 size); 151 args->retval = ret; 152 153 hid_bpf_release_context(ctx); 154 155 return 0; 156} 157 158SEC("syscall") 159int hid_user_input_report(struct hid_hw_request_syscall_args *args) 160{ 161 struct hid_bpf_ctx *ctx; 162 const size_t size = args->size; 163 int i, ret = 0; 164 165 if (size > sizeof(args->data)) 166 return -7; /* -E2BIG */ 167 168 ctx = hid_bpf_allocate_context(args->hid); 169 if (!ctx) 170 return -1; /* EPERM check */ 171 172 ret = hid_bpf_input_report(ctx, HID_INPUT_REPORT, args->data, size); 173 args->retval = ret; 174 175 hid_bpf_release_context(ctx); 176 177 return 0; 178} 179 180static const __u8 rdesc[] = { 181 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 182 0x09, 0x32, /* USAGE (Z) */ 183 0x95, 0x01, /* REPORT_COUNT (1) */ 184 0x81, 0x06, /* INPUT (Data,Var,Rel) */ 185 186 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 187 0x19, 0x01, /* USAGE_MINIMUM (1) */ 188 0x29, 0x03, /* USAGE_MAXIMUM (3) */ 189 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 190 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 191 0x95, 0x03, /* REPORT_COUNT (3) */ 192 0x75, 0x01, /* REPORT_SIZE (1) */ 193 0x91, 0x02, /* Output (Data,Var,Abs) */ 194 0x95, 0x01, /* REPORT_COUNT (1) */ 195 0x75, 0x05, /* REPORT_SIZE (5) */ 196 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 197 198 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 199 0x19, 0x06, /* USAGE_MINIMUM (6) */ 200 0x29, 0x08, /* USAGE_MAXIMUM (8) */ 201 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 202 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 203 0x95, 0x03, /* REPORT_COUNT (3) */ 204 0x75, 0x01, /* REPORT_SIZE (1) */ 205 0xb1, 0x02, /* Feature (Data,Var,Abs) */ 206 0x95, 0x01, /* REPORT_COUNT (1) */ 207 0x75, 0x05, /* REPORT_SIZE (5) */ 208 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 209 210 0xc0, /* END_COLLECTION */ 211 0xc0, /* END_COLLECTION */ 212}; 213 214/* 215 * the following program is marked as sleepable (struct_ops.s). 216 * This is not strictly mandatory but is a nice test for 217 * sleepable struct_ops 218 */ 219SEC("?struct_ops.s/hid_rdesc_fixup") 220int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx) 221{ 222 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */); 223 224 if (!data) 225 return 0; /* EPERM check */ 226 227 callback2_check = data[4]; 228 229 /* insert rdesc at offset 73 */ 230 __builtin_memcpy(&data[73], rdesc, sizeof(rdesc)); 231 232 /* Change Usage Vendor globally */ 233 data[4] = 0x42; 234 235 return sizeof(rdesc) + 73; 236} 237 238SEC(".struct_ops.link") 239struct hid_bpf_ops rdesc_fixup = { 240 .hid_rdesc_fixup = (void *)hid_rdesc_fixup, 241}; 242 243SEC("?struct_ops/hid_device_event") 244int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 245{ 246 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 247 248 if (!data) 249 return 0; /* EPERM check */ 250 251 /* we need to be run first */ 252 if (data[2] || data[3]) 253 return -1; 254 255 data[1] = 1; 256 257 return 0; 258} 259 260SEC(".struct_ops.link") 261struct hid_bpf_ops test_insert1 = { 262 .hid_device_event = (void *)hid_test_insert1, 263 .flags = BPF_F_BEFORE, 264}; 265 266SEC("?struct_ops/hid_device_event") 267int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 268{ 269 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 270 271 if (!data) 272 return 0; /* EPERM check */ 273 274 /* after insert0 and before insert2 */ 275 if (!data[1] || data[3]) 276 return -1; 277 278 data[2] = 2; 279 280 return 0; 281} 282 283SEC(".struct_ops.link") 284struct hid_bpf_ops test_insert2 = { 285 .hid_device_event = (void *)hid_test_insert2, 286}; 287 288SEC("?struct_ops/hid_device_event") 289int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 290{ 291 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 292 293 if (!data) 294 return 0; /* EPERM check */ 295 296 /* at the end */ 297 if (!data[1] || !data[2]) 298 return -1; 299 300 data[3] = 3; 301 302 return 0; 303} 304 305SEC(".struct_ops.link") 306struct hid_bpf_ops test_insert3 = { 307 .hid_device_event = (void *)hid_test_insert3, 308}; 309 310SEC("?struct_ops/hid_hw_request") 311int 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 317SEC(".struct_ops.link") 318struct hid_bpf_ops test_filter_raw_request = { 319 .hid_hw_request = (void *)hid_test_filter_raw_request, 320}; 321 322static struct file *current_file; 323 324SEC("fentry/hidraw_open") 325int BPF_PROG(hidraw_open, struct inode *inode, struct file *file) 326{ 327 current_file = file; 328 return 0; 329} 330 331SEC("?struct_ops.s/hid_hw_request") 332int 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 357SEC(".struct_ops.link") 358struct hid_bpf_ops test_hidraw_raw_request = { 359 .hid_hw_request = (void *)hid_test_hidraw_raw_request, 360}; 361 362SEC("?struct_ops.s/hid_hw_request") 363int 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 384SEC(".struct_ops.link") 385struct hid_bpf_ops test_infinite_loop_raw_request = { 386 .hid_hw_request = (void *)hid_test_infinite_loop_raw_request, 387}; 388 389SEC("?struct_ops/hid_hw_output_report") 390int 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 396SEC(".struct_ops.link") 397struct hid_bpf_ops test_filter_output_report = { 398 .hid_hw_output_report = (void *)hid_test_filter_output_report, 399}; 400 401SEC("?struct_ops.s/hid_hw_output_report") 402int 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 417SEC(".struct_ops.link") 418struct hid_bpf_ops test_hidraw_output_report = { 419 .hid_hw_output_report = (void *)hid_test_hidraw_output_report, 420}; 421 422SEC("?struct_ops.s/hid_hw_output_report") 423int 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 442SEC(".struct_ops.link") 443struct hid_bpf_ops test_infinite_loop_output_report = { 444 .hid_hw_output_report = (void *)hid_test_infinite_loop_output_report, 445}; 446 447struct elem { 448 struct bpf_wq work; 449}; 450 451struct { 452 __uint(type, BPF_MAP_TYPE_HASH); 453 __uint(max_entries, 1); 454 __type(key, int); 455 __type(value, struct elem); 456} hmap SEC(".maps"); 457 458static int wq_cb_sleepable(void *map, int *key, void *work) 459{ 460 __u8 buf[9] = {2, 3, 4, 5, 6, 7, 8, 9, 10}; 461 struct hid_bpf_ctx *hid_ctx; 462 463 hid_ctx = hid_bpf_allocate_context(*key); 464 if (!hid_ctx) 465 return 0; /* EPERM check */ 466 467 hid_bpf_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf)); 468 469 hid_bpf_release_context(hid_ctx); 470 471 return 0; 472} 473 474static int test_inject_input_report_callback(int *key) 475{ 476 struct elem init = {}, *val; 477 struct bpf_wq *wq; 478 479 if (bpf_map_update_elem(&hmap, key, &init, 0)) 480 return -1; 481 482 val = bpf_map_lookup_elem(&hmap, key); 483 if (!val) 484 return -2; 485 486 wq = &val->work; 487 if (bpf_wq_init(wq, &hmap, 0) != 0) 488 return -3; 489 490 if (bpf_wq_set_callback(wq, wq_cb_sleepable, 0)) 491 return -4; 492 493 if (bpf_wq_start(wq, 0)) 494 return -5; 495 496 return 0; 497} 498 499SEC("?struct_ops/hid_device_event") 500int BPF_PROG(hid_test_multiply_events_wq, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 501{ 502 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */); 503 int hid = hid_ctx->hid->id; 504 int ret; 505 506 if (!data) 507 return 0; /* EPERM check */ 508 509 if (data[0] != 1) 510 return 0; 511 512 ret = test_inject_input_report_callback(&hid); 513 if (ret) 514 return ret; 515 516 data[1] += 5; 517 518 return 0; 519} 520 521SEC(".struct_ops.link") 522struct hid_bpf_ops test_multiply_events_wq = { 523 .hid_device_event = (void *)hid_test_multiply_events_wq, 524}; 525 526SEC("?struct_ops/hid_device_event") 527int BPF_PROG(hid_test_multiply_events, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 528{ 529 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */); 530 __u8 buf[9]; 531 int ret; 532 533 if (!data) 534 return 0; /* EPERM check */ 535 536 if (data[0] != 1) 537 return 0; 538 539 /* 540 * we have to use an intermediate buffer as hid_bpf_input_report 541 * will memset data to \0 542 */ 543 __builtin_memcpy(buf, data, sizeof(buf)); 544 545 buf[0] = 2; 546 buf[1] += 5; 547 ret = hid_bpf_try_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf)); 548 if (ret < 0) 549 return ret; 550 551 /* 552 * In real world we should reset the original buffer as data might be garbage now, 553 * but it actually now has the content of 'buf' 554 */ 555 data[1] += 5; 556 557 return 9; 558} 559 560SEC(".struct_ops.link") 561struct hid_bpf_ops test_multiply_events = { 562 .hid_device_event = (void *)hid_test_multiply_events, 563}; 564 565SEC("?struct_ops/hid_device_event") 566int BPF_PROG(hid_test_infinite_loop_input_report, struct hid_bpf_ctx *hctx, 567 enum hid_report_type report_type, __u64 source) 568{ 569 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 6 /* size */); 570 __u8 buf[6]; 571 572 if (!data) 573 return 0; /* EPERM check */ 574 575 /* 576 * we have to use an intermediate buffer as hid_bpf_input_report 577 * will memset data to \0 578 */ 579 __builtin_memcpy(buf, data, sizeof(buf)); 580 581 /* always forward the request as-is to the device, hid-bpf should prevent 582 * infinite loops. 583 * the return value is ignored so the event is passing to userspace. 584 */ 585 586 hid_bpf_try_input_report(hctx, report_type, buf, sizeof(buf)); 587 588 /* each time we process the event, we increment by one data[1]: 589 * after each successful call to hid_bpf_try_input_report, buf 590 * has been memcopied into data by the kernel. 591 */ 592 data[1] += 1; 593 594 return 0; 595} 596 597SEC(".struct_ops.link") 598struct hid_bpf_ops test_infinite_loop_input_report = { 599 .hid_device_event = (void *)hid_test_infinite_loop_input_report, 600};
~/
Read-only file preview.