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 struct uhid_device hid;
13 int hidraw_fd;
14 };
close_hidraw(FIXTURE_DATA (hidraw)* self)15 static void close_hidraw(FIXTURE_DATA(hidraw) * self)
16 {
17 if (self->hidraw_fd)
18 close(self->hidraw_fd);
19 self->hidraw_fd = 0;
20 }
21
FIXTURE_TEARDOWN(hidraw)22 FIXTURE_TEARDOWN(hidraw) {
23 void *uhid_err;
24
25 uhid_destroy(_metadata, &self->hid);
26
27 close_hidraw(self);
28 pthread_join(self->hid.tid, &uhid_err);
29 }
30 #define TEARDOWN_LOG(fmt, ...) do { \
31 TH_LOG(fmt, ##__VA_ARGS__); \
32 hidraw_teardown(_metadata, self, variant); \
33 } while (0)
34
FIXTURE_SETUP(hidraw)35 FIXTURE_SETUP(hidraw)
36 {
37 int err;
38
39 err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a37, rdesc, sizeof(rdesc));
40 ASSERT_OK(err);
41
42 self->hidraw_fd = open_hidraw(&self->hid);
43 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
44 }
45
46 /*
47 * A simple test to see if the fixture is working fine.
48 * If this fails, none of the other tests will pass.
49 */
TEST_F(hidraw,test_create_uhid)50 TEST_F(hidraw, test_create_uhid)
51 {
52 }
53
54 /*
55 * Inject one event in the uhid device,
56 * check that we get the same data through hidraw
57 */
TEST_F(hidraw,raw_event)58 TEST_F(hidraw, raw_event)
59 {
60 __u8 buf[10] = {0};
61 int err;
62
63 /* inject one event */
64 buf[0] = 1;
65 buf[1] = 42;
66 uhid_send_event(_metadata, &self->hid, buf, 6);
67
68 /* read the data from hidraw */
69 memset(buf, 0, sizeof(buf));
70 err = read(self->hidraw_fd, buf, sizeof(buf));
71 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
72 ASSERT_EQ(buf[0], 1);
73 ASSERT_EQ(buf[1], 42);
74 }
75
76 /*
77 * After initial opening/checks of hidraw, revoke the hidraw
78 * node and check that we can not read any more data.
79 */
TEST_F(hidraw,raw_event_revoked)80 TEST_F(hidraw, raw_event_revoked)
81 {
82 __u8 buf[10] = {0};
83 int err;
84
85 /* inject one event */
86 buf[0] = 1;
87 buf[1] = 42;
88 uhid_send_event(_metadata, &self->hid, buf, 6);
89
90 /* read the data from hidraw */
91 memset(buf, 0, sizeof(buf));
92 err = read(self->hidraw_fd, buf, sizeof(buf));
93 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
94 ASSERT_EQ(buf[0], 1);
95 ASSERT_EQ(buf[1], 42);
96
97 /* call the revoke ioctl */
98 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
99 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
100
101 /* inject one other event */
102 buf[0] = 1;
103 buf[1] = 43;
104 uhid_send_event(_metadata, &self->hid, 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, -1) TH_LOG("read_hidraw");
110 ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while reading the hidraw node: %d",
111 errno);
112 }
113
114 /*
115 * Revoke the hidraw node and check that we can not do any ioctl.
116 */
TEST_F(hidraw,ioctl_revoked)117 TEST_F(hidraw, ioctl_revoked)
118 {
119 int err, desc_size = 0;
120
121 /* call the revoke ioctl */
122 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
123 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
124
125 /* do an ioctl */
126 err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size);
127 ASSERT_EQ(err, -1) TH_LOG("ioctl_hidraw");
128 ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while doing an ioctl: %d",
129 errno);
130 }
131
132 /*
133 * Setup polling of the fd, and check that revoke works properly.
134 */
TEST_F(hidraw,poll_revoked)135 TEST_F(hidraw, poll_revoked)
136 {
137 struct pollfd pfds[1];
138 __u8 buf[10] = {0};
139 int err, ready;
140
141 /* setup polling */
142 pfds[0].fd = self->hidraw_fd;
143 pfds[0].events = POLLIN;
144
145 /* inject one event */
146 buf[0] = 1;
147 buf[1] = 42;
148 uhid_send_event(_metadata, &self->hid, buf, 6);
149
150 while (true) {
151 ready = poll(pfds, 1, 5000);
152 ASSERT_EQ(ready, 1) TH_LOG("poll return value");
153
154 if (pfds[0].revents & POLLIN) {
155 memset(buf, 0, sizeof(buf));
156 err = read(self->hidraw_fd, buf, sizeof(buf));
157 ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
158 ASSERT_EQ(buf[0], 1);
159 ASSERT_EQ(buf[1], 42);
160
161 /* call the revoke ioctl */
162 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
163 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
164 } else {
165 break;
166 }
167 }
168
169 ASSERT_TRUE(pfds[0].revents & POLLHUP);
170 }
171
172 /*
173 * After initial opening/checks of hidraw, revoke the hidraw
174 * node and check that we can not read any more data.
175 */
TEST_F(hidraw,write_event_revoked)176 TEST_F(hidraw, write_event_revoked)
177 {
178 struct timespec time_to_wait;
179 __u8 buf[10] = {0};
180 int err;
181
182 /* inject one event from hidraw */
183 buf[0] = 1; /* report ID */
184 buf[1] = 2;
185 buf[2] = 42;
186
187 pthread_mutex_lock(&uhid_output_mtx);
188
189 memset(output_report, 0, sizeof(output_report));
190 clock_gettime(CLOCK_REALTIME, &time_to_wait);
191 time_to_wait.tv_sec += 2;
192
193 err = write(self->hidraw_fd, buf, 3);
194 ASSERT_EQ(err, 3) TH_LOG("unexpected error while writing to hidraw node: %d", err);
195
196 err = pthread_cond_timedwait(&uhid_output_cond, &uhid_output_mtx, &time_to_wait);
197 ASSERT_OK(err) TH_LOG("error while calling waiting for the condition");
198
199 ASSERT_EQ(output_report[0], 1);
200 ASSERT_EQ(output_report[1], 2);
201 ASSERT_EQ(output_report[2], 42);
202
203 /* call the revoke ioctl */
204 err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
205 ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
206
207 /* inject one other event */
208 buf[0] = 1;
209 buf[1] = 43;
210 err = write(self->hidraw_fd, buf, 3);
211 ASSERT_LT(err, 0) TH_LOG("unexpected success while writing to hidraw node: %d", err);
212 ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while writing to hidraw node: %d",
213 errno);
214
215 pthread_mutex_unlock(&uhid_output_mtx);
216 }
217
main(int argc,char ** argv)218 int main(int argc, char **argv)
219 {
220 return test_harness_run(argc, argv);
221 }
222