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 <a...@kernel.org>
Signed-off-by: Benjamin Tissoires <bent...@kernel.org>
---
 tools/testing/selftests/hid/hidraw.c | 127 +++++++++++++++++++++++++++++++++++
 1 file changed, 127 insertions(+)

diff --git a/tools/testing/selftests/hid/hidraw.c 
b/tools/testing/selftests/hid/hidraw.c
index 
6d61d03e2ef05e1900fe5a3938d93421717b2621..d625772f8b7cf71fd94956d3a49d54ff44e2b34d
 100644
--- a/tools/testing/selftests/hid/hidraw.c
+++ b/tools/testing/selftests/hid/hidraw.c
@@ -332,6 +332,133 @@ TEST_F(hidraw, ioctl_gfeature_invalid)
        ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno);
 }
 
+/*
+ * Test ioctl with incorrect nr bits
+ */
+TEST_F(hidraw, ioctl_invalid_nr)
+{
+       char buf[256] = {0};
+       int err;
+       unsigned int bad_cmd;
+
+       /*
+        * craft an ioctl command with wrong _IOC_NR bits
+        */
+       bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x00, sizeof(buf)); /* 0 is 
not valid */
+
+       /* test the ioctl */
+       err = ioctl(self->hidraw_fd, bad_cmd, buf);
+       ASSERT_LT(err, 0) TH_LOG("ioctl read-write with wrong _IOC_NR (0) 
should have failed");
+       ASSERT_EQ(errno, ENOTTY)
+               TH_LOG("expected ENOTTY for wrong read-write _IOC_NR (0), got 
errno %d", errno);
+
+       /*
+        * craft an ioctl command with wrong _IOC_NR bits
+        */
+       bad_cmd = _IOC(_IOC_READ, 'H', 0x00, sizeof(buf)); /* 0 is not valid */
+
+       /* test the ioctl */
+       err = ioctl(self->hidraw_fd, bad_cmd, buf);
+       ASSERT_LT(err, 0) TH_LOG("ioctl read-only with wrong _IOC_NR (0) should 
have failed");
+       ASSERT_EQ(errno, ENOTTY)
+               TH_LOG("expected ENOTTY for wrong read-only _IOC_NR (0), got 
errno %d", errno);
+
+       /* also test with bigger number */
+       bad_cmd = _IOC(_IOC_READ, 'H', 0x42, sizeof(buf)); /* 0x42 is not valid 
as well */
+
+       err = ioctl(self->hidraw_fd, bad_cmd, buf);
+       ASSERT_LT(err, 0) TH_LOG("ioctl read-only with wrong _IOC_NR (0x42) 
should have failed");
+       ASSERT_EQ(errno, ENOTTY)
+               TH_LOG("expected ENOTTY for wrong read-only _IOC_NR (0x42), got 
errno %d", errno);
+
+       /* also test with bigger number: 0x42 is not valid as well */
+       bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x42, sizeof(buf));
+
+       err = ioctl(self->hidraw_fd, bad_cmd, buf);
+       ASSERT_LT(err, 0) TH_LOG("ioctl read-write with wrong _IOC_NR (0x42) 
should have failed");
+       ASSERT_EQ(errno, ENOTTY)
+               TH_LOG("expected ENOTTY for wrong read-write _IOC_NR (0x42), 
got errno %d", errno);
+}
+
+/*
+ * Test ioctl with incorrect type bits
+ */
+TEST_F(hidraw, ioctl_invalid_type)
+{
+       char buf[256] = {0};
+       int err;
+       unsigned int bad_cmd;
+
+       /*
+        * craft an ioctl command with wrong _IOC_TYPE bits
+        */
+       bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'I', 0x01, sizeof(buf)); /* 'I' 
should be 'H' */
+
+       /* test the ioctl */
+       err = ioctl(self->hidraw_fd, bad_cmd, buf);
+       ASSERT_LT(err, 0) TH_LOG("ioctl with wrong _IOC_TYPE (I) should have 
failed");
+       ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_NR, got 
errno %d", errno);
+}
+
+/*
+ * Test HIDIOCGFEATURE ioctl with incorrect _IOC_DIR bits
+ */
+TEST_F(hidraw, ioctl_gfeature_invalid_dir)
+{
+       __u8 buf[10] = {0};
+       int err;
+       unsigned int bad_cmd;
+
+       /* set report ID 1 in first byte */
+       buf[0] = 1;
+
+       /*
+        * craft an ioctl command with wrong _IOC_DIR bits
+        * HIDIOCGFEATURE should have _IOC_WRITE|_IOC_READ, let's use only 
_IOC_WRITE
+        */
+       bad_cmd = _IOC(_IOC_WRITE, 'H', 0x07, sizeof(buf)); /* should be 
_IOC_WRITE|_IOC_READ */
+
+       /* try to get feature report with wrong direction bits */
+       err = ioctl(self->hidraw_fd, bad_cmd, buf);
+       ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE with wrong _IOC_DIR should 
have failed");
+       ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, 
got errno %d", errno);
+
+       /* also test with only _IOC_READ */
+       bad_cmd = _IOC(_IOC_READ, 'H', 0x07, sizeof(buf)); /* should be 
_IOC_WRITE|_IOC_READ */
+
+       err = ioctl(self->hidraw_fd, bad_cmd, buf);
+       ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE with wrong _IOC_DIR should 
have failed");
+       ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, 
got errno %d", errno);
+}
+
+/*
+ * Test read-only ioctl with incorrect _IOC_DIR bits
+ */
+TEST_F(hidraw, ioctl_readonly_invalid_dir)
+{
+       char buf[256] = {0};
+       int err;
+       unsigned int bad_cmd;
+
+       /*
+        * craft an ioctl command with wrong _IOC_DIR bits
+        * HIDIOCGRAWNAME should have _IOC_READ, let's use _IOC_WRITE
+        */
+       bad_cmd = _IOC(_IOC_WRITE, 'H', 0x04, sizeof(buf)); /* should be 
_IOC_READ */
+
+       /* try to get device name with wrong direction bits */
+       err = ioctl(self->hidraw_fd, bad_cmd, buf);
+       ASSERT_LT(err, 0) TH_LOG("HIDIOCGRAWNAME with wrong _IOC_DIR should 
have failed");
+       ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, 
got errno %d", errno);
+
+       /* also test with _IOC_WRITE|_IOC_READ */
+       bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x04, sizeof(buf)); /* should 
be only _IOC_READ */
+
+       err = ioctl(self->hidraw_fd, bad_cmd, buf);
+       ASSERT_LT(err, 0) TH_LOG("HIDIOCGRAWNAME with wrong _IOC_DIR should 
have failed");
+       ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, 
got errno %d", errno);
+}
+
 /*
  * Test HIDIOCSFEATURE ioctl to set feature report
  */

-- 
2.51.0


Reply via email to