238 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (c) 2022-2024 Red Hat */3 4#include "hid_common.h"5 6/* for older kernels */7#ifndef HIDIOCREVOKE8#define HIDIOCREVOKE _IOW('H', 0x0D, int) /* Revoke device access */9#endif /* HIDIOCREVOKE */10 11FIXTURE(hidraw) {12 int dev_id;13 int uhid_fd;14 int hidraw_fd;15 int hid_id;16 pthread_t tid;17};18static 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 25FIXTURE_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 38FIXTURE_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 */66TEST_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 hidraw73 */74TEST_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 hidraw94 * node and check that we can not read any more data.95 */96TEST_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 */133TEST_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 */151TEST_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 hidraw190 * node and check that we can not read any more data.191 */192TEST_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 234int main(int argc, char **argv)235{236 return test_harness_run(argc, argv);237}238