Skip to content

Commit 8c62074

Browse files
Benjamin TissoiresJiri Kosina
authored andcommitted
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>
1 parent bb6c861 commit 8c62074

1 file changed

Lines changed: 127 additions & 0 deletions

File tree

tools/testing/selftests/hid/hidraw.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,133 @@ TEST_F(hidraw, ioctl_gfeature_invalid)
332332
ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno);
333333
}
334334

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+
335462
/*
336463
* Test HIDIOCSFEATURE ioctl to set feature report
337464
*/

0 commit comments

Comments
 (0)