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: hidraw: forge wrong ioctls and tests them

We also need coverage for when the malicious user is not using the
proper ioctls definitions and tries to work around the driver.

Most of the scaffholding has been generated by claude-4-sonnet and then
carefully reviewed.

Suggested-by: Arnd Bergmann <arnd@kernel.org>
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Jiri Kosina <jkosina@suse.com>

authored by

Benjamin Tissoires and committed by
Jiri Kosina
8c62074f bb6c861a

+127
+127
tools/testing/selftests/hid/hidraw.c
··· 333 333 } 334 334 335 335 /* 336 + * Test ioctl with incorrect nr bits 337 + */ 338 + TEST_F(hidraw, ioctl_invalid_nr) 339 + { 340 + char buf[256] = {0}; 341 + int err; 342 + unsigned int bad_cmd; 343 + 344 + /* 345 + * craft an ioctl command with wrong _IOC_NR bits 346 + */ 347 + bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x00, sizeof(buf)); /* 0 is not valid */ 348 + 349 + /* test the ioctl */ 350 + err = ioctl(self->hidraw_fd, bad_cmd, buf); 351 + ASSERT_LT(err, 0) TH_LOG("ioctl read-write with wrong _IOC_NR (0) should have failed"); 352 + ASSERT_EQ(errno, ENOTTY) 353 + TH_LOG("expected ENOTTY for wrong read-write _IOC_NR (0), got errno %d", errno); 354 + 355 + /* 356 + * craft an ioctl command with wrong _IOC_NR bits 357 + */ 358 + bad_cmd = _IOC(_IOC_READ, 'H', 0x00, sizeof(buf)); /* 0 is not valid */ 359 + 360 + /* test the ioctl */ 361 + err = ioctl(self->hidraw_fd, bad_cmd, buf); 362 + ASSERT_LT(err, 0) TH_LOG("ioctl read-only with wrong _IOC_NR (0) should have failed"); 363 + ASSERT_EQ(errno, ENOTTY) 364 + TH_LOG("expected ENOTTY for wrong read-only _IOC_NR (0), got errno %d", errno); 365 + 366 + /* also test with bigger number */ 367 + bad_cmd = _IOC(_IOC_READ, 'H', 0x42, sizeof(buf)); /* 0x42 is not valid as well */ 368 + 369 + err = ioctl(self->hidraw_fd, bad_cmd, buf); 370 + ASSERT_LT(err, 0) TH_LOG("ioctl read-only with wrong _IOC_NR (0x42) should have failed"); 371 + ASSERT_EQ(errno, ENOTTY) 372 + TH_LOG("expected ENOTTY for wrong read-only _IOC_NR (0x42), got errno %d", errno); 373 + 374 + /* also test with bigger number: 0x42 is not valid as well */ 375 + bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x42, sizeof(buf)); 376 + 377 + err = ioctl(self->hidraw_fd, bad_cmd, buf); 378 + ASSERT_LT(err, 0) TH_LOG("ioctl read-write with wrong _IOC_NR (0x42) should have failed"); 379 + ASSERT_EQ(errno, ENOTTY) 380 + TH_LOG("expected ENOTTY for wrong read-write _IOC_NR (0x42), got errno %d", errno); 381 + } 382 + 383 + /* 384 + * Test ioctl with incorrect type bits 385 + */ 386 + TEST_F(hidraw, ioctl_invalid_type) 387 + { 388 + char buf[256] = {0}; 389 + int err; 390 + unsigned int bad_cmd; 391 + 392 + /* 393 + * craft an ioctl command with wrong _IOC_TYPE bits 394 + */ 395 + bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'I', 0x01, sizeof(buf)); /* 'I' should be 'H' */ 396 + 397 + /* test the ioctl */ 398 + err = ioctl(self->hidraw_fd, bad_cmd, buf); 399 + ASSERT_LT(err, 0) TH_LOG("ioctl with wrong _IOC_TYPE (I) should have failed"); 400 + ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_NR, got errno %d", errno); 401 + } 402 + 403 + /* 404 + * Test HIDIOCGFEATURE ioctl with incorrect _IOC_DIR bits 405 + */ 406 + TEST_F(hidraw, ioctl_gfeature_invalid_dir) 407 + { 408 + __u8 buf[10] = {0}; 409 + int err; 410 + unsigned int bad_cmd; 411 + 412 + /* set report ID 1 in first byte */ 413 + buf[0] = 1; 414 + 415 + /* 416 + * craft an ioctl command with wrong _IOC_DIR bits 417 + * HIDIOCGFEATURE should have _IOC_WRITE|_IOC_READ, let's use only _IOC_WRITE 418 + */ 419 + bad_cmd = _IOC(_IOC_WRITE, 'H', 0x07, sizeof(buf)); /* should be _IOC_WRITE|_IOC_READ */ 420 + 421 + /* try to get feature report with wrong direction bits */ 422 + err = ioctl(self->hidraw_fd, bad_cmd, buf); 423 + ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE with wrong _IOC_DIR should have failed"); 424 + ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno); 425 + 426 + /* also test with only _IOC_READ */ 427 + bad_cmd = _IOC(_IOC_READ, 'H', 0x07, sizeof(buf)); /* should be _IOC_WRITE|_IOC_READ */ 428 + 429 + err = ioctl(self->hidraw_fd, bad_cmd, buf); 430 + ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE with wrong _IOC_DIR should have failed"); 431 + ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno); 432 + } 433 + 434 + /* 435 + * Test read-only ioctl with incorrect _IOC_DIR bits 436 + */ 437 + TEST_F(hidraw, ioctl_readonly_invalid_dir) 438 + { 439 + char buf[256] = {0}; 440 + int err; 441 + unsigned int bad_cmd; 442 + 443 + /* 444 + * craft an ioctl command with wrong _IOC_DIR bits 445 + * HIDIOCGRAWNAME should have _IOC_READ, let's use _IOC_WRITE 446 + */ 447 + bad_cmd = _IOC(_IOC_WRITE, 'H', 0x04, sizeof(buf)); /* should be _IOC_READ */ 448 + 449 + /* try to get device name with wrong direction bits */ 450 + err = ioctl(self->hidraw_fd, bad_cmd, buf); 451 + ASSERT_LT(err, 0) TH_LOG("HIDIOCGRAWNAME with wrong _IOC_DIR should have failed"); 452 + ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno); 453 + 454 + /* also test with _IOC_WRITE|_IOC_READ */ 455 + bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x04, sizeof(buf)); /* should be only _IOC_READ */ 456 + 457 + err = ioctl(self->hidraw_fd, bad_cmd, buf); 458 + ASSERT_LT(err, 0) TH_LOG("HIDIOCGRAWNAME with wrong _IOC_DIR should have failed"); 459 + ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno); 460 + } 461 + 462 + /* 336 463 * Test HIDIOCSFEATURE ioctl to set feature report 337 464 */ 338 465 TEST_F(hidraw, ioctl_sfeature)