1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2022-2024 Red Hat */ 3 4 #include "hid_common.h" 5 #include <linux/input.h> 6 #include <string.h> 7 #include <sys/ioctl.h> 8 9 /* for older kernels */ 10 #ifndef HIDIOCREVOKE 11 #define HIDIOCREVOKE _IOW('H', 0x0D, int) /* Revoke device access */ 12 #endif /* HIDIOCREVOKE */ 13 14 FIXTURE(hidraw) { 15 struct uhid_device hid; 16 int hidraw_fd; 17 }; 18 static void close_hidraw(FIXTURE_DATA(hidraw) * self) 19 { 20 if (self->hidraw_fd) 21 close(self->hidraw_fd); 22 self->hidraw_fd = 0; 23 } 24 25 FIXTURE_TEARDOWN(hidraw) { 26 void *uhid_err; 27 28 uhid_destroy(_metadata, &self->hid); 29 30 close_hidraw(self); 31 pthread_join(self->hid.tid, &uhid_err); 32 } 33 #define TEARDOWN_LOG(fmt, ...) do { \ 34 TH_LOG(fmt, ##__VA_ARGS__); \ 35 hidraw_teardown(_metadata, self, variant); \ 36 } while (0) 37 38 FIXTURE_SETUP(hidraw) 39 { 40 int err; 41 42 err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a37, rdesc, sizeof(rdesc)); 43 ASSERT_OK(err); 44 45 self->hidraw_fd = open_hidraw(&self->hid); 46 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); 47 } 48 49 /* 50 * A simple test to see if the fixture is working fine. 51 * If this fails, none of the other tests will pass. 52 */ 53 TEST_F(hidraw, test_create_uhid) 54 { 55 } 56 57 /* 58 * Inject one event in the uhid device, 59 * check that we get the same data through hidraw 60 */ 61 TEST_F(hidraw, raw_event) 62 { 63 __u8 buf[10] = {0}; 64 int err; 65 66 /* inject one event */ 67 buf[0] = 1; 68 buf[1] = 42; 69 uhid_send_event(_metadata, &self->hid, buf, 6); 70 71 /* read the data from hidraw */ 72 memset(buf, 0, sizeof(buf)); 73 err = read(self->hidraw_fd, buf, sizeof(buf)); 74 ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 75 ASSERT_EQ(buf[0], 1); 76 ASSERT_EQ(buf[1], 42); 77 } 78 79 /* 80 * After initial opening/checks of hidraw, revoke the hidraw 81 * node and check that we can not read any more data. 82 */ 83 TEST_F(hidraw, raw_event_revoked) 84 { 85 __u8 buf[10] = {0}; 86 int err; 87 88 /* inject one event */ 89 buf[0] = 1; 90 buf[1] = 42; 91 uhid_send_event(_metadata, &self->hid, buf, 6); 92 93 /* read the data from hidraw */ 94 memset(buf, 0, sizeof(buf)); 95 err = read(self->hidraw_fd, buf, sizeof(buf)); 96 ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 97 ASSERT_EQ(buf[0], 1); 98 ASSERT_EQ(buf[1], 42); 99 100 /* call the revoke ioctl */ 101 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); 102 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); 103 104 /* inject one other event */ 105 buf[0] = 1; 106 buf[1] = 43; 107 uhid_send_event(_metadata, &self->hid, buf, 6); 108 109 /* read the data from hidraw */ 110 memset(buf, 0, sizeof(buf)); 111 err = read(self->hidraw_fd, buf, sizeof(buf)); 112 ASSERT_EQ(err, -1) TH_LOG("read_hidraw"); 113 ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while reading the hidraw node: %d", 114 errno); 115 } 116 117 /* 118 * Revoke the hidraw node and check that we can not do any ioctl. 119 */ 120 TEST_F(hidraw, ioctl_revoked) 121 { 122 int err, desc_size = 0; 123 124 /* call the revoke ioctl */ 125 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); 126 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); 127 128 /* do an ioctl */ 129 err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size); 130 ASSERT_EQ(err, -1) TH_LOG("ioctl_hidraw"); 131 ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while doing an ioctl: %d", 132 errno); 133 } 134 135 /* 136 * Setup polling of the fd, and check that revoke works properly. 137 */ 138 TEST_F(hidraw, poll_revoked) 139 { 140 struct pollfd pfds[1]; 141 __u8 buf[10] = {0}; 142 int err, ready; 143 144 /* setup polling */ 145 pfds[0].fd = self->hidraw_fd; 146 pfds[0].events = POLLIN; 147 148 /* inject one event */ 149 buf[0] = 1; 150 buf[1] = 42; 151 uhid_send_event(_metadata, &self->hid, buf, 6); 152 153 while (true) { 154 ready = poll(pfds, 1, 5000); 155 ASSERT_EQ(ready, 1) TH_LOG("poll return value"); 156 157 if (pfds[0].revents & POLLIN) { 158 memset(buf, 0, sizeof(buf)); 159 err = read(self->hidraw_fd, buf, sizeof(buf)); 160 ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 161 ASSERT_EQ(buf[0], 1); 162 ASSERT_EQ(buf[1], 42); 163 164 /* call the revoke ioctl */ 165 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); 166 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); 167 } else { 168 break; 169 } 170 } 171 172 ASSERT_TRUE(pfds[0].revents & POLLHUP); 173 } 174 175 /* 176 * After initial opening/checks of hidraw, revoke the hidraw 177 * node and check that we can not read any more data. 178 */ 179 TEST_F(hidraw, write_event_revoked) 180 { 181 struct timespec time_to_wait; 182 __u8 buf[10] = {0}; 183 int err; 184 185 /* inject one event from hidraw */ 186 buf[0] = 1; /* report ID */ 187 buf[1] = 2; 188 buf[2] = 42; 189 190 pthread_mutex_lock(&uhid_output_mtx); 191 192 memset(output_report, 0, sizeof(output_report)); 193 clock_gettime(CLOCK_REALTIME, &time_to_wait); 194 time_to_wait.tv_sec += 2; 195 196 err = write(self->hidraw_fd, buf, 3); 197 ASSERT_EQ(err, 3) TH_LOG("unexpected error while writing to hidraw node: %d", err); 198 199 err = pthread_cond_timedwait(&uhid_output_cond, &uhid_output_mtx, &time_to_wait); 200 ASSERT_OK(err) TH_LOG("error while calling waiting for the condition"); 201 202 ASSERT_EQ(output_report[0], 1); 203 ASSERT_EQ(output_report[1], 2); 204 ASSERT_EQ(output_report[2], 42); 205 206 /* call the revoke ioctl */ 207 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); 208 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); 209 210 /* inject one other event */ 211 buf[0] = 1; 212 buf[1] = 43; 213 err = write(self->hidraw_fd, buf, 3); 214 ASSERT_LT(err, 0) TH_LOG("unexpected success while writing to hidraw node: %d", err); 215 ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while writing to hidraw node: %d", 216 errno); 217 218 pthread_mutex_unlock(&uhid_output_mtx); 219 } 220 221 /* 222 * Test HIDIOCGRDESCSIZE ioctl to get report descriptor size 223 */ 224 TEST_F(hidraw, ioctl_rdescsize) 225 { 226 int desc_size = 0; 227 int err; 228 229 /* call HIDIOCGRDESCSIZE ioctl */ 230 err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size); 231 ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESCSIZE ioctl failed"); 232 233 /* verify the size matches our test report descriptor */ 234 ASSERT_EQ(desc_size, sizeof(rdesc)) 235 TH_LOG("expected size %zu, got %d", sizeof(rdesc), desc_size); 236 } 237 238 /* 239 * Test HIDIOCGRDESC ioctl to get report descriptor data 240 */ 241 TEST_F(hidraw, ioctl_rdesc) 242 { 243 struct hidraw_report_descriptor desc; 244 int err; 245 246 /* get the full report descriptor */ 247 desc.size = sizeof(rdesc); 248 err = ioctl(self->hidraw_fd, HIDIOCGRDESC, &desc); 249 ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESC ioctl failed"); 250 251 /* verify the descriptor data matches our test descriptor */ 252 ASSERT_EQ(memcmp(desc.value, rdesc, sizeof(rdesc)), 0) 253 TH_LOG("report descriptor data mismatch"); 254 } 255 256 /* 257 * Test HIDIOCGRDESC ioctl with smaller buffer size 258 */ 259 TEST_F(hidraw, ioctl_rdesc_small_buffer) 260 { 261 struct hidraw_report_descriptor desc; 262 int err; 263 size_t small_size = sizeof(rdesc) / 2; /* request half the descriptor size */ 264 265 /* get partial report descriptor */ 266 desc.size = small_size; 267 err = ioctl(self->hidraw_fd, HIDIOCGRDESC, &desc); 268 ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESC ioctl failed with small buffer"); 269 270 /* verify we got the first part of the descriptor */ 271 ASSERT_EQ(memcmp(desc.value, rdesc, small_size), 0) 272 TH_LOG("partial report descriptor data mismatch"); 273 } 274 275 /* 276 * Test HIDIOCGRAWINFO ioctl to get device information 277 */ 278 TEST_F(hidraw, ioctl_rawinfo) 279 { 280 struct hidraw_devinfo devinfo; 281 int err; 282 283 /* get device info */ 284 err = ioctl(self->hidraw_fd, HIDIOCGRAWINFO, &devinfo); 285 ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRAWINFO ioctl failed"); 286 287 /* verify device info matches our test setup */ 288 ASSERT_EQ(devinfo.bustype, BUS_USB) 289 TH_LOG("expected bustype 0x03, got 0x%x", devinfo.bustype); 290 ASSERT_EQ(devinfo.vendor, 0x0001) 291 TH_LOG("expected vendor 0x0001, got 0x%x", devinfo.vendor); 292 ASSERT_EQ(devinfo.product, 0x0a37) 293 TH_LOG("expected product 0x0a37, got 0x%x", devinfo.product); 294 } 295 296 /* 297 * Test HIDIOCGFEATURE ioctl to get feature report 298 */ 299 TEST_F(hidraw, ioctl_gfeature) 300 { 301 __u8 buf[10] = {0}; 302 int err; 303 304 /* set report ID 1 in first byte */ 305 buf[0] = 1; 306 307 /* get feature report */ 308 err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); 309 ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGFEATURE ioctl failed, got %d", err); 310 311 /* verify we got the expected feature data */ 312 ASSERT_EQ(buf[0], feature_data[0]) 313 TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]); 314 ASSERT_EQ(buf[1], feature_data[1]) 315 TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]); 316 } 317 318 /* 319 * Test HIDIOCGFEATURE ioctl with invalid report ID 320 */ 321 TEST_F(hidraw, ioctl_gfeature_invalid) 322 { 323 __u8 buf[10] = {0}; 324 int err; 325 326 /* set invalid report ID (not 1) */ 327 buf[0] = 2; 328 329 /* try to get feature report */ 330 err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); 331 ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE should have failed with invalid report ID"); 332 ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno); 333 } 334 335 /* 336 * Test HIDIOCSFEATURE ioctl to set feature report 337 */ 338 TEST_F(hidraw, ioctl_sfeature) 339 { 340 __u8 buf[10] = {0}; 341 int err; 342 343 /* prepare feature report data */ 344 buf[0] = 1; /* report ID */ 345 buf[1] = 0x42; 346 buf[2] = 0x24; 347 348 /* set feature report */ 349 err = ioctl(self->hidraw_fd, HIDIOCSFEATURE(3), buf); 350 ASSERT_EQ(err, 3) TH_LOG("HIDIOCSFEATURE ioctl failed, got %d", err); 351 352 /* 353 * Note: The uhid mock doesn't validate the set report data, 354 * so we just verify the ioctl succeeds 355 */ 356 } 357 358 /* 359 * Test HIDIOCGINPUT ioctl to get input report 360 */ 361 TEST_F(hidraw, ioctl_ginput) 362 { 363 __u8 buf[10] = {0}; 364 int err; 365 366 /* set report ID 1 in first byte */ 367 buf[0] = 1; 368 369 /* get input report */ 370 err = ioctl(self->hidraw_fd, HIDIOCGINPUT(sizeof(buf)), buf); 371 ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGINPUT ioctl failed, got %d", err); 372 373 /* verify we got the expected input data */ 374 ASSERT_EQ(buf[0], feature_data[0]) 375 TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]); 376 ASSERT_EQ(buf[1], feature_data[1]) 377 TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]); 378 } 379 380 /* 381 * Test HIDIOCGINPUT ioctl with invalid report ID 382 */ 383 TEST_F(hidraw, ioctl_ginput_invalid) 384 { 385 __u8 buf[10] = {0}; 386 int err; 387 388 /* set invalid report ID (not 1) */ 389 buf[0] = 2; 390 391 /* try to get input report */ 392 err = ioctl(self->hidraw_fd, HIDIOCGINPUT(sizeof(buf)), buf); 393 ASSERT_LT(err, 0) TH_LOG("HIDIOCGINPUT should have failed with invalid report ID"); 394 ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno); 395 } 396 397 /* 398 * Test HIDIOCSINPUT ioctl to set input report 399 */ 400 TEST_F(hidraw, ioctl_sinput) 401 { 402 __u8 buf[10] = {0}; 403 int err; 404 405 /* prepare input report data */ 406 buf[0] = 1; /* report ID */ 407 buf[1] = 0x55; 408 buf[2] = 0xAA; 409 410 /* set input report */ 411 err = ioctl(self->hidraw_fd, HIDIOCSINPUT(3), buf); 412 ASSERT_EQ(err, 3) TH_LOG("HIDIOCSINPUT ioctl failed, got %d", err); 413 414 /* 415 * Note: The uhid mock doesn't validate the set report data, 416 * so we just verify the ioctl succeeds 417 */ 418 } 419 420 /* 421 * Test HIDIOCGOUTPUT ioctl to get output report 422 */ 423 TEST_F(hidraw, ioctl_goutput) 424 { 425 __u8 buf[10] = {0}; 426 int err; 427 428 /* set report ID 1 in first byte */ 429 buf[0] = 1; 430 431 /* get output report */ 432 err = ioctl(self->hidraw_fd, HIDIOCGOUTPUT(sizeof(buf)), buf); 433 ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGOUTPUT ioctl failed, got %d", err); 434 435 /* verify we got the expected output data */ 436 ASSERT_EQ(buf[0], feature_data[0]) 437 TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]); 438 ASSERT_EQ(buf[1], feature_data[1]) 439 TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]); 440 } 441 442 /* 443 * Test HIDIOCGOUTPUT ioctl with invalid report ID 444 */ 445 TEST_F(hidraw, ioctl_goutput_invalid) 446 { 447 __u8 buf[10] = {0}; 448 int err; 449 450 /* set invalid report ID (not 1) */ 451 buf[0] = 2; 452 453 /* try to get output report */ 454 err = ioctl(self->hidraw_fd, HIDIOCGOUTPUT(sizeof(buf)), buf); 455 ASSERT_LT(err, 0) TH_LOG("HIDIOCGOUTPUT should have failed with invalid report ID"); 456 ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno); 457 } 458 459 /* 460 * Test HIDIOCSOUTPUT ioctl to set output report 461 */ 462 TEST_F(hidraw, ioctl_soutput) 463 { 464 __u8 buf[10] = {0}; 465 int err; 466 467 /* prepare output report data */ 468 buf[0] = 1; /* report ID */ 469 buf[1] = 0x33; 470 buf[2] = 0xCC; 471 472 /* set output report */ 473 err = ioctl(self->hidraw_fd, HIDIOCSOUTPUT(3), buf); 474 ASSERT_EQ(err, 3) TH_LOG("HIDIOCSOUTPUT ioctl failed, got %d", err); 475 476 /* 477 * Note: The uhid mock doesn't validate the set report data, 478 * so we just verify the ioctl succeeds 479 */ 480 } 481 482 /* 483 * Test HIDIOCGRAWNAME ioctl to get device name string 484 */ 485 TEST_F(hidraw, ioctl_rawname) 486 { 487 char name[256] = {0}; 488 char expected_name[64]; 489 int err; 490 491 /* get device name */ 492 err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(name)), name); 493 ASSERT_GT(err, 0) TH_LOG("HIDIOCGRAWNAME ioctl failed, got %d", err); 494 495 /* construct expected name based on device id */ 496 snprintf(expected_name, sizeof(expected_name), "test-uhid-device-%d", self->hid.dev_id); 497 498 /* verify the name matches expected pattern */ 499 ASSERT_EQ(strcmp(name, expected_name), 0) 500 TH_LOG("expected name '%s', got '%s'", expected_name, name); 501 } 502 503 /* 504 * Test HIDIOCGRAWPHYS ioctl to get device physical address string 505 */ 506 TEST_F(hidraw, ioctl_rawphys) 507 { 508 char phys[256] = {0}; 509 char expected_phys[64]; 510 int err; 511 512 /* get device physical address */ 513 err = ioctl(self->hidraw_fd, HIDIOCGRAWPHYS(sizeof(phys)), phys); 514 ASSERT_GT(err, 0) TH_LOG("HIDIOCGRAWPHYS ioctl failed, got %d", err); 515 516 /* construct expected phys based on device id */ 517 snprintf(expected_phys, sizeof(expected_phys), "%d", self->hid.dev_id); 518 519 /* verify the phys matches expected value */ 520 ASSERT_EQ(strcmp(phys, expected_phys), 0) 521 TH_LOG("expected phys '%s', got '%s'", expected_phys, phys); 522 } 523 524 /* 525 * Test HIDIOCGRAWUNIQ ioctl to get device unique identifier string 526 */ 527 TEST_F(hidraw, ioctl_rawuniq) 528 { 529 char uniq[256] = {0}; 530 int err; 531 532 /* get device unique identifier */ 533 err = ioctl(self->hidraw_fd, HIDIOCGRAWUNIQ(sizeof(uniq)), uniq); 534 ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWUNIQ ioctl failed, got %d", err); 535 536 /* uniq is typically empty in our test setup */ 537 ASSERT_EQ(strlen(uniq), 0) TH_LOG("expected empty uniq, got '%s'", uniq); 538 } 539 540 /* 541 * Test device string ioctls with small buffer sizes 542 */ 543 TEST_F(hidraw, ioctl_strings_small_buffer) 544 { 545 char small_buf[8] = {0}; 546 char expected_name[64]; 547 int err; 548 549 /* test HIDIOCGRAWNAME with small buffer */ 550 err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(small_buf)), small_buf); 551 ASSERT_EQ(err, sizeof(small_buf)) 552 TH_LOG("HIDIOCGRAWNAME with small buffer failed, got %d", err); 553 554 /* construct expected truncated name */ 555 snprintf(expected_name, sizeof(expected_name), "test-uhid-device-%d", self->hid.dev_id); 556 557 /* verify we got truncated name (first 8 chars, no null terminator guaranteed) */ 558 ASSERT_EQ(strncmp(small_buf, expected_name, sizeof(small_buf)), 0) 559 TH_LOG("expected truncated name to match first %zu chars", sizeof(small_buf)); 560 561 /* Note: hidraw driver doesn't guarantee null termination when buffer is too small */ 562 } 563 564 int main(int argc, char **argv) 565 { 566 return test_harness_run(argc, argv); 567 } 568