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
FIXTURE(hidraw)11 FIXTURE(hidraw) {
12 int dev_id;
13 int uhid_fd;
14 int hidraw_fd;
15 int hid_id;
16 pthread_t tid;
17 };
close_hidraw(FIXTURE_DATA (hidraw)* self)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
FIXTURE_TEARDOWN(hidraw)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
FIXTURE_SETUP(hidraw)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 */
TEST_F(hidraw,test_create_uhid)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 */
TEST_F(hidraw,raw_event)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 */
TEST_F(hidraw,raw_event_revoked)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 */
TEST_F(hidraw,ioctl_revoked)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 */
TEST_F(hidraw,poll_revoked)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 */
TEST_F(hidraw,write_event_revoked)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
main(int argc,char ** argv)234 int main(int argc, char **argv)
235 {
236 return test_harness_run(argc, argv);
237 }
238