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/hid_device_event") 259 int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 260 { 261 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 262 263 if (!data) 264 return 0; /* EPERM check */ 265 266 /* we need to be run first */ 267 if (data[2] || data[3]) 268 return -1; 269 270 data[1] = 1; 271 272 return 0; 273 } 274 275 SEC(".struct_ops.link") 276 struct hid_bpf_ops test_insert1 = { 277 .hid_device_event = (void *)hid_test_insert1, 278 .flags = BPF_F_BEFORE, 279 }; 280 281 SEC("?struct_ops/hid_device_event") 282 int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 283 { 284 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 285 286 if (!data) 287 return 0; /* EPERM check */ 288 289 /* after insert0 and before insert2 */ 290 if (!data[1] || data[3]) 291 return -1; 292 293 data[2] = 2; 294 295 return 0; 296 } 297 298 SEC(".struct_ops.link") 299 struct hid_bpf_ops test_insert2 = { 300 .hid_device_event = (void *)hid_test_insert2, 301 }; 302 303 SEC("?struct_ops/hid_device_event") 304 int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 305 { 306 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); 307 308 if (!data) 309 return 0; /* EPERM check */ 310 311 /* at the end */ 312 if (!data[1] || !data[2]) 313 return -1; 314 315 data[3] = 3; 316 317 return 0; 318 } 319 320 SEC(".struct_ops.link") 321 struct hid_bpf_ops test_insert3 = { 322 .hid_device_event = (void *)hid_test_insert3, 323 }; 324 325 SEC("?struct_ops/hid_hw_request") 326 int BPF_PROG(hid_test_filter_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum, 327 enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) 328 { 329 return -20; 330 } 331 332 SEC(".struct_ops.link") 333 struct hid_bpf_ops test_filter_raw_request = { 334 .hid_hw_request = (void *)hid_test_filter_raw_request, 335 }; 336 337 static struct file *current_file; 338 339 SEC("fentry/hidraw_open") 340 int BPF_PROG(hidraw_open, struct inode *inode, struct file *file) 341 { 342 current_file = file; 343 return 0; 344 } 345 346 SEC("?struct_ops.s/hid_hw_request") 347 int BPF_PROG(hid_test_hidraw_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum, 348 enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) 349 { 350 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); 351 int ret; 352 353 if (!data) 354 return 0; /* EPERM check */ 355 356 /* check if the incoming request comes from our hidraw operation */ 357 if (source == (__u64)current_file) { 358 data[0] = reportnum; 359 360 ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype); 361 if (ret != 2) 362 return -1; 363 data[0] = reportnum + 1; 364 data[1] = reportnum + 2; 365 data[2] = reportnum + 3; 366 return 3; 367 } 368 369 return 0; 370 } 371 372 SEC(".struct_ops.link") 373 struct hid_bpf_ops test_hidraw_raw_request = { 374 .hid_hw_request = (void *)hid_test_hidraw_raw_request, 375 }; 376 377 SEC("?struct_ops.s/hid_hw_request") 378 int BPF_PROG(hid_test_infinite_loop_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum, 379 enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) 380 { 381 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); 382 int ret; 383 384 if (!data) 385 return 0; /* EPERM check */ 386 387 /* always forward the request as-is to the device, hid-bpf should prevent 388 * infinite loops. 389 */ 390 data[0] = reportnum; 391 392 ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype); 393 if (ret == 2) 394 return 3; 395 396 return 0; 397 } 398 399 SEC(".struct_ops.link") 400 struct hid_bpf_ops test_infinite_loop_raw_request = { 401 .hid_hw_request = (void *)hid_test_infinite_loop_raw_request, 402 }; 403 404 SEC("?struct_ops/hid_hw_output_report") 405 int BPF_PROG(hid_test_filter_output_report, struct hid_bpf_ctx *hctx, unsigned char reportnum, 406 enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) 407 { 408 return -25; 409 } 410 411 SEC(".struct_ops.link") 412 struct hid_bpf_ops test_filter_output_report = { 413 .hid_hw_output_report = (void *)hid_test_filter_output_report, 414 }; 415 416 SEC("?struct_ops.s/hid_hw_output_report") 417 int BPF_PROG(hid_test_hidraw_output_report, struct hid_bpf_ctx *hctx, __u64 source) 418 { 419 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); 420 int ret; 421 422 if (!data) 423 return 0; /* EPERM check */ 424 425 /* check if the incoming request comes from our hidraw operation */ 426 if (source == (__u64)current_file) 427 return hid_bpf_hw_output_report(hctx, data, 2); 428 429 return 0; 430 } 431 432 SEC(".struct_ops.link") 433 struct hid_bpf_ops test_hidraw_output_report = { 434 .hid_hw_output_report = (void *)hid_test_hidraw_output_report, 435 }; 436 437 SEC("?struct_ops.s/hid_hw_output_report") 438 int BPF_PROG(hid_test_infinite_loop_output_report, struct hid_bpf_ctx *hctx, __u64 source) 439 { 440 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); 441 int ret; 442 443 if (!data) 444 return 0; /* EPERM check */ 445 446 /* always forward the request as-is to the device, hid-bpf should prevent 447 * infinite loops. 448 */ 449 450 ret = hid_bpf_hw_output_report(hctx, data, 2); 451 if (ret == 2) 452 return 2; 453 454 return 0; 455 } 456 457 SEC(".struct_ops.link") 458 struct hid_bpf_ops test_infinite_loop_output_report = { 459 .hid_hw_output_report = (void *)hid_test_infinite_loop_output_report, 460 }; 461 462 struct elem { 463 struct bpf_wq work; 464 }; 465 466 struct { 467 __uint(type, BPF_MAP_TYPE_HASH); 468 __uint(max_entries, 1); 469 __type(key, int); 470 __type(value, struct elem); 471 } hmap SEC(".maps"); 472 473 static int wq_cb_sleepable(void *map, int *key, void *work) 474 { 475 __u8 buf[9] = {2, 3, 4, 5, 6, 7, 8, 9, 10}; 476 struct hid_bpf_ctx *hid_ctx; 477 478 hid_ctx = hid_bpf_allocate_context(*key); 479 if (!hid_ctx) 480 return 0; /* EPERM check */ 481 482 hid_bpf_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf)); 483 484 hid_bpf_release_context(hid_ctx); 485 486 return 0; 487 } 488 489 static int test_inject_input_report_callback(int *key) 490 { 491 struct elem init = {}, *val; 492 struct bpf_wq *wq; 493 494 if (bpf_map_update_elem(&hmap, key, &init, 0)) 495 return -1; 496 497 val = bpf_map_lookup_elem(&hmap, key); 498 if (!val) 499 return -2; 500 501 wq = &val->work; 502 if (bpf_wq_init(wq, &hmap, 0) != 0) 503 return -3; 504 505 if (bpf_wq_set_callback(wq, wq_cb_sleepable, 0)) 506 return -4; 507 508 if (bpf_wq_start(wq, 0)) 509 return -5; 510 511 return 0; 512 } 513 514 SEC("?struct_ops/hid_device_event") 515 int BPF_PROG(hid_test_multiply_events_wq, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 516 { 517 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */); 518 int hid = hid_ctx->hid->id; 519 int ret; 520 521 if (!data) 522 return 0; /* EPERM check */ 523 524 if (data[0] != 1) 525 return 0; 526 527 ret = test_inject_input_report_callback(&hid); 528 if (ret) 529 return ret; 530 531 data[1] += 5; 532 533 return 0; 534 } 535 536 SEC(".struct_ops.link") 537 struct hid_bpf_ops test_multiply_events_wq = { 538 .hid_device_event = (void *)hid_test_multiply_events_wq, 539 }; 540 541 SEC("?struct_ops/hid_device_event") 542 int BPF_PROG(hid_test_multiply_events, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) 543 { 544 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */); 545 __u8 buf[9]; 546 int ret; 547 548 if (!data) 549 return 0; /* EPERM check */ 550 551 if (data[0] != 1) 552 return 0; 553 554 /* 555 * we have to use an intermediate buffer as hid_bpf_input_report 556 * will memset data to \0 557 */ 558 __builtin_memcpy(buf, data, sizeof(buf)); 559 560 buf[0] = 2; 561 buf[1] += 5; 562 ret = hid_bpf_try_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf)); 563 if (ret < 0) 564 return ret; 565 566 /* 567 * In real world we should reset the original buffer as data might be garbage now, 568 * but it actually now has the content of 'buf' 569 */ 570 data[1] += 5; 571 572 return 9; 573 } 574 575 SEC(".struct_ops.link") 576 struct hid_bpf_ops test_multiply_events = { 577 .hid_device_event = (void *)hid_test_multiply_events, 578 }; 579 580 SEC("?struct_ops/hid_device_event") 581 int BPF_PROG(hid_test_infinite_loop_input_report, struct hid_bpf_ctx *hctx, 582 enum hid_report_type report_type, __u64 source) 583 { 584 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 6 /* size */); 585 __u8 buf[6]; 586 587 if (!data) 588 return 0; /* EPERM check */ 589 590 /* 591 * we have to use an intermediate buffer as hid_bpf_input_report 592 * will memset data to \0 593 */ 594 __builtin_memcpy(buf, data, sizeof(buf)); 595 596 /* always forward the request as-is to the device, hid-bpf should prevent 597 * infinite loops. 598 * the return value is ignored so the event is passing to userspace. 599 */ 600 601 hid_bpf_try_input_report(hctx, report_type, buf, sizeof(buf)); 602 603 /* each time we process the event, we increment by one data[1]: 604 * after each successful call to hid_bpf_try_input_report, buf 605 * has been memcopied into data by the kernel. 606 */ 607 data[1] += 1; 608 609 return 0; 610 } 611 612 SEC(".struct_ops.link") 613 struct hid_bpf_ops test_infinite_loop_input_report = { 614 .hid_device_event = (void *)hid_test_infinite_loop_input_report, 615 }; 616