1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2022-2024 Red Hat */ 3 4 #include "hid_common.h" 5 6 /* for older kernels */ 7 #ifndef HIDIOCREVOKE 8 #define HIDIOCREVOKE _IOW('H', 0x0D, int) /* Revoke device access */ 9 #endif /* HIDIOCREVOKE */ 10 11 FIXTURE(hidraw) { 12 int dev_id; 13 int uhid_fd; 14 int hidraw_fd; 15 int hid_id; 16 pthread_t tid; 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->uhid_fd); 29 30 close_hidraw(self); 31 pthread_join(self->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 time_t t; 41 int err; 42 43 /* initialize random number generator */ 44 srand((unsigned int)time(&t)); 45 46 self->dev_id = rand() % 1024; 47 48 self->uhid_fd = setup_uhid(_metadata, self->dev_id); 49 50 /* locate the uev, self, variant);ent file of the created device */ 51 self->hid_id = get_hid_id(self->dev_id); 52 ASSERT_GT(self->hid_id, 0) 53 TEARDOWN_LOG("Could not locate uhid device id: %d", self->hid_id); 54 55 err = uhid_start_listener(_metadata, &self->tid, self->uhid_fd); 56 ASSERT_EQ(0, err) TEARDOWN_LOG("could not start udev listener: %d", err); 57 58 self->hidraw_fd = open_hidraw(self->dev_id); 59 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); 60 } 61 62 /* 63 * A simple test to see if the fixture is working fine. 64 * If this fails, none of the other tests will pass. 65 */ 66 TEST_F(hidraw, test_create_uhid) 67 { 68 } 69 70 /* 71 * Inject one event in the uhid device, 72 * check that we get the same data through hidraw 73 */ 74 TEST_F(hidraw, raw_event) 75 { 76 __u8 buf[10] = {0}; 77 int err; 78 79 /* inject one event */ 80 buf[0] = 1; 81 buf[1] = 42; 82 uhid_send_event(_metadata, self->uhid_fd, buf, 6); 83 84 /* read the data from hidraw */ 85 memset(buf, 0, sizeof(buf)); 86 err = read(self->hidraw_fd, buf, sizeof(buf)); 87 ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 88 ASSERT_EQ(buf[0], 1); 89 ASSERT_EQ(buf[1], 42); 90 } 91 92 /* 93 * After initial opening/checks of hidraw, revoke the hidraw 94 * node and check that we can not read any more data. 95 */ 96 TEST_F(hidraw, raw_event_revoked) 97 { 98 __u8 buf[10] = {0}; 99 int err; 100 101 /* inject one event */ 102 buf[0] = 1; 103 buf[1] = 42; 104 uhid_send_event(_metadata, self->uhid_fd, buf, 6); 105 106 /* read the data from hidraw */ 107 memset(buf, 0, sizeof(buf)); 108 err = read(self->hidraw_fd, buf, sizeof(buf)); 109 ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 110 ASSERT_EQ(buf[0], 1); 111 ASSERT_EQ(buf[1], 42); 112 113 /* call the revoke ioctl */ 114 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); 115 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); 116 117 /* inject one other event */ 118 buf[0] = 1; 119 buf[1] = 43; 120 uhid_send_event(_metadata, self->uhid_fd, buf, 6); 121 122 /* read the data from hidraw */ 123 memset(buf, 0, sizeof(buf)); 124 err = read(self->hidraw_fd, buf, sizeof(buf)); 125 ASSERT_EQ(err, -1) TH_LOG("read_hidraw"); 126 ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while reading the hidraw node: %d", 127 errno); 128 } 129 130 /* 131 * Revoke the hidraw node and check that we can not do any ioctl. 132 */ 133 TEST_F(hidraw, ioctl_revoked) 134 { 135 int err, desc_size = 0; 136 137 /* call the revoke ioctl */ 138 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); 139 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); 140 141 /* do an ioctl */ 142 err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size); 143 ASSERT_EQ(err, -1) TH_LOG("ioctl_hidraw"); 144 ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while doing an ioctl: %d", 145 errno); 146 } 147 148 /* 149 * Setup polling of the fd, and check that revoke works properly. 150 */ 151 TEST_F(hidraw, poll_revoked) 152 { 153 struct pollfd pfds[1]; 154 __u8 buf[10] = {0}; 155 int err, ready; 156 157 /* setup polling */ 158 pfds[0].fd = self->hidraw_fd; 159 pfds[0].events = POLLIN; 160 161 /* inject one event */ 162 buf[0] = 1; 163 buf[1] = 42; 164 uhid_send_event(_metadata, self->uhid_fd, buf, 6); 165 166 while (true) { 167 ready = poll(pfds, 1, 5000); 168 ASSERT_EQ(ready, 1) TH_LOG("poll return value"); 169 170 if (pfds[0].revents & POLLIN) { 171 memset(buf, 0, sizeof(buf)); 172 err = read(self->hidraw_fd, buf, sizeof(buf)); 173 ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 174 ASSERT_EQ(buf[0], 1); 175 ASSERT_EQ(buf[1], 42); 176 177 /* call the revoke ioctl */ 178 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); 179 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); 180 } else { 181 break; 182 } 183 } 184 185 ASSERT_TRUE(pfds[0].revents & POLLHUP); 186 } 187 188 /* 189 * After initial opening/checks of hidraw, revoke the hidraw 190 * node and check that we can not read any more data. 191 */ 192 TEST_F(hidraw, write_event_revoked) 193 { 194 struct timespec time_to_wait; 195 __u8 buf[10] = {0}; 196 int err; 197 198 /* inject one event from hidraw */ 199 buf[0] = 1; /* report ID */ 200 buf[1] = 2; 201 buf[2] = 42; 202 203 pthread_mutex_lock(&uhid_output_mtx); 204 205 memset(output_report, 0, sizeof(output_report)); 206 clock_gettime(CLOCK_REALTIME, &time_to_wait); 207 time_to_wait.tv_sec += 2; 208 209 err = write(self->hidraw_fd, buf, 3); 210 ASSERT_EQ(err, 3) TH_LOG("unexpected error while writing to hidraw node: %d", err); 211 212 err = pthread_cond_timedwait(&uhid_output_cond, &uhid_output_mtx, &time_to_wait); 213 ASSERT_OK(err) TH_LOG("error while calling waiting for the condition"); 214 215 ASSERT_EQ(output_report[0], 1); 216 ASSERT_EQ(output_report[1], 2); 217 ASSERT_EQ(output_report[2], 42); 218 219 /* call the revoke ioctl */ 220 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL); 221 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd"); 222 223 /* inject one other event */ 224 buf[0] = 1; 225 buf[1] = 43; 226 err = write(self->hidraw_fd, buf, 3); 227 ASSERT_LT(err, 0) TH_LOG("unexpected success while writing to hidraw node: %d", err); 228 ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while writing to hidraw node: %d", 229 errno); 230 231 pthread_mutex_unlock(&uhid_output_mtx); 232 } 233 234 int main(int argc, char **argv) 235 { 236 return test_harness_run(argc, argv); 237 } 238