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