// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2022-2024 Red Hat */ #include "hid_common.h" /* for older kernels */ #ifndef HIDIOCREVOKE #define HIDIOCREVOKE _IOW('H', 0x0D, int) /* Revoke device access */ #endif /* HIDIOCREVOKE */ FIXTURE(hidraw) { int dev_id; int uhid_fd; int hidraw_fd; int hid_id; pthread_t tid; }; static void close_hidraw(FIXTURE_DATA(hidraw) * self) { if (self->hidraw_fd) close(self->hidraw_fd); self->hidraw_fd = 0; } FIXTURE_TEARDOWN(hidraw) { void *uhid_err; uhid_destroy(_metadata, self->uhid_fd); close_hidraw(self); pthread_join(self->tid, &uhid_err); } #define TEARDOWN_LOG(fmt, ...) do { \ TH_LOG(fmt, ##__VA_ARGS__); \ hidraw_teardown(_metadata, self, variant); \ } while (0) FIXTURE_SETUP(hidraw) { time_t t; int err; /* initialize random number generator */ srand((unsigned int)time(&t)); self->dev_id = rand() % 1024; self->uhid_fd = setup_uhid(_metadata, self->dev_id); /* locate the uev, self, variant);ent file of the created device */ self->hid_id = get_hid_id(self->dev_id); ASSERT_GT(self->hid_id, 0) TEARDOWN_LOG("Could not locate uhid device id: %d", self->hid_id); err = uhid_start_listener(_metadata, &self->tid, self->uhid_fd); ASSERT_EQ(0, err) TEARDOWN_LOG("could not start udev listener: %d", err); self->hidraw_fd = open_hidraw(self->dev_id); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); } /* * A simple test to see if the fixture is working fine. * If this fails, none of the other tests will pass. */ TEST_F(hidraw, test_create_uhid) { } /* * Inject one event in the uhid device, * check that we get the same data through hidraw */ TEST_F(hidraw, raw_event) { __u8 buf[10] = {0}; int err; /* inject one event */ buf[0] = 1; buf[1] = 42; uhid_send_event(_metadata, self->uhid_fd, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); err = read(self->hidraw_fd, buf, sizeof(buf)); ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); ASSERT_EQ(buf[0], 1); ASSERT_EQ(buf[1], 42); } /* * After initial opening/checks of hidraw, revoke the hidraw * node and check that we can not read any more data. */ TEST_F(hidraw, raw_event_revoked) { __u8 buf[10] = {0}; int err; /* inject one event */ buf[0] = 1; buf[1] = 42; uhid_send_event(_metadata, self->uhid_fd, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); err = read(self->hidraw_fd, buf, sizeof(buf)); ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); ASSERT_EQ(buf[0], 1); ASSERT_EQ(buf[1], 42); /* call the revoke ioctl */ err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); /* inject one other event */ buf[0] = 1; buf[1] = 43; uhid_send_event(_metadata, self->uhid_fd, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); err = read(self->hidraw_fd, buf, sizeof(buf)); ASSERT_EQ(err, -1) TH_LOG("read_hidraw"); ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while reading the hidraw node: %d", errno); } /* * Revoke the hidraw node and check that we can not do any ioctl. */ TEST_F(hidraw, ioctl_revoked) { int err, desc_size = 0; /* call the revoke ioctl */ err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); /* do an ioctl */ err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size); ASSERT_EQ(err, -1) TH_LOG("ioctl_hidraw"); ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while doing an ioctl: %d", errno); } /* * Setup polling of the fd, and check that revoke works properly. */ TEST_F(hidraw, poll_revoked) { struct pollfd pfds[1]; __u8 buf[10] = {0}; int err, ready; /* setup polling */ pfds[0].fd = self->hidraw_fd; pfds[0].events = POLLIN; /* inject one event */ buf[0] = 1; buf[1] = 42; uhid_send_event(_metadata, self->uhid_fd, buf, 6); while (true) { ready = poll(pfds, 1, 5000); ASSERT_EQ(ready, 1) TH_LOG("poll return value"); if (pfds[0].revents & POLLIN) { memset(buf, 0, sizeof(buf)); err = read(self->hidraw_fd, buf, sizeof(buf)); ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); ASSERT_EQ(buf[0], 1); ASSERT_EQ(buf[1], 42); /* call the revoke ioctl */ err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); } else { break; } } ASSERT_TRUE(pfds[0].revents & POLLHUP); } /* * After initial opening/checks of hidraw, revoke the hidraw * node and check that we can not read any more data. */ TEST_F(hidraw, write_event_revoked) { struct timespec time_to_wait; __u8 buf[10] = {0}; int err; /* inject one event from hidraw */ buf[0] = 1; /* report ID */ buf[1] = 2; buf[2] = 42; pthread_mutex_lock(&uhid_output_mtx); memset(output_report, 0, sizeof(output_report)); clock_gettime(CLOCK_REALTIME, &time_to_wait); time_to_wait.tv_sec += 2; err = write(self->hidraw_fd, buf, 3); ASSERT_EQ(err, 3) TH_LOG("unexpected error while writing to hidraw node: %d", err); err = pthread_cond_timedwait(&uhid_output_cond, &uhid_output_mtx, &time_to_wait); ASSERT_OK(err) TH_LOG("error while calling waiting for the condition"); ASSERT_EQ(output_report[0], 1); ASSERT_EQ(output_report[1], 2); ASSERT_EQ(output_report[2], 42); /* call the revoke ioctl */ err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); /* inject one other event */ buf[0] = 1; buf[1] = 43; err = write(self->hidraw_fd, buf, 3); ASSERT_LT(err, 0) TH_LOG("unexpected success while writing to hidraw node: %d", err); ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while writing to hidraw node: %d", errno); pthread_mutex_unlock(&uhid_output_mtx); } int main(int argc, char **argv) { return test_harness_run(argc, argv); }